Nostr
Nostr (Notes and Other Stuff Transmitted by Relays) is a simple, censorship-resistant protocol for decentralized communication.
Core Concepts
Events
Everything in Nostr is an event — a signed JSON object:
{
"id": "abc123...",
"pubkey": "npub1...",
"created_at": 1234567890,
"kind": 1,
"tags": [
["e", "reply-to-event-id"],
["p", "mentioned-pubkey"],
["t", "bitcoin"]
],
"content": "Hello, Nostr!",
"sig": "signature..."
}
Key properties:
| Field | Description |
|---|---|
| id | SHA256 hash of the serialized event |
| pubkey | Author's public key (hex) |
| created_at | Unix timestamp |
| kind | Event type (determines meaning) |
| tags | Array of arrays for metadata |
| content | Event payload |
| sig | Schnorr signature (secp256k1) |
Keys
Your identity is a keypair:
| Private Key (nsec) | Public Key (npub) |
|---|---|
| Keep secret! | Share freely |
| Signs events | Your identity |
| Controls account | Others mention you |
| Cannot be recovered | Derived from nsec |
nsec1xyz789... → npub1abc123...
No registration. No username. Just cryptographic keys.
Key Formats
| Format | Prefix | Use |
|---|---|---|
| npub | npub1 | Public key (bech32) |
| nsec | nsec1 | Private key (bech32) |
| note | note1 | Event ID |
| nevent | nevent1 | Event with relay hints |
| nprofile | nprofile1 | Profile with relay hints |
| naddr | naddr1 | Addressable event reference |
Relays
Relays are servers that store and forward events:
Key properties:
- Relays don't verify identity — the signature does
- Relays can filter, rate-limit, or charge
- Users choose which relays to use
- Events are replicated across relays
Multiple Relays
Users connect to multiple relays for redundancy:
const relays = [
"wss://relay.damus.io",
"wss://relay.nostr.band",
"wss://nos.lol",
"wss://relay.snort.social"
];
If one relay goes down or censors you, others still have your events.
Event Kinds
Basic Kinds
| Kind | Purpose | Replaceable |
|---|---|---|
| 0 | Profile metadata | Yes |
| 1 | Text note (short post) | No |
| 2 | Relay list (deprecated) | Yes |
| 3 | Contact list (follows) | Yes |
| 4 | Encrypted DM (legacy) | No |
| 5 | Event deletion | No |
| 6 | Repost | No |
| 7 | Reaction (like) | No |
Extended Kinds
| Kind | Purpose |
|---|---|
| 1063 | File metadata |
| 1984 | Report |
| 9734 | Zap request |
| 9735 | Zap receipt |
| 10002 | Relay list metadata |
| 30000 | Categorized people list |
| 30008 | Profile badges |
| 30009 | Badge definition |
| 30023 | Long-form content |
| 30311 | Live event |
Replaceable vs Regular
| Event Type | Kinds | Behavior |
|---|---|---|
| Regular | 1, 4, 7, etc. | Permanent and unique, can only be deleted |
| Replaceable | 0, 3, 10002, etc. | Latest event replaces previous |
| Addressable | 30000-39999 | Identified by kind + pubkey + d-tag, updatable |
Tags
Tags provide metadata and enable threading:
Common Tags
| Tag | Format | Purpose |
|---|---|---|
e | ["e", "<event-id>", "<relay>", "<marker>"] | Reference event |
p | ["p", "<pubkey>", "<relay>"] | Mention user |
t | ["t", "hashtag"] | Hashtag |
a | ["a", "<kind>:<pubkey>:<d-tag>"] | Reference addressable |
d | ["d", "identifier"] | Addressable identifier |
r | ["r", "url"] | Reference URL |
Threading with e-tags
{
"kind": 1,
"tags": [
["e", "root-event-id", "", "root"],
["e", "reply-to-id", "", "reply"],
["p", "author-of-root"],
["p", "author-of-reply"]
],
"content": "This is a reply in a thread"
}
NIPs (Nostr Implementation Possibilities)
NIPs are specifications for Nostr features:
Core NIPs
| NIP | Description |
|---|---|
| NIP-01 | Basic protocol |
| NIP-02 | Contact list |
| NIP-05 | DNS verification |
| NIP-19 | bech32 encoding |
Identity & Auth
| NIP | Description |
|---|---|
| NIP-07 | Browser extension signing |
| NIP-46 | Remote signing (bunker) |
| NIP-98 | HTTP authentication |
Messaging
| NIP | Description |
|---|---|
| NIP-04 | Encrypted DMs (legacy) |
| NIP-17 | Private DMs (improved) |
| NIP-44 | Versioned encryption |
Payments
| NIP | Description |
|---|---|
| NIP-57 | Zaps (Lightning) |
| NIP-47 | Nostr Wallet Connect |
Content
| NIP | Description |
|---|---|
| NIP-23 | Long-form content |
| NIP-51 | Lists |
| NIP-52 | Calendar events |
| NIP-53 | Live activities |
| NIP-58 | Badges |
| NIP-65 | Relay list metadata |
WebSocket Protocol
Clients communicate with relays via WebSocket:
Client to Relay
// Subscribe to events
["REQ", "sub-id", { filters... }]
// Publish an event
["EVENT", { event... }]
// Close subscription
["CLOSE", "sub-id"]
// Authenticate (NIP-42)
["AUTH", { auth-event... }]
Relay to Client
// Event matching subscription
["EVENT", "sub-id", { event... }]
// End of stored events
["EOSE", "sub-id"]
// Notice/error
["NOTICE", "message"]
// Command result
["OK", "event-id", true/false, "message"]
// Auth challenge
["AUTH", "challenge-string"]
Filters
Query events with filters:
{
"ids": ["abc123..."],
"authors": ["pubkey1...", "pubkey2..."],
"kinds": [1, 6, 7],
"since": 1234567890,
"until": 1234599999,
"#e": ["event-id"],
"#p": ["pubkey"],
"#t": ["bitcoin", "nostr"],
"limit": 50
}
NIP-05 Verification
Human-readable identifiers via DNS:
alice@example.com
Resolution:
GET https://example.com/.well-known/nostr.json?name=alice
Response:
{
"names": {
"alice": "pubkey-hex..."
},
"relays": {
"pubkey-hex...": ["wss://relay1...", "wss://relay2..."]
}
}
Zaps (Lightning Payments)
NIP-46 Remote Signing
Keep keys secure with remote signing:
Connection string:
bunker://npub...@relay.example?secret=abc123
NIP-98 HTTP Authentication
Use Nostr identity for HTTP APIs:
GET /api/data HTTP/1.1
Authorization: Nostr <base64-encoded-kind-27235-event>
Enables:
- Solid pod access via Nosdav
- API authentication
- File uploads
Implementations
Clients
| Client | Platform | Focus |
|---|---|---|
| Damus | iOS | Full-featured |
| Amethyst | Android | Full-featured |
| Primal | Web, iOS, Android | Caching, search |
| Snort | Web | Clean UI |
| Coracle | Web | Privacy |
| Gossip | Desktop | Outbox model |
Relays
| Relay | Language | Features |
|---|---|---|
| strfry | C++ | High performance |
| nostream | TypeScript | Easy setup |
Libraries
| Library | Language |
|---|---|
| nostr-tools | JavaScript |
| NDK | JavaScript |
| rust-nostr | Rust |
| python-nostr | Python |
Signers
| Signer | Platform |
|---|---|
| Amber | Android |
| nos2x | Browser |
| Alby | Browser |
| nsec.app | Web |
Quick Example
import {
generateSecretKey,
getPublicKey,
finalizeEvent,
SimplePool
} from 'nostr-tools';
// Generate keys
const sk = generateSecretKey();
const pk = getPublicKey(sk);
// Create and sign an event
const event = finalizeEvent({
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags: [["t", "nostr"]],
content: 'Hello, Nostr!'
}, sk);
// Publish to relays
const pool = new SimplePool();
const relays = ['wss://relay.damus.io', 'wss://nos.lol'];
await Promise.all(
pool.publish(relays, event)
);
// Subscribe to events
const sub = pool.subscribeMany(
relays,
[{ kinds: [1], limit: 10 }],
{
onevent(event) {
console.log('Received:', event.content);
},
oneose() {
console.log('End of stored events');
}
}
);
Learn More
- nostr.com — Protocol overview
- Nostr User Guide — Getting started
- Nostr Dev Guide — Building on Nostr
- did:nostr — DIDs using Nostr keys
- NIPs Repository — All specifications