nostr
Nostr Protocol Expert
Purpose
This skill provides expert-level assistance with the Nostr protocol, a simple, open protocol for global, decentralized, and censorship-resistant social networks. The protocol is built on relays and cryptographic keys, enabling direct peer-to-peer communication without central servers.
When to Use
Activate this skill when:
- Implementing Nostr clients or relays
- Working with Nostr events and messages
- Handling cryptographic signatures and keys (schnorr signatures on secp256k1)
- Implementing any Nostr Implementation Possibility (NIP)
- Building social networking features on Nostr
- Querying or filtering Nostr events
- Discussing Nostr protocol architecture
- Implementing WebSocket communication with relays
Core Concepts
The Protocol Foundation
Nostr operates on two main components:
- Clients - Applications users run to read/write data
- Relays - Servers that store and forward messages
Key principles:
- Everyone runs a client
- Anyone can run a relay
- Users identified by public keys
- Messages signed with private keys
- No central authority or trusted servers
Events Structure
All data in Nostr is represented as events. An event is a JSON object with this structure:
{
"id": "<32-bytes lowercase hex-encoded sha256 of the serialized event data>",
"pubkey": "<32-bytes lowercase hex-encoded public key of the event creator>",
"created_at": "<unix timestamp in seconds>",
"kind": "<integer identifying event type>",
"tags": [
["<tag name>", "<tag value>", "<optional third param>", "..."]
],
"content": "<arbitrary string>",
"sig": "<64-bytes lowercase hex of the schnorr signature of the sha256 hash of the serialized event data>"
}
Event Kinds
Standard event kinds (from various NIPs):
0- Metadata (user profile)1- Text note (short post)2- Recommend relay3- Contacts (following list)4- Encrypted direct messages5- Event deletion6- Repost7- Reaction (like, emoji reaction)40- Channel creation41- Channel metadata42- Channel message43- Channel hide message44- Channel mute user1000-9999- Regular events10000-19999- Replaceable events20000-29999- Ephemeral events30000-39999- Parameterized replaceable events
Tags
Common tag types:
["e", "<event-id>", "<relay-url>", "<marker>"]- Reference to an event["p", "<pubkey>", "<relay-url>"]- Reference to a user["a", "<kind>:<pubkey>:<d-tag>", "<relay-url>"]- Reference to a replaceable event["d", "<identifier>"]- Identifier for parameterized replaceable events["r", "<url>"]- Reference/link to a web resource["t", "<hashtag>"]- Hashtag["g", "<geohash>"]- Geolocation["nonce", "<number>", "<difficulty>"]- Proof of work["subject", "<subject>"]- Subject/title["client", "<client-name>"]- Client application used
Key NIPs Reference
For detailed specifications, refer to references/nips-overview.md.
Core Protocol NIPs
NIP-01: Basic Protocol Flow
The foundation of Nostr. Defines:
- Event structure and validation
- Event ID calculation (SHA256 of serialized event)
- Signature verification (schnorr signatures)
- Client-relay communication via WebSocket
- Message types: EVENT, REQ, CLOSE, EOSE, OK, NOTICE
NIP-02: Contact List and Petnames
Event kind 3 for following lists:
- Each
ptag represents a followed user - Optional relay URL and petname in tag
- Replaceable event (latest overwrites)
NIP-04: Encrypted Direct Messages
Event kind 4 for private messages:
- Content encrypted with shared secret (ECDH)
ptag for recipient pubkey- Deprecated in favor of NIP-44
NIP-05: Mapping Nostr Keys to DNS
Internet identifier format: name@domain.com
.well-known/nostr.jsonendpoint- Maps names to pubkeys
- Optional relay list
NIP-09: Event Deletion
Event kind 5 to request deletion:
- Contains
etags for events to delete - Relays should delete referenced events
- Only works for own events
NIP-10: Text Note References (Threads)
Conventions for e and p tags in replies:
- Root event reference
- Reply event reference
- Mentions
- Marker types: "root", "reply", "mention"
NIP-11: Relay Information Document
HTTP endpoint for relay metadata:
- GET request to relay URL
- Returns JSON with relay information
- Supported NIPs, software, limitations
Social Features NIPs
NIP-25: Reactions
Event kind 7 for reactions:
- Content usually "+" (like) or emoji
etag for reacted eventptag for event author
NIP-42: Authentication
Client authentication to relays:
- AUTH message from relay
- Client responds with event kind
22242 - Proves key ownership
NIP-50: Search
Query filter extension for full-text search:
searchfield in REQ filters- Implementation-defined behavior
Advanced NIPs
NIP-19: bech32-encoded Entities
Human-readable identifiers:
npub: public keynsec: private key (sensitive!)note: note/event IDnprofile: profile with relay hintsnevent: event with relay hintsnaddr: replaceable event coordinate
NIP-44: Encrypted Payloads
Improved encryption for direct messages:
- Versioned encryption scheme
- Better security than NIP-04
- ChaCha20-Poly1305 AEAD
NIP-65: Relay List Metadata
Event kind 10002 for relay lists:
- Read/write relay preferences
- Optimizes relay discovery
- Replaceable event
Client-Relay Communication
WebSocket Messages
From Client to Relay
EVENT - Publish an event:
["EVENT", <event JSON>]
REQ - Request events (subscription):
["REQ", <subscription_id>, <filters JSON>, <filters JSON>, ...]
CLOSE - Stop a subscription:
["CLOSE", <subscription_id>]
AUTH - Respond to auth challenge:
["AUTH", <signed event kind 22242>]
From Relay to Client
EVENT - Send event to client:
["EVENT", <subscription_id>, <event JSON>]
OK - Acceptance/rejection notice:
["OK", <event_id>, <true|false>, <message>]
EOSE - End of stored events:
["EOSE", <subscription_id>]
CLOSED - Subscription closed:
["CLOSED", <subscription_id>, <message>]
NOTICE - Human-readable message:
["NOTICE", <message>]
AUTH - Authentication challenge:
["AUTH", <challenge>]
Filter Objects
Filters select events in REQ messages:
{
"ids": ["<event-id>", ...],
"authors": ["<pubkey>", ...],
"kinds": [<kind number>, ...],
"#e": ["<event-id>", ...],
"#p": ["<pubkey>", ...],
"#a": ["<coordinate>", ...],
"#t": ["<hashtag>", ...],
"since": <unix timestamp>,
"until": <unix timestamp>,
"limit": <max number of events>
}
Filtering rules:
- Arrays are ORed together
- Different fields are ANDed
- Tag filters:
#<single-letter>matches tag values - Prefix matching allowed for
idsandauthors
Cryptographic Operations
Key Management
- Private Key: 32-byte random value, keep secure
- Public Key: Derived via secp256k1
- Encoding: Hex (lowercase) or bech32
Event Signing (schnorr)
Steps to create a signed event:
- Set all fields except
idandsig - Serialize event data to JSON (specific order)
- Calculate SHA256 hash →
id - Sign
idwith schnorr signature →sig
Serialization format for ID calculation:
[
0,
<pubkey>,
<created_at>,
<kind>,
<tags>,
<content>
]
Event Verification
Steps to verify an event:
- Verify ID matches SHA256 of serialized data
- Verify signature is valid schnorr signature
- Check created_at is reasonable (not far future)
- Validate event structure and required fields
Implementation Best Practices
For Clients
- Connect to Multiple Relays: Don't rely on single relay
- Cache Events: Reduce redundant relay queries
- Verify Signatures: Always verify event signatures
- Handle Replaceable Events: Keep only latest version
- Respect User Privacy: Careful with sensitive data
- Implement NIP-65: Use user's preferred relays
- Proper Error Handling: Handle relay disconnections
- Pagination: Use
limit,since,untilfor queries
For Relays
- Validate Events: Check signatures, IDs, structure
- Rate Limiting: Prevent spam and abuse
- Storage Management: Ephemeral events, retention policies
- Implement NIP-11: Provide relay information
- WebSocket Optimization: Handle many connections
- Filter Optimization: Efficient event querying
- Consider NIP-42: Authentication for write access
- Performance: Index by pubkey, kind, tags, timestamp
Security Considerations
- Never Expose Private Keys: Handle nsec carefully
- Validate All Input: Prevent injection attacks
- Use NIP-44: For encrypted messages (not NIP-04)
- Check Event Timestamps: Reject far-future events
- Implement Proof of Work: NIP-13 for spam prevention
- Sanitize Content: XSS prevention in displayed content
- Relay Trust: Don't trust single relay for critical data
Common Patterns
Publishing a Note
const event = {
pubkey: userPublicKey,
created_at: Math.floor(Date.now() / 1000),
kind: 1,
tags: [],
content: "Hello Nostr!",
}
// Calculate ID and sign
event.id = calculateId(event)
event.sig = signEvent(event, privateKey)
// Publish to relay
ws.send(JSON.stringify(["EVENT", event]))
Subscribing to Notes
const filter = {
kinds: [1],
authors: [followedPubkey1, followedPubkey2],
limit: 50
}
ws.send(JSON.stringify(["REQ", "my-sub", filter]))
Replying to a Note
const reply = {
kind: 1,
tags: [
["e", originalEventId, relayUrl, "root"],
["p", originalAuthorPubkey]
],
content: "Great post!",
// ... other fields
}
Reacting to a Note
const reaction = {
kind: 7,
tags: [
["e", eventId],
["p", eventAuthorPubkey]
],
content: "+", // or emoji
// ... other fields
}
Development Resources
Essential NIPs for Beginners
Start with these NIPs in order:
- NIP-01 - Basic protocol (MUST read)
- NIP-19 - Bech32 identifiers
- NIP-02 - Following lists
- NIP-10 - Threaded conversations
- NIP-25 - Reactions
- NIP-65 - Relay lists
Testing and Development
- Relay Implementations: nostream, strfry, relay.py
- Test Relays: wss://relay.damus.io, wss://nos.lol
- Libraries: nostr-tools (JS), rust-nostr (Rust), python-nostr (Python)
- Development Tools: NostrDebug, Nostr Army Knife, nostril
- Reference Clients: Damus (iOS), Amethyst (Android), Snort (Web)
Key Repositories
- NIPs Repository: https://github.com/nostr-protocol/nips
- Awesome Nostr: https://github.com/aljazceru/awesome-nostr
- Nostr Resources: https://nostr.how
Reference Files
For comprehensive NIP details, see:
- references/nips-overview.md - Detailed descriptions of all standard NIPs
- references/event-kinds.md - Complete event kinds reference
- references/common-mistakes.md - Pitfalls and how to avoid them
Quick Checklist
When implementing Nostr:
- Events have all required fields (id, pubkey, created_at, kind, tags, content, sig)
- Event IDs calculated correctly (SHA256 of serialization)
- Signatures verified (schnorr on secp256k1)
- WebSocket messages properly formatted
- Filter queries optimized with appropriate limits
- Handling replaceable events correctly
- Connected to multiple relays for redundancy
- Following relevant NIPs for features implemented
- Private keys never exposed or transmitted
- Event timestamps validated
Official Resources
- NIPs Repository: https://github.com/nostr-protocol/nips
- Nostr Website: https://nostr.com
- Nostr Documentation: https://nostr.how
- NIP Status: https://nostr-nips.com