nrc-types.ts raw
1 import { Filter, Event } from 'nostr-tools'
2 import { ISigner } from '@/types'
3
4 // NRC Event Kinds
5 export const KIND_NRC_REQUEST = 24891
6 export const KIND_NRC_RESPONSE = 24892
7
8 // Session types
9 export interface NRCSession {
10 id: string
11 clientPubkey: string
12 conversationKey?: Uint8Array // Optional - only set when using direct key access
13 deviceName?: string
14 createdAt: number
15 lastActivity: number
16 subscriptions: Map<string, NRCSubscription>
17 }
18
19 export interface NRCSubscription {
20 id: string
21 filters: Filter[]
22 createdAt: number
23 eventCount: number
24 eoseSent: boolean
25 }
26
27 // Message types (encrypted content)
28 export interface RequestMessage {
29 type: 'REQ' | 'CLOSE' | 'EVENT' | 'COUNT' | 'IDS'
30 payload: unknown[]
31 }
32
33 export interface ResponseMessage {
34 type: 'EVENT' | 'EOSE' | 'OK' | 'NOTICE' | 'CLOSED' | 'COUNT' | 'CHUNK' | 'IDS'
35 payload: unknown[]
36 }
37
38 // ===== Sync Types =====
39
40 /**
41 * Event manifest entry - describes an event we have
42 * Used by IDS request/response for diffing
43 */
44 export interface EventManifestEntry {
45 kind: number
46 id: string
47 created_at: number
48 d?: string // For parameterized replaceable events (kinds 30000-39999)
49 }
50
51 // Chunked message for large payloads
52 export interface ChunkMessage {
53 type: 'CHUNK'
54 messageId: string // Unique ID for this chunked message
55 index: number // 0-based chunk index
56 total: number // Total number of chunks
57 data: string // Base64 encoded chunk data
58 }
59
60 // Helper to check if a message is a chunk
61 export function isChunkMessage(msg: ResponseMessage): msg is ResponseMessage & { payload: [ChunkMessage] } {
62 return msg.type === 'CHUNK'
63 }
64
65 // Connection management
66 export interface NRCConnection {
67 id: string
68 label: string
69 secret?: string // For secret-based auth
70 clientPubkey?: string // Derived from secret
71 createdAt: number
72 lastUsed?: number
73 }
74
75 // Listener configuration
76 export interface NRCListenerConfig {
77 rendezvousUrl: string
78 signer: ISigner
79 authorizedSecrets: Map<string, string> // clientPubkey → deviceName
80 sessionTimeout?: number // Session inactivity timeout in ms (default 30 min)
81 maxSubscriptionsPerSession?: number // Max subscriptions per session (default 100)
82 }
83
84 // Authorization result
85 export interface AuthResult {
86 conversationKey?: Uint8Array // Optional - only set when using direct key access
87 deviceName: string
88 }
89
90 // Parsed connection URI
91 export interface ParsedConnectionURI {
92 relayPubkey: string // Hex pubkey of the listening relay/client
93 rendezvousUrl: string // URL of the rendezvous relay
94 // For secret-based auth
95 secret?: string // 32-byte hex secret
96 clientPubkey?: string // Derived pubkey from secret
97 clientPrivkey?: Uint8Array // Derived private key from secret
98 // Optional
99 deviceName?: string
100 }
101
102 // Listener state for React context
103 export interface NRCListenerState {
104 isEnabled: boolean
105 isListening: boolean
106 connections: NRCConnection[]
107 activeSessions: number
108 rendezvousUrl: string
109 }
110
111 // Event with simplified typing for storage queries
112 export type StoredEvent = Event
113
114 // Device-specific event check
115 export function isDeviceSpecificEvent(event: Event): boolean {
116 const dTag = event.tags.find((t) => t[0] === 'd')?.[1]
117 return dTag?.startsWith('device:') ?? false
118 }
119