Basic Protocol Flow
NIP-01 defines the core Nostr protocol including event structure, relay communication, signatures, and basic operations. It's the foundation that all other NIPs build upon.
NIP-01: Basic Protocol Flow
Status: Final Author: fiatjaf Category: Core Protocol
Overview
NIP-01 is the foundational specification of the Nostr protocol. It defines:
- Event structure - How all Nostr data is formatted
- Client-relay communication - How clients and relays talk to each other
- Cryptographic signatures - How events are signed and verified
- Basic operations - Publishing, subscribing, and filtering events
Every Nostr client and relay must implement NIP-01 to be compatible with the network. All other NIPs extend or modify this base protocol.
Why NIP-01 Matters
Understanding NIP-01 is essential because:
- It’s the Foundation: All Nostr functionality builds on these concepts
- Protocol Simplicity: The entire core protocol fits in one readable specification
- Universal Compatibility: All clients and relays speak this common language
- Extensibility: Simple design allows endless innovation through other NIPs
Unlike complex federated protocols, Nostr’s core is refreshingly simple—yet powerful enough to support diverse applications from social media to marketplaces.
Core Concepts
1. Events
Everything in Nostr is an event. An event is a JSON object with this structure:
{
"id": "4376c65d2f232afbe9b882a35baa4f6fe8667c4e684749af565f981833ed6a65",
"pubkey": "6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93",
"created_at": 1673347337,
"kind": 1,
"tags": [
["e", "3da979448d9ba263864c4d6f14984c423a3838364ec255f03c7904b1ae77f206"],
["p", "bf2376e17ba4ec269d10fcc996a4746b451152be9031fa48e74553dde5526bce"]
],
"content": "Hello, Nostr! This is my first note.",
"sig": "908a15e46fb4d8675bab026fc230a0e3542bfade63da02d542fb78b2a8513fcd0092619a2c8c1221e581946e0191f2af505dfdf8657a414dbca329186f009262"
}
Field Breakdown:
- id: SHA-256 hash of the serialized event (unique identifier)
- pubkey: Public key of the event creator (hex format)
- created_at: Unix timestamp (seconds since epoch)
- kind: Event type (1 = text note, see Kind Reference below)
- tags: Array of tags (arbitrary metadata)
- content: The event’s content (meaning depends on kind)
- sig: Schnorr signature over the id field
2. Event Kinds
Different event kinds serve different purposes:
| Kind | Description | Example Use |
|---|---|---|
| 0 | User metadata | Profile name, bio, picture |
| 1 | Text note | Social media post, status update |
| 2 | Recommend relay | Suggest relay to followers |
| 3 | Contact list | Following list (see NIP-02) |
| 4 | Encrypted DM | Private message (see NIP-04) |
| 5 | Event deletion | Request to delete event (see NIP-09) |
| 6 | Repost | Share another event |
| 7 | Reaction | Like, emoji reaction (see NIP-25) |
Many more kinds exist through other NIPs. This is just the foundation.
3. Tags
Tags add metadata and relationships to events:
"tags": [
["e", "event_id_being_replied_to"],
["p", "pubkey_being_mentioned"],
["t", "hashtag"],
["r", "https://example.com"]
]
Common Tag Types:
- e: Reference to another event (replies, quotes)
- p: Reference to a public key (mentions, tags)
- t: Hashtag (for categorization)
- r: URL reference
Tags enable threading, mentions, and rich metadata without changing the core protocol.
Client-Relay Communication
Clients and relays communicate via WebSocket connections using simple JSON messages.
Message Types
1. EVENT (Client → Relay)
Publish a new event to the relay:
["EVENT", {
"id": "...",
"pubkey": "...",
"created_at": 1234567890,
"kind": 1,
"tags": [],
"content": "Hello, world!",
"sig": "..."
}]
Relay Response:
["OK", "event_id", true, ""]
Or if rejected:
["OK", "event_id", false, "invalid: signature verification failed"]
2. REQ (Client → Relay)
Subscribe to events matching filters:
["REQ", "subscription_id", {
"kinds": [1],
"authors": ["pubkey1", "pubkey2"],
"#t": ["nostr"],
"since": 1234567890,
"limit": 100
}]
Filter Fields:
- ids: Match specific event IDs
- authors: Match specific authors (pubkeys)
- kinds: Match event kinds
- #<tag>: Match tag values (e.g., #e for event tags)
- since: Events after this timestamp
- until: Events before this timestamp
- limit: Maximum number of events
Relay Response: Sends matching events as:
["EVENT", "subscription_id", { event object }]
When done sending stored events:
["EOSE", "subscription_id"]
(EOSE = End Of Stored Events)
3. CLOSE (Client → Relay)
Close a subscription:
["CLOSE", "subscription_id"]
Cryptographic Signatures
Nostr uses Schnorr signatures over the secp256k1 elliptic curve (same as Bitcoin).
Event ID Calculation
The event ID is a SHA-256 hash of the serialized event data:
// Serialize event (before hashing)
const serialized = JSON.stringify([
0, // Reserved for future use
event.pubkey, // Public key (hex)
event.created_at, // Timestamp
event.kind, // Event kind
event.tags, // Tags array
event.content // Content string
]);
// Hash to get ID
const id = sha256(utf8Encode(serialized));
Signature Creation
The signature is created by signing the event ID with the private key:
// Sign the event ID with private key
const signature = schnorrSign(eventId, privateKey);
Signature Verification
Relays and clients verify signatures before accepting events:
// Verify signature
const isValid = schnorrVerify(signature, eventId, publicKey);
This ensures:
- Authenticity: Event really came from the claimed pubkey
- Integrity: Event wasn’t modified after signing
- Non-repudiation: Author can’t deny creating the event
Example: Publishing Your First Note
Here’s a complete example of creating and publishing a text note:
// 1. Create the event object
const event = {
pubkey: "6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93",
created_at: Math.floor(Date.now() / 1000),
kind: 1,
tags: [],
content: "Hello, Nostr! This is my first note."
};
// 2. Calculate the event ID
const serialized = JSON.stringify([
0,
event.pubkey,
event.created_at,
event.kind,
event.tags,
event.content
]);
const id = sha256(utf8Encode(serialized));
event.id = id;
// 3. Sign the event
const privateKey = "your_private_key_hex";
event.sig = schnorrSign(id, privateKey);
// 4. Publish to relay via WebSocket
const ws = new WebSocket('wss://relay.damus.io');
ws.send(JSON.stringify(["EVENT", event]));
// 5. Listen for confirmation
ws.onmessage = (msg) => {
const [type, eventId, accepted, message] = JSON.parse(msg.data);
if (type === "OK" && accepted) {
console.log("Event published successfully!");
}
};
Client Support
Universal Support: All Nostr clients implement NIP-01 as it’s the foundation of the protocol.
Notable implementations:
- Damus (iOS) - Native Swift implementation
- Primal (Web, iOS, Android) - Fast caching service
- Amethyst (Android) - Comprehensive Android client
- Snort (Web) - Rust-based relay integration
- Iris (Web) - JavaScript implementation
- Nostrudel (Web) - Power user features
For a complete list, see our Client Directory.
Related NIPs
NIP-01 is extended and modified by many other NIPs:
- NIP-02 - Contact lists and petnames (kind 3)
- NIP-04 - Encrypted direct messages (kind 4)
- NIP-05 - DNS-based verification
- NIP-09 - Event deletion (kind 5)
- NIP-10 - Reply conventions (e and p tags)
- NIP-25 - Reactions (kind 7)
- NIP-28 - Public chat (kind 40-42)
For Developers
Implementing NIP-01
If you’re building a Nostr client or relay, NIP-01 compliance requires:
Clients must:
- Generate valid events with correct ID and signature
- Send EVENT, REQ, and CLOSE messages
- Handle EVENT and EOSE messages from relays
- Verify signatures on received events
Relays must:
- Accept EVENT messages and validate signatures
- Handle REQ subscriptions with filters
- Send matching events to subscribers
- Send EOSE after stored events
- Handle CLOSE messages
Libraries
Popular libraries that handle NIP-01 operations:
- JavaScript: nostr-tools, nostr-dev-kit
- Rust: nostr-sdk, nostr-relay
- Python: python-nostr
- Go: go-nostr
- Swift: nostr-sdk-ios
- Kotlin: nostr-sdk-android
For more developer resources, visit our Developer Hub.
Common Questions
Why JSON over WebSockets?
JSON is human-readable, language-agnostic, and well-supported. WebSockets provide real-time bidirectional communication. Together they create a simple, universal protocol.
Can events be edited?
No. Events are immutable (the signature ensures this). To “edit,” you publish a new event and optionally send a deletion request (NIP-09) for the old one. Relays may or may not honor deletion requests.
What prevents spam?
NIP-01 doesn’t specify spam prevention. That’s handled by:
- Relay policies (may require payment, proof-of-work, etc.)
- Client-side filtering (web-of-trust, mute lists)
- Specialized NIPs (NIP-13 for proof-of-work, NIP-42 for authentication)
How many relays should I use?
Clients typically connect to 5-15 relays for redundancy and reach. Users can choose their own relay set for sovereignty.
Technical Specification
For the complete technical specification, see the official NIP-01 document on GitHub.
Summary
NIP-01 defines Nostr’s elegant foundation:
✅ Simple event structure - JSON objects with signatures ✅ Universal compatibility - All clients and relays speak the same language ✅ Cryptographic security - Schnorr signatures ensure authenticity ✅ Extensible design - Other NIPs add features without breaking core protocol
Understanding NIP-01 is the first step to understanding Nostr. Everything else builds on these concepts.
Next Steps:
- Learn about contact lists in NIP-02
- Explore encrypted messaging in NIP-04
- Understand identity verification in NIP-05
- Browse all NIPs in our reference
Last updated: January 2024 Official specification: GitHub
Client Support
This NIP is supported by the following clients: