Nostr Agent Onboarding · start here · index · source:
Nostr-exp/core/04-how-to-design.md· snapshot 2026-10-10
How to design for Nostr
Part 4 of the core synthesis: the design methodology — events, kinds, tags, communities, money, and trust, plus the principles that arbitrate every trade-off. Sources: Building Nostr chs. 2, 6, 7; the nostr-dev
design-synthesis.mdand kind cheatsheet; nostrdesign.org.
Designing on Nostr = two artifacts
You are never designing "a system." The runtime is whatever relays exist. You are designing:
- An event schema — kind(s) + tag set + content shape, and
- A routing strategy — which relays each kind goes to and is read from
(
02-how-it-should-work.md).
Every other decision is downstream. Take them in this order:
Decision 1 — the schema
- Tags are your data model; content is for humans. Structured data in tags (strings only, short, 2–3 positions); prose/markdown in content. JSON-in-content is legacy exception, not precedent.
- Prefer more kinds over overloaded kinds. "No more than one way to do the same thing" cuts both ways: reuse the existing kind when your thing is the same thing; mint a separate kind when it differs even modestly (NIP-52 splits date-based and time-based calendar events — that's the taste to copy). Overloading is the root of the cross-client data-loss bug family. Similarly, resist packing many concerns into one event: contention over a shared event (kind 0, the shared lists) is where sync conflicts live. Hodlbod: redesigning profiles today, he'd split every field into its own event.
- Reuse tag conventions:
e/p/a/d/t/r/alt/expiration/subject/client… — if your data is "a reference plus an annotation," it is an existing tag plus content, not a new kind. Know the three tag roles — data (display), filter (single-letter, relay-indexed), behavior (kind-independent semantics like-,h,expiration) — and when resolving overloaded tags let the kind's own spec win over generic meanings. - Positional tags rot. If a tag wants 4+ positions, split it into associative companion tags instead.
- The optional-becomes-mandatory test (fiatjaf's razor — apply it to every extension you're tempted to ship): if this became widespread, could a client ignore it and still show correct content? If not, it is not optional, however innocent it looks — it will ratchet, via user pressure, into an obligation on every present and future client, raising the barrier to entry. That mechanism (his YAML-NIP-05 parable) is what killed NIP-26 delegation as a general scheme and grounds his case against kind:1 edits: a client that doesn't fetch edits "will be displaying false information," so edits, once widespread, are mandatory — "a centralizing force… a slippery slope that should not be accepted." Extensions that pass the test: things a non-implementing client can simply not show (reactions, zaps, annotations with graceful fallback). Extensions that fail: anything that changes the meaning of already-published content out from under simple readers.
Decision 2 — the event class
Default regular; regular events keep history, keep referential transparency (id = content hash), and degrade gracefully. Escalate only with a written justification of what you're discarding and why:
one-current-value-per-user?
/ \
yes no
| |
keyed by a d-tag? instantaneous signal?
/ \ / \
yes no yes no
| | | |
ADDRESSABLE REPLACEABLE EPHEMERAL REGULAR ← default
30000–39999 10000–19999 20000–29999
Smell tests: a settings event that accumulates → replaceable. A "current location" stream → ephemeral. An edited blog post → addressable. An editable comment → wrong, comments are accountable: regular + NIP-09 tombstone. A draft → addressable. Remember what replaceability costs: history gone, race conditions in, and only a social (not technical) resolution when two writes collide.
Decision 3 — routing per kind
For every kind you define: who reads this, with what query, and which relays must therefore hold it? Write the heuristic into the spec. A kind without a routing heuristic is an incomplete design (the geocache/topic-tag lesson: events whose natural query has no relay-selection story end up on hard-coded relays — centralization — or unfindable).
Decision 4 — the failure and abuse model
Duplicates, gaps, reordering, lying relays, race-prone replaceables — plus: who can spam this? impersonate in it? what leaks? An event schema is not done until you can answer "what does a hostile pubkey do with this?" (spam → WoT/PoW/paid relays; impersonation → WoT validation, never NIP-05 alone — addresses locate users, they do not authenticate them) and "what does this reveal?" (Nostr is publicity technology; metadata is data).
Kind allocation, concretely
- Search the NIPs registry table (vendored:
../sources/nips-readme-kinds.md). nak nip <n>for the relevant NIPs; read them.- Probe the live network —
nak req -k <kind> -l 5against big relays; undocumented-but-occupied kinds are common, and colliding with one hurts both parties. - Fit an existing kind if you honestly can; match its schema exactly.
- Else pick a free number in the correct class range, document it in-repo, ship, and NIP-PR it once a second implementation exists.
Designing privacy: encryption vs. relay policy
Two mechanisms give confidentiality, with opposite trade-offs:
- Encryption (NIP-44 payloads, NIP-59 gift wrap, NIP-17 DMs, MLS for large groups): trustless — no relay can leak what it can't read — but policy is frozen into the protocol; every client must implement the same rules, and mistakes are catastrophic and unpatchable.
- Relay-enforced access control (NIP-42 AUTH + policy, the NIP-29 model): flexible, cheap, admin-evolvable — any policy a script can express — but the relay is trusted; "forward secrecy" by policy is a promise, not a guarantee.
Choose by threat model, and be honest in your spec about which you chose. Gift-wrapped events deliberately hide kind, author, and timestamp from relays; remember that even then, traffic metadata (who talks to which relay, when) still leaks — say so in your privacy story.
Two more honesty checks from the practitioners. Group encryption past roughly twenty members is theater — "you'll either have a mole or an asshole in there" (Gigi); don't pay heavy cryptography costs for an assurance the social layer can't deliver, and consider Martti Malmi's alternative: use Nostr as the lookup substrate (publish your Signal/SimpleX handles on your profile) rather than forcing every private medium onto relays. And deletion is make-believe everywhere, not just here: "computers are copying machines… all deletions in a networked world are pinky-promise deletions." NIP-09/62 are courtesy requests; design as if nothing can be unpublished, because nothing can.
Designing communities
Building Nostr ch. 7's taxonomy — five community shapes, each wanting a different architecture. Naming which one you're serving is half the design:
| Shape | Nature | Architecture fit |
|---|---|---|
| Social cluster | emergent, open, graph-defined | broadcast social + outbox; no membership machinery at all |
| Group chat | small, every-member-trusted, quadratic intimacy | encrypted (NIP-17 pairwise; MLS for scale); doesn't scale socially past trust, don't pretend it does |
| Discussion forum | open, topic-defined, needs moderation not gatekeeping | relay-based groups (NIP-29) or moderation-as-user-preference; forkable by design — same group on two relays merges in the client |
| Owned community | exists for/by a central figure (creator, company) | centralized control is a feature; NIP-29 relay owned by the owner; interop is the value-add |
| Commons | member-owned, politically self-organized | the hard one: roles, gradated membership, partitions by identity/topic/content-type; design with the community, not for it |
Lessons paid for in the wild: NIP-72's require-moderator-approval design killed its forums (moderation must be added under load, not a precondition for content to exist); moderation policy generally belongs to users and relays, not frozen into the event spec; missing data is not a bug when the user's own filters caused it — partition tolerance is also a social feature. And know which value applies where — Constant on NIP-29: a group "is a house, you are a guest, and the fact that you can be yeeted out is a feature, not a bug. In this context, censorship resistance does not make sense." Demanding broadcast-network values inside a community context is a category error in both directions.
The generalization of all this is Constant's TEPP formula: Nostr is "a permissionless system for the creation of permissioned environments." Permission lists over the protocol's own primitives — people (npubs), places (relays), things (kinds/events) — enforced redundantly at signer, client, and relay, extensible through webs of trust. Child-safe internet (Kidstr) is the flagship case, but the pattern covers any curated, bounded, or supervised space you might need to design.
Designing for value-for-value
Money is a first-class design material on Nostr, and its semantics are patronage, not trade: digital content has ~zero marginal cost, so payment buys future production plus identity/belonging — leaderboards, boosts with messages, in-group signaling. Design for those dynamics, not for a store.
- NIP-57 lightning zaps: public receipts tie payment to event/recipient. Trust caveats: the receipt is only as honest as the receiver's wallet, and zap-driven feeds must be WoT-filtered or sybils farm them.
- NIP-60/61 cashu nutzaps: wallet lives in events (follows the user), pubkey-locked tokens verify without trusting the wallet; custodian risk moves to mints. "Uncle Jim" community mints/nodes mitigate.
- The underexplored direction: paying infrastructure — relays, DVMs, library authors — in-band and per-use, so the commons isn't ad-funded. If your design creates load on someone's server, design the payment path too.
Trust and reputation as design material
There is no global truth on Nostr, so reputation is always relative to a starting point. Design rules:
- Compute WoT from this user's follow graph outward; global scores (PageRank-style) are sybil bait. Garbage in, garbage out.
- Weight attestations by strength: a follow is weak-but-real interest; a reaction emoji is nearly nothing. Don't over-read weak signals.
- Explicit attestation schemes ("I trust X for Y") mostly fail — PGP proved incentives beat elegance; piggyback on actions users already take.
- Trust is domain-specific: good for spam/impersonation filtering, not a character reference.
- Supplements for cold-start: PoW (NIP-13), attestation services, payments — each gameable, useful in combination.
- Identity ≠ key (Constant): "an npub is a mere pointer to some identity… identity consists of expression and relation." Key rotation has no accepted formal solution — and Constant argues it shouldn't get one: rigid formal schemes "turn against you the moment an attacker penetrates them." Design instead for social recovery: WoT attestations of a new key, expiring musig2 "association events" whose silence signals compromise. fiatjaf lands nearby from the relaxed side: identity, unlike money, "can be recovered — slowly and painfully," so many optional schemes beat any mandatory one.
- Timestamps as trust material:
created_atis a claim, but an OpenTimestamps proof (NIP-03; Constant's NIP-3B/3C fixes — stamp hash(id+sig), embed the proof) makes backdating detectable: "timestamps only give very weak indications of truth, but they are really good at catching certain lies." Post-compromise, a stamped history is what lets an identity prove which of its record predates the theft. Cheap to add, rarely regretted.
The principles that arbitrate
When two design options tie, these break the tie (nostrdesign.org guiding principles + the synthesis of everything above):
- User sovereignty over identity — keys never leave the user; no design may require an intermediary that can impersonate.
- User sovereignty over relays — never hard-code, never hide; relay selection is user-facing UI, not an implementation detail.
- Modularity over monoliths — the smaller and sharper your piece, the more the ecosystem carries it.
- Filter, don't censor — clients give users filtering tools (WoT, PoW, mute, relay choice) instead of deciding for them; moderation is relay- and user-policy, not client fiat.
- Interoperability over features — a feature that breaks other clients' reading of shared data is a bug with good PR.
- Trust users, trust developers, keep specs simple — write NIPs a human reads in five minutes; complexity is capture surface.
- Embrace compromise — Nostr is "good enough" by design; perfect protocols with no users lose to messy protocols with them. Protocol work is politics in the Aristotelian sense: a community building the place it intends to inhabit. Participate accordingly.
- State your trade-offs out loud — the No Solutions rule ("no solutions, only trade-offs"). Mints can rug you: that's the design, keep pocket change there. Zapstore dials decentralization down for credible exit, and says so. The characteristic newcomer failure is refusing to accept a trade-off — and thereby smuggling a central authority back in (Passkeys, home servers, global registries, "smart" proxies, DRM). A design document that names what it gives up is the mark of someone who understood the protocol.