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:
json
{
"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):
- - Metadata (user profile)
- - Text note (short post)
- - Recommend relay
- - Contacts (following list)
- - Encrypted direct messages
- - Event deletion
- - Repost
- - Reaction (like, emoji reaction)
- - Channel creation
- - Channel metadata
- - Channel message
- - Channel hide message
- - Channel mute user
- - Regular events
- - Replaceable events
- - Ephemeral events
- - 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
- - Identifier for parameterized replaceable events
- - Reference/link to a web resource
- - Hashtag
- - Geolocation
["nonce", "<number>", "<difficulty>"]
- Proof of work
- - 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
for following lists:
- Each tag represents a followed user
- Optional relay URL and petname in tag
- Replaceable event (latest overwrites)
NIP-04: Encrypted Direct Messages
Event kind
for private messages:
- Content encrypted with shared secret (ECDH)
- tag for recipient pubkey
- Deprecated in favor of NIP-44
NIP-05: Mapping Nostr Keys to DNS
Internet identifier format:
- endpoint
- Maps names to pubkeys
- Optional relay list
NIP-09: Event Deletion
Event kind
to request deletion:
- Contains tags for events to delete
- Relays should delete referenced events
- Only works for own events
NIP-10: Text Note References (Threads)
Conventions for
and
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
for reactions:
- Content usually "+" (like) or emoji
- tag for reacted event
- tag for event author
NIP-42: Authentication
Client authentication to relays:
- AUTH message from relay
- Client responds with event kind
- Proves key ownership
NIP-50: Search
Query filter extension for full-text search:
- field in REQ filters
- Implementation-defined behavior
Advanced NIPs
NIP-19: bech32-encoded Entities
Human-readable identifiers:
- : public key
- : private key (sensitive!)
- : note/event ID
- : profile with relay hints
- : event with relay hints
- : 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
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:
REQ - Request events (subscription):
json
["REQ", <subscription_id>, <filters JSON>, <filters JSON>, ...]
CLOSE - Stop a subscription:
json
["CLOSE", <subscription_id>]
AUTH - Respond to auth challenge:
json
["AUTH", <signed event kind 22242>]
From Relay to Client
EVENT - Send event to client:
json
["EVENT", <subscription_id>, <event JSON>]
OK - Acceptance/rejection notice:
json
["OK", <event_id>, <true|false>, <message>]
EOSE - End of stored events:
json
["EOSE", <subscription_id>]
CLOSED - Subscription closed:
json
["CLOSED", <subscription_id>, <message>]
NOTICE - Human-readable message:
AUTH - Authentication challenge:
Filter Objects
Filters select events in REQ messages:
json
{
"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: matches tag values
- Prefix matching allowed for and
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 and
- Serialize event data to JSON (specific order)
- Calculate SHA256 hash →
- Sign with schnorr signature →
Serialization format for ID calculation:
json
[
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 , , for 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
javascript
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
javascript
const filter = {
kinds: [1],
authors: [followedPubkey1, followedPubkey2],
limit: 50
}
ws.send(JSON.stringify(["REQ", "my-sub", filter]))
Replying to a Note
javascript
const reply = {
kind: 1,
tags: [
["e", originalEventId, relayUrl, "root"],
["p", originalAuthorPubkey]
],
content: "Great post!",
// ... other fields
}
Reacting to a Note
javascript
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
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:
Official Resources