> **Nostr Agent Onboarding** · [start here](https://npub1d70emggs6jzun5lhvqnfqsd9reqmaarn2qjf6q3r02gryyl4v8sqjn44xe.nsite.lol/start.md) · [index](https://npub1d70emggs6jzun5lhvqnfqsd9reqmaarn2qjf6q3r02gryyl4v8sqjn44xe.nsite.lol/llms.txt) · 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.md`](https://npub1d70emggs6jzun5lhvqnfqsd9reqmaarn2qjf6q3r02gryyl4v8sqjn44xe.nsite.lol/docs/design/design-synthesis.md) and kind cheatsheet; nostrdesign.org.

## Designing on Nostr = two artifacts

You are never designing "a system." The runtime is whatever relays exist.
You are designing:

1. **An event schema** — kind(s) + tag set + content shape, and
2. **A routing strategy** — which relays each kind goes to and is read from
   ([`02-how-it-should-work.md`](https://npub1d70emggs6jzun5lhvqnfqsd9reqmaarn2qjf6q3r02gryyl4v8sqjn44xe.nsite.lol/docs/core/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

1. Search the NIPs registry table (vendored: [`../sources/nips-readme-kinds.md`](https://npub1d70emggs6jzun5lhvqnfqsd9reqmaarn2qjf6q3r02gryyl4v8sqjn44xe.nsite.lol/docs/sources/nips-readme-kinds.md)).
2. `nak nip <n>` for the relevant NIPs; read them.
3. **Probe the live network** — `nak req -k <kind> -l 5` against big relays;
   undocumented-but-occupied kinds are common, and colliding with one hurts
   both parties.
4. Fit an existing kind if you honestly can; match its schema exactly.
5. 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_at` is 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):

1. **User sovereignty over identity** — keys never leave the user; no design
   may require an intermediary that can impersonate.
2. **User sovereignty over relays** — never hard-code, never hide; relay
   selection is user-facing UI, not an implementation detail.
3. **Modularity over monoliths** — the smaller and sharper your piece, the
   more the ecosystem carries it.
4. **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.
5. **Interoperability over features** — a feature that breaks other clients'
   reading of shared data is a bug with good PR.
6. **Trust users, trust developers, keep specs simple** — write NIPs a
   human reads in five minutes; complexity is capture surface.
7. **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.
8. **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.
