DRAFT 0.3.0 · definitions of the BSA kinds are a draft (round 5); two pilot circles run on test infrastructure · last checked 27 Oct 2026
On this page

The world relay network: technical specification DRAFT 0.3.0

Plain-words text · Technical specification · All documents

English text rendered from the project's source documents. The Slovene original is on lananostr.site. Where this text and the definitions differ, the definitions win.

Precedence: where this document and definitions 0.3.0 differ, the definitions (docs/bsa-records-0.3.0.json) win; the difference is a bug to fix in this document. The definitions pin a frozen copy of this text (docs/0.3.0/) by its sha256 (meta.normative_spec); this living text may change afterwards, and a change reaches the definitions only when they are generated again, which then needs a new yes.

Scope and status#

  • Proposals for definitions 0.3.0. 0.2.0 was never published and is superseded. Nothing is published: RELAY_PUBLISH_OFF=1 stays until 0.3.0 is published (KIND 42893, signed by the Lana authority) and the project owner says yes.
  • Status words: decided = the project owner's decision, dated; [Decided 16 Oct 2026, An] = the project owner's answer to question n (1–9) of 16 Oct 2026 (source: _reports/2026-10-16-knjiga-na-relejih/odlocitve-2026-10-16.md); [Decided 16 Oct 2026, 4a / 6a / 8a / 8b / 8c / 8e / 8f / 8i / 8j / start source] = the project owner's answer, the same afternoon, to a point the answers 1–9 had left open (same source file, section "Odgovori na odprte točke"; 6b fell with 6a); [Decided 16 Oct 2026, 6c] = the project owner's later answer the same day to the new open point 6c (»6c, da«: one start of 1,000 in the circle's currency for the reserve at founding, from the keyless origin; section "Odgovor na 6c"); [Decided 17 Oct 2026, C1–C7] = the project owner's answers to the phase C points of 17 Oct 2026 (same source file, sections "Odgovori na fazo C" and "Odgovor na pojasnilo točk 5 in 1"; C1 the world rules record 43920, C2 payments never wait for the morning, C3 refusals and act validity, C4 no rejection by one person (replaced by E-5), C5 the circle id and the replaceable circle key, C6 the first-seen rule in the letter to the relay owner, C7 time zone, morning hour and tzdata fixed at founding); [Decided 17 Oct 2026 (evening), 1A / 2A / 3A] = the project owner's answers of the evening of 17 Oct 2026 to three questions after phase D (»vsi da«; same source file, section "Odgovori na tri vprašanja po fazi D"; 1A for now only the key that publishes the definitions signs 43920, several signatures before the first circle the project owner does not run joins, who: open; 2A the tzdata version pinned at founding, part of C7, and witnesses compute with it; 3A the transition runs per account from its first receipt); [Decided 23 Oct 2026, E-n] = the project owner's answers before phase E (same source file, section "Odgovori pred fazo E" with "Dopolnitev" and "Referenca"; E-1 circle parameters instead of the world rules record (43920 retired, its number reserved; 1A void), E-2 the Circle Heart, E-3 exclusion and closing old accounts, E-4 OWN, E-5 the consensus vote (replaces 8c and C4), E-6 joining, E-7 witnesses by lot (the direction for phase I), E-8 founding only by a member, one circle per founder key, one circle per person at a time, E-9 the reference); [Decided 23 Oct 2026, E-9 (a) / (b)] = his answers the same day to the two questions on the reference (section "Odgovora na vprašanji o referenci": an open OWN process blocks a reference also without a debt; the built step moves to the new circle and the amount goes back to the old circle's community, which returns it to its origin); [Consequence] as in docs/upravljanje-kroga.md; consequence = follows from the principles or the answers and needs no new decision; Open 3a, 8g, 8h = a point still open, written as a proposal; 8d follows from decision 8 [A8]; everything else is a proposal and names its open question (Q10–Q24 of the question list of 16 Oct 2026). Proposals P1, P2, P5 and P7 keep their names; P3 and P4 are dropped (A9, A2).
  • Governing text: docs/nacela-razvoja.md. Block format, entries, lines, write and read paths, seal, snapshot, projects and the reserve, and restore in full: docs/knjiga-na-relejih.md. Witnesses, the Circle Heart and every act type: docs/upravljanje-kroga.md. Rates and quotes: docs/valute-in-tecaji.md. Identity and KIND 0: docs/bsa-id.md. Cloning: docs/nov-krog.md. Step outward and safeguards: docs/prepoznavanje-goljufov.md. Code that must change: docs/implementation-notes.md.
  • Glossary additions: project (projekt), project wallet (denarnica projekta, keyless project:<id>), reserve (rezerva, keyless), rate quote (ponudba tečaja, 43919 v3), vote (glas, vote: for or against; it replaces the objection, E-5), circle id (rojstna številka kroga: the event id of the found act, the circle's name for ever), found act (ustanovna listina), genesis digest (prstni odtis prvega bloka), circle key (ključ kroga: stamps the 30910 mirror; replaceable by circle_key_change), circle parameters (nastavitve kroga; 43920 BSA Rules is retired, E-1), Circle Heart (Srce kroga, formerly the stewards, E-2), reference (referenca, 43921, E-9), parent circle (matični krog, E-8), witness draw (žreb prič, the direction for phase I, E-7), first-seen rule (pravilo »prva knjiga velja«).
  • Labels: [M] measured on 16 Oct 2026 with read-only requests (REQ, COUNT, NEG-OPEN; no EVENT sent) · [C] relay configuration read on 16 Oct 2026 · [R] earlier report or memory · [E] estimate from the model in "Capacity".
  • KIND 38888 (author 9eb71bf1…, d=main): only the relay tags are read (decided, 14 Sep 2026); the fx values are LANA prices and are never used (decided, 15 Oct 2026). Never use a relay address that is not in the newest 38888 (an old alias of one root relay still appears in operators' configs [C]).
  • HEX keys only, never npub. Canonical JSON (RFC 8785) for every content. Integers only: amounts in minor units as base-10 strings, at most 15 digits, BigInt end to end; times in unix seconds; rates in whole ppm.

Relay tiers#

TierRun byCarriesRules
Root (the relays in KIND 38888; four today) [Decided 14 Sep 2026; A2]the Lana relay owner(s)38888, 42893 definitions, the 30910 of every circle id (d = circle id; newest per author, and only the one signed by the circle key that the log's mirror at its mirror_of names counts) [Decided 17 Oct 2026, C5], every circle's found act (43916 with ["c", ""]) [proposal] and every founder's first found act (one circle per founder key) [Decided 23 Oct 2026, E-8; the mechanism: proposal], 30915 heads (spread over 24 h), KIND 0; while borrowed: small circles' ledgers with limited space [A2, A9] (size: Q18); optional 30917 offers. No 43919: quotes live on circle relays [A7]. No 43920: retired [Decided 23 Oct 2026, E-1]no payments at scale; member homes during the borrowed phase (Q16); [proposal, E-8 and E-9] member homes are never deleted, since a new circle reads a key's newest member home before admitting it from another circle (at scale: on the index relays); phones only at first install
Circle relays [Decided 16 Oct 2026, A2]the circle's box relay + signers' relays + partners, cooperatives, hosting providers (+ Lana relays while borrowed)the circle's whole log: 43911 blocks (with embedded 43910 notes and 43916 acts), 43917 seals, 43918 chunks, 43924 statements, 30916 homes (newest version), the 43919 quotes the circle issued and those its blocks rely on, partner blocks, every 30910 version; the 43921 references its node issued [proposal, E-9]≥ 4 relays, ≥ 3 operators [A2]; bootstrap ≥ 2 operators (Q14); write policy below
Archive relaysanyonefull histories, compressedno 10-year lower limit, no expiry, KIND 5 refused
Index relaysanyone30916 member homes sharded by key prefix; name searchat scale only (Q16)
Read gatewaysanyoneverified HTTP caches with pushcan hide, never forge; apps compare two sources
  • The four Lana relays count as one operator for the ≥ 3-operator rule until shown otherwise: NIP-11 lists four different contacts [M], but the relay-owner letter is addressed to one owner and one key reaches all four hosts (Q14).
  • Signers per circle [Decided 16 Oct 2026, A3]: n = 3f+1, q = 2f+1; default n = 4, q = 3 [A3]; n = 7, q = 5 above about 10,000 members [Proposal, Open 3a]; bootstrap n = 1, publicly labelled "not yet decentralised", with no money cap; the daily independent verifier and the public restore drill stay. Rule in the log: at most one signer per operator [Decided A3]; whether a seven-witness circle may allow two per operator is Open 3a (proposal, see docs/upravljanje-kroga.md). The architecture's minimum "no operator holds q or more signers" only stops one operator from committing alone and is not enough for safety: two quorums overlap in an honest signer only if no operator holds more than f (see docs/upravljanje-kroga.md).

Kinds and where they live (proposed for 0.3.0)#

KindNameClassSigned byCircle relaysRootArchiveSize [E]
0Lana profile (exists; minimal)replaceablemember–yes–~0.5 KB
38888Lana System Parameters (exists; only relay tags read)addressableLana authority–yes––
42893Kind definitions (exists)regularLana authority–yes––
30910 v3Circle Record: a mirror of the governance state; d = circle id, address 30910:<circle key in office>:<circle id>addressablethe circle key in office (replaceable by circle_key_change) [Decided 17 Oct 2026, C5]every version (also inside the log)newest per author; readers query #d and take the record by the key in office–1.5–2.5 KB
30915 v3Circle Head: newest seal, for discovery; d = circle id; names the circle key in office [proposal]; no rules version any more (43920 retired, E-1)addressablethe seal's proposer, with quorumyesyes–~2.5 KB
30916 v3Member Home (mandatory); replaceable by the member, never copied into the log [A5]addressablemember, attested by a signernewest versionwhile borrowed (Q16)–~0.8 KB [M 739 B for 0.2.0]
43910 v3Payment Noteregularpayerembedded in 43911; standalone only as censorship evidencewhile borrowedyes~0.37 KB embedded, 0.8–1.0 KB standalone
43911 v3Circle Block: the ledgerregularproposer, with ≥ q signer signatures insideyes (≥ 4)while borrowed, under quotayes1.9 KB (one payment, one signer) to ≤ 100 KB
43916Circle Actregularthe acting personembedded in 43911; standalone copy optionalwhile borrowedyes0.5–1.2 KB
43917 v3Day Seal (checkpoint)regularproposer, with quorumyeswhile borrowedyes~2 KB
43918Snapshot Chunkregularproposer (valid through the seal's roots)yeswhile borrowedyes≤ 100 KB, ~450 lines
43919 v3Rate Quote (repurposed) [Decided 16 Oct 2026, A7]regularthe node (proposer) of the seller's (receiving) circlethose the circle issued, and those its blocks rely on (republished)–yes~0.5 KB [E]
43920retired [Decided 23 Oct 2026, E-1]: formerly BSA Rules, the world-wide rule values; the number stays reserved and is never reused; there is no event of this kind––––––
43921Reference (new) [a new kind: Decided 23 Oct 2026, E-9; the number, free in the BSA family, and the shape: proposal]: the findable copy of the reference entry of a member who left with no minus (the ledger entry is the authority)regularthe node of the circle that booked the entryyes, for everwhile borrowedyes~1–8 KB [E] (up to 100 + 100 project ids)
43924Statement (attestation or challenge)regularsigner, verifier, auditoryes (+ partners)–yes~0.7 KB + evidence ids
30917Service Offer (relay, signer, archive, index operator; optional, later)addressableoperator–yes–~1 KB

43925 (free text beside the chain) is not defined: dropped with the legal-review gate [Decided 16 Oct 2026, A5].

Rules for every BSA kind (relay-relevant):

  1. Ledger facts are regular events in 40000+ (strfry stores them as regular events on all four root relays [C]). Pointers are addressable (30000–39999), and every version of 30910 is also an entry in the log, and every 30915 mirrors a 43917 seal, which is kept for ever. 43921 (Reference) is regular too: every reference is kept for ever [proposal, E-9]; 43920 is retired [E-1]. 30916 is the exception: it carries name words, so its versions are never copied into the log, and relays keep only the newest [A5]. Never 10000–19999 (replaceable: 166 definitions once collapsed into one [R]) or 20000–29999 (ephemeral, kept 300 s [C]).
  2. No expiration tag and no NIP-70 - tag on any BSA kind; readers treat such events as invalid. Ledger keys never sign KIND 5 or KIND 62.
  3. Every event ≤ 100,000 bytes serialized (relay limit 131,072 [C]) and ≤ 1,000 tags (limit 5,000 [C]); multi-account events are cut by bytes, never by a count; tested at maximum field lengths.
  4. Indexed single-letter tags only where a reader queries (every single-letter tag is indexed on all four relays [M]): c circle id, h height, m day, i chunk, p party, e reference, t type, d pointer (the circle id in 30910 and 30915; letsbalance in 30916), n name words (30916) (x, a content hash for 43925, is no longer used: 43925 was dropped [A5]). Every BSA event except the 30910 (circle id as d) names its circle by ["c", <circle id>] [Decided 17 Oct 2026, C5; the tag shapes: proposal]: a payment note by ["c", <payer circle id>, "", "from"] and, across circles, ["c", <receiver circle id>, "", "to"]; the found act, whose own id is the circle id, by ["c", ""]; relays and readers query #c. No BSA kind uses the a tag any more. Chain fields and signatures use multi-letter tags (qs, prev, seq, at, root, genesis), which are not indexed (multi-letter filters answer CLOSED "unindexed tag filter" [M]).
  5. Tag values are scoped to the circle: scope16 = the first 16 hex characters of the circle id [C5], so two circles never share values; h = <scope16>:<height>, m = <scope16>:<YYYY-MM-DD>, i = <scope16>:<YYYY-MM-DD>:<index> (unscoped values would match every circle on a shared relay).
  6. Trust anchor: the circle id together with the genesis digest [Decided 17 Oct 2026, C5 and C6; the details: proposal] (the circle id: the NIP-01 event id of the found act, 43916 t = found, signed by the founder; the genesis digest: the digest of block 1, its NIP-01 hash without the qs tags). The circle link and QR carry the circle id, at least two relay hints and the genesis digest as an extra pin. A reader accepts a chain only if its block 1 embeds the found act whose id is the circle id and its digest is the pinned genesis (from the link, or the first g it saw for the circle id), and never follows another g for that circle id. A thief cannot found a false circle under the same circle id: his found act has another id. Blocks and seals are named by their digest, never by their event id, and every digest reference carries the block's height or the seal's day; witnesses sign sha256("lb-block/3|" ‖ digest) or sha256("lb-seal/3|" ‖ digest), so a witness signature is never a Nostr signature of a stripped copy.
  • Retired for good from 0.1.0: 30911–30914, 43912–43915. 43910 and 43911 are not retired; they return as v3. 43916 and 43918 are now used. 0.2.0's "retire 43910/43911" is withdrawn.
  • 0 events of 43910–43919 and 23910 exist on any Lana relay today [M]; 43920 was checked free on the four Lana relays and in kinds.json on 17 Oct 2026; it is now retired and stays reserved (E-1). 43921 was listed as unused in the BSA family; like every number it is checked against kinds.json and the four relays before publication [proposal, E-9]. Every number is re-checked against the live kinds.json and all four relays (COUNT, read only) before publication.

Where things live (precise)#

ItemCircle relaysRootArchiveSigner / proposer hostPhone
43911 blocks (embedded 43910, 43916)all heights 1…H, permanentwhile borrowed, under quota, only after the write policy existsallwrite-ahead log of blocks not yet on 3 relays (seconds); disposable cachereads on demand; disposable cache
43917 seals, 43918 chunksallwhile borrowedalldisposable cachethe newest seal seen
43924 statementsall (+ partners)–allits own–
30915 head (d = circle id; names the circle key in office [proposal])yesyes (newest)–––
30910 mirror (d = circle id)every versionnewest per author; only the one signed by the circle key that the log's mirror at its mirror_of names counts (the newest 30915 head names that key)–––
43921 reference (a copy of a reference entry) [proposal, E-9]every reference the circle's node issuedwhile borrowedallthe node signs it after the block's quorum– (a new circle's node reads it when the person joins there)
30916 member homeevery member, newest version onlywhile borrowed (Q16)––own
43919 rate quotesissued by this circle's node, and those its blocks rely on (republished)–allthe node signs them–
Project wallets and the reserve (lines in 43911, 43918)every move, permanentwhile borrowedalldisposable cache–
Standalone 43910 (censorship evidence)≥ 3 relays, rate-limited––––
Sign-guard {circle, key, last_h, last_digest}–––each signer, fsync'd before releasing a signature (tens of bytes)–
Private keysneverneverneveron the holder's machine, file mode 600BSA WIF in browser memory only

The SQLite database, day files and backups become disposable caches rebuilt from the relays; restore from relays is checked daily (consequence).

Publication and finality#

  1. Single publisher. The proposer sends the final signed 43911 on persistent sockets to every listed relay, pipelined in height order. Signers republish only what is still missing from a relay after 5 s. (Today's publisher, every 20 s with a fresh socket per relay and done only when every relay acked, is replaced.)
  2. Final [Decided 16 Oct 2026, A1] when the block carries ≥ q valid signatures of the signer set S(h) and ≥ 3 listed relays [A1]; of ≥ 3 operators (bootstrap ≥ 2) [Proposal Q14]; answered OK true for this exact id and every lower height is final. The proposer answers the POST with the block's height and digest and the relays that confirmed.
  3. Sending continues to the remaining relays after finality; the app always shows the real "public on N of M relays" (proposal, Q17; replaces the rule of 14 Sep 2026 that publishing waits for every relay).
  4. Payer's read-back: one REQ {"kinds":[43911],"authors":S,"#h":["<scope16>:<h>"]} to 2 relays; exactly one block at that height on each, same digest (events with one digest are one block, whatever witness signatures they carry), valid quorum, embedded note, own line arithmetic. Then "Paid · block h · public on N of M relays · confirmed by q of n". This height-uniqueness read also exposes a single signer that equivocates across different relay sets.
  5. Receiver: a live subscription {"kinds":[43911],"authors":S,"#p":[me],"limit":0} on 2 circle relays while the app is open (limit:0 returns EOSE at once [M]).
  6. No relay-side "first block wins". Relays never refuse a second block at a height (a stateful rule could lock the honest chain out on a relay that saw a bad branch first); readers detect forks from the public evidence (43924 equivocation).
  7. At switch-on (consequence): the whole held backlog goes out from genesis, oldest first, paged at a rate the relays accept.

Latency budget [E] (EVENT→OK never measured; REQ→EOSE used as proxy):

StepSingle signer, borrowed Lana relaysOwn regional relays, quorum 3 of 4p99 target
Sign in browser5–20 ms5–20 ms50 ms
POST to proposer30–100 ms30–100 ms300 ms
Checks, ordering, roots2–10 ms2–10 ms30 ms
Block closing (idle / busy)0–50 ms / ≤ W_max0–50 ms / ≤ W_max2 s
Signer round–50–150 ms400 ms
fsync guard + write-ahead log1–10 ms1–10 ms30 ms
Third relay OK (third-fastest Lana relay ~390 ms REQ→EOSE from the server [R])300–1,000 ms80–300 ms1.5 s
Answer to app30–100 ms30–100 ms300 ms
App's own read-back on 2 relays (warm)60–220 ms [M]60–220 ms [M]700 ms
Payer sees "Paid"~0.5–1.5 s~0.3–0.9 s< 3 s

Today's same-machine v2 payment takes ~12 ms [R].

Write policy for every relay that stores ledger kinds (proposal)#

The box relay ships with it; the Lana relays need their owner to apply it (Q18). It is a strfry write-policy plugin, tested on a separate test relay before the first publish.

  • Circles by circle id [Decided 17 Oct 2026, C5]: a relay serves circles by their circle id (in relay/ledger-write-policy.py's policy.json: circles[].id, the 64-hex id of the found act, which never changes) together with the circle key in office (circles[].circle_key; after a key change the witnesses booked, the new key goes there and the old one moves to retired with the role circle_key and its last created_at as until), the signers, the nodes and, optionally, the members, Heart members and applicants. Every BSA event except 30910 must carry a c tag naming a served circle id, or it is refused; a payment note is served when its from or its to circle is.
  • retired entries carry their role [proposal]: {"key": <HEX>, "until": <unix>, "role": "signer" | "node" | "circle_key"}; a retired key counts only in its own role up to its until (a retired circle key can sign only an old 30910, never a block, seal, chunk, head or quote).
  • Ledger kinds (43911, 43917, 43918, 30915) accepted only when c names a served circle id and the author is in the circle's current signer set (or is a retired key with the role signer and created_at ≤ its until), as named by the circle's log. The plugin reads the set from policy.json, not yet from the log: when a roles entry changes the witnesses or the circle key, an operator tool updates policy.json on every relay of the circle (following the committed roles entries directly: open). This is spam control only; readers still verify against the log.
  • 30910 (circle record): a v3 record whose d is a circle id the relay serves (in mode root: any circle id) and whose author is the circle_key it names and, for a configured circle, the circle key in office (or a retired key with the role circle_key up to its until); it carries g and founder, and the founder's BIP-340 signature in g must verify over sha256("lb-genesis/3|" + circle id + "|" + genesis digest). A record without ["v","3"] (0.1.0 history) passes only in mode root, unpinned.
  • 43916 and standalone 43910 only from members and applicants of that circle (the plugin also admits its Heart members, signers and nodes, and act types listed as open), rate-limited per author. The found act names no circle (["c", ""]): its own id is the circle id, so a relay accepts a found act whose id is a circle id it serves, from its author. [Proposal] In mode root the relay keeps every circle's found act (a 43916 whose first tag is ["c", ""] and that has no other c tag, from any author: its id is the circle id, so nobody can make a false one); the plugin remembers at most seen_ids_max found acts, and beyond it a newer circle's found acts are kept but not remembered, so its records are kept unpinned, never falsely pinned.
  • 43919 only from the circle's proposer of the time, as named by its log (the plugin: one of the circle's nodes in policy.json, or a retired key with the role node up to its until).
  • No 43920 [Decided 23 Oct 2026, E-1]: the kind is retired and there is no rules authority; the write policy accepts no 43920 [the relay rule: proposal]. 43921 references only from the circle's node [proposal, E-9]. Root: one circle per founder key [Decided 23 Oct 2026, E-8; the mechanism: proposal]: in mode root the relay keeps the first found act per founder key (first seen, like the genesis pin) and refuses a second circle record whose founder already founded another circle id. Member homes are never deleted in mode root (no KIND 5 against a 30916) [proposal, E-9].
  • [Open] the policy must also admit 43916 from keys the log names as witness or guardian who are not members (docs/upravljanje-kroga.md).
  • 43924 from the circle's signers, and from any other author rate-limited per author (a verifier's or auditor's challenge must always be publishable).
  • Refuse KIND 5 and KIND 62 that target any ledger kind or are authored by any ledger key (signers, nodes, retired keys, circle keys, the rules authorities); refuse expiration and - on every BSA kind.
  • First-seen rule, "the first ledger stands" [Decided 17 Oct 2026, C6: a request in the letter to the Lana relay owner, plan phase G; with C5 it is now only extra safety]: keep the first-seen g (genesis digest) and founder of every circle id, from the circle link or the first v3 30910 accepted, and refuse a later 30910 for that circle id with another g or founder (blocked:, logged as TAKEOVER ATTEMPT). A thief cannot found a false circle under the same circle id, because his found act has another id; what the pin still guards against is a competing genesis for the same circle id (a block 1 that embeds the same found act, signed by the first witnesses, with the founder's signature in g). A new version with the same g and founder, also one signed by a new circle key, passes. relay/ledger-write-policy.py does this in mode root for every circle id, configured or not, after checking the founder's signature in g over the circle id and the genesis digest; pins from a circle link in the configuration win over seen pins, also on reload. [Proposal] A root relay pins a circle id's genesis digest and founder only when it holds that circle's found act and the record's founder is the found act's author (or when the circle is configured on the relay and its circle key signed the record); without it the relay stores the record unpinned (readers check it anyway), so nobody can claim a circle id first on a root relay; a record whose founder is not the author of the found act the relay holds is refused. A pin is written only after every plugin of the relay (the chain) accepted the record. Pins and the found acts' authors live in the plugin's pins_file, not in LMDB. Said openly: the pin is not the circle key; in mode root any key that names itself in circle_key can store a 30910 for a pinned circle id with its g; only the record signed by the circle key that the log's mirror at its mirror_of names counts, and every 30915 head names that key (proposal). [Open] whether a root relay should accept a 30910 for a pinned circle id only from the key its newest head names. In mode root the other BSA kinds (except the found acts above), 30915 heads included, are accepted only for configured circles; whether a root relay should accept the heads of any pinned circle is open as well (relay/README.md, "Open").
  • Keep every BSA kind out of every purge and hard-delete filter.
  • Archives: no 10-year lower limit (rejectEventsOlderThanSeconds = 4000000000 (larger than the current unix time; measured 16 Oct 2026 on strfry 1.1.0: the value 0 refuses every event, even one made now)); today all four Lana relays refuse events older than 10 years and newer than now + 1 day [C].
  • Per-IP limits sized for carrier-grade NAT; per-author limits instead. Today nginx allows 100 connections per IP with status 429 [C]; the LetsBalance host is not exempt [C] (exception requested in the letter, Q18).
  • Must be tested before the first publish, including whether strfry refuses to re-insert an id after a KIND 5 (unverified).

Directory kinds on root (proposal):

  • 30910 v3 (tags in this order): ["d", <circle id>] (the id of the found act, the circle's name for ever [Decided 17 Oct 2026, C5]), ["code", <circle code>], name, currency (exactly one), tz, ["tzdata","2026a"], morning (time zone, tzdata and morning hour are fixed at founding and do not change in 0.3.0 [Decided 17 Oct 2026, C7]; a block's morning header carries this pinned tzdata, and a witness computes the boundary with that version, never with a newer one of its own [Decided 17 Oct 2026 (evening), 2A]; a witness without that version signs no morning block and no seal [proposal]), ["g", genesis digest, the founder's signature over sha256("lb-genesis/3|" + circle id + "|" + genesis digest)], ["mirror_of", <height of the genesis or roles entry it mirrors>] (at most one roles entry per block [proposal], so a height names exactly one mirror and a second roles entry can never undo what the first put in office; block 1 books no roles entry, since its genesis mirror is the mirror of height 1 [proposal]), ["founder", <founder HEX>], ["parent", <parent circle id>, <parent genesis digest>] (only when the found act names one [Decided 23 Oct 2026, E-8; the shape: proposal]), ["circle_key", <HEX of the circle key in office>], ["signer", hex, operator, since_h, until_h] × n [A3] (operators are names the circle declares itself), ["quorum", q, n], ["proposer", hex, since_h], ["steward", hex] × m + ["stewards_required", k] (the Circle Heart [Decided 23 Oct 2026, E-2; the technical names stay: proposal]; names [A4]; live circle m = 4 [Decided 16 Oct 2026, 4a]; k for other acts: proposal, 3; for replacing the circle key three of four is decided [C5]), ["relay", wss, operator] × ≥ 4 of ≥ 3 operators [A2], ["archive", wss]…, optional ["bootstrap","Q14"] (open question 14, not decided: while declared, two operators give a warning instead of a refusal), ["rate_source", <source id>, <public description>] [A7: any source the circle chooses; start: the ECB daily reference rate [Decided 16 Oct 2026, start source]; how it is changed: proposal], ["api", https] (write intake), ["definitions","0.3.0"] (Q20), v=3. Canonical address 30910:<circle key in office>:<circle id>: the address changes when the circle key is replaced, so readers query #d = the circle id and take the record the log confirms. No quote_ttl and no project_approvals: they are circle parameters, carried in the ledger's state (g3|param:*), not in the mirror [Decided 23 Oct 2026, E-1]. No fund key [A8]; no rate_panel or rate_rule [A7]; no custodian tag [Decided 16 Oct 2026, 6a: no deposits]. The author must be the circle_key the record names; the circle key is never the founder's, a witness's or a Heart member's key [proposal]. The circle key signs only this mirror and the founding's binding in block 1 [Proposal, follows from A3 and A8, not confirmed]. The circle key can be replaced [Decided 17 Oct 2026, C5: k Heart members, three of four in the live circle, with public notice, booked by the witnesses; the notice change_notice_s, 72 h by default: Decided 23 Oct 2026, E-1; the act shape, the proof of possession and in office from the next block: proposal]: the first Heart member signs circle_key_change with ["key", <HEX of the new circle key>] and ["pop", <the new key's BIP-340 signature over sha256("lb-circle-key/3|" + circle id + "|" + new key HEX + "|" + created_at)>] (the act's created_at in decimal: a pop is good for its own act only, and an act is booked only within act_ttl_s of its created_at, so an old pop can never be used again [proposal]), the others endorse it; after change_notice_s (72 h by default) of public notice from the k-th act a roles entry, booked by the witnesses, puts the new key in office from the next block (g3|circle_key|<hex>|<since_h>); the new key already signs the 30910 of that roles entry's height, from then on only the new key signs the 30910, and a record by the old key with a mirror_of at or above the change never counts. The circle id never changes. Authority rule: a 30910 counts only if it is signed by the circle key that the log's mirror at its mirror_of names (at the height of a key change already the new key; for blocks it is in office from the next height), its tags without g equal that mirror (the committed genesis or roles entry at mirror_of), and its g names the pinned genesis digest; readers take the valid record with the highest mirror_of; where they differ, readers follow the log. Said openly: anyone can publish a 30910 that names a circle id; it counts only when the log's mirror at its mirror_of names its author. A reader of the root directory takes the key in office from the newest 30915 head of the circle id (every seal and head names it under the witnesses' quorum [proposal]) and then the 30910 by that key. Listing on root [Decided 16 Oct 2026, A9, "for now"]: no personal approval; the founder named in founder (whose signature g carries) is in the relays' 30889 list, at most 3 circles per founder, checked by the relay; limited space per circle on the borrowed Lana relays. Said openly: the 30889 list is kept by the Lana registrar, one body, and an unlisted circle keeps working through its link and QR (Q10). The 0.2.0 ["founder",…] gate tied to P3 is withdrawn: P3 is dropped [A9]; the 0.3.0 founder tag only names the founder for the A9 check.
  • 30915 v3: the newest 43917 mirrored: same pubkey, created_at, content and tags (the qs tags included), with ["d", <circle id>] and ["seal", <seal digest>, <seal day>] appended last; the seal tag must equal the digest of [0, pubkey, created_at, 43917, the seal tags without qs, content]; relays and readers refuse a 30915 where it differs. d is the circle id, so every circle keeps its own head [C5]. Like the seal, the head names the circle key (["circle_key", <hex>] [proposal]) (the world rules version tag rules is gone with 43920 [Decided 23 Oct 2026, E-1]) under the witnesses' quorum, so a reader of the root directory picks the circle's 30910 by that key in one more query. [Proposal] A seal is judged with the office after its last block h_to: the newest mirror at or below h_to (the genesis mirror or the newest roles entry's); its witnesses sign it, and it names their set (signers) and that mirror's circle key (circle_key), as the state's g3 lines at h_to do; at the height of a key change that is already the new key, which also signs the 30910 of that height. The head copies it. Append-only by the seal's prev chain (by digest). Published at a per-circle offset of the first 4 bytes, big-endian, of sha256(the circle id as its 64 hex characters), mod 86,400, seconds after the seal (test_vectors.circle_id), so heads are spread over 24 h (replaces 0.2.0's "hour after 04:00", which would bunch heads by time zone: ~560 writes/s in one hour at 10 M circles); the seal itself (43917) is made right after the morning.
  • 30916 v3: d=letsbalance, ["c", <circle id>], ["g", genesis digest], ["join", <height of the member-join rule entry>], ["attest", signer hex, since_day, sig] (by a signer current at the join height), n name words from the verified KIND 0 (name/display_name; NFKD, combining marks removed, lower-case; at most 9 n tags), w LANA address, v=3. Truth = the join (and leave) entry; 30916 is a pointer. The newest 30916 of a key names its last circle: a new circle reads it before admitting the key, and admits a key whose newest home names another circle only with that circle's reference (43921) [Decided 23 Oct 2026, E-8 and E-9; the check: proposal]; root relays never delete a 30916 [proposal]. Name queries {"kinds":[30916],"#n":[…],"limit":50}; results whose n values are not in that KIND 0 are dropped. The same signed 30916 may travel as a link or QR code and is checked the same way. Names [Decided 16 Oct 2026, A5]: KIND 0 and 30916 are the only places a name lives; both are replaceable by the person and relays keep only the newest version; when KIND 0 changes, the app republishes 30916 with the new n words and the signer re-attests; no 30916 version is copied into the log, and the join entry carries only the key.
  • 43919 v3 Rate Quote [Decided 16 Oct 2026, A7; the format is the proposal for 0.3.0, word for word as docs/knjiga-na-relejih.md §3.7]: pubkey = the proposer (node) of the seller's (receiving) circle at created_at, checked against that circle's roles windows. Tags ["c", <seller circle id>], ["source", <source id = the circle's rate_source at created_at>, "<the source's own time for this rate, unix>"], ["base", "EUR"], ["rate", "<currency>", "<whole ppm per 1 EUR>"] per currency (r_EUR = 1,000,000), ["expires", "<unix, ≤ created_at + quote_ttl_s>"] (quote_ttl_s is a parameter of the seller's circle, 600 s by default [Decided 23 Oct 2026, E-1]; never NIP-40 expiration), ["prev", "<previous 43919 id of this circle, or \"\">"], ["v","3"]; content empty. Conversion to_amount = floor(amount × r_to / r_from) in BigInt. Valid for a booking when signed by that node, c and source id match, source time ≤ created_at < expires ≤ created_at + quote_ttl_s (the seller circle's value, by which both circles judge the quote [proposal]), the payer circle's xout at ≤ expires and to_amount is exact; refusals stale_quote (expired) and bad_quote. The prev chain and the source-time order are daily audit checks (43924 fail), not booking conditions. Published by the issuing node to its circle's relays before it is handed out; at most one live quote at a time, reused by every payer while valid. The payer's circle republishes every quote its blocks reference. Not on root. the project owner's rate key is no longer needed; it may be one source a circle chooses. Start source [Decided 16 Oct 2026, start source]: the ECB daily reference rate read once a day by the node, its date shown in the app, a quote valid 600 s (quote_ttl_s by default); proposal: rate_stale_alert 120 h. Details and test vectors: docs/valute-in-tecaji.md.
  • 43920: retired [Decided 23 Oct 2026, E-1]: there is no world rules record; every time value and limit is a circle parameter, carried by the found act and the circle's state (docs/upravljanje-kroga.md, "Circle parameters"); the number stays reserved and is never reused. 1A's world signer is void, and with it the tension with principle 3 that 1A limited in time.
  • 43921 Reference (new) [a new kind: Decided 23 Oct 2026, E-9; the number, free in the BSA family, and the shape: proposal]: the node signs it right after the block that books the reference entry has its quorum, and publishes it on the circle's relays. Tags: ["c", <circle id>], ["p", <person HEX>], ["block", "<height>", <block digest>] (the block with the reference entry), ["g", <genesis digest>], ["currency", <code>], ["wave", "<minor>"] (the step the person built), ["own", "<open OWN processes>"] (always "0", since an open OWN process blocks the reference [Consequence of E-9 (a)]), ["joined", "<unix>"] and ["left", "<unix>"] (the membership), ["published", "<count>"] then up to 100 ["e", <project_propose id>, "", "published"] (newest first), ["took_part", "<count>"] then up to 100 ["e", <project id>, "", "took_part"] (projects paid to the person, newest first), ["v", "3"]; content empty. Regular, kept for ever, never deleted. The ledger entry is the authority (under the witnesses' quorum); the 43921 is the findable copy, which anyone checks against the circle's public blocks. A new circle's node checks a reference before a join from another circle: its c is the circle the key's newest member home (30916) names, its p is the key, and the named block of that circle carries the reference entry of the key with the same wave and a quorum of that circle's witnesses in office at that height (as for the 8j evidence) [the checks: proposal].
  • URLs in 30910 (api, relay, archive): https/wss with a public DNS name only; resolve and refuse loopback, private and link-local addresses; no redirects; size and time caps; never show a remote response body to users.

Copying and repair (sync)#

  • Facts [C]: strfry-router runs on all four Lana relays as a live stream with a fixed list of 45 kinds; none of 30910, 30915, 30916, 42893, 43910–43921 is in it. It does not reconcile; reconciliation (NIP-77) is strfry sync. Copies of one kind differ between the four relays by up to 18 % today (31234: 993 vs 1,172) [M].
  • The circle syncs itself: the proposer publishes to every listed relay; a replication audit runs daily and after outages, using NIP-77 negentropy scoped by #c (the circle id) and day (cheap on small scoped sets [M]: 61–291 ms for one event, 122–902 ms for 1,991 events; slow on large unscoped sets: 22.8–29.1 s for ~15,600 events [M]), republishes what is missing and publishes "copies N of M" in the app. A new or empty relay is back-filled with the full history by height before it is listed.
  • Never lana-relay-sync for any BSA kind until its four loss bugs are fixed: (1) a per-kind since cursor loses the oldest events when more than 500 new events of one kind arrive in 10 min (a ledger kind would hit this at ~0.8 events/s); (2) the backfill pages with until = oldest − 1, losing same-second events (227 of 1,753 once [R]); (3) a failed connection resolves [] and is marked complete history; (4) every cycle loads every stored event. Exclude all BSA kinds from it before 0.3.0 enters kinds.json (needs the project owner's yes; separate repository).
  • Never the strfry router for ledger kinds (a live stream with a fixed kind list).

Reading rules (summary; the full reader contract is in docs/knjiga-na-relejih.md)#

  1. Discover from the circle link or QR (circle id, genesis digest, relay hints); read root relays only at first install, and only those in the newest 38888. On root, find the circle by its circle id: the newest head {"kinds":[30915],"#d":[<circle id>]} names the circle key in office [proposal], then {"kinds":[30910],"#d":[<circle id>]} gives the records, of which only the one by that key counts (anyone can store a 30910 naming a circle id) [C5].
  2. Read the signer set from the log (genesis + roles entries), not from the newest 30910; cache ≤ 24 h; refresh on any refusal. Pin authors to that set for blocks, seals and chunks (writes are open on today's relays [C]). A run of blocks that does not start at block 1 is checked with the office and the circle parameters below it, read from the log (the newest roles mirror and the g3|param:* lines below the run); without them those checks are unverifiable, and what began before the run (an endorsement whose proposing act, a roles entry whose acts, an xdone or xundo whose hold, a project_open whose project, or a refusal whose key's membership lies before it) is left unverifiable, never refused and never passed; a witness checks it against its own state below the run [proposal].
  3. Ask 2 relays in parallel; "not found" needs EOSE from ≥ 2; a relay that did not answer never means "nothing", and silence is never balance 0 (a silent [] once wiped a wallet list in 26 ms [R]). Always show the real "answered N of M".
  4. Raw WebSocket readers with timeouts above the reader's own budget; never the nostr-tools pool for ledger reads (it drops queued reads after a 4.4 s fake EOSE [R]).
  5. Fetch by height (#h): at most 500 events per filter (the cap is per filter, not per REQ [M]), at most 200 filters per REQ (201 → CLOSED "bad req: arr too big" [M]), at most ~2,000 ids per filter [M]. Page by created_at only where no number exists, with until = oldest seen inclusive and de-duplication by id.
  6. Verify every event signature and every block's quorum before use; verify snapshot chunks against their seal before showing anything from them.
  7. Balance now: one REQ, two filters, {"kinds":[43911],"authors":S,"#p":[me],"limit":1} and {"kinds":[43911],"authors":S,"#c":[<circle id>],"limit":1} (the tip); if the tip is newer than my block, a sparse-Merkle proof of my line against the tip's quorum-signed state root. Cost: ~5–10 KB in small circles; 60–214 ms warm, 0.5–0.9 s with opening the socket [M].

The root directory at scale#

  • Writes [E]: 30910 + 30915 ≈ 9 GB on disk and 12 head writes/s at 1 M circles; ≈ 18 GB and 23/s at 2 M; ≈ 90 GB and 116/s at 10 M (the smallest root relay's 38 GB disk must grow or root gains relays). 43920 is retired (E-1). The 43921 references add one event of about 1–8 KB per member who leaves with no minus, on that circle's relays [E].
  • KIND 0 and 30916 [E]: at 10 B members ~10 TB of KIND 0 and ~15 TB of homes per root relay; the smallest relay fits ~11 M profiles and ~7 M homes today. Hence homes on root only while borrowed, then circle relays and index relays sharded by key prefix (Q16). The address carries the circle, so paying needs no global lookup.
  • Reads: phones never hold sockets to root relays; root is read at first install. At billions of users (0.2–1 billion concurrent sockets [E], carrier-grade NAT against the 100-per-IP limit [C]) phones read through verified read gateways with push; every byte is a signed event or a proof against a quorum-signed root, so a gateway can hide but never forge, and the app compares two sources.
  • More root relays are added by the Lana authority in 38888 when reads require; no circle's validity depends on 38888 (proposal, Q10): the link or QR carries the circle id, the genesis digest and relay hints, and the signed directory events may be mirrored anywhere.
  • Rates [E]: none on root [A7]. Quotes live on the seller circle's relays: one quote carries every currency of the source, and the node keeps at most one live quote and renews it only when asked and less than quote_margin (QUOTE_MARGIN_S, proposal 120 s) is left, so at most 86,400 / (600 − 120) ≈ 180 a day per circle (with quote_ttl_s = 600 s, the default), ~0.5 KB each, ≈ 90 KB a day, none on a day without payments from other currencies; plus one republished copy in each payer circle whose blocks reference a quote.

Adding and removing relays#

  • Root: only the Lana authority adds one, by listing it in 38888. It is seeded with an LMDB copy of an existing root relay and checked by count first. Circles republish their 30910 and 30915 at a random time within 24 h. Root receives directory kinds and every circle's found act [proposal] only (plus borrowed ledgers under quota); 43920 is retired [E-1]; copy the write policy's pins_file with the seed (pins are not in LMDB and are not rebuilt from lookback).
  • Circle relay (circle relays [Decided 16 Oct 2026, A2]; the act rule in docs/upravljanje-kroga.md): a relay_change act decided by k Heart members, with public notice (change_notice_s, 72 h by default [E-1]), committed by the current signer quorum, effective at a stated height. Before it is listed, the relay is back-filled with the whole history (every block from genesis, every seal, chunk, act, statement, issued and relied-on 43919 quote and relied-on partner block), read back by #h until heights 1…H are all present and the seal roots match.
  • Removing: a relay_change entry; what the relay still holds does no harm (signed).
  • Archive relays: anyone; named in 30910 v3 archive tags.
  • Bootstrap: the box relay from the first day [A2] plus the borrowed Lana relays (decided: circles start on borrowed Lana relays). The Lana relays count as copies only after their owner applies the write policy and it is confirmed on a staging copy the owner provides, never by sending test events to the production relays. Until then the app states honestly how many copies exist. [Open] (Q14, Q18): until the owner applies it, only the box relay counts, so under A1 no block could become final and the circle would depend on one outside owner. the project owner chooses: (a) the circle books nothing until then; (b) until then the Lana relays count as copies, publicly labelled "copies without protection against deletion"; (c) until then the circle finds two more relays with the same policy at other operators.

Cross-circle payments (relay view)#

  1. Quote (across currencies only) [Decided 16 Oct 2026, A7]: the payer's app asks B's node (api) for a 43919 quote for the pair (payer currency → B's currency); B's node publishes it to B's relays and returns it; the app shows the converted price and the quote's validity; the payer signs only if the price suits them, otherwise there is no payment.
  2. The payer signs one 43910 with both circle ids (["c", <payer circle id>, "", "from"] and ["c", <receiver circle id>, "", "to"] [C5]), amount in the payer's currency and to_amount in the receiver's (the payer signs both [A7]), across currencies exactly one ["e", <43919 quote id>, "", "quote"], expires ≤ created_at + note_ttl_s (a circle parameter, 600 s by default; each circle judges the note by its own value, and the app signs with the smaller of the two [proposal, E-1]).
  3. Circle A checks cover and world_used + x ≤ step and, across currencies, B's quote exactly as B will (an expired or wrong quote is refused at intake, stale_quote / bad_quote, nothing held); books xout (payer → world_A, held); final in A as above.
  4. A's proposer POSTs the note and its block reference to B's api; B's signers can also read it from A's relays.
  5. B's signers check A's quorum against A's signer set (from A's log, cached ≤ 24 h), that the receiver is B's member, B's refusal list and limits, and across currencies that the quote is B's own (signed by B's proposer of that time), that to_amount = floor(amount × r_to / r_from) under the quote, exactly, and that A's xout at is no later than the quote's expires (B honours its own valid quote exactly; the old "tolerance" check is gone); then book xin (world_B → receiver, with wave growth counter-booked to B's reserve and on by drain()) or a public rej with its reason (bad_quote, stale_quote, …).
  6. A books xdone naming B's block as [height, digest], only if B's xin embeds the same note id and books exactly to_amount (anything else counts as a refusal). A timeout alone never undoes A's booking; xundo (world_A → payer) on B's signed refusal or "expired". A payment that hangs [Decided 16 Oct 2026, 8j]: nobody pays a refund. If A has booked neither 7 days after xout (xcheck_s, A's circle parameter, 7 days by default, recorded at the xout [E-1]), every signer of A reads B's ledger from B's relays (B's signer set from B's log): the B blocks whose at lies from A's xout at − 60 s (margin: proposal) to the note's expires, found by height inside B's sealed ranges. [Proposal] B's office is taken from B's log (the office below the lowest block read, then each roles entry in the blocks read, in office from the next height); each seal is judged with the office after its h_to. A block counts only with a valid quorum of B's witnesses in office at its height: a block or seal copy without one (for example one signed only by old, retired or stolen keys that are no quorum of the witnesses in office at that height) is not B's, never counts and never hides a copy that has one; two blocks with valid quorums at one height are a fork, and the evidence is not used; what rests on an office that a missing block leaves unknown is unverifiable. So a hold whose evidence spans a key or witness change in B can still be settled, and noise signed by keys that are no quorum of the witnesses in office at that height cannot stall it; but a key that was in office at a height (for example the single witness of a one-witness circle, later stolen) can still fork that height, and a fork stops the evidence. B can never book a note after its expires, so once B's seals cover that time B's answer is fixed. B booked xin with this note id and exactly to_amount → A books xdone by rule, naming B's block; B booked none (a rej or nothing) → A books xundo by rule, naming as evidence B's consecutive seals as [day, digest] and the height range they cover: from the seal whose predecessor covered only bookings before A's xout at − 60 s to the first seal whose boundary is later than the note's expires (strictly), one or two seals (an idle B still seals every day), plus the real seal before them (before), which every signer of A checks against B's chain (B's quorum; it chains to the first seal exactly as that seal's prev describes; without it nothing is booked). Every witness checks the evidence against B's blocks, not only its seals: the seal before and every named seal must match B's blocks of their ranges (heights, digests, head, covers, entries, state root), and B's first seal always covers from its block 1. The evidence is closed by B's block right after the last seal (it follows that seal's head and books at or after its boundary) or, when B has booked no block since, by B's next daily seal, named as next (the seal of the day after, chained by prev, with B's quorum and an empty range from h_to + 1; a block at that height booked before its boundary refutes it); before relying on the next seal, a witness confirms on at least two of B's relays that no block exists at h_to + 1. A block or seal that is not given leaves the evidence unverifiable, so nothing is booked; a B that stops sealing keeps the hold waiting. Every leg names B's genesis digest (g); B's ledger cannot be read in full (relays silent, a missing height, no B seal covering expires, a seal without B's quorum, two B seals for one day, two B blocks with valid quorums at one height) → nothing is booked, the check repeats (proposal: daily after the morning), never a guess, and A's other bookings go on. The refund by public decision (xrefund) is gone. The payer gets the amount back at most once. Full rule: docs/knjiga-na-relejih.md §4.5.
  7. Each circle stays at sum 0 through its own world line; each republishes to its own relays the partner block it relied on and the 43919 quote it relied on.
  8. Legs counted: at the ~31 % cross share of the 10 Oct simulation [R], a payment averages 1.62 entries and ~45 % more bytes; every number below includes this.
  9. Optional per-partner sales limit net sales to A ≤ L(A) (proposal P5); a circle's own refusal list stays. Other circles see the whole ledger; step, used and built stay the decision inputs.

Capacity#

Model [E]: per payment ~1.1 KB of entries (1.62 entries per payment, cross legs included); block envelope ~0.85 KB + 0.205 KB per signature (3 signatures, or one FROST signature ~0.15 KB); payments per block = entries/s while awake × W_max + 1, at most ~120 (w_max_s = 1 s at founding; proposal: 2 s above about 10,000 members); morning lines ~0.25 KB for half the accounts; snapshot ~0.2 KB per account; disk factor 2× [M] (one relay: 769 MB for 253,067 events, raw average 1,547 B); 16 waking hours; peak 5× per circle, 3× for the world; relay planning budget 300 events/s (1/6 of an external ~1,856 events/s strfry benchmark [R], never measured on our relays). The sizes were estimated while every event still named its circle by the former a tag, whose value (30910:, the 64-hex circle key, : and the code) is longer than the value of the c tag that replaces it, the 64-hex circle id [C5]; where an estimate counted that tag, it is now a few dozen bytes per event on the high side. The seal and the head now also carry circle_key and rules (up to about 160 bytes), within the ~2 KB and ~2.5 KB estimates (the 30915 example of the definitions is 2,082 B). Not re-measured.

One circle [E]

CirclePayments/dayEntries/dayEvents/day (blocks + morning + chunks)Peak entries/s → blocks/sRaw per day, one copyDisk/year, one copyDisk/year, 3 copiesBytes per payment, all-in
1,000 × 22,0003,240~3,1000.2 → 0.27 MB5 GB15 GB3.5 KB
1,000 × 1010,00016,200~12,7000.9 → 0.730 MB22 GB66 GB3.0 KB
5,000 × 10 (W = 2 s, FROST)50,00081,000~21,0004.7 → 1.278 MB57 GB172 GB1.6 KB
100,000 × 101 M1.6 M~56,00094 → 3.21.2 GB0.9 TB2.7 TB1.2 KB
1,000,000 × 1010 M16.2 M~133,000940 → 7.511.6 GB8.5 TB25 TB1.2 KB
  • Every relay of a circle holds all of it, so one circle's peak must fit one relay. Blocks keep per-relay load ≤ ~8 events/s even for a 1 M-member circle. The binding limits are the proposer (today's v2 books 19–80 payments/s, an estimated 100–200 after known fixes [R]; Schnorr verification ~722/s per JS core, ~11,000/s native [R]) and serial blocks (2–9 blocks/s). Provisional cap (proposal, Q13): 100,000 members at 10 payments a day (500,000 at 2 a day).
  • Restore (seal + one day of tail, 3 relays in parallel at 356–2,416 events/s and 0.55–3.7 MB/s per relay [M]): 1,000 × 2 in 1–4 s; 1,000 × 10 in 3–18 s; 100,000 × 10 in 2–12 min; 1 M × 10 in 17 min – 1.9 h [E]. Full replay of one circle-year: 1,000 × 2 ≈ 1.1 M events, 2.6 GB; 1,000 × 10 ≈ 4.6 M events, 11 GB.

One million circles, one billion users [E]

2 payments/day10 payments/day
Payments2 B/day (23,000/s)10 B/day (116,000/s)
Events/s average / 3× peak36,000 / 107,000146,000 / 439,000
Raw per day, one copy7 TB30 TB
Disk per year, 3 copies / 4 copies15 PB / 21 PB66 PB / 87 PB
Relay-equivalents for 4 copies at 3× peak: 300 ev/s / 1,856 ev/s1,400 / 2305,900 / 950
Root: 30910 + 30915~9 GB on disk, 12 head writes/ssame

The principle-6 target: 10 billion users, 100 billion payments a day, ≥ 3 copies [E]

100 billion a day = 1.157 million payments a second on average.

Profile2 M circles × 5,000, W = 2 s, one FROST signature10 M circles × 1,000, W = 1 s, 3 signatures
Payments per block (average while awake)3.81.3
Bytes per payment, all-in1.6 KB3.0 KB
Events/s average / 3× world peak0.49 M / 1.48 M1.46 M / 4.39 M
Raw per day, one copy157 TB300 TB
Raw per year, one copy57 PB109 PB
Disk per year, 3 copies (4 copies)343 PB (458 PB)656 PB (875 PB)
Ten years, volume growing linearly to the target, 3 copies, uncompressed~1.7 EB~3.3 EB
Relay-equivalents for 4 copies at 3× peak, 300 ev/s / 1,856 ev/s19,700 / 3,20058,600 / 9,500
Circle-box model instead2 M box relays, ~57 GB a year each per copy, ~1–5 events/s at peak10 M box relays, ~22 GB a year each
Root30910 + 30915 ≈ 18 GB; 23 head writes/s≈ 90 GB; 116 head writes/s
  • Per user ~34–66 MB a year on disk across 3 copies; at ~$15 per TB-year of disk plus servers and power (~3×), about $0.002–0.004 per user a year. Bytes are cheap; running tens of thousands of reliable relays is the cost.
  • The floor is the payments' own payload (signatures, keys, amounts, balances after): ~65 TB a day per copy even with no envelope [R]; binary encoding would halve JSON-hex but would leave Nostr.
  • Nostr has never run anything like this: the largest public relays ingest 2–6 events/s [R]. 1.16 M/s is the sum of millions of light cells, not one system; no component sees the world total.

Today's four Lana relays [M/C] (16 Oct 2026; names left out)

Value
Software, hardwarestrfry 1.1.0 on each, 2 vCPU, 3.8–3.9 GB RAM [C]
Disk free11 / 90 / 23 / 51 GB; the smallest 38 GB disk is 70 % used [C]
Ingest2,245–4,513 events a day, ~0.03–0.05 events/s [M]
Limits131,072 B per event, 5,000 tags, 2,048 B per tag value; events older than 10 years or newer than now + 1 day refused; ephemeral kept 300 s [C]
NIPs9 (KIND 5) and 40 (expiration) on; 45 COUNT exact; 77 negentropy works; no 42 auth, no 50, no 62 [M]
Reads, from SloveniaTLS + WS open 418–651 ms; small indexed REQ → EOSE 56–214 ms median [M]; paging 356–2,416 events/s per connection [M]
Write policyrefuses kind 1059, gates KIND 0, accepts everything else; no per-author or per-kind limit; no refusal of KIND 5 or expiration [C]; 55,123 KIND 5 events on one relay [M]
As the only replicationthe full public ledger of about 2 circle-years of one 1,000-member circle at 2 payments a day (smallest relay: 11 GB free / ~5 GB a year per copy), half a circle-year at 10 a day [E]; the 1 Oct finding (smallest relay full at ~8,500 payments a day [R]) agrees
Role[Decided 16 Oct 2026, A2] the root directory for ever; a borrowed home for small circles with limited space [A2, A9] (size proposal: 1 GB or one year per circle, whichever first; Q18), counted as copies only after the owner applies the write policy

Storage over ten years without breaking principle 2 (proposal)

  1. Shard by circle: each circle's growth sits on its own relays and partners.
  2. Checkpoints are read compaction, not deletion.
  3. Circle boxes keep everything (it is small). Shared relay groups at scale: hot relays keep 30–90 days; older blocks move to ≥ 3 archive relays of different operators, are verified there by negentropy and against the seal roots, and only then may leave the hot relay (Q19). This is the only place an operator removes a ledger event.
  4. Archives compress (strfry zstd dictionaries, unverified on our build; JSON-hex compresses 2–3× [E]).
  5. No 10-year lower limit on circle and archive relays, or history older than 10 years can never be copied to a new relay [C].
  6. Who pays: each circle pays its relay operators through a project (Common Good no longer exists [A8]); partners by reciprocity; archives by the circles that use them (Q15).

Not measured, not certain: strfry EVENT→OK latency and sustained ingest at 2 KB and 100 KB events; whether OK means fsync'd (power-cut test); the cost of a duplicate EVENT; concurrent sockets and subscriptions per relay; MB/s served for a restore and with several connections; negentropy on 1–2 M scoped events; whether strfry refuses re-insertion after KIND 5; zstd ratio; signer round times on real networks; all event sizes until the 0.3.0 examples are signed. The first build step measures them on a separate strfry with the production configuration (needs the project owner's yes); its scripts refuse to connect to the four Lana relays.

Circle in a box: the relay part#

  • Every circle box ships its own relay: strfry with the ledger write policy above, rejectEventsOlderThanSeconds = 4000000000 (not 0, which refuses every event; measured 16 Oct 2026), BSA kinds out of every purge. For the live circle it runs on its server, bound to localhost until the project owner's yes.
  • Configuration of a clone (CIRCLE_CODE = the 30910 code; the 30910 d is the circle id, the id of the found act the founder signs [C5]; CIRCLE_NAME, CIRCLE_CURRENCY, APP_ORIGIN, admin keys, root relay list from 38888) and the setup page: docs/nov-krog.md.
  • A clone starts with an empty ledger, its own data directory and its own relay; nothing in the image is tied to the live circle.

Witnesses by lot among circles (the direction for phase I) [Decided 23 Oct 2026, E-7]#

  • The rule [Decided 23 Oct 2026, E-7, with the addition]: witnesses are computers, chosen by lot among the circles' computers; every circle's computer is in the draw, so every circle witnesses others and is witnessed by them. Docs only: 0.3.0 has no code and no record for the draw.
  • The direction for phase I [Decided 23 Oct 2026, E-7]: a public random number nobody knows in advance or can choose (for example a Lana chain block); a schedule that changes regularly and is known in advance; a new witness loads the circle from the latest seal (43917 with its 43918 chunks, then the blocks after it) and verifies from there; it needs nothing from the witness it replaces [consequence].
  • Until then [Decided 23 Oct 2026, E-7; decision 3 unchanged for 0.3.0]: a circle has one witness, its own computer, publicly labelled "one witness · not yet decentralised".
  • Exceptions [Decided 23 Oct 2026, E-7]: the Heart's vote (the larger of k and 3 Heart members "for" [E-10] and no Heart member "against") is only for exceptions, for example removing a misbehaving witness until the next draw (signer_remove, signer_replace, signer_propose, recovery; docs/upravljanje-kroga.md, "Signer set, relays"); the threshold, the larger of k and 3 [Decided 23 Oct 2026, E-10].
  • Kept [consequence of decision 3]: at most one witness per operator in any draw result; the quorum q = 2f + 1 of n = 3f + 1.
  • Open for phase I [Decided 23 Oct 2026, E-7]: fake circles (many empty circles would get more places in the draw); load matching (a large circle must not get too small a computer); absent witnesses (a drawn witness that does not work must not halt the circle until the next draw).
  • Said openly (principle 5): a circle no longer chooses its witnesses itself and depends on the network of circles rather than on any one circle (T23 below).

Tensions with the principles#

#TensionPrinciple-conform answerStatus
T1One rate key signs every exchange rate (decided 16 Oct)the seller's (receiving) circle quotes a rate from a source it chooses; the payer accepts by signing both amounts or does not pay; the quote is a public signed 43919 of that circle's node; no central rate keyResolved [Decided 16 Oct 2026, A7]; start source decided the same afternoon (the ECB daily rate)
T2The Lana authority chooses the root relays (38888)38888 stays the directory; no circle's validity depends on it; link/QR carries the circle id, the genesis digest and relay hintsQ10
T3One administrator decides orders, deposits, payouts, capsthe Circle Heart, four: the founder and three other Heart members; signed acts, public rules; a project needs at least three Heart "for" votes and no "against" in a vote of all members; no payouts at all (A6) and no deposits (6a)Resolved [A4, 4a; E-2, E-5]; k for other acts: proposal, 3 of 4 (for replacing the circle key: decided, C5); see T21
T4P3: the project owner confirms the first circles; seals only from 5 membersP3 dropped; objective listing rule checked by the relay; limited space on borrowed relays; public from block 1Resolved [A9, "for now"]
T5One node per circle as sequencerone proposer validated by n = 4 signers (q = 3), at most one per operator, replaceable; bootstrap single signer labelled "not yet decentralised", no money cap, daily verifier and restore drillResolved [A3]
T6The 4 Lana relays as the only replication; possibly one operator≥ 4 relays, ≥ 3 operators per circle; box relay from day one; Lana relays as directory + borrowed home≥ 4 / ≥ 3 and the box relay resolved [A2]; bootstrap operators and "one operator?": Q14
T7Common Good fund key on the serverkeyless project wallets and a keyless reserve; money moves only by rule entries the signers check; surplus to the reserve, the reserve to the next project that passesResolved [A8]; details decided the same afternoon (8a, 8b, 8e, 8f, 8i, 8j; 8c replaced by E-5); 8d follows from A8; 6c (the start for the reserve) decided later the same day; Open 8g, 8h
T8Principle 3 against principle 5signers chosen and replaceable by the circle; "independent" = "can replace any partner"Resolved [A3]; changed by E-7 (23 Oct 2026): witnesses by lot among the circles' computers (phase I), the Heart's vote only for exceptions (see T23)
T9Definitions signed by the Lana authority (42893)each circle pins its definitions version in its logQ20
T10"Relays only from 38888" (decided 14 Sep)the rule stays for root relays; circle relays are named by each circle's logResolved [A2]
T11KIND 0 and member homes of every member on rooton root while borrowed; then circle relays and index relaysQ16
T12"Publishing waits for every relay" (rule of 14 Sep)final at 3 [A1]; sending continues; real N of M shownfinality resolved [A1]; sending on: Q17
T13Bank details versus "everything public"—Gone [A6]: BSA holds no bank data and makes no payouts, so there is nothing to hide and no exception to principle 1
T14Public for ever versus personal-data law and people's safetythe ledger knows people only by HEX key; names only in KIND 0 and 30916, replaceable by the person, newest version only on relays; warning at every free-text field; no legal-review gate, no 43925Resolved [A5]; said openly: old saved profile copies and names typed into free text can remain
T15The BSA ID is the LANA WIF keyoptional guardians, freeze, key_moveQ11
T16Deleting ledger events from hot relays at scaleonly after ≥ 3 verified archive copies of different operators; boxes never deleteQ19
T17One circle must fit one relayprovisional cap 100,000 membersQ13
T18Fiat deposits mint money on a human signature—Gone [Decided 16 Oct 2026, 6a]: BSA has no money deposits, so no money is created on a human signature; the wave grows only with what a member receives; Q12 is moot
T19Votes, reasons and purposes public for everevery vote signed by the voter's key, an "against" with a short public reason, never a name in the ledger (A5); warning at the field; corrections add, never remove[Decided 23 Oct 2026, E-5]; replaces Q21
T20One key (the rules authority) changes the time values and the number of project approvals for every circle through 43920 BSA Rules—Gone [Decided 23 Oct 2026, E-1]: no world rules record and no rules authority; every circle sets its own parameters within the bounds of the definitions (1A void)
T21The Circle Heart has power the members cannot vote away (principle 3)everything public and signed; k signatures; the money and gravity computation and the floor of 3 Heart "for" are fixed; any member's "against" makes a project fall; a settled member leaves with a reference and founds their own circle[Decided 23 Oct 2026, E-2], said openly; the project owner decides whether the balance is right
T22Every circle grows from the live circle (the root); only a member of an existing circle founds (principles 3 and 5)the root decides nothing about other circles and cannot stop a founded circle; the chain only gates who can found[Decided 23 Oct 2026, E-8], said openly
T23Witnesses are other circles' computers, drawn by lot (principle 5)a quorum of 3 of 4; anyone can recompute; the Heart removes a misbehaving witness until the next draw; one own witness until enough circles exist[Decided 23 Oct 2026, E-7]; details phase I
T24A join from another circle depends on reading the old circle's ledger (principle 5)the new circle needs nobody's permission, only the public ledger; if it cannot be read the person waits[Decided 23 Oct 2026, E-9], said openly
T25OWN processes run encrypted (principle 1)only that a process runs, and the decision, enter the public ledger[Decided 23 Oct 2026, E-4], said openly

Tests (when built)#

  • Write policy (on the test relay): a block by a key outside the signer set is refused; an act by a non-member is refused; a KIND 5 or KIND 62 aimed at a block is refused; a KIND 5 authored by a ledger key is refused; an expiration or - tag on any BSA kind is refused; an event above 100,000 bytes is refused by the app before sending; record what strfry does when a deleted id is re-sent. Since 17 Oct 2026 also: an event whose c names no served circle id is refused; a 30910 signed by a witness or by any key other than the circle_key it names (for a configured circle: the circle key in office), or with a code as d, is refused; after a key change the new key signs and the old one is accepted only up to its until, and an old circle key never deletes; a found act whose id is a served circle id is accepted from its author; a note is served by its c from or to; no 43920 is accepted (retired, E-1); a 43921 not signed by the node of the circle it names is refused [proposal, E-9]; in mode root a 30910 for a pinned circle id with another g or founder is refused, and one by a new key with the pinned g is stored (relay/README.md, "Testing"). With the proposed changes of 17 Oct 2026 also: a retired key is accepted only in its own role (a retired circle key for an old 30910, never for a block, seal, chunk, head or quote); in mode root every circle's found act is kept, from any author (a 43916 with a second c tag is no found act; found acts beyond seen_ids_max are kept but not remembered, so a newer circle's records stay unpinned); a 30910 is pinned only when the relay holds the circle's found act and the record's founder is its author, or the circle is configured (otherwise it is stored unpinned), and only after every plugin of the chain accepted it; a 30910 whose founder is not the author of the found act the relay holds is refused. With the decisions of 23 Oct 2026 also [proposal]: in mode root a found act or a 30910 of a founder who already founded another circle id is refused (E-8), and no 30916 is deleted (E-9).
  • Publisher: silent relay, 429 on upgrade, a relay that drops every third event, a relay that hard-deletes, a relay that is 1 s slow; finality never declared below 3 copies; sending continues after finality; a backlog of a 10⁵-block test circle back-fills from genesis within the measured rate limits.
  • Reader: a relay returning exactly 500, [], a partial list, a 4.4 s stall; a missing height is reported by number; the balance shown never becomes 0 because a relay was silent; two different quorum-valid blocks at one height raise a fork alarm.
  • New circle relay: listed only after heights 1…H and the seal roots are verified on it.
  • Root: a 30915 whose seal tag does not recompute as the seal's digest is refused; a head off the prev chain is refused; a 30910 that differs from the log's mirror is ignored by readers; a 30910 not signed by the circle key that the log's mirror at its mirror_of names (also one by the old key with a mirror_of at or above a key change) is ignored by readers; readers pick the 30910 by the circle key the newest valid 30915 head names; a 43921 that does not match the reference entry of the block it names is ignored by readers, and a join from another circle without a checked reference is refused [proposal, E-9].
  • 30916: a missing attestation, an attestation by a key outside the circle's signer set, or n words outside KIND 0 are dropped.
  • Daily audit: a hard-deleted block on one relay is found on another and republished; a block missing everywhere stops the restore check with its height; "copies N of M" matches reality.
  • Cross-circle: lost answers repeat the same answer; a timeout never undoes A's booking; after 7 days a note B never booked up to its expires, with B's seals covering that time, returns the hold to the payer by xundo naming B's seals and the real seal before them (an evidence without that seal, or whose first seal misdescribes it, is not booked), a note B booked gives xdone, and with B's ledger unreadable (silent relays, a missing height, no seal covering expires) nothing is booked and A keeps booking its own payments; [proposal] a B block without a valid quorum of B's witnesses in office at its height (for example signed only by an old or stolen key) is ignored, two B blocks with valid quorums at one height stop the evidence, and a key or witness change in B inside the evidence does not stop the check; the payer never gets the amount twice; an expired quote is refused by A at intake with nothing held; xdone only for a xin with the same note id and exactly to_amount; every leg is present in both logs; a note naming another circle id is refused.
  • Rate quotes: a 43919 not signed by the seller circle's proposer of that time is refused by the write policy and by readers; a note whose to_amount does not equal the quote's conversion, or whose xout was booked after the quote's expires, is refused in public by B (bad_quote, stale_quote); a valid quote is honoured exactly; every relied-on quote is present on both circles' relays; the conversion test vectors of docs/valute-in-tecaji.md give the same cents everywhere.
  • Names: no 43911, 43916, 43917, 43918, 43919, 43921 or 43924 carries a name; a 30916 republished after a KIND 0 change replaces the old one on every circle relay.
  • Every block reaches every listed relay; a payment counts only after three relays hold it.