Nostr Agent Onboarding · start here · index · source:
nostr-dev/docs/sources/outbox-model.md· snapshot 2026-10-10
Outbox model — references and condensed rules
Vendored summary + canonical links. The mechanics are simple enough to codify here directly.
Canonical sources
- NIP-65 — Relay List Metadata (kind 10002): https://github.com/nostr-protocol/nips/blob/master/65.md
- Nostrify — "The Outbox Model": https://nostrify.dev/relay/outbox
- whynostr.org — "What is the Outbox Model?": https://www.whynostr.org/post/8yjqxm4sky-tauwjoflxs/
- Mike Dilger's gossip client — original implementation: https://github.com/mikedilger/gossip
- NDK outbox docs — https://github.com/nostr-dev-kit/ndk (the JS library
in this workspace's
sdk/already implements outbox under the hood)
The mechanics — condensed
A kind 10002 event from a user contains r tags. Each r tag has either
no second value (relay used for both reading and writing), or one of read /
write:
{
"kind": 10002,
"tags": [
["r", "wss://relay.damus.io"],
["r", "wss://nos.lol", "write"],
["r", "wss://relay.nostr.band", "read"]
],
"content": ""
}
Terminology, post NIP-65 finalization:
| Tag flag | Modern name | Use |
|---|---|---|
write (or unflagged) |
OUTBOX | where this user publishes their events |
read (or unflagged) |
INBOX | where this user expects to receive DMs and mentions |
Routing rules that fall out
- Reading someone's posts → query their OUTBOX relays (typically pick 2–3 of them, not all).
- Sending a DM (NIP-17 / NIP-44 / NIP-59) → publish to the recipient's INBOX only. Also publish to your own INBOX so you have a record.
- Posting your own content publicly → publish to all of your OUTBOX
relays + the INBOX relays of every user you
p-tagged or mentioned. - Querying a user's profile / follow list / relay list → their OUTBOX.
- Anonymous browsing / cold start → general-purpose relays
(
wss://relay.damus.io,wss://nos.lol) + index relays (wss://purplepag.es,wss://user.kindpag.es;relay.nostr.bandwas unreachable as of 2026-09).
Why outbox > fixed-list (for general social)
A fixed-list client publishes to N popular relays and reads from the same N. Result: those N relays become de facto centralization points; if they drop you, your followers can't see you. With outbox, each user is sovereign over their own distribution. Censorship-resistance comes from the routing being declared by the author, not the platform.
What outbox does NOT solve (be explicit in your design)
- Communities, group chats, topical feeds — those want a community/topic relay, not the user's outbox. NIP-29, NIP-72.
- DMs — go to inbox, not outbox.
- Search / full-text indexing — needs a search relay
(
search.nos.today; NIP-50.relay.nostr.bandwas unreachable as of 2026-09). - Discovery of users you don't follow yet — outbox is read-side only.
You need gossip-flavored hints (relay hints in
e/p/atags, NIP-05,nprofile). - Per-event routing for non-broadcast kinds — anything that is not a general-purpose social broadcast probably wants a deliberate routing choice.
Practical implementation hints
- Maintain a routing table:
pubkey → [outbox relays]. NDK calls this an "outbox database." Update lazily as you encounter new authors. - Cap outbox per author at 2–4 relays per query. More is wasteful; fewer is fragile.
- Treat kind 10002 as replaceable — only the latest matters.
- A user with no kind 10002 → fall back to general-purpose relays + any relay hints you have for them.
Gossip — the superset
Gossip = outbox + every other discoverable hint:
- relay hints in
etags (["e", "<id>", "<relay-url>"]), - relay hints in
ptags (["p", "<pubkey>", "<relay-url>"]), - relay hints in
atags for addressable references, nprofile/neventURIs that bake in relays,- NIP-05 metadata that names relays,
- observed presence of the author in replies on relays you already speak to.
The Gossip client is the canonical reference; NDK and Coracle implement gossip-flavored outbox by default.