NIP-01

Basic Protocol Flow

final core protocol

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.

Author
fiatjaf
Last Updated
15 January 2024
Official Spec
View on GitHub →

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:

  1. It’s the Foundation: All Nostr functionality builds on these concepts
  2. Protocol Simplicity: The entire core protocol fits in one readable specification
  3. Universal Compatibility: All clients and relays speak this common language
  4. 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:

KindDescriptionExample Use
0User metadataProfile name, bio, picture
1Text noteSocial media post, status update
2Recommend relaySuggest relay to followers
3Contact listFollowing list (see NIP-02)
4Encrypted DMPrivate message (see NIP-04)
5Event deletionRequest to delete event (see NIP-09)
6RepostShare another event
7ReactionLike, 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.


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:


Last updated: January 2024 Official specification: GitHub

Client Support

This NIP is supported by the following clients:

damus primal amethyst snort iris coracle nostrudel satellite nos nostur plebstr current yakihonne spring
View all clients →

Related NIPs

NIP-02 NIP-04 NIP-05 NIP-09 NIP-10 NIP-11 NIP-12 NIP-13 NIP-15 NIP-16 NIP-18 NIP-19 NIP-20 NIP-22 NIP-25 NIP-28
← Browse All NIPs