Files
next.orly.dev/pkg/protocol/directory/doc.go
mleku 5652cec845 Refactor NIP-XX Document and Protocol Implementation for Directory Consensus
- Updated the NIP-XX document to clarify terminology, replacing "attestations" with "acts" for consistency.
- Enhanced the protocol by introducing new event kinds: Trust Act (Kind 39101) and Group Tag Act (Kind 39102), with detailed specifications for their structure and usage.
- Modified the signature generation process to include the canonical WebSocket URL, ensuring proper binding and verification.
- Improved validation mechanisms for identity tags and event replication requests, reinforcing security and integrity within the directory consensus protocol.
- Added comprehensive documentation for new event types and their respective validation processes, ensuring clarity for developers and users.
- Introduced new helper functions and structures to facilitate the creation and management of directory events and acts.
2025-10-25 12:33:47 +01:00

377 lines
11 KiB
Go

// Package directory implements the distributed directory consensus protocol
// as defined in NIP-XX for Nostr relay operators.
//
// # Overview
//
// This package provides complete message encoding, validation, and helper
// functions for implementing the distributed directory consensus protocol.
// The protocol enables Nostr relay operators to form trusted consortiums
// that automatically synchronize essential identity-related events while
// maintaining decentralization and Byzantine fault tolerance.
//
// # Event Kinds
//
// The protocol defines six new event kinds:
//
// - 39100: Relay Identity Announcement - Announces relay participation
// - 39101: Trust Act - Creates trust relationships between relays
// - 39102: Group Tag Act - Attests to arbitrary string values
// - 39103: Public Key Advertisement - Advertises HD-derived keys
// - 39104: Directory Event Replication Request - Requests event replication
// - 39105: Directory Event Replication Response - Responds to replication requests
//
// # Directory Events
//
// The following existing event kinds are considered "directory events" and
// are automatically replicated among consortium members:
//
// - Kind 0: User Metadata
// - Kind 3: Follow Lists
// - Kind 5: Event Deletion Requests
// - Kind 1984: Reporting
// - Kind 10002: Relay List Metadata
// - Kind 10000: Mute Lists
// - Kind 10050: DM Relay Lists
//
// # Basic Usage
//
// ## Creating a Relay Identity Announcement
//
// pubkey := []byte{...} // 32-byte relay identity key
// announcement, err := directory.NewRelayIdentityAnnouncement(
// pubkey,
// "relay.example.com", // name
// "A community relay", // description
// "admin@example.com", // contact
// "wss://relay.example.com", // relay URL
// "abc123...", // signing key (hex)
// "def456...", // encryption key (hex)
// "1", // version
// "https://relay.example.com/.well-known/nostr.json", // NIP-11 URL
// )
// if err != nil {
// log.Fatal(err)
// }
//
// ## Creating a Trust Act
//
// act, err := directory.NewTrustAct(
// pubkey,
// "target_relay_pubkey_hex", // target relay
// directory.TrustLevelHigh, // trust level
// "wss://target.relay.com", // target URL
// nil, // no expiry
// directory.TrustReasonManual, // manual trust
// []uint16{1, 6, 7}, // additional kinds to replicate
// nil, // no identity tag
// )
//
// ## Creating a Public Key Advertisement
//
// validFrom := time.Now()
// validUntil := validFrom.Add(30 * 24 * time.Hour) // 30 days
//
// keyAd, err := directory.NewPublicKeyAdvertisement(
// pubkey,
// "signing-key-001", // key ID
// "fedcba9876543210...", // public key (hex)
// directory.KeyPurposeSigning, // purpose
// validFrom, // valid from
// validUntil, // valid until
// "secp256k1", // algorithm
// "m/39103'/1237'/0'/0/1", // derivation path
// 1, // key index
// nil, // no identity tag
// )
//
// # Identity Tags
//
// Identity tags (I tags) provide npub-encoded identities with proof-of-control
// signatures. They bind an identity to a specific delegate key, preventing
// unauthorized use.
//
// ## Creating Identity Tags
//
// // Create identity tag builder with private key
// identityPrivkey := []byte{...} // 32-byte private key
// builder, err := directory.NewIdentityTagBuilder(identityPrivkey)
// if err != nil {
// log.Fatal(err)
// }
//
// // Create signed identity tag for delegate key
// delegatePubkey := []byte{...} // 32-byte delegate public key
// identityTag, err := builder.CreateIdentityTag(delegatePubkey)
// if err != nil {
// log.Fatal(err)
// }
//
// // Use in trust act
// act, err := directory.NewTrustAct(
// pubkey,
// "target_relay_pubkey_hex",
// directory.TrustLevelHigh,
// "wss://target.relay.com",
// nil,
// directory.TrustReasonManual,
// []uint16{1, 6, 7},
// identityTag, // Include identity tag
// )
//
// # Validation
//
// All message types include comprehensive validation:
//
// // Validate a parsed event
// if err := announcement.Validate(); err != nil {
// log.Printf("Invalid announcement: %v", err)
// return
// }
//
// // Validate any consortium event
// if err := directory.ValidateConsortiumEvent(event); err != nil {
// log.Printf("Invalid consortium event: %v", err)
// return
// }
//
// // Verify NIP-11 binding
// valid, err := directory.ValidateRelayIdentityBinding(
// announcement,
// nip11Pubkey,
// nip11Nonce,
// nip11Sig,
// relayAddress,
// )
// if err != nil || !valid {
// log.Printf("Invalid relay identity binding")
// return
// }
//
// # Trust Calculation
//
// The package provides utilities for calculating trust relationships:
//
// // Create trust calculator
// calculator := directory.NewTrustCalculator()
//
// // Add trust acts
// calculator.AddAct(act1)
// calculator.AddAct(act2)
//
// // Get direct trust level
// level := calculator.GetTrustLevel("relay_pubkey_hex")
//
// // Calculate inherited trust
// inheritedLevel := calculator.CalculateInheritedTrust(
// "from_relay_pubkey",
// "to_relay_pubkey",
// )
//
// # Replication Filtering
//
// Determine which events should be replicated to which relays:
//
// // Create replication filter
// filter := directory.NewReplicationFilter(calculator)
// filter.AddTrustAct(act)
//
// // Check if event should be replicated
// shouldReplicate := filter.ShouldReplicate(event, "target_relay_pubkey")
//
// // Get all replication targets for an event
// targets := filter.GetReplicationTargets(event)
//
// # Event Batching
//
// Batch events for efficient replication:
//
// // Create event batcher
// batcher := directory.NewEventBatcher(100) // max 100 events per batch
//
// // Add events to batches
// batcher.AddEvent("wss://relay1.com", event1)
// batcher.AddEvent("wss://relay1.com", event2)
// batcher.AddEvent("wss://relay2.com", event3)
//
// // Check if batch is full
// if batcher.IsBatchFull("wss://relay1.com") {
// batch := batcher.FlushBatch("wss://relay1.com")
// // Send batch for replication
// }
//
// # Replication Requests and Responses
//
// ## Creating Replication Requests
//
// requestID, err := directory.GenerateRequestID()
// if err != nil {
// log.Fatal(err)
// }
//
// request, err := directory.NewDirectoryEventReplicationRequest(
// pubkey,
// requestID,
// "wss://target.relay.com",
// []*event.E{event1, event2, event3},
// )
//
// ## Creating Replication Responses
//
// // Success response
// results := []*directory.EventResult{
// directory.CreateEventResult("event_id_1", true, ""),
// directory.CreateEventResult("event_id_2", false, "duplicate event"),
// }
//
// response, err := directory.CreateSuccessResponse(
// pubkey,
// requestID,
// "wss://source.relay.com",
// results,
// )
//
// // Error response
// errorResponse, err := directory.CreateErrorResponse(
// pubkey,
// requestID,
// "wss://source.relay.com",
// "relay temporarily unavailable",
// )
//
// # Key Management
//
// The protocol uses BIP32 HD key derivation for deterministic key generation:
//
// // Create key pool manager
// masterSeed := []byte{...} // BIP39 seed
// manager := directory.NewKeyPoolManager(masterSeed, 0) // identity index 0
//
// // Generate derivation paths
// signingPath := manager.GenerateDerivationPath(directory.KeyPurposeSigning, 5)
// // Returns: "m/39103'/1237'/0'/0/5"
//
// encryptionPath := manager.GenerateDerivationPath(directory.KeyPurposeEncryption, 3)
// // Returns: "m/39103'/1237'/0'/1/3"
//
// // Track key usage
// nextIndex := manager.GetNextKeyIndex(directory.KeyPurposeSigning)
// manager.SetKeyIndex(directory.KeyPurposeSigning, 10) // Skip to index 10
//
// # Error Handling
//
// All functions return detailed errors using the errorf package:
//
// announcement, err := directory.ParseRelayIdentityAnnouncement(event)
// if err != nil {
// // Handle specific error types
// switch {
// case strings.Contains(err.Error(), "invalid event kind"):
// log.Printf("Wrong event kind: %v", err)
// case strings.Contains(err.Error(), "missing"):
// log.Printf("Missing required field: %v", err)
// default:
// log.Printf("Parse error: %v", err)
// }
// return
// }
//
// # Security Considerations
//
// The package implements several security measures:
//
// - All events must have valid signatures
// - Identity tags prevent unauthorized identity use
// - NIP-11 binding prevents relay impersonation
// - Timestamp validation prevents replay attacks
// - Content size limits prevent DoS attacks
// - Nonce validation ensures cryptographic security
//
// # Protocol Constants
//
// Important protocol constants:
//
// - MaxKeyDelegations: 512 unused key delegations per identity
// - KeyExpirationDays: 30 days for unused key delegations
// - MinNonceSize: 16 bytes minimum for nonces
// - MaxContentLength: 65536 bytes maximum for event content
//
// # Integration Example
//
// Complete example of implementing consortium membership:
//
// package main
//
// import (
// "log"
// "time"
//
// "next.orly.dev/pkg/protocol/directory"
// )
//
// func main() {
// // Generate relay identity key
// relayPrivkey := []byte{...} // 32 bytes
// relayPubkey := schnorr.PubkeyFromSeckey(relayPrivkey)
//
// // Create relay identity announcement
// announcement, err := directory.NewRelayIdentityAnnouncement(
// relayPubkey,
// "my-relay.com",
// "My Community Relay",
// "admin@my-relay.com",
// "wss://my-relay.com",
// hex.EncodeToString(signingPubkey),
// hex.EncodeToString(encryptionPubkey),
// "1",
// "https://my-relay.com/.well-known/nostr.json",
// )
// if err != nil {
// log.Fatal(err)
// }
//
// // Sign and publish announcement
// if err := announcement.Event.Sign(relayPrivkey); err != nil {
// log.Fatal(err)
// }
//
// // Create trust act for another relay
// act, err := directory.NewTrustAct(
// relayPubkey,
// "trusted_relay_pubkey_hex",
// directory.TrustLevelHigh,
// "wss://trusted-relay.com",
// nil, // no expiry
// directory.TrustReasonManual,
// []uint16{1, 6, 7}, // replicate text notes, reposts, reactions
// nil, // no identity tag
// )
// if err != nil {
// log.Fatal(err)
// }
//
// // Sign and publish act
// if err := act.Event.Sign(relayPrivkey); err != nil {
// log.Fatal(err)
// }
//
// // Set up replication filter
// calculator := directory.NewTrustCalculator()
// calculator.AddAct(act)
//
// filter := directory.NewReplicationFilter(calculator)
// filter.AddTrustAct(act)
//
// // When receiving events, check if they should be replicated
// for event := range eventChannel {
// targets := filter.GetReplicationTargets(event)
// for _, target := range targets {
// // Replicate event to target relay
// replicateEvent(event, target)
// }
// }
// }
//
// For more detailed examples and advanced usage patterns, see the test files
// and the reference implementation in the main relay codebase.
package directory