SKILL.md raw


name: nostr description: This skill should be used when working with the Nostr protocol, implementing Nostr clients or relays, handling Nostr events, or discussing Nostr Implementation Possibilities (NIPs). Provides comprehensive knowledge of Nostr's decentralized protocol, event structure, cryptographic operations, and all standard NIPs.


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:

Core Concepts

The Protocol Foundation

Nostr operates on two main components:

  1. Clients - Applications users run to read/write data
  2. Relays - Servers that store and forward messages

Key principles:

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):

Tags

Common tag types:

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:

NIP-02: Contact List and Petnames

Event kind 3 for following lists:

NIP-04: Encrypted Direct Messages

Event kind 4 for private messages:

NIP-05: Mapping Nostr Keys to DNS

Internet identifier format: name@domain.com

NIP-09: Event Deletion

Event kind 5 to request deletion:

NIP-10: Text Note References (Threads)

Conventions for e and p tags in replies:

NIP-11: Relay Information Document

HTTP endpoint for relay metadata:

Social Features NIPs

NIP-25: Reactions

Event kind 7 for reactions:

NIP-42: Authentication

Client authentication to relays:

CRITICAL: Clients MUST wait for OK response after AUTH

1. Alert the user that authentication failed 2. Assume the relay will reject subsequent events requiring auth 3. Check the reason field for error details (e.g., "error: failed to parse auth event")

NIP-50: Search

Query filter extension for full-text search:

Advanced NIPs

NIP-19: bech32-encoded Entities

Human-readable identifiers:

NIP-44: Encrypted Payloads

Improved encryption for direct messages:

NIP-65: Relay List Metadata

Event kind 10002 for relay lists:

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:

Cryptographic Operations

Key Management

Event Signing (schnorr)

Steps to create a signed event:

  1. Set all fields except id and sig
  2. Serialize event data to JSON (specific order)
  3. Calculate SHA256 hash → id
  4. Sign id with schnorr signature → sig

Serialization format for ID calculation:

[
  0,
  <pubkey>,
  <created_at>,
  <kind>,
  <tags>,
  <content>
]

Event Verification

Steps to verify an event:

  1. Verify ID matches SHA256 of serialized data
  2. Verify signature is valid schnorr signature
  3. Check created_at is reasonable (not far future)
  4. Validate event structure and required fields

Implementation Best Practices

For Clients

  1. Connect to Multiple Relays: Don't rely on single relay
  2. Cache Events: Reduce redundant relay queries
  3. Verify Signatures: Always verify event signatures
  4. Handle Replaceable Events: Keep only latest version
  5. Respect User Privacy: Careful with sensitive data
  6. Implement NIP-65: Use user's preferred relays
  7. Proper Error Handling: Handle relay disconnections
  8. Pagination: Use limit, since, until for queries

For Relays

  1. Validate Events: Check signatures, IDs, structure
  2. Rate Limiting: Prevent spam and abuse
  3. Storage Management: Ephemeral events, retention policies
  4. Implement NIP-11: Provide relay information
  5. WebSocket Optimization: Handle many connections
  6. Filter Optimization: Efficient event querying
  7. Consider NIP-42: Authentication for write access
  8. Performance: Index by pubkey, kind, tags, timestamp

Security Considerations

  1. Never Expose Private Keys: Handle nsec carefully
  2. Validate All Input: Prevent injection attacks
  3. Use NIP-44: For encrypted messages (not NIP-04)
  4. Check Event Timestamps: Reject far-future events
  5. Implement Proof of Work: NIP-13 for spam prevention
  6. Sanitize Content: XSS prevention in displayed content
  7. 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:

  1. NIP-01 - Basic protocol (MUST read)
  2. NIP-19 - Bech32 identifiers
  3. NIP-02 - Following lists
  4. NIP-10 - Threaded conversations
  5. NIP-25 - Reactions
  6. NIP-65 - Relay lists

Testing and Development

Key Repositories

Reference Files

For comprehensive NIP details, see:

Quick Checklist

When implementing Nostr:

Official Resources