doc.go raw
1 // Package directory implements the distributed directory consensus protocol
2 // as defined in NIP-XX for Nostr relay operators.
3 //
4 // # Overview
5 //
6 // This package provides complete message encoding, validation, and helper
7 // functions for implementing the distributed directory consensus protocol.
8 // The protocol enables Nostr relay operators to form trusted consortiums
9 // that automatically synchronize essential identity-related events while
10 // maintaining decentralization and Byzantine fault tolerance.
11 //
12 // # Event Kinds
13 //
14 // The protocol defines six new event kinds:
15 //
16 // - 39100: Relay Identity Announcement - Announces relay participation
17 // - 39101: Trust Act - Creates trust relationships between relays
18 // - 39102: Group Tag Act - Attests to arbitrary string values
19 // - 39103: Public Key Advertisement - Advertises HD-derived keys
20 // - 39104: Directory Event Replication Request - Requests event replication
21 // - 39105: Directory Event Replication Response - Responds to replication requests
22 //
23 // # Directory Events
24 //
25 // The following existing event kinds are considered "directory events" and
26 // are automatically replicated among consortium members:
27 //
28 // - Kind 0: User Metadata
29 // - Kind 3: Follow Lists
30 // - Kind 5: Event Deletion Requests
31 // - Kind 1984: Reporting
32 // - Kind 10002: Relay List Metadata
33 // - Kind 10000: Mute Lists
34 // - Kind 10050: DM Relay Lists
35 //
36 // # Basic Usage
37 //
38 // ## Creating a Relay Identity Announcement
39 //
40 // pubkey := []byte{...} // 32-byte relay identity key
41 // announcement, err := directory.NewRelayIdentityAnnouncement(
42 // pubkey,
43 // "relay.example.com", // name
44 // "A community relay", // description
45 // "admin@example.com", // contact
46 // "wss://relay.example.com", // relay URL
47 // "abc123...", // signing key (hex)
48 // "def456...", // encryption key (hex)
49 // "1", // version
50 // "https://relay.example.com/.well-known/nostr.json", // NIP-11 URL
51 // )
52 // if err != nil {
53 // log.Fatal(err)
54 // }
55 //
56 // ## Creating a Trust Act
57 //
58 // act, err := directory.NewTrustAct(
59 // pubkey,
60 // "target_relay_pubkey_hex", // target relay
61 // directory.TrustLevelHigh, // trust level
62 // "wss://target.relay.com", // target URL
63 // nil, // no expiry
64 // directory.TrustReasonManual, // manual trust
65 // []uint16{1, 6, 7}, // additional kinds to replicate
66 // nil, // no identity tag
67 // )
68 //
69 // ## Creating a Public Key Advertisement
70 //
71 // validFrom := time.Now()
72 // validUntil := validFrom.Add(30 * 24 * time.Hour) // 30 days
73 //
74 // keyAd, err := directory.NewPublicKeyAdvertisement(
75 // pubkey,
76 // "signing-key-001", // key ID
77 // "fedcba9876543210...", // public key (hex)
78 // directory.KeyPurposeSigning, // purpose
79 // validFrom, // valid from
80 // validUntil, // valid until
81 // "secp256k1", // algorithm
82 // "m/39103'/1237'/0'/0/1", // derivation path
83 // 1, // key index
84 // nil, // no identity tag
85 // )
86 //
87 // # Identity Tags
88 //
89 // Identity tags (I tags) provide npub-encoded identities with proof-of-control
90 // signatures. They bind an identity to a specific delegate key, preventing
91 // unauthorized use.
92 //
93 // ## Creating Identity Tags
94 //
95 // // Create identity tag builder with private key
96 // identityPrivkey := []byte{...} // 32-byte private key
97 // builder, err := directory.NewIdentityTagBuilder(identityPrivkey)
98 // if err != nil {
99 // log.Fatal(err)
100 // }
101 //
102 // // Create signed identity tag for delegate key
103 // delegatePubkey := []byte{...} // 32-byte delegate public key
104 // identityTag, err := builder.CreateIdentityTag(delegatePubkey)
105 // if err != nil {
106 // log.Fatal(err)
107 // }
108 //
109 // // Use in trust act
110 // act, err := directory.NewTrustAct(
111 // pubkey,
112 // "target_relay_pubkey_hex",
113 // directory.TrustLevelHigh,
114 // "wss://target.relay.com",
115 // nil,
116 // directory.TrustReasonManual,
117 // []uint16{1, 6, 7},
118 // identityTag, // Include identity tag
119 // )
120 //
121 // # Validation
122 //
123 // All message types include comprehensive validation:
124 //
125 // // Validate a parsed event
126 // if err := announcement.Validate(); err != nil {
127 // log.Printf("Invalid announcement: %v", err)
128 // return
129 // }
130 //
131 // // Validate any consortium event
132 // if err := directory.ValidateConsortiumEvent(event); err != nil {
133 // log.Printf("Invalid consortium event: %v", err)
134 // return
135 // }
136 //
137 // // Verify NIP-11 binding
138 // valid, err := directory.ValidateRelayIdentityBinding(
139 // announcement,
140 // nip11Pubkey,
141 // nip11Nonce,
142 // nip11Sig,
143 // relayAddress,
144 // )
145 // if err != nil || !valid {
146 // log.Printf("Invalid relay identity binding")
147 // return
148 // }
149 //
150 // # Trust Calculation
151 //
152 // The package provides utilities for calculating trust relationships:
153 //
154 // // Create trust calculator
155 // calculator := directory.NewTrustCalculator()
156 //
157 // // Add trust acts
158 // calculator.AddAct(act1)
159 // calculator.AddAct(act2)
160 //
161 // // Get direct trust level
162 // level := calculator.GetTrustLevel("relay_pubkey_hex")
163 //
164 // // Calculate inherited trust
165 // inheritedLevel := calculator.CalculateInheritedTrust(
166 // "from_relay_pubkey",
167 // "to_relay_pubkey",
168 // )
169 //
170 // # Replication Filtering
171 //
172 // Determine which events should be replicated to which relays:
173 //
174 // // Create replication filter
175 // filter := directory.NewReplicationFilter(calculator)
176 // filter.AddTrustAct(act)
177 //
178 // // Check if event should be replicated
179 // shouldReplicate := filter.ShouldReplicate(event, "target_relay_pubkey")
180 //
181 // // Get all replication targets for an event
182 // targets := filter.GetReplicationTargets(event)
183 //
184 // # Event Batching
185 //
186 // Batch events for efficient replication:
187 //
188 // // Create event batcher
189 // batcher := directory.NewEventBatcher(100) // max 100 events per batch
190 //
191 // // Add events to batches
192 // batcher.AddEvent("wss://relay1.com", event1)
193 // batcher.AddEvent("wss://relay1.com", event2)
194 // batcher.AddEvent("wss://relay2.com", event3)
195 //
196 // // Check if batch is full
197 // if batcher.IsBatchFull("wss://relay1.com") {
198 // batch := batcher.FlushBatch("wss://relay1.com")
199 // // Send batch for replication
200 // }
201 //
202 // # Replication Requests and Responses
203 //
204 // ## Creating Replication Requests
205 //
206 // requestID, err := directory.GenerateRequestID()
207 // if err != nil {
208 // log.Fatal(err)
209 // }
210 //
211 // request, err := directory.NewDirectoryEventReplicationRequest(
212 // pubkey,
213 // requestID,
214 // "wss://target.relay.com",
215 // []*event.E{event1, event2, event3},
216 // )
217 //
218 // ## Creating Replication Responses
219 //
220 // // Success response
221 // results := []*directory.EventResult{
222 // directory.CreateEventResult("event_id_1", true, ""),
223 // directory.CreateEventResult("event_id_2", false, "duplicate event"),
224 // }
225 //
226 // response, err := directory.CreateSuccessResponse(
227 // pubkey,
228 // requestID,
229 // "wss://source.relay.com",
230 // results,
231 // )
232 //
233 // // Error response
234 // errorResponse, err := directory.CreateErrorResponse(
235 // pubkey,
236 // requestID,
237 // "wss://source.relay.com",
238 // "relay temporarily unavailable",
239 // )
240 //
241 // # Key Management
242 //
243 // The protocol uses BIP32 HD key derivation for deterministic key generation:
244 //
245 // // Create key pool manager
246 // masterSeed := []byte{...} // BIP39 seed
247 // manager := directory.NewKeyPoolManager(masterSeed, 0) // identity index 0
248 //
249 // // Generate derivation paths
250 // signingPath := manager.GenerateDerivationPath(directory.KeyPurposeSigning, 5)
251 // // Returns: "m/39103'/1237'/0'/0/5"
252 //
253 // encryptionPath := manager.GenerateDerivationPath(directory.KeyPurposeEncryption, 3)
254 // // Returns: "m/39103'/1237'/0'/1/3"
255 //
256 // // Track key usage
257 // nextIndex := manager.GetNextKeyIndex(directory.KeyPurposeSigning)
258 // manager.SetKeyIndex(directory.KeyPurposeSigning, 10) // Skip to index 10
259 //
260 // # Error Handling
261 //
262 // All functions return detailed errors using the errorf package:
263 //
264 // announcement, err := directory.ParseRelayIdentityAnnouncement(event)
265 // if err != nil {
266 // // Handle specific error types
267 // switch {
268 // case strings.Contains(err.Error(), "invalid event kind"):
269 // log.Printf("Wrong event kind: %v", err)
270 // case strings.Contains(err.Error(), "missing"):
271 // log.Printf("Missing required field: %v", err)
272 // default:
273 // log.Printf("Parse error: %v", err)
274 // }
275 // return
276 // }
277 //
278 // # Security Considerations
279 //
280 // The package implements several security measures:
281 //
282 // - All events must have valid signatures
283 // - Identity tags prevent unauthorized identity use
284 // - NIP-11 binding prevents relay impersonation
285 // - Timestamp validation prevents replay attacks
286 // - Content size limits prevent DoS attacks
287 // - Nonce validation ensures cryptographic security
288 //
289 // # Protocol Constants
290 //
291 // Important protocol constants:
292 //
293 // - MaxKeyDelegations: 512 unused key delegations per identity
294 // - KeyExpirationDays: 30 days for unused key delegations
295 // - MinNonceSize: 16 bytes minimum for nonces
296 // - MaxContentLength: 65536 bytes maximum for event content
297 //
298 // # Integration Example
299 //
300 // Complete example of implementing consortium membership:
301 //
302 // package main
303 //
304 // import (
305 // "log"
306 // "time"
307 //
308 // "next.orly.dev/pkg/protocol/directory"
309 // )
310 //
311 // func main() {
312 // // Generate relay identity key
313 // relayPrivkey := []byte{...} // 32 bytes
314 // relayPubkey := schnorr.PubkeyFromSeckey(relayPrivkey)
315 //
316 // // Create relay identity announcement
317 // announcement, err := directory.NewRelayIdentityAnnouncement(
318 // relayPubkey,
319 // "my-relay.com",
320 // "My Community Relay",
321 // "admin@my-relay.com",
322 // "wss://my-relay.com",
323 // hex.EncodeToString(signingPubkey),
324 // hex.EncodeToString(encryptionPubkey),
325 // "1",
326 // "https://my-relay.com/.well-known/nostr.json",
327 // )
328 // if err != nil {
329 // log.Fatal(err)
330 // }
331 //
332 // // Sign and publish announcement
333 // if err := announcement.Event.Sign(relayPrivkey); err != nil {
334 // log.Fatal(err)
335 // }
336 //
337 // // Create trust act for another relay
338 // act, err := directory.NewTrustAct(
339 // relayPubkey,
340 // "trusted_relay_pubkey_hex",
341 // directory.TrustLevelHigh,
342 // "wss://trusted-relay.com",
343 // nil, // no expiry
344 // directory.TrustReasonManual,
345 // []uint16{1, 6, 7}, // replicate text notes, reposts, reactions
346 // nil, // no identity tag
347 // )
348 // if err != nil {
349 // log.Fatal(err)
350 // }
351 //
352 // // Sign and publish act
353 // if err := act.Event.Sign(relayPrivkey); err != nil {
354 // log.Fatal(err)
355 // }
356 //
357 // // Set up replication filter
358 // calculator := directory.NewTrustCalculator()
359 // calculator.AddAct(act)
360 //
361 // filter := directory.NewReplicationFilter(calculator)
362 // filter.AddTrustAct(act)
363 //
364 // // When receiving events, check if they should be replicated
365 // for event := range eventChannel {
366 // targets := filter.GetReplicationTargets(event)
367 // for _, target := range targets {
368 // // Replicate event to target relay
369 // replicateEvent(event, target)
370 // }
371 // }
372 // }
373 //
374 // For more detailed examples and advanced usage patterns, see the test files
375 // and the reference implementation in the main relay codebase.
376 package directory
377