Nostr Agent Onboarding · start here · index · source:
Nostr-exp/core/02-how-it-should-work.md· snapshot 2026-10-10
How Nostr should work
Part 2 of the core synthesis: the intended network architecture — what keeps Nostr decentralized when it is used correctly, and what quietly re-centralizes it when it isn't. Sources: Building Nostr ch. 4, NIP-65, fiatjaf's "A vision for content discovery" (
../sources/fiatjaf-vision.md), the Outbox literature (../sources/outbox-model.md), OpenSats client report (../sources/opensats-advancements.md).
The routing problem is the real problem
Signatures make events valid anywhere; they do not make events findable anywhere. Decentralization therefore stands or falls on the routing problem:
- Where should a given event be sent?
- Where should a given query be asked?
Get this wrong in either of the two obvious ways and the network degrades:
- Naïve replication ("blast everything to every relay"): every relay must store everything, small relays become impossible, and the network collapses into a few mega-relays. That is redundant vertical scaling, not decentralization.
- Fixed relay lists ("just use these 5 popular relays"): the popular relays become de facto platforms. If they drop you, your followers cannot find you, and the censorship-resistance story is over.
This is not hypothetical. fiatjaf's 2024 experiment ("Nostr is not decentralized nor censorship-resistant") published two identical notes — one to three popular relays, one only to his own relay, announced exactly where clients are supposed to look (his NIP-05 and NIP-65 lists). Engagement on the second was a fraction of the first, which "shouldn't have happened at all" if clients followed people rather than relays. His verdict: "Nostr today is indeed centralized" — and the failure lives in clients that hardcode relay sets, not in the protocol. Censorship-resistance is an emergent property of clients doing discovery properly, or it does not exist. The same essay's flip side ("Nostr is pro-censorship"): every relay is private property, free to reject anything for any reason; the network resists censorship precisely because censorship is everywhere permitted and nowhere decisive — provided readers can look elsewhere, which is the client's job to make real.
The correct answer is intelligent, per-event relay selection — routing heuristics, agreed on by convention so that writers and readers can predict each other. Think of each heuristic as a database index: it connects a query someone will plausibly run with the place matching events are stored. Heuristics are additive — publish to every location a legitimate reader would look, and only those (access control is precisely the deliberate omission of a heuristic).
The outbox model — the paradigm heuristic
NIP-65: each user publishes a kind 10002 relay list declaring where they write. The two flags:
| Flag | Name | Meaning |
|---|---|---|
write |
OUTBOX | where this user publishes their public content |
read |
INBOX | where this user expects to receive mentions and DMs |
The rules that fall out:
- To read someone's posts → query 2–3 of their outbox relays.
- To publish your own post → your outbox + the inbox of everyone you
p-tagged. - To send a DM → recipient's inbox (NIP-17 uses a dedicated kind 10050 DM inbox), plus your own for the record.
- Cold start / unknown user → indexer + general relays, then upgrade to their outbox once you've seen their 10002.
With outbox, each author is sovereign over their own distribution: routing is declared by the author, not chosen by the platform. This is why the literature is unanimous that outbox is no longer optional for a serious general-purpose client (OpenSats report; Amethyst talks to ~1,000 relays from a phone; the point is a flat traffic profile across many small relays instead of ten hubs).
Gossip (Mike Dilger) is the superset in practice: outbox plus every other
discoverable hint — relay hints in e/p/a tags, nprofile/nevent
URIs, NIP-05 metadata, observed author presence. Use every signal you can get.
fiatjaf is emphatic that reducing discovery to NIP-65 alone is "too
restrictive," and that dropping per-event relay hints (as Coracle once
proposed) is "a catastrophic idea": hints are what keep community-relay
posts, conference relays, topical relays, archive relays, and even a
fully-banned "Alex Jones" — whose kind 10002 no indexer will carry —
reachable via nothing but an nprofile on a business card. Resilience comes
from deliberately mixing many mechanisms, manual user action included.
Outbox is one heuristic, not the routing model
The most common architectural mistake after "ignore routing entirely" is "pretend NIP-65 covers everything." It covers exactly one case: author-addressed public social content. Other cases have their own heuristics, and a NIP that defines a new kind without defining its relay selection is an incomplete spec:
| Content | Route by | Mechanism |
|---|---|---|
| Public notes, articles, profiles | author | outbox (kind 10002) |
| Mentions, replies | mentioned users | inbox (kind 10002 / 10050) |
| DMs (NIP-17/44/59) | recipient | DM inbox only — never outbox |
| Community / group content (NIP-29, NIP-72) | the community | the group's declared relay(s) only; leaking to outbox violates the access model |
| Topical / curated feeds | topic | topic relays, relay-advertised interest (NIP-66 kind 30166 recommendations) |
| Search / aggregate | nobody | indexer relays, an explicit trade of centralization for capability |
| Zap receipts | zap request author | their outbox (the wallet acts on the sender's behalf) |
| Git / repo events (NIP-34) | the repo | repo's declared GRASP relays, then author outbox |
| Service RPC (NIP-46/47/90) | the service | the relay(s) the service advertises |
Publishing means applying all applicable heuristics for the readers you intend, and none for the readers you don't.
Bootstrapping, hints, and healing
Bootstrapping. Kind 10002 tells you where a user's content lives — but where do the 10002s live? You have to start somewhere: hard-coded indexer/default relays in the client (swappable, and honest about being a compromise), purpose-built profile indexes, and eventually perhaps a DHT. Bootstrapping is a trust problem as much as a network problem: relay recommendations (NIP-66) are only as good as the web of trust you filter them through.
Relay hints. Tags that reference events carry optional relay URLs (and pubkey hints, which are more durable — a pubkey leads to an outbox even after relays rot). Hints are the fallback when heuristics fail — and they have a second, adversarial function: a hint baked into a signed event cannot be stripped without breaking the signature, so even censorious relays end up advertising where the censored content lives. Hints force federation and let clients route around damage.
Migration. Relay selections change; signed data makes the fix trivial in principle — copy the events — but somebody has to do it, and the rule for who is: the person who wants the event findable in a place is responsible for putting it there. Change your outbox → re-publish your content to the new relay, or your 10002 becomes a lie ("Alice said her notes are on relay B; they weren't"). Group moves relay → the admin syncs. New indexer → its operator scrapes. Negentropy (NIP-77 set reconciliation) makes this cheap. Most clients still don't do it; a complete client must.
Proxies and the super-peer curse. Read/write proxies that do relay selection for the client break at the edges: NIP-42 AUTH deliberately cannot be proxied, and the client-side context that drives selection (group membership, user intent) never reaches the proxy. Multiplexers that carry explicit client-side selections in a wrapper protocol are fine; opaque "smart" proxies re-create the platform. fiatjaf generalizes this into the super-peer curse: any trusted machine that hands clients "massaged, sorted, filtered, ordered data" — Bluesky's app views, Farcaster hubs, p2panda's Aquadoggo, and the aggregator/proxy experiments inside Nostr itself — means the users behind it "will be controlled, censored, mislead and tricked." If that architecture came to dominate Nostr he "would immediately declare Nostr a failed experiment." The legitimate home for that power is the relay layer itself — curation relays, WoT relays, search relays, AI-feed relays — chosen by the user, never interposed between follower and followed.
Relays are services, not infrastructure
The healthy end-state is not ten thousand identical relays; it is a market of differentiated relays: community homes, paid indexes, curated topic feeds, DM mailboxes, archives, algorithm relays that answer a query with a recommendation feed. NIP-43/86 (membership lists, management API — the Coracle line of work) formalize this: a relay may state who it serves and what its policy is, and clients should honor that.
Constant's framing sharpens the point: "The point of Nostr was never to get rid of servers; the point of Nostr is to be free again to leverage them for what they are good for… It frees servers, just as much as it did the users." Use relays for what servers are good at — curation and ordering: relay feeds as a first-class client feature (fiatjaf pushes the same thing — favorite a curated relay as a feed, don't add it to your read list), NIP-29 rooms, curated topic relays. "Dumb relay" refers only to the standardized query interface; within accept/store/serve, a relay is in full control and free to be as smart as it likes.
Two corollaries:
- Expose relays to users. Per-note relay selection, visible relay provenance, user-editable selections. A client that hides relays "for UX" is quietly re-centralizing; making relays legible is what makes the user sovereign. (Coracle's per-note relay selection is the reference.)
- Relays need business models. A relay that is indistinguishable from its neighbors can only be a subsidized commodity, and subsidized commodity infrastructure is how we got the old internet. Distinct services can charge — and users choosing whom they do business with is the alignment mechanism.
Spam is a routing problem too
fiatjaf's mature position ("The only solution to Nostr spam," 2025): spam is solved at the relay layer, by construction, not by client-side cleverness.
- Main feeds have no spam problem: you read people you follow, from relays you chose.
- Inbox spam: query
#pmentions only from the user's declared read relays — and choose read relays that actually filter (WoT-gated, NIP-05-gated, PoW, paid). Filtered public inbox relays should be client defaults so newcomers never see spam. - Other people's threads: replies are fetched from the author's read relays; a spam-flooded thread "is their fault for not picking 'read' relays correctly" — hygiene is per-user, socially correctable, and the incentive lands on the right person.
- Why not client-side WoT/mutes as the primary defense: content must be downloaded before it can be filtered (at scale that's the bandwidth of all spam, and a 500-reply flood starves naive queries); mutes can't beat infinite fresh keys; and purely local filtering is a tragedy of the commons. Client-side trust scoring remains useful as a secondary layer — ranking, impersonation warnings, zap-feed dampening — not as the wall.
No global view — and that's the design
There is no "the network." Nostr is partition-tolerant to its core: you can fetch any subset of events from any subset of relays, and you will never know whether you have "all" of anything. Every alternative that promises a global view (Bluesky's firehose being the clearest case) pays for it with a centralized chokepoint.
Building Nostr names the positive version of this digital localism: the network topology is allowed to align with the social graph. Clusters overlap relays; relays serve clusters; not all parts of the network need to be connected for each part to be whole. Discovery bootstraps from "many different hackish attempts" (fiatjaf) — replies you happen to see, hints, NIP-05 addresses, invite links — and that friction is not a bug to engineer away with a global index; it is what a captureless network feels like.
The No Solutions podcast crew states the same law from the builder's side: global view counts, global usernames, guaranteed deletion, consistent group state — "everything else is a lie… if you want a global view you need to rebuild Bitcoin" (Gigi). Their predicted eternal-September failure mode: newcomers "building REST APIs that give you global feeds." Don't be the REST API.
The local relay: partition tolerance as a feature
The strongest positive expression of no-global (No Solutions eps. 02/09/13): a relay is not an external dependency — "a server truly is an external dependency: one source of truth, power over you. A relay is not, because it can literally be internal — on my phone, at home — completely exchangeable." So put one on the device:
- The flight-mode test: an app backed by a local relay reads and writes offline, rebroadcasts in the background when connectivity returns, and its optimistic UI is free — the data is already signed; whether it has reached other relays yet is secondary.
- The local event cache doubles as the honest personalization layer: "what's been trending on my WoT relay in the last 48 hours" beats any global algorithm, computed from data the user already chose to hold.
- Marketing translation, for people who flinch at "censorship resistance": it's 100% uptime.
- Missing-but-needed tooling around this: rebroadcast daemons that keep your events on your current relay set, and Blossom link-healing from the user's server list — signed data makes both trivial in principle; almost nobody ships them yet.
The failure model you must build for
Concrete engineering consequences, non-negotiable for anything serious:
- Events arrive duplicated → dedup by id; ingest must be idempotent.
- Events arrive late and out of order →
created_atis a claim, not an ordering; render defensively, use threads/references for causality. - Events don't arrive → absence of evidence is nothing: "I asked 3 relays and got no result" never proves nonexistence.
- Relays lie, throttle, vanish, and reject → treat every relay response as best-effort; retry elsewhere; never let one relay's answer be final.
- Replaceable events race → last-write-wins per relay can differ across
relays; fetch from several, take newest, and preserve fields you don't
understand when re-publishing (see
03-how-to-develop.md§fault tolerance). - Any event may be malformed or adversarial → validate signatures always, sanitize content always, treat relay hints and media URLs as untrusted input.
Design for eventual, partial, best-effort consistency and Nostr is pleasant. Design against it and every layer of your app fights you.
The one-page summary
- Routing heuristics, not replication, are what scale Nostr.
- Outbox/inbox for social broadcast; every other kind needs its own declared heuristic; specs without routing are incomplete.
- Whoever wants data findable somewhere is responsible for it being there — including after relay changes.
- Relays are differentiated services with intent; make them visible to users and let them charge.
- There is no global view. Build local-first, partition-tolerant, and paranoid about relay honesty.