Nostr Protocol — Complete Technical Reference

Core protocol architecture, events, tags, subscriptions, WebSocket communication, identity, and key management

Nostr Protocol

Notes Other Stuff Transmitted by Relays

An open, decentralized protocol for censorship-resistant social networking and beyond.

Core Architecture

┌─────────┐     WebSocket      ┌─────────┐
│ Client  │ ────────────────> │  Relay  │
│ (you)   │ <──────────────── │(server) │
└─────────┘   EVENT / REQ     └─────────┘
     │                                │
     │  secp256k1 keypair            │
     │  + Schnorr signatures         │
     └────────────────────────────────

Three primitives only:

  1. Keypair (secp256k1) = Identity
  2. Event (signed JSON) = Every piece of data
  3. Relay (WebSocket server) = Dumb storage and forwarding

No servers you depend on. No accounts. No passwords.

The Event Object

Every object in Nostr is an event. Format:

{
  "id": "<sha256 of serialized event data>",
  "pubkey": "<32-byte hex public key>",
  "created_at": <unix timestamp>,
  "kind": <integer 0-65535>,
  "tags": [["e", "<event-id>"], ["p", "<pubkey>"], ...],
  "content": "<arbitrary string>",
  "sig": "<64-byte Schnorr signature>"
}

Event Serialization (for ID generation)

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

Critical serialization rules:

  • UTF-8 encoding
  • No extra whitespace
  • Escape: \n, \", \\, \r, \t, \b, \f in content
  • id = sha256(this serialization)

Signature

  • Algorithm: Schnorr signatures (BIP-340) on secp256k1
  • Signed over the id field
  • Verifiable by anyone using the pubkey

Standard Tags

Tag Meaning Indexed?
e Event ID reference Yes
p Pubkey reference Yes
a Addressable event (kind:pubkey:d) Yes
r Relay URL No
t Hashtag Yes
relays Relays for fetching data No
amount Lightning amount in msats No
lnurl Lightning URL No
d Unique identifier (addressable events) Yes
alt Human-readable description No

Event Kind Ranges

Range Category Behavior
0 Metadata Replaceable (latest per pubkey)
1 Text Note Regular (stored forever)
3 Follows Replaceable
4 Encrypted DM Regular
5 Deletion Regular
6 Repost Regular
7 Reaction Regular
40-49 Channels Regular
1k-10k Regular Stored forever
10k-20k Replaceable Latest per (pubkey, kind)
20k-30k Ephemeral Not stored
30k-40k Addressable Latest per (pubkey, kind, d)
50xx Job Request (DVM) Regular
60xx Job Result (DVM) Regular
9734 Zap Request Regular
9735 Zap Receipt Regular
10002 Relay List Metadata Replaceable
13194 NWC Info Replaceable
23194 NWC Request Ephemeral
23195 NWC Response Ephemeral
30023 Long-form Article Addressable
31339 Agent Metadata Addressable

Client-Relay Communication

WebSocket Messages

All messages are JSON arrays.

Client → Relay

EVENT — Publish an event

["EVENT", "<subscription-id>", <event object>]

REQ — Subscribe to events

["REQ", "<subscription-id>", {
  "authors": ["<pubkey>", ...],
  "kinds": [0, 1, 4],
  "#e": ["<event-id>"],
  "since": 1700000000,
  "until": 1700100000,
  "limit": 100
}]

CLOSE — Unsubscribe

["CLOSE", "<subscription-id>"]

Relay → Client

EVENT — Matching event

["EVENT", "<subscription-id>", <event object>]

EOSE — End of stored events

["EOSE", "<subscription-id>"]

NOTICE — Human-readable message

["NOTICE", "This relay is full"]

OK — Event acceptance/rejection

["OK", "<event-id>", true, ""]
["OK", "<event-id>", false, "error: duplicate"]

Filter Parameters

Param Type Description
ids hex[] Event IDs (64 chars each)
authors hex[] Pubkeys (64 chars each)
kinds int[] Event kind numbers
#e hex[] Events referenced (by e tag)
#p hex[] Pubkeys referenced (by p tag)
#a strings[] Addressable events
since unix ts Events created at or after
until unix ts Events created at or before
limit int Max events in initial query

Multiple filters in one REQ = OR logic. Multiple conditions in one filter = AND logic.

Identifier Formats (NIP-19)

Prefix Format Example
npub Public key (bech32) npub1...
nsec Private key (bech32) nsec1...
note Event ID (bech32) note1...
nevent Event with relays nevent1...
naddr Addressable event naddr1...
nprofile Profile with relays nprofile1...

Convert hex to npub:

# Using nostr-cli
echo "<hex-pubkey>" | nostr nip19 encode

NIP-65: Relay List Metadata

Published as kind 10002. Tells clients which relays to use.

{
  "kind": 10002,
  "content": "",
  "tags": [
    ["r", "wss://relay.damus.io"],
    ["r", "wss://nos.lol", "read"],
    ["r", "wss://relay.primal.net", "write"],
    ["r", "wss://expensive-relay.example.com", "write"]
  ]
}
  • No marker = both read and write
  • read marker = only read from
  • write marker = only publish to

Best practice: 2-4 relays of each type. Spread your kind 10002 widely.

Key Properties

Censorship Resistance

  • No central authority
  • Content persists as long as one relay stores it
  • Client-side signing prevents relay forgery
  • Multi-relay publishing for redundancy

Extensibility

  • New event kinds via NIPs
  • Relays don’t need updates for new kinds
  • Clients implement whatever NIPs they choose

Privacy

  • DMs encrypted (NIP-44, NIP-17)
  • Metadata hiding (NIP-59 gift wrap)
  • No phone number, no email, no KYC

Further Reading

  • NIPs — Full NIP catalog
  • Relays — Relay management
  • Encryption — Privacy specs
  • Zaps — Lightning payments on Nostr

Write a comment
No comments yet.