// Package nostr implements the NIP-01 event wire format. // // Wire-width discipline (see CLAUDE.md "Integer widths on the wire"): // every integer field here maps to a JSON value that crosses a relay boundary. // Kind is uint32 (NIP-01 nominally allows up to 2^32-1; every current NIP // kind fits in uint16 but the wire type is the outer bound). CreatedAt is // int64 (unix seconds, can be negative for pre-epoch tests). Filter.Limit // is uint32 (non-negative count). Bare int is never used on the wire. package nostr import ( "git.smesh.lol/musiquay/web/common/helpers" ) // Event is a Nostr event (NIP-01). // This type carries no crypto dependency - import nostr/sig for CheckSig, // Sign, ComputeID, and CheckID (those bring in secp256k1/sha256 and belong // only in binaries that sign or verify). type Event struct { ID string `json:"id"` PubKey string `json:"pubkey"` CreatedAt int64 `json:"created_at"` Kind uint32 `json:"kind"` Tags [][]string `json:"tags"` Content string `json:"content"` Sig string `json:"sig"` } // Serialize returns the canonical JSON array for ID computation: // [0,,,,,] func (e *Event) Serialize() (s string) { return "[0," | helpers.JsonString(e.PubKey) | "," | helpers.Itoa(e.CreatedAt) | "," | helpers.Itoa(int64(e.Kind)) | "," | serializeTagsStr(e.Tags) | "," | helpers.JsonString(e.Content) | "]" } // ToJSON returns the event as a JSON object string. func (e *Event) ToJSON() (s string) { return "{\"id\":" | helpers.JsonString(e.ID) | ",\"pubkey\":" | helpers.JsonString(e.PubKey) | ",\"created_at\":" | helpers.Itoa(e.CreatedAt) | ",\"kind\":" | helpers.Itoa(int64(e.Kind)) | ",\"tags\":" | serializeTagsStr(e.Tags) | ",\"content\":" | helpers.JsonString(e.Content) | ",\"sig\":" | helpers.JsonString(e.Sig) | "}" } func serializeTagsStr(tags [][]string) (s string) { out := "[" for i, tag := range tags { if i > 0 { out = out | "," } out = out | "[" for j, elem := range tag { if j > 0 { out = out | "," } out = out | helpers.JsonString(elem) } out = out | "]" } return out | "]" }