Nostr Codec
A high-performance, pure Go implementation of Nostr protocol encoding/decoding, cryptography, and utilities. This library provides a complete toolkit for building Nostr clients and relays with optimized performance through SIMD acceleration and zero-copy operations.
Features
- Pure Go with Purego - No CGO dependencies, uses
ebitengine/puregoto dynamically loadlibsecp256k1.so - SIMD Optimization - Accelerated SHA256 (
minio/sha256-simd) and hex encoding (templexxx/xhex) - Zero-Copy Operations - Efficient buffer pooling and reuse
- Complete NIP Support - Events, filters, tags, envelopes, and cryptographic operations
- Multiple Encodings - JSON, binary, and canonical encodings for events
- Comprehensive Crypto - Schnorr signatures, ECDH, NIP-04/NIP-44 encryption
Installation
go get git.mleku.dev/mleku/nostr-codec
Table of Contents
- Event Package - Event encoding/decoding and manipulation
- Filter Package - Query filters for event subscriptions
- Tag Package - Tag encoding and manipulation
- Kind Package - Event kind constants and utilities
- Envelope Packages - WebSocket message envelopes
- Cryptography - Signatures, encryption, and key management
- Utilities - Buffer pools, atomic operations, and helpers
Event Package
The event package provides the core Nostr event type with JSON, binary, and canonical encodings.
Types
type E struct {
ID []byte // SHA256 hash of canonical encoding
Pubkey []byte // Public key (32 bytes)
CreatedAt int64 // UNIX timestamp
Kind uint16 // Event kind
Tags *tag.S // Tag list
Content []byte // Arbitrary content
Sig []byte // Schnorr signature (64 bytes)
}
type S []*E // Slice of events (sortable by CreatedAt)
type C chan *E // Channel for event streaming
Constructor
func New() *E
Methods
Lifecycle
func (ev *E) Free() // Nil all fields for GC
func (ev *E) Clone() *E // Deep copy with independent memory
func (ev *E) EstimateSize() int // Estimate serialized size
Encoding/Decoding
func (ev *E) Marshal(dst []byte) []byte // Marshal to JSON
func (ev *E) MarshalJSON() ([]byte, error) // Standard JSON marshaler
func (ev *E) Serialize() []byte // Marshal to new buffer
func (ev *E) Unmarshal(b []byte) ([]byte, error) // Unmarshal from JSON
func (ev *E) UnmarshalJSON(b []byte) error // Standard JSON unmarshaler
Binary Encoding
func (ev *E) MarshalBinary(w io.Writer) // Write binary format
func (ev *E) MarshalBinaryToBytes(dst []byte) []byte // Binary to bytes
func (ev *E) UnmarshalBinary(r io.Reader) error // Read binary format
Canonical Encoding
func (ev *E) ToCanonical(dst []byte) []byte // Canonical encoding for ID
func (ev *E) GetIDBytes() []byte // Compute event ID
func Hash(in []byte) []byte // SHA256 hash
Signatures
func (ev *E) Sign(keys signer.I) error // Sign event with key pair
func (ev *E) Verify() (bool, error) // Verify signature
Usage Example
package main
import (
"fmt"
"time"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/event"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/kind"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/tag"
"git.mleku.dev/mleku/nostr-codec/pkg/interfaces/signer/p8k"
)
func main() {
// Create a signer
keys := p8k.MustNew()
keys.Generate()
// Create an event
ev := event.New()
ev.Kind = kind.TextNote.K
ev.CreatedAt = time.Now().Unix()
ev.Content = []byte("Hello Nostr!")
// Add tags
ev.Tags = tag.NewS(
tag.NewFromBytesSlice([]byte("t"), []byte("nostr")),
tag.NewFromBytesSlice([]byte("p"), keys.Pub()),
)
// Sign the event
if err := ev.Sign(keys); err != nil {
panic(err)
}
// Verify signature
valid, err := ev.Verify()
if err != nil || !valid {
panic("invalid signature")
}
// Marshal to JSON
jsonBytes := ev.Serialize()
fmt.Printf("Event JSON: %s\n", jsonBytes)
// Unmarshal from JSON
ev2 := event.New()
_, err = ev2.Unmarshal(jsonBytes)
if err != nil {
panic(err)
}
// Clone for async processing
clone := ev.Clone()
go processEvent(clone)
// Free original
ev.Free()
}
func processEvent(ev *event.E) {
defer ev.Free()
// Process event...
}
Filter Package
The filter package implements Nostr subscription filters for querying events.
Type
type F struct {
IDs *schnorr.Bytes // Event IDs (hex-encoded in JSON)
Authors *schnorr.Bytes // Author pubkeys (hex-encoded in JSON)
Kinds *kind.S // Event kinds
Tags *tag.S // Tag filters (#e, #p, etc.)
Since *int64 // UNIX timestamp
Until *int64 // UNIX timestamp
Limit *int // Max results
}
Constructor
func New() *F
Methods
func (f *F) Sort() // Sort fields for deterministic output
func (f *F) Matches(ev *event.E) bool // Check if event matches filter
func (f *F) MatchesIgnoringTimestampConstraints(ev *event.E) bool
func (f *F) EstimateSize() int // Estimate serialized size
func (f *F) Marshal(dst []byte) []byte // Marshal to JSON
func (f *F) Serialize() []byte // Marshal to new buffer
func (f *F) Unmarshal(b []byte) ([]byte, error) // Unmarshal from JSON
Usage Example
package main
import (
"fmt"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/filter"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/kind"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/event"
"git.mleku.dev/mleku/nostr-codec/pkg/crypto/ec/schnorr"
)
func main() {
// Create a filter
f := filter.New()
// Filter by kinds
f.Kinds = kind.NewS(kind.TextNote, kind.EncryptedDirectMessage)
// Filter by authors
pubkey, _ := schnorr.ParseBytes("...")
f.Authors = schnorr.NewBytes(pubkey)
// Time constraints
since := int64(1234567890)
until := int64(9999999999)
limit := 100
f.Since = &since
f.Until = &until
f.Limit = &limit
// Tag filters (e.g., #p tag)
f.Tags = tag.NewS(
tag.NewFromBytesSlice([]byte("#p"), pubkey),
)
// Sort for deterministic output
f.Sort()
// Marshal to JSON
jsonBytes := f.Serialize()
fmt.Printf("Filter: %s\n", jsonBytes)
// Check if event matches
ev := event.New()
ev.Kind = kind.TextNote.K
if f.Matches(ev) {
fmt.Println("Event matches filter")
}
}
Tag Package
The tag package provides tag encoding and manipulation.
Types
type T struct {
T [][]byte // Tag elements (first is key, rest are values)
}
type S []*T // Slice of tags
Constructors
func New() *T
func NewWithCap(c int) *T
func NewFromBytesSlice(t ...[]byte) *T
func NewFromAny(t ...any) *T
func NewS(t ...*T) *S
func NewSWithCap(c int) *S
Tag Methods
func (t *T) Free() // Nil all fields
func (t *T) Len() int // Number of elements
func (t *T) Less(i, j int) bool // Compare elements
func (t *T) Swap(i, j int) // Swap elements
func (t *T) Contains(s []byte) bool // Check if contains element
func (t *T) Marshal(dst []byte) []byte // Marshal to JSON
func (t *T) MarshalJSON() ([]byte, error) // Standard marshaler
func (t *T) Unmarshal(b []byte) ([]byte, error) // Unmarshal from JSON
func (t *T) UnmarshalJSON(b []byte) error // Standard unmarshaler
func (t *T) Key() []byte // Get first element (key)
func (t *T) Value() []byte // Get second element (value)
func (t *T) Relay() []byte // Get third element (relay)
func (t *T) ValueHex() []byte // Get value as hex
func (t *T) ValueBinary() []byte // Get value as binary
func (t *T) ToSliceOfStrings() []string // Convert to string slice
func (t *T) Equals(other *T) bool // Compare tags
Tag Slice Methods
func (s *S) Len() int
func (s *S) Less(i, j int) bool
func (s *S) Swap(i, j int)
func (s *S) Append(t ...*T)
func (s *S) Marshal(dst []byte) []byte
func (s *S) Unmarshal(b []byte) ([]byte, error)
func (s *S) GetFirst(tagName []byte) *T // Get first tag with key
func (s *S) GetAll(tagName []byte) []*T // Get all tags with key
func (s *S) FilterOut(tagName []byte) *S // Remove tags with key
Usage Example
package main
import (
"fmt"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/tag"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/hex"
)
func main() {
// Create individual tags
eTag := tag.NewFromBytesSlice(
[]byte("e"),
hex.Dec("...event-id..."),
[]byte("wss://relay.example.com"),
[]byte("reply"),
)
pTag := tag.NewFromBytesSlice(
[]byte("p"),
hex.Dec("...pubkey..."),
)
tTag := tag.NewFromAny("t", "nostr", "protocol")
// Create tag collection
tags := tag.NewS(eTag, pTag, tTag)
// Access tag elements
fmt.Printf("Key: %s\n", eTag.Key()) // "e"
fmt.Printf("Value: %x\n", eTag.Value()) // event-id bytes
fmt.Printf("Relay: %s\n", eTag.Relay()) // relay URL
// Find tags
pTags := tags.GetAll([]byte("p"))
for _, pt := range pTags {
fmt.Printf("P tag: %x\n", pt.Value())
}
// Filter tags
withoutE := tags.FilterOut([]byte("e"))
// Marshal to JSON
jsonBytes := tags.Marshal(nil)
fmt.Printf("Tags: %s\n", jsonBytes)
}
Kind Package
The kind package provides event kind constants and utilities.
Type
type K struct {
K uint16 // Kind number
}
type S struct {
K []*K // Slice of kinds (sortable)
}
Constructors
func New(k uint16) *K
func NewS(k ...*K) *S
func NewWithCap(c int) *S
func FromIntSlice(is []int) *S
Constants
var (
Metadata = New(0) // NIP-01
TextNote = New(1) // NIP-01
RecommendRelay = New(2) // NIP-01
Contacts = New(3) // NIP-02
EncryptedDirectMessage = New(4) // NIP-04
EventDeletion = New(5) // NIP-09
Repost = New(6) // NIP-18
Reaction = New(7) // NIP-25
BadgeAward = New(8) // NIP-58
ChannelCreation = New(40) // NIP-28
ChannelMetadata = New(41) // NIP-28
ChannelMessage = New(42) // NIP-28
ChannelHideMessage = New(43) // NIP-28
ChannelMuteUser = New(44) // NIP-28
FileMetadata = New(1063) // NIP-94
LiveChatMessage = New(1311) // NIP-53
// Replaceable events (10000-19999)
ProfileBadges = New(30008) // NIP-58
BadgeDefinition = New(30009) // NIP-58
// Ephemeral events (20000-29999)
Auth = New(22242) // NIP-42
// ... and many more
)
Methods
func (k *S) Len() int
func (k *S) Less(i, j int) bool
func (k *S) Swap(i, j int)
func (k *S) ToUint16() []uint16
func (k *S) Clone() *S
func (k *S) Contains(s uint16) bool
func (k *S) Equals(t1 *S) bool
func (k *S) Marshal(dst []byte) []byte
func (k *S) Unmarshal(b []byte) ([]byte, error)
func (k *S) IsPrivileged() bool // Check if contains privileged kinds
func (k *K) IsRegular() bool // 1000 <= k < 10000
func (k *K) IsReplaceable() bool // 10000 <= k < 20000 or k in [0,3]
func (k *K) IsEphemeral() bool // 20000 <= k < 30000
func (k *K) IsAddressable() bool // 30000 <= k < 40000
Usage Example
package main
import (
"fmt"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/kind"
)
func main() {
// Use predefined constants
textNote := kind.TextNote
fmt.Printf("Text note kind: %d\n", textNote.K)
// Create custom kind
customKind := kind.New(30023)
// Check kind properties
if customKind.IsAddressable() {
fmt.Println("This is an addressable event")
}
// Create kind filter
kinds := kind.NewS(
kind.TextNote,
kind.Reaction,
kind.Repost,
)
// Check if contains kind
if kinds.Contains(1) {
fmt.Println("Contains text notes")
}
// Convert to uint16 slice
kindNumbers := kinds.ToUint16()
fmt.Printf("Kinds: %v\n", kindNumbers)
}
Envelope Packages
Envelope packages provide WebSocket message framing for the Nostr protocol.
Available Envelopes
eventenvelope- EVENT messages (client to relay)reqenvelope- REQ messages (subscription requests)closeenvelope- CLOSE messages (close subscription)eoseenvelope- EOSE messages (end of stored events)okenvelope- OK messages (command results)noticeenvelope- NOTICE messages (human-readable messages)authenvelope- AUTH messages (NIP-42 authentication)closedenvelope- CLOSED messages (subscription closed by relay)countenvelope- COUNT messages (event counting)
Common Pattern
All envelopes follow a similar pattern:
// Create envelope
env := eventenvelope.NewSubmission()
// Unmarshal from wire format
remainder, err := env.Unmarshal(wireBytes)
// Access data
event := env.E
// Marshal to wire format
wireBytes = env.Marshal(nil)
Event Envelope Example
package main
import (
"fmt"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/envelopes/eventenvelope"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/event"
)
func main() {
// Client submitting event
ev := event.New()
// ... populate event ...
submission := eventenvelope.NewSubmission()
submission.E = ev
wireBytes := submission.Marshal(nil)
// Send wireBytes over WebSocket
// Server receiving event
received := eventenvelope.NewSubmission()
_, err := received.Unmarshal(wireBytes)
if err != nil {
panic(err)
}
// Process received.E
fmt.Printf("Received event: %x\n", received.E.ID)
}
REQ Envelope Example
package main
import (
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/envelopes/reqenvelope"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/filter"
)
func main() {
// Create subscription request
req := reqenvelope.New()
req.Label = []byte("my-subscription")
// Add filters
f1 := filter.New()
// ... configure filter ...
req.Filters = append(req.Filters, f1)
// Marshal
wireBytes := req.Marshal(nil)
// Send over WebSocket
}
OK Envelope Example
package main
import (
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/envelopes/okenvelope"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/reason"
)
func main() {
// Create OK response
ok := okenvelope.New()
ok.ID = eventID
ok.OK = true
ok.Reason = reason.Duplicate.With(": event already exists")
// Marshal
wireBytes := ok.Marshal(nil)
// Send to client
}
Cryptography
Signer Interface
The signer.I interface abstracts key management and cryptographic operations.
type I interface {
Generate() error // Generate new key pair
InitSec(sec []byte) error // Load secret key
InitPub(pub []byte) error // Load public key
Sec() []byte // Get secret key
Pub() []byte // Get public key (x-only)
Sign(msg []byte) ([]byte, error) // Sign message
Verify(msg, sig []byte) (bool, error) // Verify signature
Zero() // Wipe secret key
ECDH(pub []byte) ([]byte, error) // Derive shared secret
ECDHRaw(pub []byte) ([]byte, error) // Raw ECDH (for NIP-44)
}
P8K Implementation
The p8k package provides a purego-based implementation using libsecp256k1.so.
package main
import (
"fmt"
"git.mleku.dev/mleku/nostr-codec/pkg/interfaces/signer/p8k"
)
func main() {
// Create new signer
keys := p8k.MustNew()
// Generate random key pair
if err := keys.Generate(); err != nil {
panic(err)
}
fmt.Printf("Public key: %x\n", keys.Pub())
fmt.Printf("Secret key: %x\n", keys.Sec())
// Sign message
msg := []byte("hello")
sig, err := keys.Sign(msg)
if err != nil {
panic(err)
}
// Verify signature
valid, err := keys.Verify(msg, sig)
if err != nil || !valid {
panic("invalid signature")
}
// ECDH for encryption
recipientPub := []byte{/* 32 bytes */}
sharedSecret, err := keys.ECDH(recipientPub)
if err != nil {
panic(err)
}
fmt.Printf("Shared secret: %x\n", sharedSecret)
// Wipe keys when done
defer keys.Zero()
}
Loading Existing Keys
package main
import (
"git.mleku.dev/mleku/nostr-codec/pkg/interfaces/signer/p8k"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/hex"
)
func main() {
keys := p8k.MustNew()
// Load from hex secret key
secHex := "..."
secBytes := hex.Dec(secHex)
if err := keys.InitSec(secBytes); err != nil {
panic(err)
}
// Public key is automatically derived
fmt.Printf("Loaded pubkey: %x\n", keys.Pub())
}
Schnorr Signatures
The schnorr package provides low-level signature operations.
package main
import (
"git.mleku.dev/mleku/nostr-codec/pkg/crypto/ec/schnorr"
)
func main() {
// Parse public key
pubBytes, err := schnorr.ParseBytes("hex-pubkey")
if err != nil {
panic(err)
}
// Verify signature
msg := []byte("message hash")
sigBytes := []byte{/* 64 bytes */}
if !schnorr.Verify(pubBytes, msg, sigBytes) {
panic("invalid signature")
}
}
Encryption (NIP-04 and NIP-44)
package main
import (
"git.mleku.dev/mleku/nostr-codec/pkg/crypto/encryption"
"git.mleku.dev/mleku/nostr-codec/pkg/interfaces/signer/p8k"
)
func main() {
// Sender keys
sender := p8k.MustNew()
sender.Generate()
// Recipient keys
recipient := p8k.MustNew()
recipient.Generate()
plaintext := "Secret message"
// NIP-44 encryption (recommended)
ciphertext, err := encryption.Nip44Encrypt(
plaintext,
sender,
recipient.Pub(),
)
if err != nil {
panic(err)
}
// NIP-44 decryption
decrypted, err := encryption.Nip44Decrypt(
ciphertext,
recipient,
sender.Pub(),
)
if err != nil {
panic(err)
}
// NIP-04 encryption (legacy)
ciphertext04, err := encryption.Nip04Encrypt(
plaintext,
sender,
recipient.Pub(),
)
if err != nil {
panic(err)
}
// NIP-04 decryption
decrypted04, err := encryption.Nip04Decrypt(
ciphertext04,
recipient,
sender.Pub(),
)
}
Bech32 Encoding
The bech32encoding package provides npub/nsec/note encoding.
package main
import (
"fmt"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/bech32encoding"
"git.mleku.dev/mleku/nostr-codec/pkg/interfaces/signer/p8k"
)
func main() {
keys := p8k.MustNew()
keys.Generate()
// Encode public key as npub
npub := bech32encoding.PubkeyToNpub(keys.Pub())
fmt.Printf("npub: %s\n", npub)
// Encode secret key as nsec
nsec := bech32encoding.SecToNsec(keys.Sec())
fmt.Printf("nsec: %s\n", nsec)
// Decode npub
pubBytes, err := bech32encoding.NpubToPubkey(npub)
if err != nil {
panic(err)
}
// Decode nsec
secBytes, err := bech32encoding.NsecToSec(nsec)
if err != nil {
panic(err)
}
// Encode event ID as note
eventID := []byte{/* 32 bytes */}
note := bech32encoding.EventIDToNote(eventID)
fmt.Printf("note: %s\n", note)
// Decode note
decodedID, err := bech32encoding.NoteToEventID(note)
if err != nil {
panic(err)
}
}
Utilities
Buffer Pool
The bufpool package provides efficient buffer pooling for zero-copy operations.
package main
import (
"git.mleku.dev/mleku/nostr-codec/pkg/utils/bufpool"
)
func main() {
// Get buffer from pool
buf := bufpool.Get()
defer bufpool.Put(buf)
// Use buffer
buf = append(buf, []byte("data")...)
// Buffer is returned to pool on Put
}
Atomic Operations
The atomic package provides extended atomic operations beyond the standard library.
package main
import (
"git.mleku.dev/mleku/nostr-codec/pkg/utils/atomic"
)
func main() {
var counter atomic.Int64
counter.Store(0)
counter.Add(1)
value := counter.Load()
counter.CompareAndSwap(1, 2)
}
Hex Encoding
The hex package provides SIMD-accelerated hex encoding.
package main
import (
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/hex"
)
func main() {
// Encode to hex
data := []byte{0x01, 0x02, 0x03}
hexStr := hex.Enc(data)
// Append to buffer
buf := make([]byte, 0, 64)
buf = hex.EncAppend(buf, data)
// Decode from hex
decoded := hex.Dec("010203")
}
Text Utilities
The text package provides JSON escaping and text processing.
package main
import (
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/text"
)
func main() {
// Escape for JSON
raw := []byte(`Hello "world"`)
escaped := text.EscapeJSONString(nil, raw)
// Unescape from JSON
unescaped := text.UnescapeJSONString(nil, escaped)
// Check if needs escaping
if text.NeedsEscape(raw) {
// Handle escaping
}
}
Performance Optimizations
SIMD Acceleration
- SHA256: Uses
minio/sha256-simdfor hardware-accelerated hashing - Hex Encoding: Uses
templexxx/xhexfor SIMD hex encoding
Zero-Copy Operations
- Buffer pooling via
bufpoolpackage - Reusable byte slices in all encoders
Marshal(dst []byte)methods append to existing buffers
Memory Management
// Efficient encoding pattern
buf := bufpool.Get()
defer bufpool.Put(buf)
buf = event.Marshal(buf) // Append to buffer
buf = filter.Marshal(buf) // Append more
Binary Encoding
For maximum performance, use binary encoding instead of JSON:
// Binary encoding (more compact, faster)
var buf bytes.Buffer
ev.MarshalBinary(&buf)
// Or to bytes
binBytes := ev.MarshalBinaryToBytes(nil)
Testing
Run tests with:
go test ./...
Run benchmarks:
go test -bench=. -benchmem ./pkg/encoders/event
go test -bench=. -benchmem ./pkg/encoders/filter
Dependencies
Required
github.com/minio/sha256-simd- SIMD-accelerated SHA256github.com/templexxx/xhex- SIMD hex encodinggithub.com/ebitengine/purego- CGO-free library loadinglol.mleku.dev- Logging and error handlinggolang.org/x/crypto- Cryptographic primitivesgolang.org/x/exp- Experimental packages (constraints)
System Requirements
libsecp256k1.somust be available inLD_LIBRARY_PATHor the same directory as the binary- Go 1.25.3 or later
Architecture
Package Structure
pkg/
encoders/ # Nostr protocol encoders
event/ # Event encoding
filter/ # Filter encoding
tag/ # Tag encoding
kind/ # Kind constants
envelopes/ # WebSocket envelopes
hex/ # Hex encoding
text/ # Text utilities
bech32encoding/# Bech32 encoding
crypto/ # Cryptographic operations
ec/ # Elliptic curve crypto
schnorr/ # Schnorr signatures
secp256k1/# secp256k1 curve
bech32/ # Bech32 encoding
encryption/ # NIP-04/NIP-44
p8k/ # Purego secp256k1
interfaces/ # Abstract interfaces
signer/ # Signer interface
codec/ # Codec interfaces
utils/ # Utility packages
bufpool/ # Buffer pooling
atomic/ # Atomic operations
constraints/ # Type constraints
protocol/ # Protocol helpers
auth/ # NIP-42 auth
Design Principles
- Zero-Copy: All
Marshalmethods acceptdst []byteto append to existing buffers - Buffer Pooling: Use
bufpool.Get/Putfor temporary buffers - Pure Go: No CGO, uses purego for C library interop
- Performance: SIMD acceleration where available
- Correctness: Extensive test coverage
Examples
Complete Relay Handler Example
package main
import (
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/envelopes/eventenvelope"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/envelopes/okenvelope"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/reason"
"git.mleku.dev/mleku/nostr-codec/pkg/utils/bufpool"
)
func handleEventMessage(msg []byte) []byte {
// Parse event envelope
env := eventenvelope.NewSubmission()
_, err := env.Unmarshal(msg)
if err != nil {
return errorResponse(nil, reason.Invalid.With(": parse error"))
}
defer env.E.Free()
// Verify signature
valid, err := env.E.Verify()
if err != nil || !valid {
return errorResponse(env.E.ID, reason.Invalid.With(": invalid signature"))
}
// Store event (pseudo-code)
if err := database.SaveEvent(env.E); err != nil {
return errorResponse(env.E.ID, reason.Error.With(": database error"))
}
// Return OK
return successResponse(env.E.ID)
}
func successResponse(eventID []byte) []byte {
ok := okenvelope.New()
ok.ID = eventID
ok.OK = true
ok.Reason = []byte("")
return ok.Marshal(nil)
}
func errorResponse(eventID []byte, reason []byte) []byte {
ok := okenvelope.New()
ok.ID = eventID
ok.OK = false
ok.Reason = reason
return ok.Marshal(nil)
}
Complete Client Example
package main
import (
"fmt"
"time"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/event"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/kind"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/envelopes/eventenvelope"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/envelopes/reqenvelope"
"git.mleku.dev/mleku/nostr-codec/pkg/encoders/filter"
"git.mleku.dev/mleku/nostr-codec/pkg/interfaces/signer/p8k"
)
func main() {
// Initialize keys
keys := p8k.MustNew()
keys.Generate()
// Create and publish event
ev := event.New()
ev.Kind = kind.TextNote.K
ev.CreatedAt = time.Now().Unix()
ev.Content = []byte("Hello Nostr!")
ev.Sign(keys)
// Wrap in envelope
envEvent := eventenvelope.NewSubmission()
envEvent.E = ev
wireBytes := envEvent.Marshal(nil)
// Send to relay
// ws.Send(wireBytes)
// Subscribe to events
req := reqenvelope.New()
req.Label = []byte("my-feed")
f := filter.New()
f.Kinds = kind.NewS(kind.TextNote)
limit := 100
f.Limit = &limit
req.Filters = append(req.Filters, f)
subscribeBytes := req.Marshal(nil)
// Send subscription
// ws.Send(subscribeBytes)
fmt.Printf("Published event: %x\n", ev.ID)
}
Contributing
Contributions are welcome! Please ensure:
- All tests pass:
go test ./... - Code is formatted:
go fmt ./... - Benchmarks show no regressions
- Documentation is updated
License
[Insert license information]
Credits
This library is extracted from the ORLY relay implementation, designed for high-performance Nostr protocol handling.
Key Technologies
- Cryptography: Uses Bitcoin Core's
libsecp256k1via purego - SIMD: MinIO's SHA256 and templexxx's hex encoder
- Logging: lol.mleku.dev logging framework