Chapter 1
Why Nostr?
What goes wrong when one company owns the server, and how relays change the game.
Hi, I'm Nos! Before we touch any keys, let's break some servers. For science.
The morning Alice vanished
Alice bakes the best cinnamon buns in town. Over five years she built an audience of 20,000 people on a big social app. That’s where her customers find the daily menu, where she posts the sold-out alerts, where her regulars say hi.
One Tuesday she opens the app and sees a grey box: “Your account has been suspended.” No human to call. No list of her followers to export. An automated filter decided something, and five years of relationships went quiet in a single click.
Nobody had to hack Alice. Nobody had to argue with her. The company simply owned the room she was talking in, and it switched off her microphone.
The same single point of control also decides what you see (the algorithm), what it costs (fees, ads), and whether the place exists at all (shutdowns, acquisitions, outages). A ban is just the most dramatic version of the problem.
Four ways to wire up a social network
Let’s take the same five friends (Alice, Bob, Carol, Dave and Erin) and connect them four different ways. Your job is to be the chaos monkey: knock servers offline, then press Ban Alice and see who can still be heard.
- Centralized: one company, one server, everyone lives there.
- Federated: many independent servers that relay posts to each other. This is the
model of Mastodon and email.federation Federation: A design where many servers interoperate but each account belongs to one server, as in email or Mastodon. Nostr deliberately avoids it. - Nostr: simple
that store and forward notes, and people who post to several relays at once.relays Relay: A server that accepts events from clients, stores them and sends them to anyone who subscribes with a matching filter. - Bluesky: a newer design where your data lives on a personal data server (PDS), and big aggregators stitch everyone’s posts into timelines.
Topology sandbox
Five friends, four ways to wire up a social network. Knock servers and relays offline, or ban Alice, and watch who can still reach their audience.
Click or press Enter on a server or relay to knock it offline. Then try banning Alice.
One company runs the only server. Everyone lives there.
All servers and relays are online. Everyone can reach everyone.
Who can still reach their audience?
- Fully heard reaches 4 of 4 Account on BigCo
- Fully heard reaches 4 of 4 Account on BigCo
- Fully heard reaches 4 of 4 Account on BigCo
- Fully heard reaches 4 of 4 Account on BigCo
- Fully heard reaches 4 of 4 Account on BigCo
All servers and relays are online. Everyone can reach everyone.
Try this order: kill BigCo in Centralized, tea.social in Federated, alpha in Nostr, and the AppView in Bluesky. Then flip back and press Ban Alice in each one. Notice the pattern?
What just happened?
Centralized is a single trunk: chop it and the whole tree falls. Ban Alice and she is simply gone, because the company is the network.
Federated is a lot better. Losing one instance only hurts the people who live there.
But look at Alice’s scoreboard row: “Account gone with tea.social.” In a federated world
your name looks like @alice@tea.social, and that last half is the landlord. If the
instance shuts down, or the admin bans you, your account and your follower list go with
it. You can move house, but only while the old landlord is still answering the door.
Nostr shrugs. Alice’s identity is not an account on anyone’s server: it’s a
Bluesky sits in between. Accounts can move between data servers, which is great. But in practice most people read through one big relay and one big AppView, so knocking out either one still darkens almost everything today.
| Network model | Conversations surviving |
|---|---|
| Centralized | 0% |
| Federated | 30% |
| Nostr | 100% |
| Bluesky | 0% |
Why relays are dumb on purpose
Here’s the trick that makes Nostr work: relays don’t talk to each other and don’t own
anyone. A relay is a small server that accepts signed notes (called
Think of relays like post office boxes rather than a town square. Alice rents a box at
two post offices and drops a copy of every letter into both. Her friends’ mailmen (their
That’s the whole recipe:
- You are your keys. No sign-up, no username to confiscate. Your public key
(
) is your name.npub npub: A public key encoded in bech32 so humans can copy it safely, e.g. npub1… instead of 64 hex characters. - Every note is signed. Relays can’t forge or quietly edit what you said.
- Publish everywhere. You pick several relays, and you can change them any time.
- Readers follow you. Clients use your published relay list to find your notes:
that’s the
.outbox model Outbox model: A strategy where clients read each person's posts from the relays that person writes to, instead of everyone sharing the same few relays.
Under the hood: what a relay actually receives Show details
Everything in Nostr is one data shape, defined in NIP-01.
A client sends it to a relay inside an EVENT message over a WebSocket:
["EVENT", {
"id": "<32-byte sha256 of the serialized event, hex>",
"pubkey": "<Alice's 32-byte public key, hex>",
"created_at": 1735689600,
"kind": 1,
"tags": [],
"content": "Cinnamon buns are back at 8am!",
"sig": "<64-byte Schnorr signature of the id, hex>"
}]The relay answers with ["OK", "<event id>", true, ""] if it stored the note, or false
plus a reason (for example "blocked: ..." or "rate-limited: ...") if it refuses. Note
what’s missing: there’s no account id, no password, no server name in Alice’s identity.
The pubkey and sig are all a relay (or reader) needs to know who wrote it.
Under the hood: publishing to several relays with nostr-tools Show details
Redundancy is just a loop. With nostr-tools:
import { finalizeEvent, generateSecretKey } from "nostr-tools/pure";
import { SimplePool } from "nostr-tools/pool";
const sk = generateSecretKey(); // your identity: 32 random bytes, never leaves the device
const note = finalizeEvent(
{ kind: 1, created_at: Math.floor(Date.now() / 1000), tags: [], content: "hello, relays!" },
sk, // computes pubkey, id and signature
);
const pool = new SimplePool();
const relays = ["wss://relay.alpha.example", "wss://relay.beta.example"];
// One promise per relay: it's enough that *some* relay accepts the note.
await Promise.any(pool.publish(relays, note));A relay that rejects the note can’t change it or claim it as its own; it can only decline to store it.
Under the hood: the outbox model (NIP-65) Show details
How do Bob’s client and Alice’s relays find each other? Alice publishes a small
replaceable event of kind 10002 listing her relays, as described in
NIP-65:
{
"kind": 10002,
"tags": [
["r", "wss://relay.alpha.example"],
["r", "wss://relay.beta.example", "write"],
["r", "wss://relay.gamma.example", "read"]
],
"content": "",
"...": "pubkey, created_at, id, sig as usual"
}An r tag without a marker means “read and write”. To show Alice’s notes, Bob’s client
fetches her kind 10002 and opens subscriptions on her write relays; to reach Alice
(replies, mentions), it publishes to her read relays. That’s why the sandbox lets any
reader pull from any relay Alice writes to: the client goes to the author, not the other
way around.
Under the hood: Mastodon and Bluesky, a bit more precisely Show details
- Mastodon / ActivityPub. Your identity is
@name@instance, resolved through the instance’s WebFinger endpoint, and your posts are signed with keys the instance holds. Account migration exists (aMoveactivity redirects followers), but it needs the old instance to still be online and cooperative. - Bluesky / AT Protocol. Your identity is a DID (usually
did:plc) that points at your current PDS, and your posts live in a signed repository there. Accounts can migrate between PDSes, which is real portability. Timelines, though, come from relays that crawl every PDS and AppViews that index them; anyone may run one, but most traffic today goes through a few large ones. - Nostr. The identity is the signing key and every event carries its own signature, so there’s nothing to migrate: you just point at different relays.
The sandbox’s scoring (see sandbox.ts in this chapter’s components) treats a conversation
as alive when an author’s post can travel to a reader through online, non-banning
infrastructure, and people never relay for each other.
Is this just “no moderation”?
No, and that’s an important nuance. Each relay is free to set its own rules: block spam,
require payment (a
What Nostr removes is the single chokepoint. Moderation becomes a choice made by many
relays and many apps, instead of a verdict from one company that also happens to hold your
identity. That property has a name:
Owning your identity means owning the risk: if you lose your secret key there’s no
“forgot password” button (we’ll cover
Where did Nostr come from?
Nostr stands for “Notes and Other Stuff Transmitted by Relays.” It was proposed in 2020
by a developer known as fiatjaf, as a deliberately tiny protocol: a JSON event format, a
handful of WebSocket messages, and public-key signatures. Everything else is added
through optional, numbered proposals called
The rest of this course is a tour of that recipe. Next up: the keypair that is you.
Quick check
Takeaways
- On a centralized platform the company holds your account, your audience and the off switch.
- Federation spreads the servers out, but your account still lives on (and dies with) one instance.
- On Nostr your identity is a keypair you hold, and you post the same signed notes to several relays at once.
- Relays are simple and replaceable: losing one, or being banned by one, does not cost you your voice.
- Nostr is censorship-resistant, not moderation-free: each relay and client still chooses what it carries and shows.