Introduction to Nostr
Notes and Other Stuff Transmitted by Relays — a simple, open protocol that enables a genuinely censorship-resistant and globally decentralized social network.
What is Nostr?
Nostr stands for Notes and Other Stuff Transmitted by Relays. It is a lightweight, open protocol designed from the ground up to resist censorship and enable decentralized communication. Unlike traditional social networks that rely on centralized servers owned by corporations — where your account can be deleted, your posts shadowbanned, or your identity revoked at any time — Nostr gives you true ownership of your digital identity and your content.
The protocol was first proposed by fiatjaf, a pseudonymous developer, in late 2020. It gained significant traction in the Bitcoin community and beyond, with Jack Dorsey — the co-founder of Twitter — publicly donating to its development. Today, Nostr has a thriving ecosystem of clients, relays, and developer tools, all built on a specification that fits in a single document.
The core philosophy of Nostr is radical simplicity. The protocol does not try to solve every problem. It does not include built-in content moderation, algorithmic feeds, or identity verification. Instead, it provides the bare minimum necessary for decentralized messaging and leaves everything else to clients and users. This simplicity is its greatest strength — anyone can implement a Nostr client or relay in a single afternoon.
At its heart, Nostr is a publish-subscribe protocol. Users publish signed messages called events to servers called relays. Other users can connect to those same relays and subscribe to receive events. Because each event is cryptographically signed by the author's private key, it cannot be forged or tampered with. And because the protocol is open, anyone can run their own relay, ensuring that no single entity can control the flow of information.
Why Nostr Matters
To understand why Nostr is significant, it helps to understand the problems it was designed to solve. The centralized social media model has several fundamental flaws that no amount of policy change or regulation can fully fix, because they are structural in nature.
When you sign up for Twitter, Facebook, or any other centralized platform, you are not truly creating an account — you are being granted a license to use an account that the platform owns. At any moment, for any reason, the platform can suspend or delete that account. Your followers, your posts, your identity — all of it disappears. This is not a bug; it is a feature of the business model. Platforms need control over their users to enforce advertiser-friendly content policies and to keep users locked into their ecosystem.
Beyond censorship, centralized platforms are single points of failure. If Twitter's servers go down, no one can communicate through Twitter. If a government compels Twitter to shut down in a particular country, those users lose access entirely. Nostr's decentralized architecture means there is no single point that can be attacked or shut down. The network continues to function as long as at least one relay is online.
There is also the question of data portability. On centralized platforms, your social graph — the list of people you follow and who follow you — is locked into that platform. If you want to move to a competitor, you start from zero. With Nostr, your identity is your cryptographic keypair. Your followers list, your posts, and your preferences can all be moved to any relay at any time. Your social graph belongs to you, not to a corporation.
| Feature | Centralized Social Media | Nostr |
|---|---|---|
| Identity Ownership | Owned by the platform | Owned by you (private key) |
| Censorship Resistance | Can be banned at any time | Cryptographically guaranteed |
| Data Portability | Locked to platform | Move between relays freely |
| Server Dependency | Single point of failure | Multiple independent relays |
| Protocol Openness | Proprietary API | Open specification (NIPs) |
Core Concepts
Before diving into the technical details of how Nostr works, it is helpful to understand the three fundamental building blocks of the protocol: keys, events, and relays. Everything in Nostr is built on top of these three concepts, and once you understand them, the rest of the protocol follows naturally.
Keys — Your Identity
In Nostr, your identity is a cryptographic keypair — a private key and a public key. The private key is a 32-byte random number that you must keep secret. It is used to sign every message you send. The public key is derived from the private key and is used by others to verify that a message came from you. Your public key is your identity on Nostr. When someone wants to follow you, they follow your public key. When you publish a post, it is signed with your private key and anyone can verify the signature using your public key.
Nostr uses the secp256k1 elliptic curve — the same curve used by Bitcoin — for its cryptography. This means the key generation and signature algorithms are battle-tested and widely understood in the developer community. Public keys are typically displayed in a human-readable format called npub (using bech32 encoding), while private keys are displayed as nsec.
Events — The Unit of Data
Everything in Nostr is an event. A social media post is an event. A reaction (like) is an event. A follow list is an event. A direct message is an event. Every piece of data in the Nostr ecosystem is represented as a JSON object with a standardized structure, signed by the author's private key.
The event structure is intentionally minimal. It contains a unique identifier (derived by hashing the event content), the author's public key, a Unix timestamp, a kind number (indicating the type of event), optional tags for metadata, the content of the message, and a cryptographic signature. This simplicity means that every Nostr client and relay can process every event, regardless of what the event contains.
Relays — The Infrastructure
Relays are simple WebSocket servers that store and forward events. They have no special authority in the Nostr protocol — they are just message brokers. Clients connect to relays, publish events, and subscribe to receive events that match certain filters. Relays can choose to accept or reject events (for example, a relay might require payment to publish, or might filter out certain content), but they cannot forge events or impersonate users.
The power of the relay model comes from its redundancy. Users typically connect to multiple relays simultaneously. When you publish an event, it is sent to all your connected relays. When you read events, you receive them from all relays. If one relay goes offline or bans you, your events are still available on the other relays. This makes the network extremely resilient to censorship and outages.
How Nostr Works
Now that we have established the core concepts, let's look at how the protocol actually functions. The flow of a Nostr interaction can be broken down into four steps: generating keys, constructing an event, signing the event, and publishing it to relays. On the receiving end, subscribers connect to relays and request events matching specific filters.
The communication between clients and relays happens over WebSockets, using a small set of message types. There are only three types of messages that a client can send to a relay: EVENT (to publish an event), REQ (to subscribe to events matching a filter), and CLOSE (to close a subscription). The relay can respond with EVENT (delivering a matching event), EOSE (end of stored events), OK (acknowledging an event), and NOTICE (a human-readable message).
This simplicity is deliberate. The protocol specification is short enough to be read in an hour and simple enough to be implemented in any programming language. There are Nostr libraries available in JavaScript, Python, Rust, Go, Swift, Kotlin, and many other languages. The entire protocol boils down to: sign JSON, send it over WebSockets, receive JSON from WebSockets, verify signatures.
Keys & Identity in Depth
Key management is the most important — and potentially most dangerous — aspect of using Nostr. Because your private key is your identity, losing it means losing access to your account forever. There is no "forgot password" feature. There is no support team to contact. If your private key is compromised, anyone who has it can impersonate you, post on your behalf, and read your encrypted direct messages.
This is both the strength and the weakness of the key-based identity model. The strength is that it gives you absolute sovereignty over your identity. The weakness is that it requires you to take responsibility for your own security. Most Nostr clients address this by using a browser extension (like nos2x or Alby) that stores your private key securely and signs events on your behalf, so that websites never have direct access to your private key.
Code Examples
1. Generating a Keypair
The first thing any Nostr application needs to do is generate a keypair. In JavaScript, using the popular nostr-tools library, this is just a few lines of code. The library handles the secp256k1 cryptography for you, giving you a private key (as a hex string) and the corresponding public key.
// Install: npm install nostr-tools import { generateSecretKey, getPublicKey } from 'nostr-tools' import { bytesToHex } from '@noble/hashes/utils' import { nip19 } from 'nostr-tools' // Generate a new random private key (32 bytes) const sk = generateSecretKey() const pk = getPublicKey(sk) // Convert to hex strings const privateKeyHex = bytesToHex(sk) const publicKeyHex = pk // Encode in bech32 (human-readable) format const nsec = nip19.nsecEncode(sk) // nsec1... const npub = nip19.npubEncode(pk) // npub1... console.log('Private key (hex):', privateKeyHex) console.log('Public key (hex):', publicKeyHex) console.log('nsec (keep secret!):', nsec) console.log('npub (share freely):', npub)
In Python, using the pynostr library, the process is equally straightforward. Python's extensive cryptography ecosystem makes it easy to work with the secp256k1 keys that Nostr uses.
# Install: pip install pynostr from pynostr.key import PrivateKey # Generate a new private key private_key = PrivateKey() # Get the corresponding public key public_key = private_key.public_key # Access in different formats print(f"Private key (hex): {private_key.hex()}") print(f"Public key (hex): {public_key.hex()}") print(f"nsec (bech32): {private_key.bech32()}") print(f"npub (bech32): {public_key.bech32()}")
2. Creating and Publishing an Event
Once you have a keypair, you can create and publish events. An event is a JSON object with a specific structure. The most important fields are the kind (which determines what type of event it is), the content (the actual message), and the tags (metadata). After constructing the event object, you sign it with your private key to produce the sig field and the event id.
import { finalizeEvent, generateSecretKey, getPublicKey } from 'nostr-tools' import { Relay } from 'nostr-tools/relay' const sk = generateSecretKey() const pk = getPublicKey(sk) // Construct the event template const eventTemplate = { kind: 1, // Kind 1 = short text note created_at: Math.floor(Date.now() / 1000), tags: [], content: 'Hello Nostr! This is my first note on the decentralized web.', } // Sign the event — this adds the id, pubkey, and sig fields const signedEvent = finalizeEvent(eventTemplate, sk) // Connect to a relay and publish const relay = await Relay.connect('wss://relay.damus.io') await relay.publish(signedEvent) console.log('Event published! ID:', signedEvent.id) relay.close()
3. Connecting to Multiple Relays
In a real application, you will want to connect to multiple relays for redundancy and to maximize your reach. The nostr-tools library provides a SimplePool class that manages connections to multiple relays and handles publishing and subscribing across all of them.
import { SimplePool, finalizeEvent, generateSecretKey } from 'nostr-tools' const pool = new SimplePool() const relays = [ 'wss://relay.damus.io', 'wss://relay.nostr.band', 'wss://nos.lol', 'wss://nostr.wine', ] const sk = generateSecretKey() const event = finalizeEvent({ kind: 1, created_at: Math.floor(Date.now() / 1000), tags: [], content: 'Broadcasting to the entire Nostr network!', }, sk) // Publish to all relays simultaneously await Promise.allSettled(pool.publish(relays, event)) console.log('Published to all relays') // Always close the pool when done pool.close(relays)
4. Subscribing to Events
Subscribing to events is just as simple as publishing them. You provide a filter object that specifies what kinds of events you want to receive, and the relay will send you all matching events it has stored, followed by any new events as they arrive. Filters can match on event kind, author public key, time range, referenced events, and more.
import { SimplePool } from 'nostr-tools' const pool = new SimplePool() const relays = ['wss://relay.damus.io', 'wss://nos.lol'] // Subscribe to the global feed (all kind:1 notes) const sub = pool.subscribeMany( relays, [ { kinds: [1], // Kind 1 = text notes limit: 50, // Get the last 50 notes since: Math.floor(Date.now() / 1000) - 3600, // Last hour } ], { onevent(event) { console.log('New event received:', { id: event.id.slice(0, 8) + '...', author: event.pubkey.slice(0, 8) + '...', content: event.content.slice(0, 60), }) }, oneose() { console.log('End of stored events — now listening for live events') } } )
Relays — The Backbone of Nostr
Relays are the infrastructure layer of the Nostr network. They are the servers that store, forward, and serve events to clients. Without relays, there is no Nostr — they are the connective tissue that makes the protocol function. Yet despite their importance, relays are architecturally simple: they are WebSocket servers that accept events, store them in a database, and serve them back to clients upon request. A basic relay can be written in a few hundred lines of code.
What makes the relay model powerful is not any single relay, but the plurality of relays. The Nostr network currently has hundreds of publicly accessible relays, run by individuals, companies, and communities all over the world. Because the protocol is open, anyone can run a relay at any time — on a personal server, a cloud VPS, or even a Raspberry Pi at home. This diversity of relay operators ensures that no single entity can control or shut down the network.
What a Relay Actually Does
At its core, a relay is a database with a WebSocket interface. When a client connects and sends a REQ message with a filter, the relay queries its database for all events matching that filter and streams them back to the client. When the relay has sent all stored matches, it sends an EOSE (End of Stored Events) message. After EOSE, the relay keeps the subscription open and continues forwarding any new events that match the filter as they arrive in real time.
When a client publishes an event by sending an EVENT message, the relay first validates the event. It checks that the event's id is correctly computed (SHA-256 hash of the serialized event), that the sig is a valid Schnorr signature over the id using the author's pubkey, and that the event structure is well-formed. If all checks pass, the relay stores the event and acknowledges it with an OK message. If validation fails for any reason, the relay rejects the event and sends an OK with a false status and an error reason.
This validation step is critical. Because every event must carry a valid cryptographic signature, relays act as a first line of defense against forged or malformed events. A relay cannot be tricked into accepting an event signed by someone other than the stated author — the mathematics of elliptic curve cryptography makes it computationally infeasible to forge a signature without the private key.
Relay Policies and Access Control
Although the Nostr protocol itself is open, individual relays are free to set their own policies about who can publish and who can read. This is an important and often misunderstood aspect of Nostr's design. The protocol is censorship-resistant not because any single relay must accept every event, but because the network as a whole is permissionless — if one relay rejects you, you can use a different relay.
Relay policies vary widely. Some relays are completely open — anyone can read and write without authentication. These are called public relays and are the most common type. Other relays require payment to publish, typically a small Bitcoin Lightning payment, as a spam-prevention mechanism. These paid relays tend to have higher-quality content because the economic friction discourages spammers. Some relays are private or invite-only, used by specific communities or organizations who want a shared space without public interference.
A relay can also implement content filtering — choosing to reject or delete events that violate its terms of service. This is perfectly legitimate within the Nostr model. A relay operator is not obligated to host content they find objectionable. What matters is that the user's events are also stored on other relays, so removing an event from one relay does not erase it from the network. Users who are concerned about relay-level censorship should always publish to multiple relays to ensure their content remains accessible.
Relay Information Document (NIP-11)
NIP-11 defines a standard way for relays to advertise their capabilities and policies. When you make an HTTP GET request to a relay's URL with the Accept: application/nostr+json header, the relay returns a JSON document describing itself. This document includes the relay's name, description, supported NIPs, software version, contact information, and any limitations it imposes on events (such as maximum event size, maximum number of subscriptions, or a minimum proof-of-work requirement).
{
"name": "relay.example.com",
"description": "A public Nostr relay for the global community.",
"pubkey": "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d",
"contact": "admin@relay.example.com",
"supported_nips": [1, 2, 4, 9, 11, 12, 15, 20, 22, 28, 33, 40],
"software": "https://github.com/hoytech/strfry",
"version": "0.9.6",
"limitation": {
"max_message_length": 65536,
"max_subscriptions": 20,
"max_filters": 100,
"max_event_tags": 100,
"max_content_length": 8196,
"min_pow_difficulty": 0,
"auth_required": false,
"payment_required": false
}
}
Types of Relays
As the Nostr ecosystem has matured, several specialized relay types have emerged to serve different use cases. Understanding these categories helps you choose the right relays for your application or personal use.
General-purpose public relays are the workhorses of the network. They accept events of all kinds from any user and serve them to any reader. Examples include relay.damus.io, nos.lol, and relay.nostr.band. These relays are essential for bootstrapping new users and ensuring broad content availability, but they can suffer from spam and high load due to their open nature.
Paid relays require users to pay a subscription fee — usually a small amount of Bitcoin — before they are allowed to publish. This simple economic mechanism dramatically reduces spam while remaining permissionless (anyone willing to pay can join). The payment is typically made via Lightning Network. Examples include nostr.wine and relay.snort.social. Many users consider paid relays to have a higher signal-to-noise ratio than free public relays.
Community or topic-specific relays are curated around a particular language, region, or topic. For example, there are relays dedicated to the Japanese Nostr community, the Bitcoin development community, or the Nostr developers themselves. These relays create focused spaces where users can find relevant content without the noise of the global feed.
Outbox relays are personal relays that a user publishes all their events to, acting as a permanent home for their content. The "outbox model" (described in NIP-65) recommends that every user maintain a small set of relays where they consistently publish, so that followers always know where to find their content. Without an outbox model, events can be scattered across many relays and hard to retrieve.
Search relays are specialized relays that index events for full-text search. Standard Nostr relays typically only support filtering by event ID, author, kind, and tags — they cannot do full-text content search efficiently. Search relays like relay.nostr.band build inverted indexes over event content to support search queries, which is essential for discovery of new users and content.
Running Your Own Relay
Running your own relay gives you maximum control over your Nostr data and contributes to the health of the network. There are several relay implementations to choose from, each with different trade-offs in terms of performance, storage, and features.
strfry is a high-performance relay written in C++ that uses LMDB for storage. It is known for its exceptional throughput and low memory usage, making it suitable for large public relays. It supports relay-to-relay event synchronization (negentropy) which allows relays to efficiently share events without transferring duplicates.
nostr-rs-relay is a relay written in Rust that uses SQLite for storage. It is easy to deploy, well-documented, and suitable for personal or small community relays. It supports most major NIPs and is a good starting point for running your first relay.
Relay29 and khatru are Go-based relay frameworks that make it easy to build custom relays with specialized behavior. If you want to build a relay with custom access control logic — for example, a relay that only accepts events from members of a specific community — these frameworks give you the flexibility to implement that.
# Install Rust if you haven't already curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Clone and build nostr-rs-relay git clone https://git.sr.ht/~gheartsfield/nostr-rs-relay cd nostr-rs-relay cargo build --release # Copy and edit the config file cp config.toml.sample config.toml # Run the relay (listens on port 8080 by default) ./target/release/nostr-rs-relay # Your relay is now accessible at ws://localhost:8080
The Outbox Model and Relay Discovery
One of the challenges of a decentralized relay network is relay discovery — how does your client know which relays to connect to in order to find the events you care about? The naive approach is to connect to a fixed list of well-known public relays, but this leads to centralization around a few popular relays and makes it hard to find content from users on obscure relays.
The outbox model, described in NIP-65, provides a better solution. Each user publishes a special event (kind 10002) listing the relays where they publish their events (write relays) and the relays where they prefer to receive events (read relays). When your client wants to load the feed of a user you follow, it first fetches that user's NIP-65 relay list, then connects directly to their write relays to fetch their events. This means that even if a user only publishes to a small private relay, their followers can still find their content.
import { finalizeEvent, generateSecretKey } from 'nostr-tools' const sk = generateSecretKey() // Publish a NIP-65 relay list (kind 10002) // This tells the network where you publish and read const relayListEvent = finalizeEvent({ kind: 10002, created_at: Math.floor(Date.now() / 1000), tags: [ ["r", "wss://relay.damus.io"], // read + write ["r", "wss://nos.lol", "write"], // write only ["r", "wss://relay.nostr.band", "read"], // read only ["r", "wss://nostr.wine"], // read + write (paid) ], content: "", }, sk) console.log('Relay list event:', relayListEvent)
Relay Synchronization and Redundancy
Because users publish to multiple relays, and different users use different relays, the global Nostr event graph is distributed across many servers. This is by design — redundancy is the whole point. But it also means that no single relay has a complete picture of everything happening on the network. Each relay only knows about the events that have been published to it directly, or that it has fetched from other relays via synchronization.
Some relay operators choose to implement relay-to-relay synchronization, where relays periodically exchange events with each other to ensure that popular content is available on many relays. The negentropy protocol (used by strfry) provides an efficient way to do this — it uses a set reconciliation algorithm to identify which events one relay has that another relay lacks, and transfers only those events, minimizing bandwidth usage.
For end users, the practical implication of this distributed architecture is that you should connect to several relays simultaneously. Most Nostr clients let you manage a list of relays in your settings. A typical user might connect to three to seven relays: one or two high-availability public relays for broad reach, one paid relay for a higher-quality feed, and optionally a personal relay for backup and control. The client then merges the event streams from all connected relays into a single unified feed.
NIPs — Nostr Implementation Possibilities
The Nostr protocol is extended and standardized through documents called NIPs — Nostr Implementation Possibilities. A NIP is a proposal that describes a new feature, a new event kind, or a new convention that Nostr clients and relays can implement. NIPs are the equivalent of Bitcoin's BIPs (Bitcoin Improvement Proposals) or Ethereum's EIPs. Anyone can write a NIP, and the community decides whether to adopt it.
NIPs are stored in a public GitHub repository and are numbered sequentially. The most fundamental NIPs are the ones that define the core protocol — the event structure, the relay communication protocol, and the key encoding formats. More advanced NIPs define optional features like encrypted direct messages, content warnings, long-form content, and zaps (Bitcoin Lightning payments).
| NIP | Name | Description |
|---|---|---|
NIP-01 |
Basic Protocol | The core event structure, relay communication, and subscription filters. |
NIP-02 |
Follow Lists | Kind 3 events for storing and sharing contact/follow lists. |
NIP-04 |
Encrypted DMs | Direct messages encrypted with shared secret (being superseded by NIP-44). |
NIP-05 |
DNS Verification | Mapping Nostr keys to human-readable identifiers like user@domain.com. |
NIP-09 |
Event Deletion | Requesting relays to delete previously published events. |
NIP-10 |
Reply Threading | Conventions for tagging events to create threaded conversations. |
NIP-19 |
bech32 Encoding | Human-readable encoding for keys (npub/nsec) and events (nevent/naddr). |
NIP-23 |
Long-form Content | Kind 30023 events for publishing blog posts and articles. |
NIP-44 |
Versioned Encryption | Improved encryption standard for private messages using ChaCha20. |
NIP-57 |
Zaps | Lightning Network payments attached to Nostr events and profiles. |
Use Cases & Applications
While Nostr started as a censorship-resistant alternative to Twitter, the protocol's flexibility has enabled a wide variety of applications that go far beyond social media. Because everything is just signed JSON transmitted over WebSockets, developers have built clients for a remarkable range of use cases.
Social Media
The most well-known use of Nostr is as a decentralized social network. Clients like Damus (iOS), Amethyst (Android), and Snort (web) provide familiar Twitter-like interfaces on top of the Nostr protocol. Users can post notes, follow other users, reply, repost, and react — all without any central authority controlling their account.
Bitcoin Payments with Zaps
One of the most exciting integrations in the Nostr ecosystem is Zaps — Bitcoin Lightning Network micropayments sent directly between users. When you zap a note, you are sending a small Bitcoin payment to the author of that note. The zap is recorded as a Nostr event, so it appears on the note publicly. This creates a direct economic incentive for creating good content, without any platform taking a cut. Clients like Primal and Coracle have excellent Zap support.
Long-form Publishing
NIP-23 defines a long-form content event kind (30023) that is designed for blog posts and articles. Applications like Habla.news and Yakihonne have built blogging platforms on top of this NIP, allowing writers to publish articles that are stored on Nostr relays rather than on any single server. Because the articles are signed, they can be republished on any client that supports NIP-23 without fear of tampering.
Decentralized Identity
NIP-05 allows Nostr users to associate their public key with a human-readable identifier in the format user@domain.com. This is verified by making an HTTP request to the user's domain. While the domain itself is centralized, the key is not — the domain is just a discovery mechanism. This gives users a memorable, verifiable identity while keeping their actual cryptographic identity under their own control.
The Nostr Ecosystem
Since its launch, the Nostr ecosystem has grown rapidly. There are now dozens of clients, hundreds of public relays, and a thriving developer community building tools and libraries in every major programming language. The following is a brief overview of the most important parts of the ecosystem.
Popular Clients: Damus (iOS), Amethyst (Android), Snort (web), Primal (web + mobile), Coracle (web), Nostrudel (web), Habla (long-form), Yakihonne (long-form), Olas (photos).
Developer Libraries: nostr-tools (JavaScript), pynostr and python-nostr (Python), nostr-sdk (Rust, with bindings for Swift, Kotlin, and Python), go-nostr (Go).
Browser Extensions: nos2x, Alby, and Nostore implement the window.nostr API (NIP-07), which allows web clients to request event signatures from the user's key without ever exposing the private key to the web page.
The simplicity of the Nostr protocol is its most important feature. Unlike complex decentralized protocols that require significant infrastructure or expertise to participate in, anyone with a basic understanding of WebSockets and JSON can build a Nostr client or relay. This low barrier to entry has driven the rapid growth of the ecosystem and ensures that the network will continue to evolve and adapt as new use cases emerge.