Kinds: the BSA records, definitions 0.3.0 DRAFT
Draft, not published. These are the record definitions of round 5 (23 Oct 2026). They are not published as a Nostr event and no relay of the Lana network carries them. They may still change. Every example is signed with
public TEST keys; such a key must never hold or sign anything real. The same data as JSON:
kinds.json. Background in plain words:
the ledger on the relays.
Every kind is a signed Nostr event (NIP-01). Each is assigned to a layer. The ledger kinds hold one circle's book; the directory kinds help others find it.
Overview
| KIND | Name | Layer | Kind class | Signed by |
0 | Profile | 1 · directory | replaceable (plain Nostr) | the person |
30910 | Circle Record | 1 · directory | addressable | The circle key in office |
30915 | Circle Head | 1 · directory | addressable | The node |
30916 | Member Home | 1 · directory | addressable | The member's own key |
43910 | Payment Note | 2 · circle ledger | regular | The payer |
43911 | Circle Block | 2 · circle ledger | regular | The node |
43916 | Circle Act | 2 · circle ledger | regular | The acting person: member, invitee, recipient, proposer, Heart member |
43917 | Day Seal | both | regular | The node |
43918 | Snapshot Chunk | both | regular | The node |
43919 | Rate Quote | 2 · circle ledger | regular | The node |
43921 | Reference | 2 · circle ledger | regular | The node |
43924 | Statement | 2 · circle ledger | regular | Any witness, verifier or auditor |
Fixed values of 0.3.0
The same for every circle; no circle changes them. They are clock margins and the node's own timing.
| name | value | meaning | status |
|---|
XCHECK_MARGIN_S | 60 s | clock margin of the 8j check: the receiving circle's blocks are read from the payer circle's xout at minus this | proposal |
AT_TOLERANCE_S | 5 s | a booking time may precede the booked note's or act's created_at by at most this, and witnesses refuse an at more than this away from their clock | decided practice of the design (witnesses check at within ±5 s); the value: proposal |
QUOTE_MARGIN_S | 120 s | the node makes a new quote when the live one has less than this left | proposal |
RATE_STALE_ALERT_S | 432000 s | the node alerts the Heart when its newest source rate is older than this | proposal (120 h) |
GRAVITY_NOTICE_S | 2592000 s | the least public notice of a parameter change that makes gravity take more (a higher band or excess rate, a shorter transition, a rest that comes sooner), whatever change_notice_s is | decided 23 Oct 2026 (E-11 (3C): at least 30 days) |
Circle parameters
Each circle sets these in its found act and only its Circle Heart changes them, within the bounds below, with public notice and never retroactively. Gravity can never be made stronger than the start values.
The full rule (as written in the definitions)
[decided 23 Oct 2026, E-1: the circle's Heart sets them within these bounds; proposal: the names, units, bounds and the procedure] Set in the found act (with invite_only 0: a new circle has no member yet who could invite the first one, so a found act with invite_only 1 is bad_template; the Heart turns it on by param_propose once members have joined [clarification, review of round 5, 23 Oct 2026]) and changed only by param_propose: a Heart act in the endorsement form (k Heart members in office; the first act carries the full set, exactly these names in this order, each in its range, with vote_min_s ≤ vote_max_s < open_lapse_s; at most one parameter change open at a time). With k signers in office its notice starts: change_notice_s, or longer, the old value of every parameter in seconds that the change alters [proposal: a change of a time value takes effect only after the time it replaces has run out, never retroactively, so a stolen key cannot skip the warning], and at least 30 days (GRAVITY_NOTICE_S = 2592000, whatever change_notice_s is) when the change makes gravity take more: a higher band_ppm_day or excess_ppm_day, a shorter transition_days, or a rest that comes sooner (rest_after_days from 0 to on, or fewer days) [decided 23 Oct 2026, E-11 (3C)]. Gravity is never stronger than today's: band_ppm_day at most 330, excess_ppm_day at most 22840, transition_days at least 60, rest_after_days 0 (off) or at least 183 (1 to 182 is invalid) [decided 23 Oct 2026, E-11 (3C)]. At the notice end it is a duty: the first block whose first at reaches it starts (after the morning header it opens) with the rule entry params_effective, and from that entry on the block is under the new parameters (g3|param:<name>, g3|params_h = that height); with fewer than k of its signers in office then, the change is void (its line goes, no entry). Running deadlines keep their values (rules_for_every_kind). [proposal] Across circles each circle applies its own values to what it books, and a mismatch can only end in a refusal and a return to the payer, never a loss: note_ttl_s: the payer's circle applies its own at the xout, the receiving circle its own at the xin; if the receiving circle's is shorter it refuses the note (a rej with src, bad_template), and the payer's circle books xundo (refused): the payer gets the held amount back; the app signs with the shorter of the two values. quote_ttl_s: a quote belongs to the selling (receiving) circle and both circles judge it by that circle's value: the receiving circle by its parameters in force, the payer's circle by the value it reads in the receiving circle's ledger (as it reads its node and rate source); if the payer's circle read an older value, either it refuses (nothing moves) or the receiving circle refuses at the xin and the payer's circle returns the hold. xcheck_s: only the payer's circle uses it, for its own holds (recorded at the xout); a short wait can only find the evidence incomplete (the receiving circle's seals do not yet pass the note's expires): nothing is booked and the check repeats; it never returns early. Both sums stay 0 in every case. No other parameter exists; a value outside its range is invalid.
| parameter | start value | bounds | unit | meaning |
|---|
band_ppm_day | 330 | 0 to 330 | ppm of the plus within the wave, per day | gravity on the plus up to the wave (today 1 % a month: 1 − (1 − 0.01)^(1/30.4375) a day, rounded to whole ppm as today's app does) |
excess_ppm_day | 22840 | 0 to 22840 | ppm of the plus above the wave, per day | gravity on the plus above the wave (today a half-life of 30 days) |
transition_days | 60 | 60 to 3650 | days | after a first receipt the excess rate is as gentle as the band for this long |
rest_after_days | 183 | 0 to 3650 | days (0 = off) | a plus left unused this long flows at the excess rate |
wave_cap | 100000000 | 1 to 999999999999999 | minor units | the most a member's wave can grow to |
w_max_s | 1 | 1 to 10 | s | a block closes at the latest this long after its first entry |
vote_min_s | 259200 | 86400 to 2592000 | s | the shortest vote a proposer may set; a project or motion with a shorter duration is refused not_allowed |
vote_max_s | 1209600 | 86400 to 2592000 | s | the longest vote a proposer may set |
project_approvals | 3 | 3 to 9 | Heart members | Heart members in office whose newest vote is "for" that a project (and a motion) needs at its vote's end, with no member's "against"; on a project the proposer's and the recipient's "for" do not count; on a motion the proposer's own "for" counts (decided 23 Oct 2026, E-11 (2A)); a circle with fewer Heart members who may vote "for" opens no project, and its start stays in the reserve |
close_notice_s | 259200 | 86400 to 2592000 | s | public notice between the k-th project_close and the project_return |
receive_lapse_s | 7776000 | 604800 to 31536000 | s | a receiving project not full this long after it opened lapses and returns its money to the reserve |
open_lapse_s | 2592000 | 604800 to 31536000 | s | a project that has not opened this long after its publication lapses (a passed project whose consent never comes) |
change_notice_s | 259200 | 86400 to 2592000 | s | public notice between the k-th act of a Heart change (the circle key, witnesses, Heart members, k, relays, recovery) or a resignation and its roles entry; the least notice of a parameter change |
xcheck_s | 604800 | 86400 to 2592000 | s | after this long without xdone or xundo the payer's circle reads the receiving circle's sealed ledger (8j); its own value, recorded at the xout |
quote_ttl_s | 600 | 300 to 3600 | s | a rate quote of this circle's node is valid at most this long; both circles judge a quote by the value of the circle that signed it |
note_ttl_s | 600 | 60 to 3600 | s | a 43910 note's expires is at most created_at + this; each circle applies its own value to the notes it books |
act_ttl_s | 604800 | 86400 to 2592000 | s | every 43916 act carries expires at most created_at + this and is valid only before it |
rej_per_author_per_day | 20 | 1 to 1000 | refusals | refusals of one author booked per morning day; above it the intake answers 429 and books nothing |
exclusion_s | 7776000 | 86400 to 31536000 | s | how long an exclusion runs: the key cannot join; after it the person may apply, and only the Heart readmits |
invite_ttl_s | 604800 | 3600 to 2592000 | s | how long a member's invitation stays valid after its booking; one join uses it |
invite_only | 0 | 0 to 1 | 0 open, 1 invite only | 1: a join must name an open invitation of a member (an excluded key's application needs none: the Heart decides). A found act names 0: an invitation is a member's act and a new circle has no member yet, so a circle founded by invitation only could never take its first member; the Heart turns it on by param_propose once members have joined |
max_members | 0 | 0 to 999999999 | members (0 = no limit) | the most members (lines member or frozen); a join beyond it waits on the waiting list and comes in by rule, in booking order, when a place frees |
Rules for every kind
- [proposal; relay limits measured 16 Oct 2026] Every event is at most 100,000 bytes as serialized JSON and has at most 1,000 tags (the measured relays refuse above 131,072 bytes and 5,000 tags); a tag value is at most 2,048 bytes (the measured limit, no margin). Listing a relay in a circle record means that relay accepts events of 100,000 bytes and tag values of 2,048 bytes. Events that hold many accounts are cut by bytes, never by a count.
- [proposal] The last tag is ["v", "3"] (a 30915 head ends with the seal's ["v","3"], then d and seal). Tags appear in exactly the order each kind lists; any other tag makes the event invalid.
- [consequence of principle 2] No NIP-40 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.
- [consequence] Regular kinds (40000 and up) are kept for ever; addressable kinds (30000 to 39999) keep their newest version, and every version of 30910 is also carried in the ledger. BSA never uses 10000 to 19999 (replaceable) or 20000 to 29999 (ephemeral).
- [proposal] Every number is a base-10 string: amounts in minor units with at most 15 digits (BigInt end to end, never floating point), aggregates over many accounts (a seal's gravity moved, world built) at most 20 digits, carry in micro-units, rates in whole ppm, times in unix seconds. A JSON content never holds a JSON number, boolean or null; it is canonical JSON (RFC 8785).
- [decided] HEX keys only, never npub. [decided 16 Oct 2026, A5] No ledger event (43910, 43911, 43916, 43917, 43918, 43919, 43921, 43924) carries a person's name. [decided 16 Oct 2026, A6] No BSA event carries bank data.
- [decided 16 Oct 2026, A5: the warning; proposal: the limits] Every free text is one line, without control characters (C0, C1, DEL) and without bidirectional controls, in well-formed Unicode: a payment purpose up to 180 characters, act text (a vote's reason, a motion, an exclusion's reason among them) and statement text up to 500, a project's or motion's title up to 100, a circle name up to 80, a rate-source description up to 300 (the last two without "|", because they enter state lines). The app shows "public for ever; do not write names, personal or bank data" at every such field, at the circle name, the rate-source description, a statement's text, an operator name and an image URL. idem is never typed by a person: exactly 32 lowercase hex characters (128 random bits) made by the app.
- [decided 17 Oct 2026, C5: the circle id; proposal: the tag shapes] A circle is named by its circle id: the event id of its found act (43916, t = found), signed by the founder. It is a fingerprint, not a key: nobody can steal it, and another found act is another circle. The found act cannot carry its own id, so its first tag is ["c", ""]; every other BSA event names its circle by ["c", <circle id>] (a payment note by ["c", <circle id>, "", "from" | "to"]), except the 30910, which carries it only as d; the 30915 carries it as c and as d. Single-letter tags are indexed by the relays and are used only where a reader queries: c, h, m, i, p, e, t, d, n. Values are scoped to the circle by scope16 = the first 16 hex characters of the circle id: h = "<scope16>:<height>", m = "<scope16>:<YYYY-MM-DD>", i = "<scope16>:<YYYY-MM-DD>:<index>".
- [proposal] Blocks and seals are named by their digest = the NIP-01 hash of [0, pubkey, created_at, kind, tags without the qs tags, content]: prev, genesis, head, src, the 8j seals, the 30915 seal tag and the 43921 block tag all carry digests, never event ids (an event id changes with the set and order of witness signatures). Every reference to a digest also says where to find it: a block by its height, a seal by its day (src = [height, digest], seals = [day, digest], ["seal", digest, day], ["block", height, digest], ["target", digest, "h:<height>" | "d:<day>"]); NIP-10 e tags carry only event ids. Two digests at one height, each with a valid quorum of the witnesses in office there, or two valid seals for one day, are a fork; a block without such a quorum is not the circle's and is ignored. Witnesses sign sha256("lb-block/3|" or "lb-seal/3|" followed by the 32 digest bytes), never the digest itself, so a witness signature is never a valid Nostr signature of a stripped copy; qs tags are sorted by witness HEX.
- [decided 17 Oct 2026, C5 and C6; the notice a circle setting: decided 23 Oct 2026, E-1; proposal: the details] The trust anchor is the circle id together with the genesis digest (block 1). A circle link or QR carries 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). A thief cannot found a false circle under the same name: his found act has another id. The circle key only stamps the 30910 mirror (and, once, the found act in block 1), so it can be replaced: k Heart members sign circle_key_change with the new key's proof of possession, after change_notice_s (72 h by default, a circle setting, no longer fixed) of public notice a roles entry that the witnesses book puts the new key in office from the next block, and from then on a 30910 by the old key with a mirror_of at or above the change never counts; every seal and 30915 head names the circle key of the newest mirror at or below the seal's last block (at the height of a key change already the new key). Said openly: anyone can publish a 30910 that names a circle id; only the one signed by the circle key that the ledger's mirror at its mirror_of names (at the height of a key change already the new key), whose tags without g equal that mirror and whose g names the pinned genesis, counts. The first-seen rule of the root relays (keep the first g and founder per circle id; relay/ledger-write-policy.py, mode root, pins them only when the relay holds the circle's found act and the record's founder is its author, or for a circle configured on the relay when its configured circle key signed the record, and only after every plugin of the relay accepted the record, so nobody can claim a circle id first: proposal) is a request in the letter to the Lana relay owner (plan phase G) and is now only extra safety (decided C6).
- [clarification, 17 Oct 2026 (D4 review)] Encodings. A domain-separated message is sha256 of an ASCII prefix followed by the 32 raw bytes of the digest or id for "lb-block/3|", "lb-seal/3|" and "lb-found/3|" (the found act's id), and by text for "lb-genesis/3|", "lb-circle-key/3|", "lb-home/3|" and "lb-system/3|" (ids, digests and keys as 64 lowercase hex characters, numbers in decimal, fields joined by "|"). An event's size is the number of UTF-8 bytes of JSON.stringify({id, pubkey, created_at, kind, tags, content, sig}) in that key order, without spaces. Every character limit counts Unicode code points. BSA text holds no control characters, so the NIP-01 serialization of a valid BSA event equals JSON.stringify of [0, pubkey, created_at, kind, tags, content]. RFC 6962 trees over ids or digests take their 32 raw bytes as leaves (the quotes root, the votes root).
- [decided 17 Oct 2026, 2A: the pinned version; clarification, 17 Oct 2026 (D4 review, round 3): how a verifier holds it] Time zone data. A circle's morning boundaries and local dates are computed with the tzdata release its found act pins (tzdata, for example "2026a"). A verifier identifies a release by its IANA version string (the release's version file, the first line of its tzdata.zi, a +VERSION file, or the tz version a runtime reports, such as ICU's) and can obtain the pinned release from the IANA archive, which keeps every release (informative: data.iana.org/time-zones/releases/). It may compute with another release it holds only after checking, with the pinned release, that the circle's zone has identical UTC offsets and transitions over the dates it checks (from the boundary before the first date to the day after the last): identical transitions give identical boundaries and local dates. Without the pinned release, or when the transitions differ, every result that depends on a boundary or a local date is unverifiable, never a guess. [proposal (D3 review, 19 Oct 2026)] The same check binds the node: before it signs a morning block or a seal of the circle it checks that the time-zone data of its runtime gives the circle's zone the same UTC offsets and transitions as the pinned release over the dates it signs for; otherwise it waits, and signs nothing that depends on a boundary or a local date until it computes with the pinned release.
- [decided 23 Oct 2026, E-1; proposal: the notice of a change and the bounds] There are no world rules: each circle sets its own time values and limits, its parameters (meta.circle_parameters), in its found act, and only its Circle Heart changes them, within the bounds of these definitions. Fixed for every circle, so no parameter: the money and gravity computation, and that a project needs at least three Heart "for" votes and no "against" (project_approvals is at least 3; a circle may require more). The world-wide BSA Rules record (43920) of the earlier drafts and its rules authority are gone (the number stays reserved). The parameters in force are in the decision state (g3|param:<name>, g3|params_h); a change is committed by a params_effective entry, so a restore from the relays always gives the same result. A note's and an act's TTL and the daily refusal limit are those in force at the booking; every running deadline keeps the length of the parameters in force when it started (a vote, an open lapse, a receive lapse, a closing notice, the 8j wait, an exclusion, an invitation, a change's notice), recorded as a due time in its open line: never retroactive. During a parameter change's notice apps sign with the stricter of the two values (proposal).
- [decided 23 Oct 2026, E-8 and E-9; proposal: the shapes and the checks] Every circle grows from the first one: only a member of an existing circle founds a circle, and the found act names its parent circle (["parent", <circle id>, <genesis digest>]) and the founder's reference from it (43921, E-9; the root, the live circle, names neither; meta.root_circle). One circle per founder key, ever. A person is a member of one circle at a time: a key's circle is the one its newest member home (30916) names, and a new circle admits a key whose newest home names another circle only with that circle's reference, which is issued only after a leave. Said openly: one person with two keys is not prevented; that needs Lana's person check.
KIND 0 · Profile plain Nostr
Layer 1 · directory
The ordinary Nostr profile (NIP-01): a JSON object with name, about, picture and so on. It is not a BSA kind. The ledger never copies a name from it. A directory accepts a profile from anyone, with limits (16 KB, 64 tags, no expiration tag, 10 per author and 600 per sender address an hour). The member home and the account line meet the profile at the same key.
KIND 30910 · Circle Record draft 0.3.0
Layer 1 · directory Written by the circle key; stored by directories (the circle also carries every version in its ledger).
v0.3.0 — A mirror of one circle's governance as its ledger holds it: the circle id (d: the id of the found act, the circle's name for ever), the code, name, the one currency, time zone and morning hour, the genesis digest (the trust anchor) with the founder's signature over the circle id and it, the founder, the parent circle it grew from (every circle but the root; decided 23 Oct 2026, E-8), the circle key in office, the witnesses with their operators and the quorum, the node (proposer), the Circle Heart (the steward tags) and how many of its members a Heart change needs (k), the relays and archives, the rate source, the address of the write intake, and the definitions version. Signed by the circle key it names, which signs only this mirror and, once, the found act in block 1 (proposal). It counts only when its author is the circle key that the ledger's mirror at its mirror_of names (at the height of a key change already the new key), its tags without g equal that mirror (the genesis entry of block 1, or a later roles entry; a block books at most one roles entry, so a height names one mirror) and its g names the pinned genesis; when they differ, readers follow the ledger. The circle key can be replaced (decided 17 Oct 2026, C5): k Heart members sign circle_key_change with the new key's proof of possession, and after change_notice_s of public notice (72 h by default; a circle setting since 23 Oct 2026, E-1) a roles entry, booked by the witnesses, puts the new key in office from the next block; a record by the old key with a mirror_of at or above that change never counts, and every seal and 30915 head names the circle key of the newest mirror at or below the seal's last block. g carries the founder's signature over the circle id and the genesis digest, so no key can point the record at another chain: a reader keeps the genesis it pinned (from the circle link or QR, or the first g it saw for the circle id) and never follows another, and the root relays keep the first-seen g and founder per circle id (decided 17 Oct 2026, C6: a request in the letter to the Lana relay owner, now extra safety). Said openly: the circle key is no longer part of the record's address, so anyone can publish a 30910 that names a circle id; it counts only when the ledger shows its author in office. Readers take the witness set from the ledger, never from the newest 30910. The circle's parameters (its time values and limits; decided 23 Oct 2026, E-1) are not part of the mirror: they are in its ledger (the found act, then each params_effective; the state's g3|param lines). Operators are named by the circle itself (self-declared): a relay or witness operator is a name, not a proof of independence. The root directory checks decision E-8 from this record and the found act it mirrors: every circle but the root names its parent circle and the founder's reference from it, and a founder founds one circle, ever (the check by the root relays: proposal). There is no key for any system account and no field for a person's name or for bank data.
- Kind class
- addressable (parameterized replaceable, NIP-33): the relay keeps the newest version
- Replaceability
- Parameterized Replaceable (NIP-33): d = the circle id. The address 30910:<circle key>:<circle id> changes when the circle key is replaced, so readers query #d = the circle id and take the record the ledger confirms. Every version is also carried in the ledger (genesis or roles entry), so the history of the record is never lost.
- Signed by
- The circle key in office (held by the app and the creator; replaced by circle_key_change); it signs only this mirror and, once, the found act's binding in block 1 (proposal).
- Address
30910:<circle key in office>:<circle id>
- Content
- Empty. The tags are authoritative.
- Tag order
d, code, name, currency, tz, tzdata, morning, g, mirror_of, founder, parent?, circle_key, signer…, quorum, proposer, steward…, stewards_required, relay…, archive…, bootstrap?, rate_source, api, definitions, v
- What is public
- Public: the circle's description, founder, parent circle, witnesses and their operators, Heart members by HEX key, relays and rate source. No person's name and no bank data; the app shows "public for ever; do not write names, personal or bank data" at the circle name, the operator names and the rate-source description.
Required tags
| tag | meaning |
|---|
d | the circle id: the 64-hex event id of the found act; never changes e.g. <circle id> |
code | circle code: ^[a-z0-9][a-z0-9-]{2,23}$; set in the found act, never changes e.g. example-circle |
name | the circle's name (not a person's), 1 to 80 characters, one line, no "|"; public for ever e.g. Example Circle |
currency | exactly one: EUR, USD or GBP; fixed at founding (another currency is another circle) e.g. EUR |
tz | IANA time zone of the morning; fixed at founding (decided 17 Oct 2026, C7: no act or parameter changes it in 0.3.0) e.g. Europe/Ljubljana |
tzdata | the tz database version the morning boundaries use; fixed at founding (decided 17 Oct 2026, C7, the tzdata part confirmed that evening, 2A: no act or parameter changes it in 0.3.0; a later definitions version may say how a circle follows new clock rules) e.g. 2026a |
morning | local hour of the morning (gravity, due rules, then the seal); fixed at founding (decided C7). Inside a clock change: a local hour that occurs twice counts at its first occurrence, a skipped local hour at the first instant after the gap e.g. 4 |
g | ["g", <genesis digest>, <the founder's BIP-340 signature over sha256("lb-genesis/3|" + circle id + "|" + genesis digest)>] e.g. ["g","<hex>","<sig>"] |
mirror_of | the height of the ledger entry this record mirrors (1 for the genesis mirror) e.g. 1 |
founder | ["founder", <founder HEX>]: the author of the found act; never changes e.g. <hex> |
circle_key | ["circle_key", <HEX of the circle key in office from the block after mirror_of (from block 1 for the genesis mirror)>]: the record's author must be this key; never the founder's, a witness's or a Heart member's key (proposal); changed only by circle_key_change (decided 17 Oct 2026, C5) e.g. <hex> |
signer | ["signer", <hex>, <operator>, <since_h>, <until_h or "">], one per witness (at most 100 tags; when more would be needed the oldest retired ones are dropped, their windows stay in the ledger's roles entries); at most one current witness per operator e.g. ["signer","<hex>","box.example.org","1",""] |
quorum | ["quorum", q, n]: n current witnesses, q > 2n/3 (4 → 3, 1 → 1) e.g. ["quorum","3","4"] |
proposer | ["proposer", <hex of a current witness>, <since_h>]: the node that orders the blocks e.g. ["proposer","<hex>","1"] |
stewards_required | k: how many Heart members a Heart change needs (proposal: 3 of 4); at most the number of Heart members and at least 2 while there are at least 2 e.g. 3 |
relay | ["relay", <wss URL>, <operator>]; at least 4 relays of at least 3 operators (decided 16 Oct 2026, A2) e.g. ["relay","wss://relay.box.example.org","box.example.org"] |
rate_source | ["rate_source", <source id>, <public description, one line, no "|">]: any source the circle chooses itself (decided A7); ecb-daily is the start source e.g. ["rate_source","ecb-daily","…"] |
api | https URL of the circle's write intake (notes, acts, quote requests); nothing is read from it that is not on the relays e.g. https://circle.example.org |
definitions | the definitions version e.g. 0.3.0 |
v | record version e.g. 3 |
Optional tags
| tag | meaning |
|---|
parent | ["parent", <parent circle id>, <its genesis digest>]: the circle this one grew from, as its found act names it; every circle but the root (meta.root_circle) has one (decided 23 Oct 2026, E-8); never changes e.g. ["parent","<circle id>","<hex>"] |
steward | ["steward", <hex>], one per Heart member (the Circle Heart; decided 23 Oct 2026, E-2; the live circle: four) e.g. <hex> |
archive | ["archive", <wss URL>]: a relay that keeps the whole history e.g. wss://archive.example.net |
bootstrap | ["bootstrap", "Q14"]: the circle declares the bootstrap of open question 14 (not decided); two relay operators then give a warning instead of a refusal |
Validation rules
- Tags in exactly the order of tag_order; no other tag.
- g verifies: the founder's signature over the circle id and the genesis digest. A reader pins the genesis from the circle link or the first g it sees for a circle id and never follows another; root relays keep the first-seen g and founder per circle id (decided 17 Oct 2026, C6: a request to the relay owner, extra safety), pinned only when the relay holds the circle's found act and the record's founder is its author, or for a circle configured on the relay when its configured circle key signed the record (proposal).
- The author is the circle key the record names (circle_key); the circle key is not the founder's key, a witness key or a Heart member's key.
- parent, when present, names another circle id and its genesis digest; the root circle (meta.root_circle) has none (decided 23 Oct 2026, E-8).
- stewards_required (k) is at most the number of steward tags and at least 2 while there are at least 2 [proposal (D4 review, round 3, 17 Oct 2026)].
- Exactly one currency (EUR, USD or GBP).
- n of quorum equals the number of current witnesses (until_h ""), q > 2n/3, at most one current witness per operator; the proposer is a current witness.
- At least 4 relays of at least 3 operators; with ["bootstrap","Q14"] declared, 2 operators give a warning only.
- definitions = 0.3.0; v = 3. The record carries no time value and no number of approvals: those are the circle's parameters, in its ledger (decided 23 Oct 2026, E-1).
- Authority: a 30910 counts only if its author is the circle key that the ledger's mirror at its mirror_of names (the mirror in the committed genesis or roles entry at mirror_of; at the height of a key change already the new key, in office for blocks from the next height), its tags without g equal that mirror, and its g is the pinned genesis. Readers take the valid record with the highest mirror_of; a record by an old circle key with a mirror_of at or above its replacement can never match.
Signed example (public TEST keys)
{
"id": "f89d79c0fcd50d83f872199c414fd8f50ba6bfaa10b243451f9123c0b675ba8e",
"pubkey": "dab7ad80719db76adbcb29b213256d7c4d16bca26202ad3d64c622142a9cfe34",
"created_at": 1792058430,
"kind": 30910,
"tags": [
[
"d",
"3cc68eda6fd66e3ab84ed9fb7cc7afcda0f3ebbb28450839de96046a81a35712"
],
[
"code",
"example-circle"
],
[
"name",
"Example Circle"
],
[
"currency",
"EUR"
],
[
"tz",
"Europe/Ljubljana"
],
[
"tzdata",
"2026a"
],
[
"morning",
"4"
],
[
"g",
"dcc4c9e5e20994c73cf37e8bb921d39bf05a885e86dff98d94b66a5164b0ff85",
"6637c9cb51437e204b3de4d38bbc1676ef11b6cdeac84aff19789e039a2df151459502731d7f694f4c6aa21f20307edd648c6c0af9f15a62623686cb870dbb73"
],
[
"mirror_of",
"1"
],
[
"founder",
"bd14e1e5199594c2b72c27fdded47046db58eba2349e13e514bfe950827298aa"
],
[
"circle_key",
"dab7ad80719db76adbcb29b213256d7c4d16bca26202ad3d64c622142a9cfe34"
],
[
"signer",
"4a141e845d3a5d2b3b71e908792bfe8357de51534578afb0b977f347cff0824d",
"box.example.org",
"1",
""
],
[
"signer",
"f7d5bd636f637ee5286e5119d1207ce682afcba10b89c3ef30009c1d8336eb79",
"coop.example.net",
"1",
""
],
[
"signer",
"f3f3cbf435e4d299e5200da37942491a03ef96ca00521a86abc20624647bad38",
"witness.example.com",
"1",
""
],
[
"signer",
"3ad2ee91790df23a39cf91b6e8befd368b6e1b1774e556ef9d9aacae182ea32f",
"sister.example.org",
"1",
""
],
[
"quorum",
"3",
"4"
],
[
"proposer",
"4a141e845d3a5d2b3b71e908792bfe8357de51534578afb0b977f347cff0824d",
"1"
],
[
"steward",
"bd14e1e5199594c2b72c27fdded47046db58eba2349e13e514bfe950827298aa"
],
[
"steward",
"69f22f2d21fe61a72aedb558f2e4a72bd12c390d3b7573061142ee9114dbda3a"
],
[
"steward",
"6ce8bca4d4df7c8533bd07dc7deeef8644c16609304329f1161ff8b0b4593574"
],
[
"steward",
"5731510299edc4f75f7014023e739f351b3f9e461bc8dd3da07bcd0c798d7407"
],
[
"stewards_required",
"3"
],
[
"relay",
"wss://relay.box.example.org",
"box.example.org"
],
[
"relay",
"wss://relay.coop.example.net",
"coop.example.net"
],
[
"relay",
"wss://relay.witness.example.com",
"witness.example.com"
],
[
"relay",
"wss://relay.sister.example.org",
"sister.example.org"
],
[
"archive",
"wss://archive.example.net"
],
[
"rate_source",
"ecb-daily",
"The euro foreign exchange reference rates of the European Central Bank (base EUR; USD and GBP used), published once per TARGET working day and read by the node once a day; the source time is the reference date at 00:00 UTC; the app shows the rate date"
],
[
"api",
"https://circle.example.org"
],
[
"definitions",
"0.3.0"
],
[
"v",
"3"
]
],
"content": "",
"sig": "b893930a29aa37d09f858bccf4e6b367a8cc199db672685d43eccdc7ebf1c93797ceaff2a6f01366d5691ebacb2a6ff36f4d094e686d542642cebc6dac137a0e"
}
KIND 30915 · Circle Head draft 0.3.0
Layer 1 · directory Written by a witness; stored by directories as the pointer to the newest seal.
v0.3.0 — The newest daily seal of a circle, mirrored on the root relays so anyone finds a circle's latest state in one query. The same author, created_at, content and tags as its 43917 Day Seal (the witnesses' qs signatures included), with ["d", circle id] and ["seal", seal digest, seal day] appended last; the seal tag must recompute as the seal's digest. Like the seal it names the circle key of the newest mirror at or below the seal's last block and the height of the circle's parameters in force (params), under the witnesses' quorum, so a reader of the root directory picks the circle's 30910 by that key in one more query. Published at a per-circle offset spread over the day (the first 4 bytes, big-endian, of sha256(the circle id) mod 86,400 seconds after the seal; test_vectors.circle_id), so heads do not bunch into one hour; created_at stays the seal's.
- Kind class
- addressable (parameterized replaceable, NIP-33): the relay keeps the newest version
- Replaceability
- Parameterized Replaceable (NIP-33): d = the circle id.
- Signed by
- The node (a current witness) that signed the seal.
- Address
30915:<node pubkey>:<circle id>
- Content
- The seal's content (empty).
- What is public
- Public: roots, counts, the circle's world, reserve and origin numbers, its circle key and the height of its parameters in force.
Required tags
| tag | meaning |
|---|
all seal tags | exactly as in the 43917, in its order, ending with ["v","3"] |
d | the circle id (second to last) e.g. <circle id> |
seal | ["seal", <digest of the 43917 this head mirrors>, <its day>] (last) e.g. ["seal","<hex>","2026-10-16"] |
Validation rules
- The seal tag carries the digest of [0, pubkey, created_at, 43917, the seal tags without qs, content] and the seal's day.
- The seal tags follow every 43917 rule, and the qs tags verify (domain "lb-seal/3|") with at least q current witnesses.
- The author is a current witness of the circle.
Signed example (public TEST keys)
{
"id": "d655b047a0afe4a312d1e265079e0d38f6ac277668ffaba4c70094d3d55dadf5",
"pubkey": "4a141e845d3a5d2b3b71e908792bfe8357de51534578afb0b977f347cff0824d",
"created_at": 1792116060,
"kind": 30915,
"tags": [
[
"c",
"3cc68eda6fd66e3ab84ed9fb7cc7afcda0f3ebbb28450839de96046a81a35712"
],
[
"m",
"3cc68eda6fd66e3a:2026-10-16"
],
[
"day",
"2026-10-16",
"1"
],
[
"boundary",
"1792116000"
],
[
"prev",
""
],
[
"covers",
"1792060200"
],
[
"range",
"1",
"4",
"1",
"22"
],
[
"head",
"3177d14df282ccde472b50cdac391d90747e97474317a2b282c45503ad35e8a2",
"4"
],
[
"pay",
"d8ee98959084bea26a4726b8958f00080cd241908e3117241db011164d6f556f",
"22"
],
[
"state",
"df7981e5ec0191d848b06ca3ff2ce66f27a2385ef7ad39547689374b92efb359",
"76"
],
[
"snap",
"17b453e35de09fd9d3cdd67b6e8c3db803d6a6979f3b08c4a7c47a0abb0236d7",
"1",
"5042"
],
[
"gravity",
"2026-10-16",
"0",
"0"
],
[
"sum",
"0"
],
[
"world",
"EUR",
"0",
"0",
"0"
],
[
"quotes",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"0"
],
[
"reserve",
"100000"
],
[
"origin",
"-100000"
],
[
"signers",
"c3af72bd9524f92873a62c6e26411af3f3e12bb5e8fe2cc67441d3063a936abc",
"3",
"4"
],
[
"circle_key",
"dab7ad80719db76adbcb29b213256d7c4d16bca26202ad3d64c622142a9cfe34"
],
[
"params",
"1"
],
[
"qs",
"4a141e845d3a5d2b3b71e908792bfe8357de51534578afb0b977f347cff0824d",
"f65025240b057e166bd9ddd16b00c5257e03bef0c79be7261c25bfd3a119cc85a3d373c836fe787be93ebf8b663a95cbce6b3731f47a3f1cf9630554ab322f1a"
],
[
"qs",
"f3f3cbf435e4d299e5200da37942491a03ef96ca00521a86abc20624647bad38",
"5252a2c4da5bd313254f4279f3225c077184c35aeecec05fb8da5aad05ad7e72d3fa8b98267baf142e7f3818e5f9d29e37a0f65c126a900ff870a0fe8408f44c"
],
[
"qs",
"f7d5bd636f637ee5286e5119d1207ce682afcba10b89c3ef30009c1d8336eb79",
"e5838d42035682de8f94e02655273b2a9054b68fb2bec55597a0937edb013cb36d65a34eaafd99e2056a9f5963b7409568f8e026903f8d37a727320be5558619"
],
[
"v",
"3"
],
[
"d",
"3cc68eda6fd66e3ab84ed9fb7cc7afcda0f3ebbb28450839de96046a81a35712"
],
[
"seal",
"76e4791959e85b79a2e4f3f518d1d56b5b01072ee726a82850027505a11bf39c",
"2026-10-16"
]
],
"content": "",
"sig": "4b8d0b713a6751d9f3030b5cb7f0a47421eb3b3320f2bedf9cbb8bf36e8afed709f326faa8de505c55c68d5d5840b0e22ac9fb135589c1f27b57cf9e71941bbf"
}
KIND 30916 · Member Home draft 0.3.0
Layer 1 · directory Written by the member, attested by a witness; stored by directories and on the circle relays.
v0.3.0 — Where a member lives: the circle, its genesis digest and the height of the member_join entry that admitted the key, attested by a witness of the circle who was a witness at that height. Mandatory for every member (decided). Signed by the member's own key. The name words (n) come from the member's KIND 0 name and are rewritten whenever that name changes; relays keep only the newest version and no version is ever copied into the ledger (decided 16 Oct 2026, A5). The member_join entry in the ledger is the truth; this record is a pointer. Before paying, an app checks the join (and the absence of a leave, an exclusion or a close) in the circle's ledger. [decided 23 Oct 2026, E-8 and E-9; proposal: the reading] A person is a member of one circle at a time: a key's circle is the one its newest member home names, and a new circle admits a key whose newest home names another circle only with that circle's reference (43921); a key with no member home anywhere was never a member and needs none. [review of E4x, 24 Oct 2026; proposal] A node reads the homes against the ledgers they name, never by created_at alone (the key sets it, and an old attestation can be signed again): a home counts when that circle's verified ledger books the member_join it names for the key; it is passed over when that circle has since booked the return of the key's reference (the key moved on with its step); a home naming the reading circle itself counts only while the key is a member there (coming back needs no reference). A key that only left, or was excluded or closed in a circle, still belongs to it: another circle asks for that circle's reference, and without one (a minus, an open OWN process) the key does not move. A key whose reference of this circle another circle has granted (its reference_grant read) comes back here only with that circle's reference, never as if it had not left. Root relays keep member homes (no deletion by KIND 5) [proposal]; said openly: a deleted home or a second key is not caught (that needs Lana's person check).
- Kind class
- addressable (parameterized replaceable, NIP-33): the relay keeps the newest version
- Replaceability
- Parameterized Replaceable (NIP-33): d = "letsbalance". A new home replaces the old one; relays keep only the newest.
- Signed by
- The member's own key; a witness of the circle attests inside the tags.
- Address
30916:<member pubkey>:letsbalance
- Content
- Empty.
- Tag order
d, c, g, join, attest, n…, w…, v
- What is public
- Public: the member's BSA ID, circle, join height, day and name words. No balances (those are in the ledger) and nothing else.
Required tags
| tag | meaning |
|---|
d | always "letsbalance" e.g. letsbalance |
c | the circle id e.g. <circle id> |
g | the circle's genesis digest e.g. <hex> |
join | the height of the member_join entry that admitted this key e.g. 2 |
attest | ["attest", <witness hex>, <YYYY-MM-DD since>, <BIP-340 signature of the witness over sha256("lb-home/3|" + member + "|" + circle id + "|" + genesis digest + "|" + join + "|" + since)>] (the digest is a proposal); the witness was a witness at the join height e.g. ["attest","<hex>","2026-10-15","<sig>"] |
v | record version e.g. 3 |
Optional tags
| tag | meaning |
|---|
n | name words from the verified KIND 0 name: the full folded name, then up to 8 words (9 at most); folded = NFKD, marks removed, lower case, single spaces e.g. test member one |
w | a LANA address of the member's key (one of the 4 addresses of the BSA ID; readers check it) e.g. L… |
Validation rules
- The attesting key was a witness of the circle at the join height and its signature verifies.
- At most 9 n tags, all folded; readers drop results whose n values are not in the member's KIND 0 name.
- The ledger's member_join entry at join admits this key.
- [proposal, E-8 and E-9] The newest member home of a key names its circle: a join in another circle needs that circle's reference.
Signed example (public TEST keys)
{
"id": "b80bbe2c8ae0e8e8e24dec8209f9d18b5f0fbb5a53c202318bb7cfa8406f15a0",
"pubkey": "ceada170baba7790086af4456dd436eaf2afb368b6a2c933bd95f93550174af8",
"created_at": 1792058760,
"kind": 30916,
"tags": [
[
"d",
"letsbalance"
],
[
"c",
"3cc68eda6fd66e3ab84ed9fb7cc7afcda0f3ebbb28450839de96046a81a35712"
],
[
"g",
"dcc4c9e5e20994c73cf37e8bb921d39bf05a885e86dff98d94b66a5164b0ff85"
],
[
"join",
"2"
],
[
"attest",
"4a141e845d3a5d2b3b71e908792bfe8357de51534578afb0b977f347cff0824d",
"2026-10-15",
"cf7fdbe1e84ebb42bebf0cbdd593bf74ebe3b6712f824b781900b50d9dd33470f3e40e9e2ea16b850a8d9acfc753f34da65ce909faa326903db8bfb844daf2e3"
],
[
"n",
"test member one"
],
[
"n",
"test"
],
[
"n",
"member"
],
[
"n",
"one"
],
[
"v",
"3"
]
],
"content": "",
"sig": "712e66f27a1606ba8279f298303691ca523cc24f0e501a7b013370b8497db4889adf3f0873a31e18695d9353e3a38bba0254fec3a77594546a6f87a2f2e66725"
}
KIND 43910 · Payment Note draft 0.3.0
Layer 2 · circle ledger Sits inside a block; a standalone copy is evidence only.
v0.3.0 — A payment signed by the payer: receiver, amount, currency and purpose; across circles also the receiving circle and the amount it receives (the payer signs both amounts); across currencies the id of the receiving circle's 43919 quote that the payer accepted. Inside a block the note is stored in a compact form from which its exact event, id and signature are rebuilt from the template below, frozen for 0.3.0; the node rebuilds and verifies every note before booking it, and a note that does not round-trip the template is refused (bad_template), never repaired. A note is booked only at a time at from created_at − AT_TOLERANCE_S to its expires. A payment into the minus of a line that left or is excluded settles it, at most to 0, with no grow (decided 23 Oct 2026, E-9: anyone may settle a debt; the shape: proposal). A standalone copy goes to the circle's relays only as evidence when the node gives no receipt; a member's KIND 5 can remove only that copy, never the block that embeds the note. The purpose is public for ever: the app shows "public for ever; do not write names, personal or bank data".
- Kind class
- regular (40000 and up): kept for ever
- Replaceability
- Regular (kind 40000+); embedded in 43911; a standalone copy only as evidence.
- Signed by
- The payer (any member).
- Content
- The purpose, at most 180 characters on one line, public for ever.
- Tag order
p, c (from), c (to)?, amount, currency, to_amount?, e (quote)?, n, prev, expires, idem?, e (request)?, e (project)?, v
- What is public
- Public for ever: payer, receiver, amounts, purpose. No name, no bank data.
Required tags
| tag | meaning |
|---|
p | the receiver: a member's HEX key (or the key of a line that left or is excluded, only to settle its minus), a project wallet (a contribution naming a project) or the reserve (a contribution naming none) e.g. <hex> |
c (from) | ["c", <payer circle id>, "", "from"] e.g. ["c","<circle id>","","from"] |
amount | minor units of the payer's currency, 1 to 15 digits e.g. 2500 |
currency | the payer's currency e.g. EUR |
n | the payer's own note counter in this circle: 1, 2, … e.g. 1 |
prev | the payer's previous booked note id in this circle, or "genesis" e.g. genesis |
expires | unix time, at most created_at + note_ttl_s (a circle parameter, 600 s by default: the payer's circle checks its own value at the xout, the receiving circle its own at the xin; meta.circle_parameters); across currencies never later than the quote's expires; after it an unbooked note can never be booked e.g. <unix> |
v | record version e.g. 3 |
Optional tags
| tag | meaning |
|---|
c (to) | ["c", <receiver circle id>, "", "to"], only across circles |
to_amount | ["to_amount", <minor>, <receiver currency>], only across circles; across currencies = floor(amount × r_to / r_from) under the quote, exactly; in one currency = amount |
e (quote) | ["e", <43919 id>, "", "quote"], exactly one, only across currencies |
idem | exactly 32 lowercase hex characters (128 random bits) made by the app, never typed by a person |
e (request) | ["e", <pay_request act id>, "", "request"] |
e (project) | ["e", <project_propose id>, "", "project"]: a contribution names its project here |
Validation rules
- Tags exactly in tag_order, nothing else (bad_template otherwise).
- expires within note_ttl_s of the circle parameters in force at the booking (each circle its own: a receiving circle whose value is shorter refuses the note with src, bad_template, and the payer's circle returns the hold).
- Across currencies: to_amount = floor(amount × r_to / r_from) under the named quote, to_amount > 0, expires ≤ the quote's expires (refusals: bad_quote, stale_quote).
- One successor per (n, prev): a second note on the same prev is refused stale_prev; a note id is booked at most once (duplicate).
- Cover is checked by the node and every witness at the booking's place in the ledger: amount ≤ balance + wave (a give: amount ≤ max(0, balance), Open 8g; an xout of a key that left or is excluded: its plus only, max(0, balance), never the wave [proposal, E-3 and E-9]); a settlement at most −balance of the line it settles.
Signed example (public TEST keys)
{
"id": "c258aab9e101b1e4d9c2b54c158c9ccf9f59cedff7a9d7b3cea355ee30f37151",
"pubkey": "bc394f9fc5df07e8708c0c9bfcba4ce3042e7774a190a251a63ed4b11350b533",
"created_at": 1792325090,
"kind": 43910,
"tags": [
[
"p",
"ceada170baba7790086af4456dd436eaf2afb368b6a2c933bd95f93550174af8"
],
[
"c",
"3cc68eda6fd66e3ab84ed9fb7cc7afcda0f3ebbb28450839de96046a81a35712",
"",
"from"
],
[
"amount",
"2500"
],
[
"currency",
"EUR"
],
[
"n",
"1"
],
[
"prev",
"genesis"
],
[
"expires",
"1792325690"
],
[
"idem",
"6b47175496e2f4d7dd9072f84e6a9666"
],
[
"v",
"3"
]
],
"content": "Bread for the Saturday market",
"sig": "30133b6a91f31a8c225d8a970492d33329d3c84fc8bad0ba7600b00f552df59eed78283d6a0a7e8b0de7dc86fc6945351bc868c149c91690f23a78dcd7075d1d"
}
KIND 43911 · Circle Block draft 0.3.0
Layer 2 · circle ledger
v0.3.0 — The ledger. One circle's bookings as a gap-free numbered chain of blocks from block 1 (genesis), linked by digests. A block names its circle id, its height, the previous block's digest, the genesis digest, the entry numbers and booking times it covers, three roots (its entries; the lines of the parties it touches; the whole decision state after it, which holds the circle key in office and the circle's parameters in force, so every block names both), every party it touches, and, in a block that books a grav entry, the morning header. The content is canonical JSON {"e": entries, "l": lines}. The node signs the event; every witness of the quorum signs the domain-separated digest (qs tags, sorted by witness HEX) after recomputing every line and root from its own copy, built only from blocks on the relays. [decided A1] A booking counts once its block carries the quorum and at least three of the circle's relays hold it; [consequence] every lower height must count too. The sum of all balances of the circle is 0 after every block. A block closes at the first of: the intake queue is empty, w_max_s seconds after its first entry, or 100,000 bytes (a block whose work would pass 100,000 bytes closes after its last whole step and the next block, at the same at, continues that work: the block split, booking_rules [completed 24 Oct 2026, plan step E4x]); so its at span is at most w_max_s, checked exactly with the circle parameters in force (the found act's, then each params_effective; a check without them can only use 10 s, the top of the range) [clarification, 17 Oct 2026 (D4 review)], and no block can hold a whole notice.
- Kind class
- regular (40000 and up): kept for ever
- Replaceability
- Regular (kind 40000+); never deleted (circle relays refuse KIND 5 and expiration for it).
- Signed by
- The node (a current witness) of the height, with at least q witness signatures inside (qs).
- Content
- Canonical JSON {"e": [entries], "l": [lines]}; strings only.
- Tag order
c, h, prev, genesis, seq, at, root, p…, m?, morning?, more?, qs…, v
- What is public
- Public for ever: every booking with every touched account's line after it, every move into and out of project wallets and the reserve, every refusal with its reason (never the refused event's text). Keys only, never names; no bank data.
Required tags
| tag | meaning |
|---|
c | the circle id e.g. <circle id> |
h | "<scope16>:<height>", gap-free from 1 (scope16: the first 16 hex characters of the circle id) e.g. <scope16>:1 |
prev | the digest of block h-1; "" in block 1 |
genesis | the genesis digest on every block; "" in block 1 itself (it cannot carry its own digest) |
seq | ["seq", first, last]: circle-wide entry numbers, gap-free e.g. ["seq","1","2"] |
at | ["at", first, last]: booking times, never decreasing; first is its first entry's at and last its last entry's [clarification, 19 Oct 2026 (D3 review)]; created_at = last e.g. ["at","<unix>","<unix>"] |
root | ["root", entries root, lines root, state root] |
qs | ["qs", <witness hex>, <BIP-340 signature over sha256("lb-block/3|" + the 32 digest bytes)>], at least q, sorted by witness HEX, the node's own included |
v | record version e.g. 3 |
Optional tags
| tag | meaning |
|---|
p | ["p", <party hex>], every party the block touches (keyless system accounts included), in the order the entries first name them (inside an entry: party first, then each move's from and to); lines refer to them by index |
m | every block that books a grav entry of the day (a morning block, or a payment block with visits): "<scope16>:<date>" |
morning | with m: ["morning", <date>, <boundary unix>, <cutoff seq>, <params_h>, <tzdata>]; the cut-off is the last seq with at < boundary; tzdata is the version the circle pinned at founding (decided C7 and 2A; the check: proposal), and a witness computes the boundary with it, never with a newer one of its own (decided 17 Oct 2026, evening, 2A; a witness without it signs no morning block: proposal) |
more | [completed 24 Oct 2026 (plan step E4x): the block split, booking_rules] only on a block the size rule closed with work left: ["more", <trigger seq>, <work seq>, <leaver hex or "">, <"leave" | "exclude" | "">, <act id or "">]: the trigger the carried end-of-entry work names (a cut between whole entries, between the duties of a block's start or between the returns of a person, has none: trigger = work then [clarification, 25 Oct 2026, D4 N27]), the seq of the newest entry booked outside end-of-entry work, and the person whose receiving projects are still to be returned (with how the person went and the act); the next block starts at this block's last at and continues the work first. Block 1 never carries it |
Entry types in a block
genesis pay give xout xin xdone xundo act rule roles grav rej
Validation rules
- Tags in tag_order; h, prev and genesis chain the block by digests; seq and at match the entries; created_at = at last; the block spans at most w_max_s, exactly the parameter in force (from block 1 a run reads it from the found act and every params_effective); without the parameter lines only 10 s, the top of its range, can be checked [clarification, 17 Oct 2026 (D4 review)].
- Content is canonical JSON with strings only; the entries root and lines root recompute; every p index is touched by an entry, in first-naming order, and has exactly one line.
- Every embedded note and act rebuilds to a valid signed event of the 0.3.0 template; notes and acts are booked inside their window, once, judged by the circle parameters in force at the booking.
- Block 1: genesis with the found act whose id is the circle id, a valid circle_sig by the circle key it names and the mirror its found act gives, then start, and no roles entry (its genesis mirror is the mirror of height 1); its digest is the pinned genesis.
- params_effective is the first entry of its block, at most one per block, never early (its notice end at or before the block's first at); a block books at most one roles entry, and a roles entry changes the circle key only by circle_key_change.
- Moves name system accounts correctly: grow, settled, grav → reserve; start and grant origin → reserve; xout and reference_out → world; xin and xundo out of world, reference_in world → reserve; reserve_move out of the reserve into the project's wallet; project_pay out of the project's wallet; close_cover reserve → the closed line, close_wave reserve → origin; each reason only in the entry it belongs to.
- At least q valid qs signatures of the witnesses in office at the block's height over the domain-separated digest, sorted by HEX, the node's own included; the author is such a witness (in office: the genesis mirror, then each roles entry's mirror from the next block, since_h ≤ height < until_h).
- A grav entry only in a block with m and the morning header, booked at or after the boundary; in a block that books anything besides grav and rule entries, every grav entry stands right before the entry that touches its account (visit before touch), or before the rej of a refused note whose accounts it visits [proposal (D3 review, 19 Oct 2026)], several in HEX order; the remaining grav entries of a morning come in HEX order; the morning header names the tzdata the circle pinned at founding. Across blocks: after the boundary no entry touches an account before that account's grav entry of the day, and an account has one grav entry per day.
- Across a run [clarification, 17 Oct 2026 (D4 review)]: while an earlier morning is still pending and a later boundary has passed, a block carries the earlier morning's header and books only a params_effective entry (when one is due), its grav entries and their end-of-entry work; morning headers never go back; visit before touch for a morning counts from its opening. A roles entry commits a change that is open in the state (o3|change) within its commit window and carries exactly the mirror the change gives; a rules act is followed at once by member_rules, a member's leave act by member_leave, and a vote's end by vote_pass or vote_fall as the tally gives, at the first block whose first at reaches it. The o3|change lines of a block's state or a snapshot are the ones the run gives (test_vectors.change_line).
- Across a run: each block is judged with the circle parameters in force (the found act's, then each params_effective; every note's and act's TTL and the refusal limit by those in force at the booking, every running deadline by the length recorded when it started). A run checked from a later height takes the office and the parameters below it from the ledger (the mirror in office and the state's g3|param lines); without them those checks are unverifiable, never passed.
- [proposal, round 5] A member_join that names a reference carries ref, src and g together; the old circle's block it names carries the reference entry of the key with the same wave under a valid quorum of that circle's witnesses in office at its height, and that circle is the one the key's newest member home names; a reference_grant follows its member_join at once; a reference_return names the new circle's block with that grant; account_close, reference and the admissions from the waiting list come only in end-of-entry work and only when their conditions hold.
- Shape only here: cover, wave growth, gravity, drain() and the sum 0 are recomputed by every witness and verifier from the whole ledger (plan steps D1 to D4).
Signed example (public TEST keys)
{
"id": "fa7651f9c18bd85cccf49a34865ea9e224e6d41885bec15a44a3d37fb5f7aa55",
"pubkey": "4a141e845d3a5d2b3b71e908792bfe8357de51534578afb0b977f347cff0824d",
"created_at": 1792325100,
"kind": 43911,
"tags": [
[
"c",
"3cc68eda6fd66e3ab84ed9fb7cc7afcda0f3ebbb28450839de96046a81a35712"
],
[
"h",
"3cc68eda6fd66e3a:7"
],
[
"prev",
"de261be8bb1cf5612380eed26c79ea721ebeaf0ff58954033d200666a809e42f"
],
[
"genesis",
"dcc4c9e5e20994c73cf37e8bb921d39bf05a885e86dff98d94b66a5164b0ff85"
],
[
"seq",
"41",
"41"
],
[
"at",
"1792325100",
"1792325100"
],
[
"root",
"fb7023f015472952a9cfcda221b95e1d8f44f4518c7fbadfd86ec9973ab1dcc4",
"18e5dcc5ee9213019a751014234562a6dbcc4046353f30b320bc641f2050e0d7",
"b3663c3b5eb8f415745cd9b363a1b8d6cff5cee06353c0f99e7462ba3b75e2e9"
],
[
"p",
"bc394f9fc5df07e8708c0c9bfcba4ce3042e7774a190a251a63ed4b11350b533"
],
[
"p",
"ceada170baba7790086af4456dd436eaf2afb368b6a2c933bd95f93550174af8"
],
[
"p",
"dedf4ed2646531c1fd596caa827bac43b9bb4574af52c261decc32bb2ae18f9f"
],
[
"qs",
"4a141e845d3a5d2b3b71e908792bfe8357de51534578afb0b977f347cff0824d",
"d8b5c959b5beb6bec393833392169057252752df4b9f2514ed7f9bcf590200b17dc86fe4019fade250c405cf776ddf5920cac5c1b2d97fc2d87dd36a57910043"
],
[
"qs",
"f3f3cbf435e4d299e5200da37942491a03ef96ca00521a86abc20624647bad38",
"852978786bd13fe0428e9ea54b7622f5864e9deb359d02b9798d9cd05661c2ec0d47dfbfb477b4e6a8b5b14548cb989a590cd4658e9a9c19903a78675414fb1c"
],
[
"qs",
"f7d5bd636f637ee5286e5119d1207ce682afcba10b89c3ef30009c1d8336eb79",
"ce20584cce6ebeaefa67feece90264f10282525fae1a227c5d1a99d5331562cbedb38166ccaf3a5f619e1d9e62cd69d972f2bc0a87102fa718f4f93e83b51056"
],
[
"v",
"3"
]
],
"content": "{\"e\":[[\"41\",\"pay\",\"1792325100\",{\"mv\":[[\"0\",\"1\",\"2500\",\"pay\"],[\"1\",\"2\",\"2500\",\"grow\"]],\"note\":{\"amt\":\"2500\",\"c\":\"Bread for the Saturday market\",\"cur\":\"EUR\",\"exp\":\"1792325690\",\"idem\":\"6b47175496e2f4d7dd9072f84e6a9666\",\"k\":\"bc394f9fc5df07e8708c0c9bfcba4ce3042e7774a190a251a63ed4b11350b533\",\"n\":\"1\",\"p\":\"ceada170baba7790086af4456dd436eaf2afb368b6a2c933bd95f93550174af8\",\"prev\":\"genesis\",\"sig\":\"30133b6a91f31a8c225d8a970492d33329d3c84fc8bad0ba7600b00f552df59eed78283d6a0a7e8b0de7dc86fc6945351bc868c149c91690f23a78dcd7075d1d\",\"t\":\"1792325090\"}}]],\"l\":[[\"0\",\"-2500\",\"50000\",\"100000000\",\"0\",\"0\",\"3\",\"6\",\"1\",\"c258aab9e101b1e4d9c2b54c158c9ccf9f59cedff7a9d7b3cea355ee30f37151\",\"2026-10-18\",\"2026-10-18\",\"member\",\"\"],[\"1\",\"0\",\"2500\",\"100000000\",\"0\",\"0\",\"2\",\"2\",\"0\",\"\",\"\",\"2026-10-18\",\"member\",\"\"],[\"2\",\"82500\",\"0\",\"0\",\"0\",\"0\",\"5\",\"6\",\"0\",\"\",\"\",\"\",\"system\",\"\"]]}",
"sig": "608a8e8ee3821926e88a9d062a06324ef1ed2dbb0ddbb46aa7237d67708f1114b7a992e1efc19518941d954c0b481fbf55a3adbf534058009780861d397095e1"
}
KIND 43916 · Circle Act draft 0.3.0
Layer 2 · circle ledger
v0.3.0 — Everything a person decides in a circle, signed by that person: founding, membership (joining, invitations, leaving), payment requests, projects, motions and votes, the step outward, parameters, witnesses, the Circle Heart, relays, the rate source, the circle key, recovery, exclusion and readmission, closing an old account, OWN processes and member safety. The found act names no circle (["c", ""]): its id is the circle id; every other act names its circle by ["c", <circle id>]. Every act carries expires, at most act_ttl_s (a circle parameter, 604800 s by default; decided 17 Oct 2026, C3) after it was signed; it has effect only once booked in a block (an act entry, the genesis, or the input of a rule or roles entry) at created_at − AT_TOLERANCE_S ≤ at ≤ expires, judged by its author's role at the booking height, and the same act id is booked at most once (duplicate). A change that needs k Heart members is one proposing act plus endorsements that name it, booked before it expires. Acts are embedded in blocks; a standalone copy is optional. Every act's text is public for ever and the app shows "public for ever; do not write names, personal or bank data"; no act type has a field for a person's name, and a person appears only as the author or in a p tag, by HEX key. There is no act for money from or to outside the system, no act that pays a payer back out of community money, and no act naming a holder of money (decided 16 Oct 2026). The rules each act follows (votes, notice periods, who may act, limits) are in docs/upravljanje-kroga.md (pinned in meta.normative_spec).
- Kind class
- regular (40000 and up): kept for ever
- Replaceability
- Regular (kind 40000+); embedded in 43911; a standalone copy optional.
- Signed by
- The acting person: member, invitee, recipient, proposer, Heart member (signer "steward"), witness, founder or guardian.
- Content
- Text where the type has one, at most 500 characters on one line, public for ever.
- Tag order
c, t, the type's tags in act_types order, after?, expires, idem?, v
- What is public
- Public for ever: who acted (HEX key), what, when, and the text. No names, no bank data.
Required tags
| tag | meaning |
|---|
c | the circle id, first; ["c", ""] in the found act, whose own id is the circle id e.g. <circle id> |
t | the act type, second e.g. project_propose |
expires | ["expires", <unix>]: after created_at and at most created_at + act_ttl_s (the circle parameter in force at the booking, 604800 s by default); the act is refused (rej expired) after it |
v | record version, last e.g. 3 |
Optional tags
| tag | meaning |
|---|
the type's tags | as act_types lists, in that order (or, for an endorsement, only ["e", <proposing act id>, "", "proposal"]) |
after | ["after", <height>]: valid only after this height |
idem | exactly 32 lowercase hex characters made by the app |
Act types (tag t)
| type | group | signed by |
|---|
found | Founding | founder |
invite | Membership | member |
vouch | Membership | member |
join | Membership | invitee |
leave | Membership | member |
rules | Membership | member |
pay_request | Requests | member |
project_propose | Projects | member |
project_consent | Projects | recipient |
project_decline | Projects | recipient |
project_withdraw | Projects | proposer |
project_close | Projects | steward |
motion | Votes | steward |
vote | Votes | member |
step_propose | Step outward | member |
param_propose | Parameters | steward |
steward_accept | Circle Heart | nominee |
steward_resign | Circle Heart | steward |
signer_propose | Witnesses | steward |
signer_accept | Witnesses | new witness |
signer_resign | Witnesses | witness |
signer_remove | Witnesses | steward |
signer_replace | Witnesses | steward |
steward_add | Circle Heart | steward |
steward_remove | Circle Heart | steward |
stewards_required_set | Circle Heart | steward |
proposer_swap | Witnesses | steward |
relay_change | Circle Heart | steward |
rate_source_set | Circle Heart | steward |
refusal_list_set | Circle Heart | steward |
circle_key_change | Circle Heart | steward |
recovery | Witnesses | steward |
member_exclude | Membership | steward |
member_readmit | Membership | steward |
account_close | Membership | steward |
own_open | OWN | member |
own_close | OWN | steward |
guardians_set | Member safety | member |
freeze | Member safety | guardian |
key_move | Member safety | guardian |
Validation rules
- c first, t second, v last; the type's tags with their counts (or the endorsement form), then after, expires (required) and idem; nothing else.
- found and param_propose carry exactly the circle parameters of meta.circle_parameters, in that order and range (vote_min_s ≤ vote_max_s < open_lapse_s). found's relays follow the relay rule (bootstrap: see 30910). found is signed by the founder the circle record names; its id is the circle id; it names the first circle key, which is not the founder's, a witness's or a Heart member's key; for every circle but the root it names its parent circle and the founder's reference from it, together (decided 23 Oct 2026, E-8 and E-9).
- circle_key_change: pop is the new key's BIP-340 signature over sha256("lb-circle-key/3|" + circle id + "|" + new key + "|" + created_at), created_at the act's own in decimal, so nobody names a key they do not hold and a pop is good for its own act only (an act is booked only within act_ttl_s of its created_at); the new key is not the key in office, the founder's, a witness's or a Heart member's key.
- Acts whose signer is "steward" are signed by a Heart member in office; signer_resign by a current witness; a vote on a witness change by a Heart member in office.
- An endorsement names a booked proposing act of the same type (never another endorsement) that has not expired; each Heart member counts once per proposal; the change is committed only with at least k distinct Heart members in office, and for member_exclude, member_readmit, account_close and the witness exceptions at least max(k, 3) (E-10; see the roles entry; member_exclude, member_readmit and account_close at the act that reaches it). The excluded key never counts toward its own exclusion.
- project_propose and motion carry the vote's duration, from the circle's vote_min_s to its vote_max_s (else not_allowed). A vote names a project or motion whose vote runs (vote_ended at or after its end) or a witness change; "against" carries its short public reason as text, "for" none (else bad_template). There are no approvals and no objections, and no Heart member rejects a project alone (decided 23 Oct 2026, E-5; 17 Oct 2026, C4).
- Heart members who propose a project or receive from it never count toward its Heart "for" votes.
- A join names, where needed, the open invitation of its key (["e", <invite id>, "", "invite"], invite_only) and the reference of the circle its newest member home names (["e", <43921 id>, "", "reference"], E-9).
- The rules act's ["rules", version, sha256] names the circle's own rules text; the found act names no rules version (there are no world rules; decided 23 Oct 2026, E-1).
Signed example (public TEST keys)
{
"id": "9700afdd73eb49e2deb1b2d2ad79f55fe3946fd1131539a4fa0fb7f075582f9b",
"pubkey": "ceada170baba7790086af4456dd436eaf2afb368b6a2c933bd95f93550174af8",
"created_at": 1792058940,
"kind": 43916,
"tags": [
[
"c",
"3cc68eda6fd66e3ab84ed9fb7cc7afcda0f3ebbb28450839de96046a81a35712"
],
[
"t",
"project_propose"
],
[
"title",
"A relay for the circle"
],
[
"amount",
"50000"
],
[
"p",
"bc394f9fc5df07e8708c0c9bfcba4ce3042e7774a190a251a63ed4b11350b533"
],
[
"duration",
"259200"
],
[
"expires",
"1792663740"
],
[
"v",
"3"
]
],
"content": "Run a fourth relay for the circle for one year. Public for ever; no names, personal or bank data.",
"sig": "366f5a681e407e91cebd6e342b3adf76b428dab4391ffb8f1b736dc31b462e9440641258da56fd28bec734dd46e6a61ad227b4b6f385df552ad43bed12d1fb83"
}
KIND 43917 · Day Seal draft 0.3.0
Layer 1 Layer 2 The circle keeps every seal for ever; a directory keeps copies of the newest 7 days (the present state).
v0.3.0 — The daily checkpoint of a circle, once a day right after the morning, also on days without payments (decided) and on days without any block (then its range is empty). It names the day's morning boundary (the circle's morning hour of that day in its time zone), the seal before it (digest, day, boundary and what it covered), the last booking time it covers, its height and entry range, the digest and height of the last block (head), a root over the day's entries, the state root, the snapshot (43918 chunks), the morning's gravity totals, the sum 0, the world numbers (used, step, built), the rate quotes the day's entries named, the reserve and origin balances, the witness set and the circle key of the newest mirror at or below its last block (at the height of a key change already the new key), and the height of the circle's parameters in force (params); the witnesses sign it (qs, domain "lb-seal/3|") after recomputing everything. Payments never pause for the morning (decided 17 Oct 2026, C2): after the boundary, the day's morning blocks and payment blocks may interleave, and an account's grav entry is booked right before the first entry that touches it (visit before touch, 43911). The seal of day D covers every block booked before boundary(D) and, when blocks carry D's morning header (the morning blocks, and payment blocks with visits), every block up to and including the last of them (payment blocks booked during the morning among them); no block spans the boundary. So every block after its h_to books at or after boundary(D), and every block before its h_from books at or before the covers of the seal before it: this is what makes consecutive seals evidence that a note was not booked (8j). Nothing is salted and no count is withheld. A day without a seal is a public fact. A restorer that finds at least 2 independent 43924 ok statements for a seal may start from it. [clarification, 17 Oct 2026 (D4 review); proposal (D4 review, round 3, 17 Oct 2026): the complete list] A restorer from the seal of day D reads exactly: the seal; its snapshot chunks; every block whose first at is at or after boundary(D), up to h_to (none when the range ends before it: the next morning's averages need each account's balance over [boundary(D), boundary(D+1)), and the balance at boundary(D) is the snapshot balance less the moves of those blocks other than grav deducts, which count from the boundary; they also hold the current morning day's refusals; after an outage they include the catch-up blocks of earlier mornings in earlier seal ranges, and each grav deduct counts from its own morning's boundary [clarification, 19 Oct 2026 (D3 review)]); the block right before the first of them, or the seal's head block when there is none, whose last at must be before boundary(D), so no block after the boundary is left out [proposal (D3 review, 19 Oct 2026)]; the governance chain, mandatory: block 1 and every block with a roles entry up to g3|mirror_h, in height order, each signed by a quorum of the office the mirror before it puts in power, which shows from the pinned genesis on that the seal's quorum is the office in power (no block carries a pointer back to the previous roles block) [proposal (D3 review, 19 Oct 2026)]; the block at the height the state's g3|mirror_h names (its genesis or roles entry carries the mirror in office, in tag order); the blocks at the booked heights of the open o3|change lines (what each Heart change names: its relays, keys, witness, k or parameters, the key it excludes, readmits or closes, or the person an OWN process is about); and every block after h_to (the tail). Round 5 (23 Oct 2026, E-1): no rules record is read any more; the parameters are in the state. With these it judges the tail exactly as a replay from block 1 (test: circle B restored from its seal of 19 Oct, circle A from its seal of 16 Oct). Seal and tail alone cannot give the next gravity.
- Kind class
- regular (40000 and up): kept for ever
- Replaceability
- Regular (kind 40000+); never deleted. The newest one is mirrored on root as 30915.
- Signed by
- The node (a current witness), with at least q witness signatures inside (qs).
- Content
- Empty.
- Tag order
c, m, day, boundary, prev, covers, range, head, pay, state, snap, gravity, sum, world, quotes, reserve, origin, signers, circle_key, params, qs…, v
- What is public
- Public: roots, counts, the circle's world, reserve and origin numbers, its circle key and the height of its parameters in force.
Required tags
| tag | meaning |
|---|
c | the circle id |
m | "<scope16>:<date>" |
day | ["day", <local date of the morning>, <n>]: n counts the seals from 1 |
boundary | the unix time of the day's morning boundary (the circle's morning hour in its tz with the pinned tzdata; a local hour that occurs twice counts at its first occurrence, a skipped one at the first instant after the gap) |
prev | ["prev", <previous seal digest>, <its day>, <its boundary>, <its covers>]; ["prev", ""] for the first seal (n = 1), whose range starts at height 1 and seq 1 |
covers | the last booking time the seal covers: the last at of block h_to; on a day without blocks, the previous seal's covers |
range | ["range", h_from, h_to, seq_from, seq_to]; a day without blocks has h_from = h_to + 1 and seq_from = seq_to + 1 (proposal) |
head | ["head", <digest of block h_to>, <h_to>] |
pay | ["pay", <RFC 6962 root over the canonical JSON bytes of the day's entries, in seq order>, <count>] |
state | ["state", <sparse Merkle root at h_to>, <line count>] |
snap | ["snap", <RFC 6962 root over the chunks' content bytes in index order>, <chunk count>, <total content bytes>] |
gravity | ["gravity", <date>, <moved, up to 20 digits>, <accounts>], always: moved = the sum of the deducts of the morning's grav entries in the range, accounts = the number of those grav entries [clarification, 17 Oct 2026 (D4 review)] |
sum | ["sum", "0"] |
world | ["world", <currency>, <used = balance of world>, <step = balance of world-step>, <built, up to 20 digits: Σ (balance + wave) over every account line that is not a system account or a project wallet, of any status, members who left included, since their money is still in the circle [clarification, 17 Oct 2026 (D1 review)]; the formula: proposal>] |
quotes | ["quotes", <RFC 6962 root over the distinct ids of the 43919 quotes the day's entries name, each once, as 32 raw bytes, sorted ascending>, <count of distinct ids>] [clarification, 17 Oct 2026 (D4 review)] |
reserve | ["reserve", <balance of the keyless reserve>] |
origin | ["origin", <balance of origin, signed: −start − granted + returned (decided 23 Oct 2026, E-3 and E-9 (b)); it may rise above −start, even above 0>] (the tag: proposal, 6c) |
signers | ["signers", <sha256 of the canonical JSON list of the current witnesses (until_h "") of the office after h_to, each [hex, operator, since_h, ""], sorted by hex>, q, n] [clarification, 17 Oct 2026 (D4 review)] |
circle_key | ["circle_key", <the circle key of the newest mirror at or below h_to>]: the state's g3|circle_key line at h_to; at the height of a key change already the new key, which signs the 30910 of that height (the 30915 head carries it to the root directory, so a reader picks the 30910 by it; proposal) |
params | ["params", <the height of the parameters in force at h_to: g3|params_h, 1 until the first params_effective>] |
qs | ["qs", <witness hex>, <signature over sha256("lb-seal/3|" + the 32 digest bytes)>], at least q, sorted by HEX |
v | record version e.g. 3 |
Validation rules
- Tags in tag_order. A seal is judged with the office after its last block h_to, i.e. the newest mirror at or below h_to (the genesis mirror or the newest roles entry's, read from the ledger): the qs signatures verify over the domain-separated digest with at least q of its current witnesses; signers matches its witness set and circle_key its circle key, as the state's g3 lines at h_to do.
- boundary = the circle's morning hour of the day in its time zone (with the pinned tzdata; inside a clock change the first occurrence of a repeated hour, the first instant after a skipped one); prev describes the seal before exactly; covers never goes back.
- A circle's seal chain starts at its block 1: the first seal (n = 1, ["prev", ""]) has a range from height 1 and seq 1, never an empty one, so a second "first" seal for a later day is refused.
- [clarification, 17 Oct 2026 (D4 review)] One seal per day, no gap: the first seal is the seal of the first day whose boundary is later than block 1's at; each later seal is for the day after the seal before it, with n + 1 (after an outage the missed days are sealed in order). The day-chain checks apply to seals whose range lies on the chain a verifier replays; the seal at maximum lengths is a standalone fixture and gets only the checks that need no chain [clarification, 17 Oct 2026 (D4 review, round 3)]. A seal's completeness (no block before its boundary left out) needs block h_to + 1, or the next seal with an empty range from h_to + 1; without either it is unverifiable.
- Against the day's blocks: heights h_from to h_to linked by digests, head = digest and height of block h_to, covers = its last at, seq range, pay root and count, gravity totals, quotes root, state = the state root of block h_to; no block spans the boundary; with blocks that carry the day's morning header the range ends at the last of them, without them it ends before the boundary; the next block books at or after the boundary.
- Against its chunks: every chunk 0 … n−1, the snap root and bytes, the state root over every line, the sum of all balances 0, reserve, origin, world used and step read from the lines, circle_key and params read from the g3|circle_key and g3|params_h lines; with a params_effective in the range, params is the height of the last one.
- Two valid seals (two digests) for one day are a fork alarm: stop and report.
Signed example (public TEST keys)
{
"id": "2bfad113675e8b3ad85e9de629294f451c9a34ddb612bfcd5e3b5805878c3dce",
"pubkey": "4a141e845d3a5d2b3b71e908792bfe8357de51534578afb0b977f347cff0824d",
"created_at": 1792116060,
"kind": 43917,
"tags": [
[
"c",
"3cc68eda6fd66e3ab84ed9fb7cc7afcda0f3ebbb28450839de96046a81a35712"
],
[
"m",
"3cc68eda6fd66e3a:2026-10-16"
],
[
"day",
"2026-10-16",
"1"
],
[
"boundary",
"1792116000"
],
[
"prev",
""
],
[
"covers",
"1792060200"
],
[
"range",
"1",
"4",
"1",
"22"
],
[
"head",
"3177d14df282ccde472b50cdac391d90747e97474317a2b282c45503ad35e8a2",
"4"
],
[
"pay",
"d8ee98959084bea26a4726b8958f00080cd241908e3117241db011164d6f556f",
"22"
],
[
"state",
"df7981e5ec0191d848b06ca3ff2ce66f27a2385ef7ad39547689374b92efb359",
"76"
],
[
"snap",
"17b453e35de09fd9d3cdd67b6e8c3db803d6a6979f3b08c4a7c47a0abb0236d7",
"1",
"5042"
],
[
"gravity",
"2026-10-16",
"0",
"0"
],
[
"sum",
"0"
],
[
"world",
"EUR",
"0",
"0",
"0"
],
[
"quotes",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"0"
],
[
"reserve",
"100000"
],
[
"origin",
"-100000"
],
[
"signers",
"c3af72bd9524f92873a62c6e26411af3f3e12bb5e8fe2cc67441d3063a936abc",
"3",
"4"
],
[
"circle_key",
"dab7ad80719db76adbcb29b213256d7c4d16bca26202ad3d64c622142a9cfe34"
],
[
"params",
"1"
],
[
"qs",
"4a141e845d3a5d2b3b71e908792bfe8357de51534578afb0b977f347cff0824d",
"f65025240b057e166bd9ddd16b00c5257e03bef0c79be7261c25bfd3a119cc85a3d373c836fe787be93ebf8b663a95cbce6b3731f47a3f1cf9630554ab322f1a"
],
[
"qs",
"f3f3cbf435e4d299e5200da37942491a03ef96ca00521a86abc20624647bad38",
"5252a2c4da5bd313254f4279f3225c077184c35aeecec05fb8da5aad05ad7e72d3fa8b98267baf142e7f3818e5f9d29e37a0f65c126a900ff870a0fe8408f44c"
],
[
"qs",
"f7d5bd636f637ee5286e5119d1207ce682afcba10b89c3ef30009c1d8336eb79",
"e5838d42035682de8f94e02655273b2a9054b68fb2bec55597a0937edb013cb36d65a34eaafd99e2056a9f5963b7409568f8e026903f8d37a727320be5558619"
],
[
"v",
"3"
]
],
"content": "",
"sig": "379d54f4dcd14bcd820654f4e3321a1b8dca7230d47a523923eba9b66ab3b5543a66973596e14dbf5d98bc1d9a5cdaf7b52040b39d762adf579bb37205b09ded"
}
KIND 43918 · Snapshot Chunk draft 0.3.0
Layer 1 Layer 2 The circle keeps every chunk for ever; a directory keeps copies of the newest 7 days.
v0.3.0 — Part of a circle's full public snapshot at a daily seal: every line of the decision state (43911 state_lines: accounts, governance, open items such as projects and holds, booked acts and idem windows), sorted by key and cut into chunks of at most 100,000 bytes by bytes, never by a count. A chunk is valid only against its seal's snap and state roots; a reader verifies a chunk before showing anything from it. With the seal, the blocks of its range booked at or after its boundary and the blocks after it (43917), the snapshot restores a circle without replaying from genesis, including which acts were booked (a3 lines) and which Heart changes are open (o3|change lines), so a replayed act is refused the same way.
- Kind class
- regular (40000 and up): kept for ever
- Replaceability
- Regular (kind 40000+); never deleted.
- Signed by
- The node (a current witness); valid through the seal's roots.
- Content
- Canonical JSON {"l": [state lines sorted by key]}.
- Tag order
c, m, i, chunk, range, v
- What is public
- Public: every account's line by HEX key, every project and hold. No names.
Required tags
| tag | meaning |
|---|
c | the circle id |
m | "<scope16>:<date>" of the seal |
i | "<scope16>:<date>:<index>" |
chunk | ["chunk", <index>, <count>] |
range | ["range", <first key>, <last key>] |
v | record version e.g. 3 |
Validation rules
- Lines are state lines of the formats in 43911 state_lines, sorted by key, each key once; range names the first and last key.
- All chunks of a seal together give its snap root, bytes, line count and state root, and their account lines sum to 0.
Signed example (public TEST keys), loaded on request
Open this section to load the example, or open the file.
KIND 43919 · Rate Quote draft 0.3.0
Layer 2 · circle ledger
v0.3.0 — Repurposed: no longer an hourly rate of one key, but an offer of the receiving (seller's) circle (decided 16 Oct 2026, A7). Its node signs the rate of the source the circle chose itself (any source with a public description; the definitions gate no source), with the source's own time and a validity of at most the circle's quote_ttl_s (a circle parameter, 600 s by default; decided 23 Oct 2026, E-1: each circle sets its own, and both circles judge a quote by the quoting circle's value); a payer who accepts signs a note with both amounts and this quote's id, and both ledgers carry them. A quote binds only payers who sign a note naming it, and only while it is valid. A new circle also uses the old circle's quote to grant a newcomer's step across currencies (E-9 (b)). Start source (decided 16 Oct 2026): ecb-daily, the euro foreign exchange reference rates of the European Central Bank, read once a day by the node; the app always shows the rate's date. Said openly: the ECB states that its reference rates are for information only. Proposal: the node alerts the Circle Heart when its newest source rate is older than RATE_STALE_ALERT_S (120 h). The node keeps at most one live quote and publishes each quote to its circle's relays before handing it out; the payer's circle republishes every quote its blocks name.
- Kind class
- regular (40000 and up): kept for ever
- Replaceability
- Regular (kind 40000+); never deleted; validity is the expires tag, never NIP-40 expiration.
- Signed by
- The node (proposer) of the quoting circle at created_at, as its ledger names it.
- Content
- Empty.
- Tag order
c, source, base, rate…, expires, prev, v
- What is public
- Public: rates, times and the quoting circle.
Required tags
| tag | meaning |
|---|
c | the circle id of the quoting (seller's, receiving) circle |
source | ["source", <source id = the circle's rate_source at created_at>, <the source's own time for this rate, unix>]; for ecb-daily the reference date at 00:00 UTC |
base | always EUR e.g. EUR |
rate | ["rate", <USD|GBP>, <whole ppm: units of that currency per 1 EUR × 1,000,000>], once per currency the source gives; EUR is 1,000,000 and implicit e.g. ["rate","USD","1085000"] |
expires | unix, at most created_at + quote_ttl_s of the quoting circle (its parameters in force; the payer's circle reads the value in that circle's ledger) |
prev | this circle's previous 43919 id, or "" on its first quote |
v | record version e.g. 3 |
Validation rules
- Valid for a booking when: signed by that circle's node at created_at (the office at a time t is that of the newest mirror booked at or before t: its genesis or roles entry's at ≤ t [clarification, 17 Oct 2026 (D4 review)]; so a quote signed in the very second of a node change is the new node's, although that block was still ordered by the old one [clarification, 17 Oct 2026 (D4 review, round 3)]); c is that circle; the source id is its rate_source at created_at; source time ≤ created_at < expires ≤ created_at + quote_ttl_s of the quoting circle; the payer circle's xout at ≤ expires (for a newcomer's grant, the new circle's join at ≤ expires); to_amount is the conversion, exactly (test_vectors.conversion). Refusals: stale_quote (expired), bad_quote (anything else).
- Audit checks, not booking conditions: the circle's prev chain has no gap, and for one source id the source time never goes back; a break is published as a 43924 fail.
- No witness and no app reads the outside source; only the quoting circle's node does. There is never a fallback to 38888 or a fixed rate.
Signed example (public TEST keys)
{
"id": "9d02b66866d78a952cde032ab552fceb5b755a82219fc0e5942d8d7bc1bff40b",
"pubkey": "f368fe5fd9d6fffba8c967473e428b8c9e1d22a37d3b3bbeb2de16a652d0566b",
"created_at": 1792325400,
"kind": 43919,
"tags": [
[
"c",
"c72cafe83b2f0f7a2f53318fcea8f2c13b7d16d3fe9defa14f44f3ca099c365a"
],
[
"source",
"ecb-daily",
"1792108800"
],
[
"base",
"EUR"
],
[
"rate",
"USD",
"1085000"
],
[
"rate",
"GBP",
"846000"
],
[
"expires",
"1792326000"
],
[
"prev",
""
],
[
"v",
"3"
]
],
"content": "",
"sig": "9e1211f53c9e03f5e1f56360739c806271db0c40b252a617e7cbe5a1bb8146789cbd70f41b61970099a854be79bc92ab5209042d1a226c1c6772ca9904aa5776"
}
KIND 43921 · Reference draft 0.3.0
Layer 2 · circle ledger
v0.3.0 — New (decided 23 Oct 2026, E-9). The findable copy of a reference. When a member says "I want to leave" (a booked leave act), the circle's ledger issues the reference by itself once the conditions hold (no minus; no open OWN process, E-9 (a)): the rule entry reference of the key with its wave, the step the person built. Right after the block that books it has its quorum, the circle's node signs this event and publishes it on the circle's relays. Without a reference nobody moves to another circle: a new circle admits a key whose newest member home (30916) names another circle only with a valid reference of that circle (a rule for every circle, not a setting), and the found act of a new circle names its founder's reference from the parent circle (E-8). The ledger entry is the authority, under the witnesses' quorum; this event adds the node's short report from the circle's public blocks (when the membership began and ended, the projects the person published and those paid to them), which anyone checks against those blocks. The new circle checks the reference against the old circle's block it names: that block carries the reference entry of the person with the same wave under a valid quorum of the old circle's witnesses in office at its height (the office read from the old circle's ledger, as for the 8j evidence); its member_join then names the reference (ref, src, g). With a wave above 0 the new circle opens the same step for the newcomer and pays its amount to the old circle's community (reference_grant), and the old circle closes the account and returns that amount to its origin (reference_return): both communities hold what they held before (E-9 (b)). A key with no member home anywhere was never a member and needs no reference (proposal). Said openly: one person with two keys, or a deleted member home, is not caught; that needs Lana's person check. The person appears by HEX key only: no name, no bank data, nothing of an OWN process but that none is open.
- Kind class
- regular (40000 and up): kept for ever
- Replaceability
- Regular (kind 40000+); never deleted. The reference entry in the ledger is the authority; this event is its findable copy.
- Signed by
- The node (a current witness) of the issuing circle, right after the block with the reference entry has its quorum.
- Content
- Empty.
- Tag order
c, p, block, g, currency, wave, own, joined, left, published, e (published)…, took_part, e (took_part)…, v
- What is public
- Public: the person's HEX key, the circle, the step built, the times the membership began and ended and the project ids. No name, no bank data, nothing of an OWN process but that none is open.
Required tags
| tag | meaning |
|---|
c | the circle id of the issuing (old) circle e.g. <circle id> |
p | the person's HEX key e.g. <hex> |
block | ["block", "<height>", <digest>]: the block of the issuing circle that books the reference entry e.g. ["block","4","<hex>"] |
g | the issuing circle's genesis digest e.g. <hex> |
currency | the issuing circle's currency (the wave's) e.g. EUR |
wave | the step (wave) the person built, in minor units of that currency, as the reference entry states it e.g. 300 |
own | "0": a reference is issued only while no OWN process about the person is open (decided 23 Oct 2026, E-9 (a)) e.g. 0 |
joined | unix: the at of the newest member_join of the person in the issuing circle e.g. <unix> |
left | unix: the at of the entry that booked the leave act; joined is not after left e.g. <unix> |
published | ["published", "<count>"]: the projects the person published (project_propose) in the issuing circle e.g. 0 |
took_part | ["took_part", "<count>"]: the projects paid to the person in the issuing circle e.g. 0 |
v | record version e.g. 3 |
Optional tags
| tag | meaning |
|---|
e (published) | ["e", <project_propose id>, "", "published"], right after published: at most min(100, count), newest first, each once |
e (took_part) | ["e", <project_propose id>, "", "took_part"], right after took_part: at most min(100, count), newest first, each once |
Validation rules
- Tags in tag_order, nothing else; content empty; own is "0"; joined and left are unix times, joined not after left; at most min(100, count) project ids after each count, each once.
- Signed by a witness of the issuing circle (its node); c, g and currency are that circle's.
- The block it names (height and digest) is the issuing circle's and carries a rule entry reference whose party is p and whose wave is this wave; the new circle checks that block's quorum with the office in force at its height, and that the issuing circle is the one the key's newest member home names.
- [proposal] The report (joined, left, published, took_part and the ids) is the node's summary of the circle's public blocks, which anyone checks against them; the new circle relies only on the reference entry and its wave.
Signed example (public TEST keys)
{
"id": "ec05a3151322ca2ad2d1e581d7f862509311b1e0916380b632000641bb609e6a",
"pubkey": "f368fe5fd9d6fffba8c967473e428b8c9e1d22a37d3b3bbeb2de16a652d0566b",
"created_at": 1793008830,
"kind": 43921,
"tags": [
[
"c",
"c72cafe83b2f0f7a2f53318fcea8f2c13b7d16d3fe9defa14f44f3ca099c365a"
],
[
"p",
"c1b73e4d3ebb7c5e642ab8bc15ba0b93ae6c0efdb8b05b5d63b85fb3e87b37e6"
],
[
"block",
"8",
"885ad2cb9123947440b02af3d6eafd90a0c4cdc4ca66fdbc31fd66e755392306"
],
[
"g",
"b0fbd18c9fd352348fe597d673e43b5c68e8863cf42cdaeca5be7a39382a5d11"
],
[
"currency",
"USD"
],
[
"wave",
"300"
],
[
"own",
"0"
],
[
"joined",
"1792339200"
],
[
"left",
"1793008800"
],
[
"published",
"0"
],
[
"took_part",
"0"
],
[
"v",
"3"
]
],
"content": "",
"sig": "bf2c5c2e206a4a6422f52f6d6e8c21005b580c16cd131a658e8905a492fb1f09a161864f838511690b32628d818e9979f3bb596e724cb13d2d6356637e896c78"
}
KIND 43924 · Statement draft 0.3.0
Layer 2 · circle ledger
v0.3.0 — A signed attestation or challenge about a circle's ledger: each witness's daily ok with the state root it computed and a root over every digest it signed that day (proposal Q23), the daily public restore check, or any verifier's challenge (fail, equivocation, missing, ignored) carrying its evidence. A statement pauses nothing by itself; it is public evidence every member's app can show. Its text is public for ever: the app shows "public for ever; do not write names, personal or bank data".
- Kind class
- regular (40000 and up): kept for ever
- Replaceability
- Regular (kind 40000+); never deleted.
- Signed by
- Any witness, verifier or auditor.
- Content
- Short text, at most 500 characters on one line.
- Tag order
c, about, target or e (target), range?, result, state?, votes?, evidence…, e (evidence)…, v
- What is public
- Public: the author's key, the result and its evidence.
Required tags
| tag | meaning |
|---|
c | the circle id |
about | seal, range, block, signer or restore |
target | exactly one: ["target", <digest>, "h:<height>" | "d:<day>"] for a block or seal, or ["e", <id>, "", "target"] for any other event |
result | ok, fail, equivocation, missing or ignored |
v | record version e.g. 3 |
Optional tags
| tag | meaning |
|---|
range | ["range", from, to]: the heights whose blocks the author checked for this statement: from the first block it replayed (1 when it replayed from genesis; h_from of the target seal when it started from the state of a seal it had verified before) to h_to for an ok, or to the last height it could check for missing or fail [clarification, 17 Oct 2026 (D4 review)] |
state | ["state", <root the author computed>]; required for an ok about a seal or a restore |
votes | ["votes", <RFC 6962 root over the 32 raw bytes of each block digest the author signed among the blocks of the range (for a seal: the sealed day's blocks, the seal's range, also when the statement's range starts at block 1 [clarification, 17 Oct 2026 (D4 review, round 3)]; not the calendar day of signing), in height order>, <count>] (proposal Q23; the leaves: [clarification, 17 Oct 2026 (D4 review)]) |
evidence | ["evidence", <digest>, "h:<height>" | "d:<day>"] for blocks and seals, ["e", <id>, "", "evidence"] for other events; required for fail, equivocation, missing and ignored |
Validation rules
- Tags in tag_order; a statement other than ok names its evidence.
- missing also means the author could not check (the missing event as evidence); a statement is never ok about what was not checked [clarification, 17 Oct 2026 (D4 review)].
- A verifier checks a rej's reason only with the events that the reasons before it in refusal_reasons need (a bad_quote or stale_quote needs the quote and, across circles, the partner's xout time); without them the reason is unverifiable, never assumed [clarification, 17 Oct 2026 (D4 review, round 3)].
Signed example (public TEST keys)
{
"id": "707cf2f1e445ab680231763ed6e06df107a21405be8fa08ac91aa2f49c97a75d",
"pubkey": "f7d5bd636f637ee5286e5119d1207ce682afcba10b89c3ef30009c1d8336eb79",
"created_at": 1792116300,
"kind": 43924,
"tags": [
[
"c",
"3cc68eda6fd66e3ab84ed9fb7cc7afcda0f3ebbb28450839de96046a81a35712"
],
[
"about",
"seal"
],
[
"target",
"76e4791959e85b79a2e4f3f518d1d56b5b01072ee726a82850027505a11bf39c",
"d:2026-10-16"
],
[
"range",
"1",
"4"
],
[
"result",
"ok"
],
[
"state",
"df7981e5ec0191d848b06ca3ff2ce66f27a2385ef7ad39547689374b92efb359"
],
[
"votes",
"57df5b66e80afe2cd80f147ba37a86eaf2bdf4c46b1d62bdd6e3ab54b2244555",
"4"
],
[
"v",
"3"
]
],
"content": "Replayed blocks 1 to 4 from the relays; the state root is the sealed one.",
"sig": "88567ef551b0ec42e9b0d9f10d6e804cb95ea4340c6e040d0df8b523b45ebe1941e80e422ef523ced64228f0667e3a5ae8b80d289f6dc79e0044eb14a9899b74"
}
Retired and reserved kind numbers
- Retired in 0.3.0:
43920. Retired (decided 23 Oct 2026, E-1): the world-wide BSA Rules record of the earlier drafts of 0.3.0 (never published; 0 events on the Lana relays). Each circle sets its own parameters now. The number stays reserved and is never reused.
- Retired since 0.1.0:
30911, 30912, 30913, 30914, 43912, 43913, 43914, 43915.
- Reserved, unused:
43922, 43923 (free for a later revision).
- Not defined:
43925 (free text kept beside the chain was dropped; free text stays inside the signed note or act).
- Reserved for later:
30917 (a Service Offer for a relay, witness, archive or index operator; not defined in 0.3.0).
- Kind numbers 10000 to 19999 (replaceable) and 20000 to 29999 (ephemeral) are never used by BSA.