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=1stays 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 keylessorigin; 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 thetzdataversion 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 itsorigin); [Consequence] as indocs/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 bycircle_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 therelaytags are read (decided, 14 Sep 2026); thefxvalues 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#
| Tier | Run by | Carries | Rules |
|---|---|---|---|
| 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 relays | anyone | full histories, compressed | no 10-year lower limit, no expiry, KIND 5 refused |
| Index relays | anyone | 30916 member homes sharded by key prefix; name search | at scale only (Q16) |
| Read gateways | anyone | verified HTTP caches with push | can 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 (seedocs/upravljanje-kroga.md).
Kinds and where they live (proposed for 0.3.0)#
| Kind | Name | Class | Signed by | Circle relays | Root | Archive | Size [E] |
|---|---|---|---|---|---|---|---|
| 0 | Lana profile (exists; minimal) | replaceable | member | – | yes | – | ~0.5 KB |
| 38888 | Lana System Parameters (exists; only relay tags read) | addressable | Lana authority | – | yes | – | – |
| 42893 | Kind definitions (exists) | regular | Lana authority | – | yes | – | – |
| 30910 v3 | Circle Record: a mirror of the governance state; d = circle id, address 30910:<circle key in office>:<circle id> | addressable | the 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 v3 | Circle Head: newest seal, for discovery; d = circle id; names the circle key in office [proposal]; no rules version any more (43920 retired, E-1) | addressable | the seal's proposer, with quorum | yes | yes | – | ~2.5 KB |
| 30916 v3 | Member Home (mandatory); replaceable by the member, never copied into the log [A5] | addressable | member, attested by a signer | newest version | while borrowed (Q16) | – | ~0.8 KB [M 739 B for 0.2.0] |
| 43910 v3 | Payment Note | regular | payer | embedded in 43911; standalone only as censorship evidence | while borrowed | yes | ~0.37 KB embedded, 0.8–1.0 KB standalone |
| 43911 v3 | Circle Block: the ledger | regular | proposer, with ≥ q signer signatures inside | yes (≥ 4) | while borrowed, under quota | yes | 1.9 KB (one payment, one signer) to ≤ 100 KB |
| 43916 | Circle Act | regular | the acting person | embedded in 43911; standalone copy optional | while borrowed | yes | 0.5–1.2 KB |
| 43917 v3 | Day Seal (checkpoint) | regular | proposer, with quorum | yes | while borrowed | yes | ~2 KB |
| 43918 | Snapshot Chunk | regular | proposer (valid through the seal's roots) | yes | while borrowed | yes | ≤ 100 KB, ~450 lines |
| 43919 v3 | Rate Quote (repurposed) [Decided 16 Oct 2026, A7] | regular | the node (proposer) of the seller's (receiving) circle | those the circle issued, and those its blocks rely on (republished) | – | yes | ~0.5 KB [E] |
| 43920 | retired [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 | – | – | – | – | – | – |
| 43921 | Reference (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) | regular | the node of the circle that booked the entry | yes, for ever | while borrowed | yes | ~1–8 KB [E] (up to 100 + 100 project ids) |
| 43924 | Statement (attestation or challenge) | regular | signer, verifier, auditor | yes (+ partners) | – | yes | ~0.7 KB + evidence ids |
| 30917 | Service Offer (relay, signer, archive, index operator; optional, later) | addressable | operator | – | 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):
- 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]).
- No
expirationtag and no NIP-70-tag on any BSA kind; readers treat such events as invalid. Ledger keys never sign KIND 5 or KIND 62. - 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.
- Indexed single-letter tags only where a reader queries (every single-letter tag is indexed on all four relays [M]):
ccircle id,hheight,mday,ichunk,pparty,ereference,ttype,dpointer (the circle id in 30910 and 30915;letsbalancein 30916),nname words (30916) (x, a content hash for 43925, is no longer used: 43925 was dropped [A5]). Every BSA event except the 30910 (circle id asd) 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 theatag any more. Chain fields and signatures use multi-letter tags (qs,prev,seq,at,root,genesis), which are not indexed (multi-letter filters answerCLOSED "unindexed tag filter"[M]). - 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). - 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 theqstags). 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 firstgit saw for the circle id), and never follows anothergfor 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 signsha256("lb-block/3|" ‖ digest)orsha256("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.jsonon 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 againstkinds.jsonand the four relays before publication [proposal, E-9]. Every number is re-checked against the livekinds.jsonand all four relays (COUNT, read only) before publication.
Where things live (precise)#
| Item | Circle relays | Root | Archive | Signer / proposer host | Phone |
|---|---|---|---|---|---|
| 43911 blocks (embedded 43910, 43916) | all heights 1…H, permanent | while borrowed, under quota, only after the write policy exists | all | write-ahead log of blocks not yet on 3 relays (seconds); disposable cache | reads on demand; disposable cache |
| 43917 seals, 43918 chunks | all | while borrowed | all | disposable cache | the newest seal seen |
| 43924 statements | all (+ partners) | – | all | its own | – |
30915 head (d = circle id; names the circle key in office [proposal]) | yes | yes (newest) | – | – | – |
30910 mirror (d = circle id) | every version | newest 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 issued | while borrowed | all | the node signs it after the block's quorum | – (a new circle's node reads it when the person joins there) |
| 30916 member home | every member, newest version only | while borrowed (Q16) | – | – | own |
| 43919 rate quotes | issued by this circle's node, and those its blocks rely on (republished) | – | all | the node signs them | – |
| Project wallets and the reserve (lines in 43911, 43918) | every move, permanent | while borrowed | all | disposable 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 keys | never | never | never | on the holder's machine, file mode 600 | BSA 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#
- 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.)
- 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 truefor 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. - 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).
- 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. - Receiver: a live subscription
{"kinds":[43911],"authors":S,"#p":[me],"limit":0}on 2 circle relays while the app is open (limit:0returns EOSE at once [M]). - 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). - 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):
| Step | Single signer, borrowed Lana relays | Own regional relays, quorum 3 of 4 | p99 target |
|---|---|---|---|
| Sign in browser | 5–20 ms | 5–20 ms | 50 ms |
| POST to proposer | 30–100 ms | 30–100 ms | 300 ms |
| Checks, ordering, roots | 2–10 ms | 2–10 ms | 30 ms |
| Block closing (idle / busy) | 0–50 ms / ≤ W_max | 0–50 ms / ≤ W_max | 2 s |
| Signer round | – | 50–150 ms | 400 ms |
| fsync guard + write-ahead log | 1–10 ms | 1–10 ms | 30 ms |
Third relay OK (third-fastest Lana relay ~390 ms REQ→EOSE from the server [R]) | 300–1,000 ms | 80–300 ms | 1.5 s |
| Answer to app | 30–100 ms | 30–100 ms | 300 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'spolicy.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 toretiredwith the rolecircle_keyand its lastcreated_atasuntil), the signers, the nodes and, optionally, the members, Heart members and applicants. Every BSA event except 30910 must carry actag naming a served circle id, or it is refused; a payment note is served when itsfromor itstocircle is. retiredentries carry their role [proposal]:{"key": <HEX>, "until": <unix>, "role": "signer" | "node" | "circle_key"}; a retired key counts only in its own role up to itsuntil(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
cnames a served circle id and the author is in the circle's current signer set (or is a retired key with the rolesignerandcreated_at≤ itsuntil), as named by the circle's log. The plugin reads the set frompolicy.json, not yet from the log: when arolesentry changes the witnesses or the circle key, an operator tool updatespolicy.jsonon every relay of the circle (following the committedrolesentries directly: open). This is spam control only; readers still verify against the log. - 30910 (circle record): a v3 record whose
dis a circle id the relay serves (in moderoot: any circle id) and whose author is thecircle_keyit names and, for a configured circle, the circle key in office (or a retired key with the rolecircle_keyup to itsuntil); it carriesgandfounder, and the founder's BIP-340 signature ingmust verify over sha256("lb-genesis/3|" + circle id + "|" + genesis digest). A record without["v","3"](0.1.0 history) passes only in moderoot, 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 moderootthe relay keeps every circle'sfoundact (a 43916 whose first tag is["c", ""]and that has no otherctag, from any author: its id is the circle id, so nobody can make a false one); the plugin remembers at mostseen_ids_maxfound 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
nodesinpolicy.json, or a retired key with the rolenodeup to itsuntil). - 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
rootthe 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 moderoot(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
expirationand-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) andfounderof every circle id, from the circle link or the first v3 30910 accepted, and refuse a later 30910 for that circle id with anothergorfounder(blocked:, logged asTAKEOVER 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 ing). A new version with the samegandfounder, also one signed by a new circle key, passes.relay/ledger-write-policy.pydoes this in moderootfor every circle id, configured or not, after checking the founder's signature ingover 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'sfounderis 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 whosefounderis 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'spins_file, not in LMDB. Said openly: the pin is not the circle key; in moderootany key that names itself incircle_keycan store a 30910 for a pinned circle id with itsg; only the record signed by the circle key that the log's mirror at itsmirror_ofnames 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 moderootthe 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 value0refuses 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 pinnedtzdata, 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 onerolesentry per block [proposal], so a height names exactly one mirror and a secondrolesentry can never undo what the first put in office; block 1 books norolesentry, 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 address30910:<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. Noquote_ttland noproject_approvals: they are circle parameters, carried in the ledger's state (g3|param:*), not in the mirror [Decided 23 Oct 2026, E-1]. Nofundkey [A8]; norate_panelorrate_rule[A7]; nocustodiantag [Decided 16 Oct 2026, 6a: no deposits]. The author must be thecircle_keythe 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 noticechange_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 signscircle_key_changewith["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'screated_atin decimal: a pop is good for its own act only, and an act is booked only withinact_ttl_sof itscreated_at, so an old pop can never be used again [proposal]), the others endorse it; afterchange_notice_s(72 h by default) of public notice from the k-th act arolesentry, 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 thatrolesentry's height, from then on only the new key signs the 30910, and a record by the old key with amirror_ofat 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 itsmirror_ofnames (at the height of a key change already the new key; for blocks it is in office from the next height), its tags withoutgequal that mirror (the committed genesis orrolesentry atmirror_of), and itsgnames the pinned genesis digest; readers take the valid record with the highestmirror_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 itsmirror_ofnames 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 infounder(whose signaturegcarries) 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.0foundertag only names the founder for the A9 check. - 30915 v3: the newest 43917 mirrored: same pubkey,
created_at, content and tags (theqstags 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.dis 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 tagrulesis 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 blockh_to: the newest mirror at or belowh_to(the genesis mirror or the newestrolesentry's); its witnesses sign it, and it names their set (signers) and that mirror's circle key (circle_key), as the state'sg3lines ath_todo; 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'sprevchain (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),nname words from the verified KIND 0 (name/display_name; NFKD, combining marks removed, lower-case; at most 9ntags),wLANA 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 whosenvalues 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 newnwords and the signer re-attests; no 30916 version is copied into the log, and thejoinentry 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 atcreated_at, checked against that circle'sroleswindows. 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_sis a parameter of the seller's circle, 600 s by default [Decided 23 Oct 2026, E-1]; never NIP-40expiration),["prev", "<previous 43919 id of this circle, or \"\">"],["v","3"]; content empty. Conversionto_amount = floor(amount × r_to / r_from)in BigInt. Valid for a booking when signed by that node,cand 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'sxoutat≤expiresandto_amountis exact; refusalsstale_quote(expired) andbad_quote. Theprevchain and the source-time order are daily audit checks (43924fail), 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_sby default); proposal:rate_stale_alert120 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
referenceentry 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 ajoinfrom another circle: itscis the circle the key's newest member home (30916) names, itspis the key, and the named block of that circle carries thereferenceentry 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-routerruns 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) isstrfry 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
sincecursor 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 withuntil = 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 enterskinds.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)#
- 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]. - Read the signer set from the log (genesis +
rolesentries), not from the newest 30910; cache ≤ 24 h; refresh on any refusal. Pinauthorsto 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 newestrolesmirror and theg3|param:*lines below the run); without them those checks are unverifiable, and what began before the run (an endorsement whose proposing act, arolesentry whose acts, anxdoneorxundowhose hold, aproject_openwhose 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]. - 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". - 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]).
- 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 bycreated_atonly where no number exists, withuntil= oldest seen inclusive and de-duplication by id. - Verify every event signature and every block's quorum before use; verify snapshot chunks against their seal before showing anything from them.
- 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 (withquote_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_filewith 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): arelay_changeact 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#huntil heights 1…H are all present and the seal roots match. - Removing: a
relay_changeentry; what the relay still holds does no harm (signed). - Archive relays: anyone; named in 30910 v3
archivetags. - 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)#
- 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. - The payer signs one 43910 with both circle ids (
["c", <payer circle id>, "", "from"]and["c", <receiver circle id>, "", "to"][C5]),amountin the payer's currency andto_amountin 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]). - Circle A checks cover and
world_used + x ≤ stepand, across currencies, B's quote exactly as B will (an expired or wrong quote is refused at intake,stale_quote/bad_quote, nothing held); booksxout(payer →world_A, held); final in A as above. - 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. - 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'sxoutatis no later than the quote'sexpires(B honours its own valid quote exactly; the old "tolerance" check is gone); then bookxin(world_B→ receiver, with wave growth counter-booked to B's reserve and on bydrain()) or a publicrejwith its reason (bad_quote,stale_quote, …). - A books
xdonenaming B's block as[height, digest], only if B'sxinembeds the same note id and books exactlyto_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 afterxout(xcheck_s, A's circle parameter, 7 days by default, recorded at thexout[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 whoseatlies from A'sxoutat− 60 s (margin: proposal) to the note'sexpires, 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 eachrolesentry in the blocks read, in office from the next height); each seal is judged with the office after itsh_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 itsexpires, so once B's seals cover that time B's answer is fixed. B bookedxinwith this note id and exactlyto_amount→ A booksxdoneby rule, naming B's block; B booked none (arejor nothing) → A booksxundoby 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'sxoutat− 60 s to the first seal whose boundary is later than the note'sexpires(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'sprevdescribes; 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'sheadand books at or after its boundary) or, when B has booked no block since, by B's next daily seal, named asnext(the seal of the day after, chained byprev, with B's quorum and an empty range fromh_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 ath_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 coveringexpires, 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. - Each circle stays at sum 0 through its own
worldline; each republishes to its own relays the partner block it relied on and the 43919 quote it relied on. - 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.
- 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]
| Circle | Payments/day | Entries/day | Events/day (blocks + morning + chunks) | Peak entries/s → blocks/s | Raw per day, one copy | Disk/year, one copy | Disk/year, 3 copies | Bytes per payment, all-in |
|---|---|---|---|---|---|---|---|---|
| 1,000 × 2 | 2,000 | 3,240 | ~3,100 | 0.2 → 0.2 | 7 MB | 5 GB | 15 GB | 3.5 KB |
| 1,000 × 10 | 10,000 | 16,200 | ~12,700 | 0.9 → 0.7 | 30 MB | 22 GB | 66 GB | 3.0 KB |
| 5,000 × 10 (W = 2 s, FROST) | 50,000 | 81,000 | ~21,000 | 4.7 → 1.2 | 78 MB | 57 GB | 172 GB | 1.6 KB |
| 100,000 × 10 | 1 M | 1.6 M | ~56,000 | 94 → 3.2 | 1.2 GB | 0.9 TB | 2.7 TB | 1.2 KB |
| 1,000,000 × 10 | 10 M | 16.2 M | ~133,000 | 940 → 7.5 | 11.6 GB | 8.5 TB | 25 TB | 1.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/day | 10 payments/day | |
|---|---|---|
| Payments | 2 B/day (23,000/s) | 10 B/day (116,000/s) |
| Events/s average / 3× peak | 36,000 / 107,000 | 146,000 / 439,000 |
| Raw per day, one copy | 7 TB | 30 TB |
| Disk per year, 3 copies / 4 copies | 15 PB / 21 PB | 66 PB / 87 PB |
| Relay-equivalents for 4 copies at 3× peak: 300 ev/s / 1,856 ev/s | 1,400 / 230 | 5,900 / 950 |
| Root: 30910 + 30915 | ~9 GB on disk, 12 head writes/s | same |
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.
| Profile | 2 M circles × 5,000, W = 2 s, one FROST signature | 10 M circles × 1,000, W = 1 s, 3 signatures |
|---|---|---|
| Payments per block (average while awake) | 3.8 | 1.3 |
| Bytes per payment, all-in | 1.6 KB | 3.0 KB |
| Events/s average / 3× world peak | 0.49 M / 1.48 M | 1.46 M / 4.39 M |
| Raw per day, one copy | 157 TB | 300 TB |
| Raw per year, one copy | 57 PB | 109 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/s | 19,700 / 3,200 | 58,600 / 9,500 |
| Circle-box model instead | 2 M box relays, ~57 GB a year each per copy, ~1–5 events/s at peak | 10 M box relays, ~22 GB a year each |
| Root | 30910 + 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, hardware | strfry 1.1.0 on each, 2 vCPU, 3.8–3.9 GB RAM [C] |
| Disk free | 11 / 90 / 23 / 51 GB; the smallest 38 GB disk is 70 % used [C] |
| Ingest | 2,245–4,513 events a day, ~0.03–0.05 events/s [M] |
| Limits | 131,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] |
| NIPs | 9 (KIND 5) and 40 (expiration) on; 45 COUNT exact; 77 negentropy works; no 42 auth, no 50, no 62 [M] |
| Reads, from Slovenia | TLS + WS open 418–651 ms; small indexed REQ → EOSE 56–214 ms median [M]; paging 356–2,416 events/s per connection [M] |
| Write policy | refuses 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 replication | the 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)
- Shard by circle: each circle's growth sits on its own relays and partners.
- Checkpoints are read compaction, not deletion.
- 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.
- Archives compress (strfry zstd dictionaries, unverified on our build; JSON-hex compresses 2–3× [E]).
- 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].
- 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(not0, 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 30910code; the 30910dis 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#
| # | Tension | Principle-conform answer | Status |
|---|---|---|---|
| T1 | One 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 key | Resolved [Decided 16 Oct 2026, A7]; start source decided the same afternoon (the ECB daily rate) |
| T2 | The 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 hints | Q10 |
| T3 | One administrator decides orders, deposits, payouts, caps | the 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 |
| T4 | P3: the project owner confirms the first circles; seals only from 5 members | P3 dropped; objective listing rule checked by the relay; limited space on borrowed relays; public from block 1 | Resolved [A9, "for now"] |
| T5 | One node per circle as sequencer | one 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 drill | Resolved [A3] |
| T6 | The 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 |
| T7 | Common Good fund key on the server | keyless 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 passes | Resolved [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 |
| T8 | Principle 3 against principle 5 | signers 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) |
| T9 | Definitions signed by the Lana authority (42893) | each circle pins its definitions version in its log | Q20 |
| T10 | "Relays only from 38888" (decided 14 Sep) | the rule stays for root relays; circle relays are named by each circle's log | Resolved [A2] |
| T11 | KIND 0 and member homes of every member on root | on root while borrowed; then circle relays and index relays | Q16 |
| T12 | "Publishing waits for every relay" (rule of 14 Sep) | final at 3 [A1]; sending continues; real N of M shown | finality resolved [A1]; sending on: Q17 |
| T13 | Bank 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 |
| T14 | Public for ever versus personal-data law and people's safety | the 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 43925 | Resolved [A5]; said openly: old saved profile copies and names typed into free text can remain |
| T15 | The BSA ID is the LANA WIF key | optional guardians, freeze, key_move | Q11 |
| T16 | Deleting ledger events from hot relays at scale | only after ≥ 3 verified archive copies of different operators; boxes never delete | Q19 |
| T17 | One circle must fit one relay | provisional cap 100,000 members | Q13 |
| T18 | Fiat 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 |
| T19 | Votes, reasons and purposes public for ever | every 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 |
| T20 | One 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) |
| T21 | The 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 |
| T22 | Every 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 |
| T23 | Witnesses 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 |
| T24 | A 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 |
| T25 | OWN 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
expirationor-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 whosecnames no served circle id is refused; a 30910 signed by a witness or by any key other than thecircle_keyit names (for a configured circle: the circle key in office), or with a code asd, is refused; after a key change the new key signs and the old one is accepted only up to itsuntil, 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 itscfromorto; 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 moderoota 30910 for a pinned circle id with anothergorfounderis refused, and one by a new key with the pinnedgis 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 moderootevery circle's found act is kept, from any author (a 43916 with a secondctag is no found act; found acts beyondseen_ids_maxare 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'sfounderis its author, or the circle is configured (otherwise it is stored unpinned), and only after every plugin of the chain accepted it; a 30910 whosefounderis not the author of the found act the relay holds is refused. With the decisions of 23 Oct 2026 also [proposal]: in moderoota 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
sealtag does not recompute as the seal's digest is refused; a head off theprevchain 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 itsmirror_ofnames (also one by the old key with amirror_ofat 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 thereferenceentry of the block it names is ignored by readers, and ajoinfrom 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
nwords 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 byxundonaming 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 givesxdone, and with B's ledger unreadable (silent relays, a missing height, no seal coveringexpires) 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;xdoneonly for axinwith the same note id and exactlyto_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_amountdoes not equal the quote's conversion, or whosexoutwas booked after the quote'sexpires, 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 ofdocs/valute-in-tecaji.mdgive 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.