From af30945803d440d1f803c814f4a37a1890494f1d Mon Sep 17 00:00:00 2001 From: bndw Date: Fri, 13 Feb 2026 17:35:33 -0800 Subject: docs: add design documentation and examples --- PLAN.md | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 PLAN.md (limited to 'PLAN.md') diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..39d8318 --- /dev/null +++ b/PLAN.md @@ -0,0 +1,186 @@ +# Minimal Nostr Go Library - Implementation Plan + +## Overview + +Build a minimal Go library for Nostr split into two modules: + +**Module 1: Core** (`nostr-go` root) - 1 external dep +- Types, signing, serialization +- `github.com/btcsuite/btcd/btcec/v2` - BIP-340 Schnorr signatures + +**Module 2: Relay** (`nostr-go/relay`) - 1 additional dep +- WebSocket connection, pub/sub +- `github.com/coder/websocket` - WebSocket library +- Imports core module + +Users who only need types/signing don't pull in websocket dependencies. + +## Package Structure + +``` +nostr-go/ +├── go.mod # Core module +├── event.go # Event struct, ID computation, serialization +├── tags.go # Tag/Tags types and helpers +├── kinds.go # Event kind constants +├── filter.go # Filter struct and matching logic +├── keys.go # Key generation, signing, verification +├── bech32.go # Bech32 encoding/decoding (our impl, ~150 lines) +├── nip19.go # npub/nsec/note/nprofile encode/decode +├── envelope.go # Protocol messages (EVENT, REQ, OK, etc.) +├── *_test.go +│ +└── relay/ + ├── go.mod # Relay module (imports core) + ├── relay.go # WebSocket connection primitives + ├── subscription.go # Subscription handling + └── *_test.go +``` + +## Core Types + +### Event (event.go) +```go +type Event struct { + ID string `json:"id"` // 64-char hex (SHA256) + PubKey string `json:"pubkey"` // 64-char hex (x-only pubkey) + CreatedAt int64 `json:"created_at"` + Kind int `json:"kind"` + Tags Tags `json:"tags"` + Content string `json:"content"` + Sig string `json:"sig"` // 128-char hex (Schnorr sig) +} +``` + +**Design note**: Starting with hex strings for simplicity. Can evaluate byte arrays (`[32]byte`, `[64]byte`) later if type safety becomes important. + +Key methods: +- `Serialize() []byte` - Canonical JSON for ID computation: `[0,"pubkey",created_at,kind,tags,"content"]` +- `ComputeID() string` - SHA256 hash of serialized form +- `Sign(privKeyHex string) error` - Sign with Schnorr, sets PubKey/ID/Sig +- `Verify() bool` - Verify signature + +### Tags (tags.go) +```go +type Tag []string +type Tags []Tag +``` +Methods: `Key()`, `Value()`, `Find(key)`, `FindAll(key)`, `GetD()` + +### Filter (filter.go) +```go +type Filter struct { + IDs []string `json:"ids,omitempty"` + Kinds []int `json:"kinds,omitempty"` + Authors []string `json:"authors,omitempty"` + Tags map[string][]string `json:"-"` // Custom marshal for #e, #p + Since *int64 `json:"since,omitempty"` + Until *int64 `json:"until,omitempty"` + Limit int `json:"limit,omitempty"` +} +``` +Methods: `Matches(event) bool`, custom `MarshalJSON`/`UnmarshalJSON` for tag filters + +### Kinds (kinds.go) +Essential constants only: +```go +const ( + KindMetadata = 0 + KindTextNote = 1 + KindContactList = 3 + KindEncryptedDM = 4 + KindDeletion = 5 + KindRepost = 6 + KindReaction = 7 +) +``` +Helpers: `IsRegular()`, `IsReplaceable()`, `IsEphemeral()`, `IsAddressable()` + +### Envelopes (envelope.go) +Protocol messages as types with `Label()` and `MarshalJSON()`: +- Client→Relay: `EventEnvelope`, `ReqEnvelope`, `CloseEnvelope` +- Relay→Client: `EventEnvelope`, `OKEnvelope`, `EOSEEnvelope`, `ClosedEnvelope`, `NoticeEnvelope` +- `ParseEnvelope(data []byte) (Envelope, error)` + +## Keys & Signing (keys.go) + +Using `github.com/btcsuite/btcd/btcec/v2/schnorr`: +```go +func GenerateKey() (string, error) +func GetPublicKey(privKeyHex string) (string, error) +func (e *Event) Sign(privKeyHex string) error +func (e *Event) Verify() bool +``` + +## NIP-19 Encoding (nip19.go) + +Bech32 encoding for human-readable identifiers: +```go +func EncodePublicKey(pubKeyHex string) (string, error) // -> npub1... +func EncodeSecretKey(secKeyHex string) (string, error) // -> nsec1... +func EncodeNote(eventID string) (string, error) // -> note1... + +func DecodePublicKey(npub string) (string, error) // npub1... -> hex +func DecodeSecretKey(nsec string) (string, error) // nsec1... -> hex +func DecodeNote(note string) (string, error) // note1... -> hex + +// TLV-encoded types (nprofile, nevent, naddr) can be added later +``` + +## WebSocket Primitives (relay.go) + +Simple design - no complex goroutine orchestration: +```go +type Relay struct { + URL string + conn *websocket.Conn + mu sync.Mutex +} + +func Connect(ctx context.Context, url string) (*Relay, error) +func (r *Relay) Close() error +func (r *Relay) Send(ctx context.Context, env Envelope) error +func (r *Relay) Receive(ctx context.Context) (Envelope, error) +func (r *Relay) Publish(ctx context.Context, event *Event) error +func (r *Relay) Subscribe(ctx context.Context, id string, filters ...Filter) (*Subscription, error) + +type Subscription struct { + ID string + Events chan *Event + EOSE chan struct{} +} +func (s *Subscription) Listen() error +func (s *Subscription) Close() error +``` + +## Implementation Order + +### Phase 1: Core Module (nostr-go) +1. **go.mod** - Module definition with btcec/v2 dependency +2. **event.go, tags.go, kinds.go** - Core types, serialization, ID computation +3. **keys.go** - Schnorr signing with btcec/v2 +4. **bech32.go** - Bech32 encode/decode (~150 lines) +5. **nip19.go** - npub/nsec/note encoding +6. **filter.go** - Filter struct with custom JSON and matching +7. **envelope.go** - All envelope types and ParseEnvelope +8. **Core tests** + +### Phase 2: Relay Module (nostr-go/relay) +1. **relay/go.mod** - Module definition with websocket dep, imports core +2. **relay/relay.go** - WebSocket connection primitives +3. **relay/subscription.go** - Subscription handling +4. **Relay tests** + +## What's Omitted (v0.1) + +- NIP-42 AUTH +- NIP-04 encrypted DMs +- Connection pooling / relay pool +- Automatic reconnection +- Advanced kinds (10000+) + +## Verification + +1. Unit tests for each module +2. Integration test: connect to `wss://relay.damus.io`, publish event, subscribe +3. Verify signature interop with existing Nostr clients/libraries -- cgit v1.2.3