Merge pull request 'Design 25: the grants part takes a first view of the bus module's bindings at every start, so a missed created event is recovered (issue 1966)' (#543) from design/25-bindings-first-view into main

This commit was merged in pull request #543.
This commit is contained in:
2026-11-11 09:27:00 +00:00
6 changed files with 590 additions and 357 deletions
+3 -2
View File
@@ -995,8 +995,9 @@ becomes a message). **Decided by** ADR 0227, 0231, 0240, 0252, 0283, 0564, 0668.
graph orders the bus module's build before a module asking bus state (ADR 0614 §5). Provided by the holder of the graph orders the bus module's build before a module asking bus state (ADR 0614 §5). Provided by the holder of the
seat `mesh-broker`. seat `mesh-broker`.
- **binding** of bus state — the names on the bus of a module's bus-state entries, which the grants part writes into - **binding** of bus state — the names on the bus of a module's bus-state entries, which the grants part writes into
the module's membership on the bus module's `created`, rewrites on `recreated` and removes on `dropped`; a module the module's membership on the bus module's `created`, rewrites on `recreated` and removes on `dropped`, and also
whose binding is missing at start fails loudly (ADR 0595 §1, §7). Never bare where it could be read as a sticky takes, at every start, from its first view of the seat `mesh-broker`'s `list` (design 25 bindings-first-view); a
module whose binding is missing at start fails loudly (ADR 0595 §1, §7). Never bare where it could be read as a sticky
binding or a placement's bindings (see [Homonyms](#homonyms)). binding or a placement's bindings (see [Homonyms](#homonyms)).
- **first view** — what a reader is answered once, at its start, by the owner's verb or tool, before it follows the - **first view** — what a reader is answered once, at its start, by the owner's verb or tool, before it follows the
owner's events; it never reads the owner's bus state instead, and asks again only on a gap in the events owner's events; it never reads the owner's bus state instead, and asks again only on a gap in the events
@@ -0,0 +1,138 @@
---
layer: to-be
design: 25
milestone: bindings-first-view
updated: 2026-11-11
code: [mesh-contracts seats/mesh-broker, mesh-sdk go/seatlist, mesh-catalog modules/nats, mesh-delivery modules/mesh-grants]
decisions:
- "ADR 0595"
- "ADR 0615"
- "ADR 0621"
- "ADR 0596"
---
# Design 25, milestone bindings-first-view — the grants part asks the bus module for every binding
**What it builds.** The grants part writes a module's binding (the names on the bus of its bus-state entries) into
its membership when it hears the bus module's `created`, rewrites it on `recreated` and removes it on `dropped`
(design 25 bus-state §4; ADR 0595 §1, §7). Until now those live events were the binding's only source, so an event
the grants part never heard lost the binding for good: on 2026-11-11 a registry holder's membership lacked three of
the entries its manifest asks for, although the bus held all three, and about 190 publishes since 2026-11-09 were
never said `published` (issue 1966). This milestone gives the grants part a first view of every binding from the
owner of that data, the seat `mesh-broker`, at every start and when asked again by hand, so a missed event is
recovered with nobody's hand. The live events stay the way a binding changes between starts.
**Where it comes from.** The operator, 2026-11-11 (about 03:35 CET, in the terminal), chose "Ask the bus module":
at every start, and on a re-ask, the grants part takes a first view from the bus module's seat of the entries it
holds and composes the bindings from it; the owner answers for its own data. Within that answer, these lines follow
ADR 0595 §1 and §7 (the binding is the bus module's to say and the grants part's to write), ADR 0596 §2 (another
module's data is asked through its seat, never read from its bus state) and the first-view rules of ADR 0615 §3 to
§6, which decide them for the indexer's and the assigner's state and which this milestone applies, as the design's
choice, to the bus module's. Each choice below says so.
**Cited as** "design 25 bindings-first-view §n". It amends design 25 bus-state §4, which says so.
## The lines
- §1 **The first view is the seat `mesh-broker`'s `list`, asked with no `module`.** No new verb: `list` already
answers every entry the bus module created, from its own state `asked`, by module and local name, and lacks only
the binding (§2); a second verb answering the same entries would be a second form of one fact. The grants part
declares `seat:mesh-broker.list` in its manifest's `invokes`, and composes its own grant of it as it composes any
invoker's. `list` names no class, so from milestone G of design 47 it is a `read` and takes that class's callers,
invokers, persons and sessions (design 47 G §391); until then its callers are "any caller whose role grants it",
which the grants part's invoke grant meets. The verb stays answered where the bus module's tools answer it today,
by its tools bundle in the tool runner of the machine holding the seat, and gains no runtime of its own: ADR 0621
§1 gives the indexer's and the assigner's first views a runtime of their own so that the tool runner never asks
through itself, and here the asker is the grants part, which composes every account without this answer (§5), so
no part waits on itself. Why: the design's choice under the operator's answer and ADR 0596 §2. How it is checked:
§8, the whole case.
- §2 **Each entry of `list`'s answer carries its binding.** The seat's `Held` gains the field `binding`, of the
seat's own form `Binding`, the same value the bus module's `created` and `recreated` carry for that entry in their
field `binding`, taken from the entry's plan in the bus module's state `asked`. **`Binding` is reshaped to the form
on the wire.** Today the seat's definition says `Binding` is a bus name, a storage type, a template and a stream's
subjects, while the bus module sends, and the grants part needs, mesh-sdk's membership binding: `local` (the local
name), `storage`, `template`, `stream` and `subject` (a stream's, and a ledger's history's subject), `bucket`,
`object-store`, and for a ledger `history`, `state`, `indexes` (each index's bucket by its name), `snapshot` and
`types` (which the bus module's events leave empty; the grants part copies them from `manifest-accepted`). The
definition's `Binding` takes exactly those fields, with those names and forms, so `created`, `recreated` and
`list`'s `binding` carry one form, the generated contract decodes what the bus module sends, and no byte changes
on the wire; the fields design 47 G §399 adds come on top of it. Why: the design's choice following this line's
own words (one value in the events and the answer), met by the developer on 2026-11-11 and directed by lead-47. It is present on every entry whose objects the bus holds (the
entries whose answer carries `settings`) and absent on one it does not hold (waiting to be made, or refused), as a
refused create hands no binding (design 25 bus-state §4). `show` and `orphans`, which answer `Held` too, carry it
alike. A field G's part adds to `Binding` (design 47 G §399: `objects` and `sent-objects`) reaches `list`'s answer
with it, since the form is one. What changes: the seat's definition in mesh-contracts (`seats/mesh-broker`), its
generated Go contract, and mesh-sdk's bootstrap copy of the definition in `go/seatlist`, which its test against
mesh-contracts holds equal; the bus module's tools fill the field. **The size**: 18 entries in the new form are
asked by the manifests on main (counted 2026-11-11), so the answer is a few KiB, an estimate; it stays one reply
well under the bus's largest message (1 MiB) while the entries number in the hundreds, and from G a reply over that
limit goes as an object by design 47 G §407. Why: the design's choice. How it is checked: unit (mesh-contracts' generated contract), a
`created` body as the bus module's keeper writes it, a ledger's among them, decodes into the generated type and
encodes back to the same bytes; unit (the bus module's
tools), a row where `list`'s `binding` for an entry equals the `binding` its `created` carried, and a row where an
entry not held has none; the seat's fixtures.
- §3 **It is asked at every start and when asked again by hand, never on a timer.** The grants part's process asks
`list` once at each start, as it asks its other first views (design 25 bus-state §5), and again when a person
calls `grants_first_view_again` with `state` `bindings`, or with no `state`, which asks every first view (§6).
Nothing else asks it: no timer, and no event, since the bus module's events carry the indexer's revision of the
verdict they acted on and no revision of the bus module's own, so a gap in them cannot be seen (ADR 0615 §5 asks
again on a gap only where one can). **At a start the answer comes first**: the grants part takes no event from its
durable consumer of `mesh-broker.created`, `recreated` and `dropped` until the answer is applied, or until the
bound of §5 passes. Since the shared stream keeps the events in order and each event sets or removes one entry's
whole binding, the events held then, taken after the answer, leave each binding as the last word on it said,
whichever came later. Why: the design's choice under ADR 0615 §4 to §6. How it is checked: §8.
- §4 **How the answer is applied.** For each entry of the answer that carries a binding, the grants part writes the
binding into the module's membership on every machine the module is assigned, kept in its state `memberships` as
on `created`, where it holds none, and rewrites it, as on `recreated`, where it holds a different one; where it
holds the same, it writes nothing. Each binding written or rewritten is said in its log, naming the module, the
local name and the first view as the cause, and recomposes that module's memberships as an event's would. An entry
listed without a binding (§2) changes nothing the grants part holds for it. **A binding the bus no longer
lists is kept, never removed by a first view**: the grants part removes a binding only on `dropped` (ADR 0595 §1,
§7), so a bus module whose own state `asked` was lost or rebuilt, and answers few entries or none, strips no
module of its data. Instead it raises the condition `mesh-grants.binding-unlisted`, one per such binding, naming
the module and the local name and the operator's step (read the entry with the seat's `show`; an entry its
manifest no longer asks for leaves by the bus module's `drop`, whose `dropped` removes the binding); a later answer
that lists the entry, or a `dropped` for it, clears it. **On a re-ask the events keep flowing**: an answer is
applied only to entries no `created`, `recreated` or `dropped` named since that ask was sent, so an answer never
writes over a newer event. Why: the design's choice under ADR 0595 §7, and ADR 0615 §6's rule that a reader keeps
what it holds rather than taking an empty answer as the truth. How it is checked: §8.
- §5 **While the bus module does not answer, the grants part composes the rest and says so.** It composes every
account and keeps every binding it holds in `memberships`, as it does today; only a binding it never heard waits.
Past two minutes without an answer, the bound of its other first views (design 25 bus-state §5), it raises its
condition `mesh-grants.first-view-unanswered` naming the seat `mesh-broker` and the verb `list`, as that condition
names the owner and the tool of its other first views, and it then takes the events from its durable consumer as
they come, so the live path never waits on the first view. It keeps asking, from 10 seconds, doubling, at most
every 5 minutes, the backoff the bus module keeps for its own first view (design 25 bus-state §1); the answer
clears the condition. This form holds until milestone G of design 47 is delivered; from then the grants part, one
of G's observers (design 47 G §387), writes `first-view-unanswered` and `binding-unlisted` (§4) as findings in its
state `findings`, said as `finding-written` and `finding-cleared` on its finding stream (design 47 G §383) and
stated at its start (§384), naming the same things. Why: the design's choice under ADR 0615 §6. How it is checked:
§8; from G, G's own checks of §383 and §387.
- §6 **The tools that read and steer it.** `grants_first_views` shows the first view `bindings` beside the others:
the seat and verb (`mesh-broker.list`), when it was asked and answered, how many entries the answer listed, how
many bindings it wrote and rewrote, the bindings it found unlisted, how many asks and hand acts, and whether its
condition is raised, kept in the grants part's own state `user-list` under the key `first-views` as the others are.
`grants_first_view_again` takes `bindings` among its `state` values, by a person's act with why, recorded as a hand
act; its description names it. From G these two tools are served by mesh-sdk's `go/views` as every part's are
(design 47 G §245), with `bindings` among the first views they show. Why: tools-are-how-a-module-is-spoken-to. How
it is checked: unit (grants part's tools), `bindings` shown and asked again with its hand act recorded.
- §7 **What exists today, and its fate.** The live-event path stays whole (design 25 bus-state §4). The bus module's
`list`, `show` and `orphans` keep every field they answer and gain `binding`. The grants part reads no other
module's bus state and gains no grant on the shared stream `EVENTS` beyond its durable consumer of the three events:
a missed event is recovered by asking its owner, never by reading the stream back by hand. The order between the
three repositories needs no edge of the module graph: a grants part that meets an answer without `binding` (a bus
module's build from before this milestone) writes nothing from it, says so once in its log naming the seat and
the verb, and keeps working on the events, and the edges between the grants part and the bus module lie on the
foundation's cycle, where ADR 0615 §2 has the owner's answer and the reader's wait carry the order. One delivery
carries mesh-contracts, mesh-sdk, the nats module (mesh-catalog) and the grants part (mesh-delivery).
- §8 **How it is checked.** **Whole**: a case in the grants part's existing whole-module integration test (mesh-delivery
is over its integration budget, so no new test function): a fixture bus module answers `mesh-broker.list` with an
entry whose `created` was never said, and after the grants part starts, the module's membership holds that
entry's binding; a binding the grants part holds that the answer does not list stays and raises
`mesh-grants.binding-unlisted`; with `list` unanswered, the part composes every account, raises
`mesh-grants.first-view-unanswered` naming `mesh-broker` and `list` past its bound, and applies a `created` heard
meanwhile; an answer that comes after a `dropped` heard since the ask does not write the dropped binding back.
**Unit**: the bus module's tools (§2), the grants part's tools (§6), and a table of the grants part's reconcile
with one row per case of §4. **Live**: after the delivery, at the grants part's first start, the registry holder's
membership names every entry its manifest asks for (`grants_membership`), and `grants_first_views` shows
`bindings` answered with no binding unlisted.
@@ -0,0 +1,399 @@
---
layer: to-be
design: 25
milestone: bus-state
updated: 2026-11-11
code: [mesh-sdk, mesh-contracts, mesh-delivery, mesh-tools, mesh-catalog modules/nats]
decisions:
- "ADR 0595"
- "ADR 0596"
- "ADR 0597"
- "ADR 0259"
- "ADR 0363"
- "ADR 0386"
- "ADR 0387"
- "ADR 0436"
- "ADR 0564"
- "ADR 0578"
- "ADR 0603"
- "ADR 0612"
- "ADR 0613"
- "ADR 0614"
- "ADR 0615"
- "ADR 0619"
- "ADR 0620"
- "ADR 0621"
- "ADR 0622"
- "ADR 0625"
- "ADR 0626"
- "ADR 0627"
- "ADR 0631"
---
# Design 25, milestone bus-state — what a module keeps on the bus, created by the bus module
**What it builds.** The lines of [design 25](../25-the-bus-on-nats.md) § 12 that are built under issue 1966: who
creates a module's bus state and how the module receives its binding (§ 12.2), the seat `mesh-broker`'s verbs,
events and fill (§ 12.3), the ledger (§ 12.4), the calls (§ 12.5) and the order of the build and the move (§ 12.6).
The three layers and the templates are the design's main file (§ 12.1), and so is how each rule is checked
(§ 12.7). The sections keep their numbers, so "design 25 § 12.2" still names them. *Moved here from the
main file on 2026-11-11, when the main file was put in the shape of playbook 02, and numbered; their words are
unchanged but for the note on bus-state §4 that design 25 bindings-first-view amends it.*
**How a line is cited.** Each statement is cited as "design 25 bus-state §n". A bare "§n" inside a statement is a
clause of the record that statement cites, as it was before the move; "§ 12.n" is a section of this design.
### 12.2 Who creates it, and how a module receives it
- §1 **The trigger is the manifest validator's verdict** (ADR 0595 §4), heard as the indexer's event `manifest-accepted`, which
carries the module's entries whole, so the bus module never reads the indexer's state and never polls. The indexer
says it for every newly accepted manifest, with its entries empty when it asks for none, and the bus module reads an
empty one as "this module asks nothing now", which is what its orphans need. A module whose manifest leaves the
trunk (its directory deleted) gets an empty `manifest-accepted` too, so its entries reach `orphans` and raise
`orphaned`; a manifest the manifest validator set aside to wait gets none, so its entries stay asked. The indexer's tool
`indexer_say_accepted` says `manifest-accepted` again for one module by hand, from the verdict it holds, recorded as a
hand act, for a verdict the bus module is thought to have missed. It goes, as every event of the
indexer, on the module events stream (ADR 0363) under the indexer's own subjects. A publish of it that fails is
published again, and one still failing writes the indexer's finding naming the module, cleared once said (today
the indexer only logs it). It hears the verdicts on a durable consumer, so one given while it was down is heard
when it is back. **Its first view** at start is the indexer's
tool `indexer_bus_state` (every accepted manifest's bus-state entries, by module and digest), asked once; the
events keep it current after that, and a stream that keeps a verdict only for its age is never its source. A
verdict replayed from its consumer is applied only if newer than what the first view answered for that module, by
the indexer's revision alone (a digest has no order), so a replay never takes an entry back. An `indexer_bus_state`
that does not answer makes the bus module's health unhealthy, naming the verb, and is asked again on a bounded
backoff (from 10 seconds, doubling, at most every 5 minutes); nothing is created or orphaned meanwhile. Beside the
backoff, the bus module's tool `nats_first_view_again` asks `indexer_bus_state` at once by hand, recorded as a hand
act. It keeps
what it last heard per module in its own state `asked` (an old `state` section, its own kept data staying there,
ADR 0595 §10), from which `list`, `check` and `orphans` answer; a key goes only on an empty `manifest-accepted` for
the module, never because a manifest the manifest validator set aside to wait is missing from the first view. Then it runs `check` over what it holds, creating what
is missing. The manifest validator checks every entry once, at the merge check and at take-in (ADR 0578; ADR 0595 §8), and also
each ledger type's operations: a set-entry or a remove names `into` and `key`, `fields` and an index's `on` name
fields of the schema, and `digest-of` names a text field.
- §2 **A module that asks bus state waits for the bus module** (ADR 0614 §5). Each bus-state entry of a manifest is a
requirement on the provision `bus-state`, which the bus module's manifest provides; the module graph reads it as any
requirement, so the assigner holds a consumer's new build until a build of the bus module that offers `bus-state`
runs healthy (ADR 0603 §5). That each entry exists before its user starts is the bus module's own work, above, not
an edge.
- §3 **The bus module has a process of its own** beside its tool bundle, with a declared health check. Its credential
creates streams and buckets under modules' names only, and deletes only through `drop` and `recreate`. Its health
turns unhealthy, naming the cause, when an accepted verdict has waited on its durable consumer, unanswered, for
more than three minutes, or when its last pass over fill, which runs every minute, is more than three minutes old, so the only guard against a store filling never stops without a sound; another module's entry past the raise mark
raises that module's condition and leaves the bus module healthy. **Its process has a process account of its own,
granted more than any other process's** (ADR 0626 §1; ADR 0436 §1, §2). The process declares `bus` as any process
may, with one key more, `keeps`, which is allowed on this process alone; the grants part composes the account from
that declaration as it composes every process account, and the vault mints it as the module's own secret. Beyond
what a process's `bus` grants, `keeps` grants the bus's create and update subjects for streams, buckets and object
stores under modules' names only (`<module>_*`), the snapshot request for an entry before `recreate` or `drop` with
its chunks' acknowledgement subjects and its own inbox, and the delete subjects for those two acts. It is granted
no subject on which a tool or a seat's verb is answered (ADR 0436 §3), and no subscription to any restricted
stream's own subjects: the snapshot is the one way a restricted stream's data reaches it, kept only in the bus's
snapshot store, and the process reads no message of it. No other account holds those subjects, the tool runner's
included. **The snapshot account stays as it is** (ADR 0626 §2, § 3 above). **The manifest validator refuses any other bus
declaration of the bus module** (ADR 0626 §3): anything it would say or hear on the bus beyond its process's `bus`
and the bus state the foundation keeps on the old sections (ADR 0595 §10), `keeps` on any other process or module,
and a `bus` of its process that names a subject on which a tool or a verb is answered.
- §4 **The binding travels on the bus module's events** (ADR 0595 §1, §7, as their progressive insight of 2026-11-03
has it, waiting for the operator's Approve). The grants part, which issues every membership, hears them on a durable
consumer: it writes the **binding** (the names on the bus) into the module's membership on `created`, on every
machine the module is assigned, rewrites it on `recreated` and removes it on `dropped`, kept in its own state
`memberships`, so no part writes another's state and a binding outlives the 7 days its event is kept on the shared stream (§ 12.3).
*Amended 2026-11-11 by design 25 bindings-first-view §1 to §5*: the events are no longer the binding's only
source; at every start, and when asked again by hand, the grants part also takes a first view of every binding
from the seat `mesh-broker`'s `list`, so a missed event loses no binding for good. A module whose binding is missing at start fails loudly, naming the entry; it never starts blind. A `refused`
create hands no binding. **A refused or overdue creation is loud**: overdue means no `created` or `refused` within
five minutes of an accepted verdict that asks for the entry. The condition is raised by the watchman, which today raises the manifest validator's
refusals as findings and gains, through its `consumes`, the accepted verdicts and the seat `mesh-broker`'s `created`
and `refused`; never by the bus module, which cannot raise it while it is down; it names the module
and the entry; a later `created` clears it, and so does a later accepted verdict of that module that no longer asks
for the entry.
- §5 **A foundation part's first view of another's state is a tool its owner answers in a runtime of its own** (ADR 0615,
ADR 0621). The four
foundation parts that read each other (the grants part, the tool runner, the indexer and the assigner) stop
reading the indexer's and the assigner's bus state in the milestone of ADR 0612, with the other 19 readers of that
state (ADR 0615 §1). **Each owner serves them as tools of its own module**, the indexer `manifests`,
`modules`, `verdicts` (ADR 0622 §1) and `graph` (ADR 0631 §1), the assigner `placement`, `seat-holders` and `wants`, and declares `runs-as`, so its tools bundle runs in
a runtime of its own on the module's own bus account, never in the node's tool runner, and the tool runner never
asks through itself (ADR 0621 §1). No new manifest form is added: a process still answers no tool call or seat verb
(ADR 0436 §3). **Each verb is granted to exactly its readers** (ADR 0621 §2, as ADR 0615 §3 lists them): `placement`
to the grants part, the tool runner and the indexer; `seat-holders` to the tool runner; `wants` to the grants part;
`manifests` and `modules` to the grants part, the tool runner and the assigner; `verdicts` to the grants part
alone, its `callers` list holding the grants part only (ADR 0622 §1); `graph` to the assigner alone, its `callers`
list holding the assigner only, the indexer itself refused, and the assigner's `reads` entry of the indexer's
`graph` goes (ADR 0631 §1). These seven are the first-view tools, four of the indexer and three of the assigner.
`verdicts` follows the first-view rules whole (ADR 0622 §2, under ADR 0615 §4 to §6): its answer carries the
revision, the grants part then follows `manifest-accepted` and `manifest-refused` and asks once more on a gap in
their revisions, it raises the condition naming the indexer and `verdicts` past the bound and never reads the
indexer's state, and `grants_first_views` and `grants_first_view_again` cover it. `graph` follows them whole too
(ADR 0631 §2, under ADR 0615 §4 to §6): its answer carries the revision, the assigner then follows the indexer's
`indexer.views` events for the bucket `graph`, which carry the revision, and asks once more on a gap, said in its
log; started while the indexer is down, it raises the condition naming the indexer and `graph` past the bound and
never reads the indexer's state; and `assigner_first_views` and `assigner_first_view_again` cover it. A reader declares each in `invokes`
as `<module>.<tool>` only where it is granted, which is the grant the grants part composes as for any tool, and
each tool checks the proved caller against a `callers` list holding exactly its readers and refuses any other, the
owner itself included. **The manifest validator of the milestone is its own group's** (ADR 0621 §4): since no field is added,
the running manifest validator takes the indexer's and the assigner's manifests in as it does today, and the merge check judges
them with the manifest validator built from the group's own head; no release of the manifest validator goes ahead of the milestone. **The
owner's runtime answers before any first view of its own** (ADR 0621 §3): at once from its module's own state, as
soon as it is connected and before it has asked any other part anything, with the revision of its state it answered at,
so the indexer and the assigner, each asking the other at start, never wait on each other (§4). The reader then
follows the owner's declared events, each carrying the revision of the state it follows, and never asks the verb
again on a timer; a gap in revisions makes it ask once more, said in its log (§5). The owners' events are new
families of their own, apart from the indexer's `manifest-refused` and `manifest-accepted` and the assigner's
`put`: the indexer's `indexer.views` and the assigner's `assigner.views`, one event per changed key with its
bucket, key and revision and the key's value whole, or a delete mark for a key removed, so a reader keeps its
picture from the events alone and asks the verb only on a gap (ADR 0615 §5). **Both families are declared in
their owners' old `streams` sections**, each an entry with `max-age-days` 7 and `max-mib` 256: the old section keeps
a stream by limits, which drops its oldest when full (only a work queue refuses the new instead), so a reader that
misses a dropped event sees the gap in revisions and asks once more. The indexer's own kept data stays on the old
sections (ADR 0595 §10 names it among the foundation); the assigner's goes beside its existing `streams` entry
`placement`, under ADR 0619, which extends ADR 0595 §10's foundation to the parts
the bus module's own composition, placement, build or delivery needs before it can answer: the assigner, the tool
runner, the builder, the checker, mesh-config and the vault. **The size, an estimate**: read on the bus on
2026-11-04, the buckets the readers read hold about 3 MiB of current values at the indexer (its manifests about
5 KiB a value) and about 1.5 MiB at the assigner (its wants about 0.25 KiB a value). The milestone's one mesh-sdk
rebuild rewrites every module's keys, up to three times in its window (design 51 *How many builds*), so a week of
it is some 20 MiB on `indexer.views` and some 10 MiB on `assigner.views` with ordinary merges added, each well
under its 256 MiB. Each event carries one value whole, the largest today a manifest of tens of KiB, under the bus's
largest message, 1 MiB (the bus server's `max_payload`); an owner whose value with its event would pass it emits
the key's revision alone, as a gap, so the reader asks the first view once, and the owner writes its finding
naming the key.
A waiter on an existing event, `manifest-accepted` or the
assigner's `put`, never wakes on them. **When an owner's revisions go back**: should an owner's state be rebuilt
by the bus module's `recreate`, its revisions start again; a reader that hears a revision lower than the one it
holds takes it as a gap and asks the first view again, and keeps what that answer says, whatever revision it held
before. A `recreate` that could not keep the data answers empty at revision 0, so the grants part keeps its list
in force (below): a login revoked meanwhile stays until the owner's first change, and the grants part's
first-view condition stays raised meanwhile, naming the owner, the empty answer and the operator's step: let the
owner write its state again from its sources (the indexer by taking each module in again, `indexer_take_in`; the
assigner by its next composition of the wants), then ask the grants part's first view again,
`grants_first_view_again`. It is cleared by an answer above revision 0. A reader that starts while its owner is down raises a
condition naming the owner and the verb past its bound and keeps asking until it answers, never reading the
owner's state instead; once it had its first view, it keeps working on what it heard (§6). The bound is longer
than an owner's start (two minutes), so a normal start, readers and owners coming up together, notifies nobody;
the owner's answer clears the condition. An answer that is empty at revision 0 (an owner that holds nothing yet)
is a first view of nothing, never a reason for the grants part to drop a login it holds: it keeps the list in
force until an answer at a revision above 0. Each reader's tools say the owner, verb and revision its first view
holds, when it asked and when it was answered (a read tool), and ask the first view again by hand (a re-read tool,
recorded as a hand act): the grants part's `grants_first_views` and `grants_first_view_again`, the tool runner's
`mesh_first_views` and `mesh_first_view_again`, the indexer's `indexer_first_views` and
`indexer_first_view_again`, and the assigner's `assigner_first_views` and `assigner_first_view_again`. The tool
runner's two are tools of the mesh MCP server, beside `mesh_runtimes`, since the tool runner serves its own state
there: the mesh MCP server gains them, and the operator sees them among its tools. **On a fresh mesh** the
assigner starts before any new-form bus state exists and needs no binding of the bus module first: its own state
and `assigner.views` are on its old sections, which the grants part creates, so it answers the indexer, the grants
part and the tool runner their first views while the bus module still waits on it. The order of owner before reader is kept by the module graph's served-verb edge for the 19 readers (ADR 0614 §2), and,
for the four, whose verb edges lie on cycles the graph leaves out (ADR 0614 §3), by the owner's answer at once and
the reader's wait (ADR 0615 §2).
- §6 **The other 19 readers ask the owners' read tools, one per bucket they read** (ADR 0615 §1, §2). Beside the seven
first-view tools above, each owner serves a read tool for every bucket of its state one of the 19 reads today,
named `indexer_read_<bucket>` or `assigner_read_<bucket>` and marked as a read: fifteen. The indexer's nine:
`indexer_read_manifests`, `indexer_read_modules`, `indexer_read_predefined`, `indexer_read_config_candidates`,
`indexer_read_candidates`, `indexer_read_packages`, `indexer_read_graph`, `indexer_read_heard` and
`indexer_read_taken`. The assigner's six: `assigner_read_placement`, `assigner_read_seat_holders`,
`assigner_read_wants`, `assigner_read_findings`, `assigner_read_asks` and `assigner_read_differences`. These are
the nine buckets of the indexer and the six of the assigner the 19 read (§ 12.6), `modules`, `wants` and
`placement` among them for the status view; the indexer's tenth, `verdicts`, is read by the grants part alone,
through its first-view tool. **They are ordinary module tools**: granted, as any tool is, to every
reader that declares it in `invokes`, and callable by a session; the `callers` list of ADR 0621 §2, ADR 0622 §1 and ADR 0631 §1 covers
only the seven first-view tools. **The status view keeps all its tools**, their names, arguments and answers
unchanged, its `indexer_*` and `assigner_*` tools included: as a reader it answers them by asking the owners' read
tools instead of reading their buckets. Checked live after the delivery: a session's call of each status view tool
answers, and `mesh_search` finds each owner's read tool by its bucket's words.
- §7 **Only the owner reaches it** (ADR 0596 §2). The membership grants the owner's account its own entries and no
other module's: the new form has no readers. What another module needs of that data it asks through the owner's
tools or seat verbs, and what the owner lets others hear is its declared events, subscribed to through
`consumes` (ADR 0363, unchanged). A part watches only its own state, and hears another's events (ADR 0596 §4).
### 12.3 The seat's verbs, events and fill
- §8 **The seat gains eight verbs.** The seat `mesh-broker`, until now a seat with no verbs, gains these (ADR 0595 §5, §6). It is one of the ten seats
defined whole in the milestone of ADR 0612, its contract a package of its own in `mesh-contracts` (ADR 0613 §1,
ADR 0627 §1, ADR 0625 §1; design 26 *A seat's definition*), which settles where its definition lives (ADR 0595 §12). An entry's arguments are the module, its local
name, its storage type, its template, its options and its configuration; what is held answers the settings held and
the fill (count, bytes, the cap); `drop` and `recreate` carry the warrant and why.
| Verb | Does | Who may call it |
|---|---|---|
| `list` | every entry, its template, its settings, its fill against its cap | any caller whose role grants it (ADR 0386) |
| `show` | one entry, the same | the same |
| `check` | compares what the bus holds with what was asked, entry by entry | the same |
| `orphans` | entries the bus holds that no manifest asks for; never one whose manifest the manifest validator refused or outgrew, nor one of the foundation's old sections | the same |
| `create` | creates one entry now, for an entry an accepted manifest asks for | an act of the bus module's process alone, answered on no subject |
| `apply` | applies a difference that only grows: a larger cap, a longer age | the same |
| `recreate` | rebuilds an entry whose settings differ in a way that cannot grow, from its data where the storage type allows | the same, on the operator's warrant, which the process verifies itself |
| `drop` | removes one orphan | the same, on the operator's warrant, which the process verifies itself |
- §9 **Four of them are acts of the process alone.** `create`, `apply`, `recreate` and `drop` stay in the seat's definition as acts its holder's process alone performs,
answered on no subject (ADR 0626 §4, ADR 0436 §3): they are not verbs whose subjects a credential is granted, but
acts on the bus's own create, update and delete subjects, which only the process's account holds, so no module can
ask the bus for them. A person asks for them through the bus module's tools, which hand the request to the process
through its own state (below); `recreate` and `drop` act only on the operator's warrant (ADR 0259 §6), asked
through the operator channel. Before `recreate` or `drop` acts, the process takes a stream snapshot of the entry
(ADR 0595 §5), and it acts only once the snapshot is whole. A drifted entry is said, never quietly fixed: only a
difference that grows is applied without a person.
- §10 **Events**: `created`, `refused` (with why), `drifted` and `drift-cleared`, `full` and `full-cleared`, `orphaned`
(cleared by a `drop`, or by a manifest asking for the entry again, which emits `created` for it), `recreated` and
`dropped` (ADR 0595 §6, as its progressive insight of 2026-11-03 has it, waiting for the operator's Approve);
each carries the module, the local name, the bus name, the template, and the settings asked against those held.
**They follow every seat's events**: each is said on `mesh.seat.mesh-broker.event.<event>`, on the shared stream
`EVENTS` (§ 3), kept 7 days, and heard through `consumes` `mesh-broker.<event>` like any seat's event. **The bus
module keeps their history apart**, because a drift or an orphan is read back days later by a person: a stream of
its own in its old `streams` section, on its module subjects, kept 30 days (ADR 0363's bound), created by the grants
part as today, with a cap of 256 MiB, to which the process appends every event it says. Its own kept data stays on
the old sections (ADR 0595 §10), because it cannot wait for its own process to create it. That stream drops its
oldest when full, which loses no live state, because every drift, orphan and fill stands as a condition until it
clears; the conditions hold the live state. The tool `history` of one entry reads it back: every event the bus
module said of the entry, in order. This settles where they are kept, which ADR 0595 §12 left to the design, and
needs no new manifest form: a module's stream never takes a seat's subjects, and every seat keeps one form for its
events.
- §11 **Fill is the bus module's one periodic read** (ADR 0595 §6): every minute, for every entry that refuses when full,
because the bus says nothing as a store fills. The condition `full` is raised at a raise mark and cleared below a
lower clear mark, so an entry sitting at the mark does not flap; the marks are settings of the bus module, by default
a raise at 75 percent of the cap and a clear below 70 (ADR 0595 §6, as its progressive insight of 2026-11-03 has it).
The grants part's `mesh-grants.history-full` moves to it: the new condition is raised first, then the old key cleared;
no manifest on main asks for a history stream or objects today, so the move loses nothing. A bare stream that refuses
when full is read the same way. **The conditions**, each naming the module and the entry: `full` (cleared below the
clear mark), `drifted` (cleared by `drift-cleared`), `orphaned` (cleared by a
`drop` or by a manifest asking for it again) and a refused create (cleared by a later `created`).
- §12 **Its tools** (tools-are-how-a-module-is-spoken-to): the four reading verbs, marked reads, answer every entry the
bus module created and its fill, and `history` its events for one entry; `create` and `apply`, marked acts, and
`recreate` and `drop`, marked dangerous and so granted only by name (ADR 0386 §6, ADR 0387), are its tools for a
person. **A person's request reaches the process through the process's own state** (ADR 0626 §4), because a process
account answers no tool call (ADR 0436 §3). Such a tool, in the bus module's bundle, holds no grant of the bus's
create, update or delete subjects, and neither does the tool runner's account: it writes the request into the bus
module's state `requests` (an old `state` section, as `asked` is, ADR 0595 §10), stamped with the caller the tool
runner proved and recorded (ADR 0386 §8), and watches that key for the process's answer, never asking again on a
timer. The process watches its own state `requests` (ADR 0596 §4) and takes a request up when it is written. The stamp names
who asked, for the record; it is no boundary, because every session on a machine reaches the bus as the tool
runner's account (ADR 0436 §9). **So the process verifies the operator's warrant itself**: for `recreate` and `drop`
it trusts nothing in the request, reads the operator channel's record of the ask under its own account and checks
that the warrant binds exactly this act on this entry (ADR 0259 §6), as the mail sender does (ADR 0436 §9); a request
without a valid warrant it refuses, deleting nothing and writing why into its answer; with one, it takes the entry's
stream snapshot first. **The process wakes on the warrant, never on a timer**: a warrant is said on the operator
channel's `decided` for the asker that raised the ask (ADR 0259 §6; ADR 0620 §2), here the bus module itself, which
hears its own `decided` on a durable consumer of the process, whose filter the grants part derives from the
process's `uses` of the operator channel seat (not from a `consumes`, which a process has none of), and runs its pass over the waiting `recreate` and
`drop` requests when one arrives, so a warranted `drop` acts within seconds of the operator's answer, and one given
while the process was down is heard when it is back. **A warrant acts once** (ADR 0259 §6): before it acts, the
process claims the warrant's ask id by writing the key `acted.<ask id>` in the state `spent` of the process's own bus state, which the tools bundle is not given (a person's request and the
record of what it spent are never in one bucket, and no tool can write the record), created by compare-and-set after the snapshot and before any deletion, and a request naming an ask it already
acted on is refused, deleting nothing and saying which act that warrant already performed. **An ask lasts 12 hours**,
shorter than the 24 hours `requests` keeps a request: a request whose ask ended unanswered is ended and said, and a
warrant is not acted on once its ask ended; the tool raises the ask before it hands the request over. A request
that cannot act yet says why in its field Waiting, and a hand act the process could not record is written on the
request in its field Unrecorded. The tool `nats_requests` reads the waiting and ended requests, and the warrants
spent from `spent`, which is declared `read-only: true` (design 32 § 4) so that the tool reads it and holds no publish on it, and drops a waiting request by hand, recorded as a hand act. `create` and `apply` need no warrant, since they only make or grow what an accepted manifest
asks for, and the process refuses either for an entry no accepted manifest asks for. A tool acts only for a caller
whose role grants it; the process records each act as a hand act (`mesh-hand-acts`) and answers what it did. What
stands between the tool runner's account and the process's delete power is that check of the warrant, so its tests
are this section's central check (§ 12.7, in the main file). **The mesh deletes kept data only through `drop` and `recreate`** (ADR 0595 §4, as its
progressive insight of 2026-11-03 has it): the bus module's older remove tools (its stream, bucket and consumer
removals, ADR 0564) refuse an entry of the new form, naming `drop`'s tool, and keep working for the old sections. The bus seat's `apply` (a difference that grows) and the
ledger's `apply` (§ 12.4) are two verbs on two subjects, granted apart; this design writes them qualified. Its health
names the process's last pass, the entries it could not create, and the entries past the raise mark.
### 12.4 The ledger
- §13 **The ledger is a storage type of its own.** A **ledger** is a storage type whose state is derived from its history (ADR 0597). It is built on a stream and
key-value buckets, and asked for with the template ledger.
```
module ──apply(instance, operation)──▶ tool runner on its own machine
│ 1 checks the operation against the type
│ 2 reads the history's last sequence for the instance; replays if the state is behind
│ 3 appends to the history, that sequence expected ──▶ history (stream, a subject per instance)
│ 4 derives the state by the operation's effect ──▶ state (key-value, updated at the revision read)
│ 5 writes the instance's snapshot ──▶ snapshot subject (latest only, never expires)
▼
answers: taken, with its sequence (and "state behind" when the state bucket is full)
module's process: a watcher keeps each declared index (a second key-value bucket) from the history
```
- §14 **A type** is a schema and the operations allowed on it; an **instance** of a type is named by a path
(`listing/a17`, `decision/0290`); every change is an **operation**, kept in the instance's history.
- §15 **A ledger effect** is what an operation does to the state, one of a fixed set: set fields, set an entry of a
map, append to a list, remove (ADR 0597 §2). An effect copies only the fields its type names, and may add the
operation's sequence and author. Where a type pairs a text field with its digest, `apply` refuses an operation
whose digest does not match. A module's own rules are checked by the module before it calls `apply`, never by the
effects. Only the owning module calls `apply` on its ledger, for every effect, remove among them; a person
removes through the owner's tools. This settles who may call the remove effect, which ADR 0597 §13 left to the
design.
- §16 **One writer wins, and the state never goes back** (§5): the expected sequence makes the bus refuse a racing
writer; the state is written as an update of the revision read, never a plain put; when that update loses to
another writer, `apply` answers success with its operation's sequence and re-reads, never appending again.
- §17 **A full state bucket loses no operation** (§6): `apply` answers that the operation is taken, with its sequence,
and that the state is behind because the bucket is full; the next `apply` after room is made catches it up.
- §18 **The author is proven** (§7): `apply` stamps the owning module's authenticated account and the account that
called the owner's tool. The owner passes that author on by naming the tool call it serves: the tool runner, which
served that call and proved its caller, stamps the caller it proved, so the module cannot set the author to words of
its own; an `apply` naming no call in flight carries the owner's account alone, and one naming another module's
call is refused. A copy of old records (design 55 § 8.1) is stamped with the owner's account and keeps the old
author as a separate field marked imported, never as the proven author.
- §19 **Every instance has a snapshot** (§8): the latest state on a subject of its own, never expiring, written by every
`apply`. It makes a kept ledger's rebuild fast and an expiring ledger's possible; a repair that finds it behind
the state rewrites it.
- §20 **Only the tool runner's ledger credential publishes on a ledger's history** (§9): one per module assignment, that
is per node the module runs on, the account `ledger.<node>.<module>` in the tool runner's own secret `ledgers` on
that node (ADR 0597 §9, as its progressive insight of 2026-11-03 has it, waiting for the operator's Approve), held by
the tool runner and never by the module's own code, whose account may read its ledger and not publish on it. A
person's `rotate` of that secret is refused by the vault, as a bus account's is. The grants part issues it with the
module's membership on that machine, scoped to the subjects of that module's ledger
histories and snapshots, and removes it when the module is unassigned from the machine. Only the
owner's account reads the history, the snapshot and the state.
- §21 **An index** is a second key-value bucket kept by a watcher in the owning module's process, rebuildable from the
history; it raises a condition when it lags behind (§4).
### 12.5 The calls, and where they live
- §22 **The new calls live in a package of their own.** The new calls live in a small mesh-sdk package of their own, served by the tool runner and imported only by the
modules that use them; the module library, which 112 modules import, does not change (ADR 0595 §9). They are the
object store's (put, get, list, delete, info, watch); a stream's read-back and fill; a work queue's size and dead
letters; and the ledger's: `apply`, `get`, `history`, `watch`, `query`, and its repairs `rebuild` (the state from
the history or the snapshot), `reindex`, `snapshot` and `lag` (ADR 0597 §10). The tool runner serves the repairs as
tools of every module that owns a ledger, limited to its own ledgers, so no owner declares them and the next ledger's
owner has them on its first day. mesh-sdk's catalog and membership
packages change once for the typed `requires` entry and the binding, rebuilding about 30 modules.
### 12.6 The move, and the order of the build
- §23 **The build, each step undoable on its own** (ADR 0595 §10): (1) mesh-sdk's typed `requires`, binding and calls
package; (2) the manifest validator's rules; (3) the bus module's process, verbs and events, creating only for the new form, with the grants part's writing of
the binding on `created`; (4) the tool runner's calls, the ledger's `apply` and its ledger credential among them; (5)
modules move, each owner first. Each undo: undoing step 2 refuses the new form again, which no module uses before
step 5; undoing step 3 leaves what it created in place, and a module moved after it fails loudly on its missing
binding; undoing step 4 leaves a moved module without its calls, which the placement holds off that machine. The
placement holds a moved module off a machine whose tool runner does not serve the typed binding and the calls the
module asks for (a ledger's `apply` among them), and says so; where the placement reads what a tool runner serves is
left to the build of step 4 and tracked on issue 1966.
- §24 **A module moves when it is next touched** (ADR 0595 §11), keeping its local names and carrying its old settings
as options, so its bus objects and data stay and no drift is raised. It asks for each entry once: an old section
and the new form are never both used for one local name. During the move the grants part keeps creating the old
sections' entries, so each entry has one creator. The indexer counts the modules on each old section, a tool reads
the count, and the manifest validator refuses a section once its count is 0. The foundation's own kept data (the bus module's,
the grants part's and the indexer's) stays on the old sections for good and is not counted: those modules cannot
wait for a process that needs them first. ADR 0619, extending ADR 0595 §10's foundation, adds the parts the bus module's own composition, placement, build or delivery needs before it can answer:
the assigner, the tool runner, the builder, the checker, mesh-config and the vault; their new state and streams of
this milestone go on their old sections too (§ 12.2; design 51 *The move*; design 32).
- §25 **Readers move after their owners** (ADR 0596 §3). Today 195 `reads` entries in 37 modules read another module's
bus state (counted 2026-11-03 02:00). **A correction of fact**: the indexer's and the assigner's state, counted again
from every manifest on main later that day, is read by 23 modules, 10 buckets of the indexer (`verdicts`, read by the grants part, among them) and 6 of the assigner,
not by the 19 the first plan counted; and 151 state entries were counted, not 158 (the template table of § 12.1 keeps
the first count, which chose the templates). The owners first gain their events, first-view tools and read tools: the
indexer a read tool per bucket its readers read, the assigner the same (§ 12.2 lists them). Then all 23 readers move in the milestone of ADR 0612, the
four foundation readers included, by the first view of § 12.2 (ADR 0615 §1), the grants part's of the indexer's
`verdicts` by its first-view tool `verdicts` (ADR 0622) and then `manifest-accepted` and `manifest-refused`, and the
assigner's of the indexer's `graph` by its first-view tool `graph` (ADR 0631) and then `indexer.views`; their order is the module graph's, not
a review reading an owner delivered (ADR 0615 §2). The status view's move keeps every tool it serves, each answered by
asking the owners' read tools of § 12.2. **The node-engine's reads granted in code stay**: the grants part's code
grants each node's node-engine, with no manifest declaring them (`CandidateReads` and the composed reads beside it),
the indexer's `candidates` and `verdicts` and its own node's keys of the assigner's `wants`. They are not among the
23 readers of ADR 0615 §1 and move in the next milestone, queued on issue 1966. The manifest validator still refuses a moved module's subscription to an event
its owner does not declare. Every other reader moves when its module is next touched. These moves are tracked by
issue 1966.
+40 -353
View File
@@ -10,7 +10,8 @@ code:
- mesh-tools node-tools/internal/bus (a module's state, ADR 0201; the new calls and apply, ADR 0595, ADR 0597) - mesh-tools node-tools/internal/bus (a module's state, ADR 0201; the new calls and apply, ADR 0595, ADR 0597)
- mesh-sdk (the typed bus-state entry, its binding and the new calls' package, ADR 0595) - mesh-sdk (the typed bus-state entry, its binding and the new calls' package, ADR 0595)
- mesh-delivery (the manifest validator's rules and the grants part's binding, ADR 0595) - mesh-delivery (the manifest validator's rules and the grants part's binding, ADR 0595)
updated: 2026-11-04 - mesh-contracts seats/mesh-broker (the seat's definition: `list` answers each entry's binding)
updated: 2026-11-11
decisions: decisions:
- "ADR 0595" - "ADR 0595"
- "ADR 0626" - "ADR 0626"
@@ -61,6 +62,12 @@ provision of the bus module asked for by template, created by the bus module, re
**ledger** is a storage type of its own. § 12 is the design; the `KV_<module>_<name>` row of § 3 and the module's **ledger** is a storage type of its own. § 12 is the design; the `KV_<module>_<name>` row of § 3 and the module's
state in § 4 hold for a module still on the old sections. state in § 4 hold for a module still on the old sections.
**Amended, 2026-11-11** (ADR 0595 §1, §7; ADR 0615 §3 to §6; the operator, 2026-11-11, in the terminal: "Ask the
bus module"): the grants part no longer learns a binding only from the bus module's live events; at every start and
when asked again by hand it takes a first view of every binding from the seat `mesh-broker`'s `list`, so a missed
`created` loses no binding for good (milestone bindings-first-view). The same change puts this design in the shape
of playbook 02: § 12.2 to § 12.6 moved, unchanged and numbered, into the part of milestone bus-state.
## 1. What the bus is for ## 1. What the bus is for
**The bus is where the mesh happens.** Not a transport the mesh sends things over — the place a **The bus is where the mesh happens.** Not a transport the mesh sends things over — the place a
@@ -694,368 +701,32 @@ The template log is the **module log** of ADR 0297. The mesh, never the module,
subjects (ADR 0201 §4): `<module>_<local name>`, and a local name holding the separator is refused so no two subjects (ADR 0201 §4): `<module>_<local name>`, and a local name holding the separator is refused so no two
modules' names meet. modules' names meet.
### 12.2 Who creates it, and how a module receives it ### 12.2 to 12.6 Who creates it, the seat's verbs and events, the ledger, the calls and the move
``` ```
manifest validator ─verdict: accepted with the module's bus-state entries whole─▶ bus module manifest validator ─verdict: accepted with the module's bus-state entries whole─▶ bus module
│ creates each entry │ creates each entry
▼ ▼
grants part ◀──────────────── created (module, local name, bus name, template, settings) grants part ◀──────────────── created, recreated, dropped (module, local name, bus name, binding)
│ ◀───── at its start, and when asked again: mesh-broker.list (every entry, its binding)
│ writes the binding into the module's membership │ writes the binding into the module's membership
▼ ▼
the tool runner reads the membership ──▶ the module's calls reach its own entries, and only those the tool runner reads the membership ──▶ the module's calls reach its own entries, and only those
``` ```
- **The trigger is the manifest validator's verdict** (ADR 0595 §4), heard as the indexer's event `manifest-accepted`, which These sections are the lines of milestone **bus-state**, in its part
carries the module's entries whole, so the bus module never reads the indexer's state and never polls. The indexer [25-milestones/bus-state.md](25-milestones/bus-state.md), where they keep their numbers. The shape, in short: the
says it for every newly accepted manifest, with its entries empty when it asks for none, and the bus module reads an indexer's `manifest-accepted` carries a module's bus-state entries whole to the bus module, whose process creates
empty one as "this module asks nothing now", which is what its orphans need. A module whose manifest leaves the each entry under the module's names and says `created`, `refused`, `drifted` and `drift-cleared`, `full` and
trunk (its directory deleted) gets an empty `manifest-accepted` too, so its entries reach `orphans` and raise `full-cleared`, `orphaned`, `recreated` and `dropped` on the seat `mesh-broker`. The seat's reading verbs `list`,
`orphaned`; a manifest the manifest validator set aside to wait gets none, so its entries stay asked. The indexer's tool `show`, `check` and `orphans` are answered by the bus module's tools; its acts `create`, `apply`, `recreate` and
`indexer_say_accepted` says `manifest-accepted` again for one module by hand, from the verdict it holds, recorded as a `drop` by its process alone, the last two on the operator's warrant and after a stream snapshot. The grants part
hand act, for a verdict the bus module is thought to have missed. It goes, as every event of the writes each entry's binding into the module's membership, kept in its own state `memberships`, from those events
indexer, on the module events stream (ADR 0363) under the indexer's own subjects. A publish of it that fails is and, at every start and when asked again by hand, from its first view of the seat's `list` (milestone
published again, and one still failing writes the indexer's finding naming the module, cleared once said (today **bindings-first-view**, in its part [25-milestones/bindings-first-view.md](25-milestones/bindings-first-view.md)).
the indexer only logs it). It hears the verdicts on a durable consumer, so one given while it was down is heard The ledger is a storage type whose state the tool runner derives from its history; the new calls are a mesh-sdk
when it is back. **Its first view** at start is the indexer's package of their own; and the foundation parts take their first views of each other's state from the owner's own
tool `indexer_bus_state` (every accepted manifest's bus-state entries, by module and digest), asked once; the tools, then follow its events.
events keep it current after that, and a stream that keeps a verdict only for its age is never its source. A
verdict replayed from its consumer is applied only if newer than what the first view answered for that module, by
the indexer's revision alone (a digest has no order), so a replay never takes an entry back. An `indexer_bus_state`
that does not answer makes the bus module's health unhealthy, naming the verb, and is asked again on a bounded
backoff (from 10 seconds, doubling, at most every 5 minutes); nothing is created or orphaned meanwhile. Beside the
backoff, the bus module's tool `nats_first_view_again` asks `indexer_bus_state` at once by hand, recorded as a hand
act. It keeps
what it last heard per module in its own state `asked` (an old `state` section, its own kept data staying there,
ADR 0595 §10), from which `list`, `check` and `orphans` answer; a key goes only on an empty `manifest-accepted` for
the module, never because a manifest the manifest validator set aside to wait is missing from the first view. Then it runs `check` over what it holds, creating what
is missing. The manifest validator checks every entry once, at the merge check and at take-in (ADR 0578; ADR 0595 §8), and also
each ledger type's operations: a set-entry or a remove names `into` and `key`, `fields` and an index's `on` name
fields of the schema, and `digest-of` names a text field.
- **A module that asks bus state waits for the bus module** (ADR 0614 §5). Each bus-state entry of a manifest is a
requirement on the provision `bus-state`, which the bus module's manifest provides; the module graph reads it as any
requirement, so the assigner holds a consumer's new build until a build of the bus module that offers `bus-state`
runs healthy (ADR 0603 §5). That each entry exists before its user starts is the bus module's own work, above, not
an edge.
- **The bus module has a process of its own** beside its tool bundle, with a declared health check. Its credential
creates streams and buckets under modules' names only, and deletes only through `drop` and `recreate`. Its health
turns unhealthy, naming the cause, when an accepted verdict has waited on its durable consumer, unanswered, for
more than three minutes, or when its last pass over fill, which runs every minute, is more than three minutes old, so the only guard against a store filling never stops without a sound; another module's entry past the raise mark
raises that module's condition and leaves the bus module healthy. **Its process has a process account of its own,
granted more than any other process's** (ADR 0626 §1; ADR 0436 §1, §2). The process declares `bus` as any process
may, with one key more, `keeps`, which is allowed on this process alone; the grants part composes the account from
that declaration as it composes every process account, and the vault mints it as the module's own secret. Beyond
what a process's `bus` grants, `keeps` grants the bus's create and update subjects for streams, buckets and object
stores under modules' names only (`<module>_*`), the snapshot request for an entry before `recreate` or `drop` with
its chunks' acknowledgement subjects and its own inbox, and the delete subjects for those two acts. It is granted
no subject on which a tool or a seat's verb is answered (ADR 0436 §3), and no subscription to any restricted
stream's own subjects: the snapshot is the one way a restricted stream's data reaches it, kept only in the bus's
snapshot store, and the process reads no message of it. No other account holds those subjects, the tool runner's
included. **The snapshot account stays as it is** (ADR 0626 §2, § 3 above). **The manifest validator refuses any other bus
declaration of the bus module** (ADR 0626 §3): anything it would say or hear on the bus beyond its process's `bus`
and the bus state the foundation keeps on the old sections (ADR 0595 §10), `keeps` on any other process or module,
and a `bus` of its process that names a subject on which a tool or a verb is answered.
- **The binding travels on the bus module's events** (ADR 0595 §1, §7, as their progressive insight of 2026-11-03
has it, waiting for the operator's Approve). The grants part, which issues every membership, hears them on a durable
consumer: it writes the **binding** (the names on the bus) into the module's membership on `created`, on every
machine the module is assigned, rewrites it on `recreated` and removes it on `dropped`, kept in its own state
`memberships`, so no part writes another's state and a binding outlives the 7 days its event is kept on the shared stream (§ 12.3).
A module whose binding is missing at start fails loudly, naming the entry; it never starts blind. A `refused`
create hands no binding. **A refused or overdue creation is loud**: overdue means no `created` or `refused` within
five minutes of an accepted verdict that asks for the entry. The condition is raised by the watchman, which today raises the manifest validator's
refusals as findings and gains, through its `consumes`, the accepted verdicts and the seat `mesh-broker`'s `created`
and `refused`; never by the bus module, which cannot raise it while it is down; it names the module
and the entry; a later `created` clears it, and so does a later accepted verdict of that module that no longer asks
for the entry.
- **A foundation part's first view of another's state is a tool its owner answers in a runtime of its own** (ADR 0615,
ADR 0621). The four
foundation parts that read each other (the grants part, the tool runner, the indexer and the assigner) stop
reading the indexer's and the assigner's bus state in the milestone of ADR 0612, with the other 19 readers of that
state (ADR 0615 §1). **Each owner serves them as tools of its own module**, the indexer `manifests`,
`modules`, `verdicts` (ADR 0622 §1) and `graph` (ADR 0631 §1), the assigner `placement`, `seat-holders` and `wants`, and declares `runs-as`, so its tools bundle runs in
a runtime of its own on the module's own bus account, never in the node's tool runner, and the tool runner never
asks through itself (ADR 0621 §1). No new manifest form is added: a process still answers no tool call or seat verb
(ADR 0436 §3). **Each verb is granted to exactly its readers** (ADR 0621 §2, as ADR 0615 §3 lists them): `placement`
to the grants part, the tool runner and the indexer; `seat-holders` to the tool runner; `wants` to the grants part;
`manifests` and `modules` to the grants part, the tool runner and the assigner; `verdicts` to the grants part
alone, its `callers` list holding the grants part only (ADR 0622 §1); `graph` to the assigner alone, its `callers`
list holding the assigner only, the indexer itself refused, and the assigner's `reads` entry of the indexer's
`graph` goes (ADR 0631 §1). These seven are the first-view tools, four of the indexer and three of the assigner.
`verdicts` follows the first-view rules whole (ADR 0622 §2, under ADR 0615 §4 to §6): its answer carries the
revision, the grants part then follows `manifest-accepted` and `manifest-refused` and asks once more on a gap in
their revisions, it raises the condition naming the indexer and `verdicts` past the bound and never reads the
indexer's state, and `grants_first_views` and `grants_first_view_again` cover it. `graph` follows them whole too
(ADR 0631 §2, under ADR 0615 §4 to §6): its answer carries the revision, the assigner then follows the indexer's
`indexer.views` events for the bucket `graph`, which carry the revision, and asks once more on a gap, said in its
log; started while the indexer is down, it raises the condition naming the indexer and `graph` past the bound and
never reads the indexer's state; and `assigner_first_views` and `assigner_first_view_again` cover it. A reader declares each in `invokes`
as `<module>.<tool>` only where it is granted, which is the grant the grants part composes as for any tool, and
each tool checks the proved caller against a `callers` list holding exactly its readers and refuses any other, the
owner itself included. **The manifest validator of the milestone is its own group's** (ADR 0621 §4): since no field is added,
the running manifest validator takes the indexer's and the assigner's manifests in as it does today, and the merge check judges
them with the manifest validator built from the group's own head; no release of the manifest validator goes ahead of the milestone. **The
owner's runtime answers before any first view of its own** (ADR 0621 §3): at once from its module's own state, as
soon as it is connected and before it has asked any other part anything, with the revision of its state it answered at,
so the indexer and the assigner, each asking the other at start, never wait on each other (§4). The reader then
follows the owner's declared events, each carrying the revision of the state it follows, and never asks the verb
again on a timer; a gap in revisions makes it ask once more, said in its log (§5). The owners' events are new
families of their own, apart from the indexer's `manifest-refused` and `manifest-accepted` and the assigner's
`put`: the indexer's `indexer.views` and the assigner's `assigner.views`, one event per changed key with its
bucket, key and revision and the key's value whole, or a delete mark for a key removed, so a reader keeps its
picture from the events alone and asks the verb only on a gap (ADR 0615 §5). **Both families are declared in
their owners' old `streams` sections**, each an entry with `max-age-days` 7 and `max-mib` 256: the old section keeps
a stream by limits, which drops its oldest when full (only a work queue refuses the new instead), so a reader that
misses a dropped event sees the gap in revisions and asks once more. The indexer's own kept data stays on the old
sections (ADR 0595 §10 names it among the foundation); the assigner's goes beside its existing `streams` entry
`placement`, under ADR 0619, which extends ADR 0595 §10's foundation to the parts
the bus module's own composition, placement, build or delivery needs before it can answer: the assigner, the tool
runner, the builder, the checker, mesh-config and the vault. **The size, an estimate**: read on the bus on
2026-11-04, the buckets the readers read hold about 3 MiB of current values at the indexer (its manifests about
5 KiB a value) and about 1.5 MiB at the assigner (its wants about 0.25 KiB a value). The milestone's one mesh-sdk
rebuild rewrites every module's keys, up to three times in its window (design 51 *How many builds*), so a week of
it is some 20 MiB on `indexer.views` and some 10 MiB on `assigner.views` with ordinary merges added, each well
under its 256 MiB. Each event carries one value whole, the largest today a manifest of tens of KiB, under the bus's
largest message, 1 MiB (the bus server's `max_payload`); an owner whose value with its event would pass it emits
the key's revision alone, as a gap, so the reader asks the first view once, and the owner writes its finding
naming the key.
A waiter on an existing event, `manifest-accepted` or the
assigner's `put`, never wakes on them. **When an owner's revisions go back**: should an owner's state be rebuilt
by the bus module's `recreate`, its revisions start again; a reader that hears a revision lower than the one it
holds takes it as a gap and asks the first view again, and keeps what that answer says, whatever revision it held
before. A `recreate` that could not keep the data answers empty at revision 0, so the grants part keeps its list
in force (below): a login revoked meanwhile stays until the owner's first change, and the grants part's
first-view condition stays raised meanwhile, naming the owner, the empty answer and the operator's step: let the
owner write its state again from its sources (the indexer by taking each module in again, `indexer_take_in`; the
assigner by its next composition of the wants), then ask the grants part's first view again,
`grants_first_view_again`. It is cleared by an answer above revision 0. A reader that starts while its owner is down raises a
condition naming the owner and the verb past its bound and keeps asking until it answers, never reading the
owner's state instead; once it had its first view, it keeps working on what it heard (§6). The bound is longer
than an owner's start (two minutes), so a normal start, readers and owners coming up together, notifies nobody;
the owner's answer clears the condition. An answer that is empty at revision 0 (an owner that holds nothing yet)
is a first view of nothing, never a reason for the grants part to drop a login it holds: it keeps the list in
force until an answer at a revision above 0. Each reader's tools say the owner, verb and revision its first view
holds, when it asked and when it was answered (a read tool), and ask the first view again by hand (a re-read tool,
recorded as a hand act): the grants part's `grants_first_views` and `grants_first_view_again`, the tool runner's
`mesh_first_views` and `mesh_first_view_again`, the indexer's `indexer_first_views` and
`indexer_first_view_again`, and the assigner's `assigner_first_views` and `assigner_first_view_again`. The tool
runner's two are tools of the mesh MCP server, beside `mesh_runtimes`, since the tool runner serves its own state
there: the mesh MCP server gains them, and the operator sees them among its tools. **On a fresh mesh** the
assigner starts before any new-form bus state exists and needs no binding of the bus module first: its own state
and `assigner.views` are on its old sections, which the grants part creates, so it answers the indexer, the grants
part and the tool runner their first views while the bus module still waits on it. The order of owner before reader is kept by the module graph's served-verb edge for the 19 readers (ADR 0614 §2), and,
for the four, whose verb edges lie on cycles the graph leaves out (ADR 0614 §3), by the owner's answer at once and
the reader's wait (ADR 0615 §2).
- **The other 19 readers ask the owners' read tools, one per bucket they read** (ADR 0615 §1, §2). Beside the seven
first-view tools above, each owner serves a read tool for every bucket of its state one of the 19 reads today,
named `indexer_read_<bucket>` or `assigner_read_<bucket>` and marked as a read: fifteen. The indexer's nine:
`indexer_read_manifests`, `indexer_read_modules`, `indexer_read_predefined`, `indexer_read_config_candidates`,
`indexer_read_candidates`, `indexer_read_packages`, `indexer_read_graph`, `indexer_read_heard` and
`indexer_read_taken`. The assigner's six: `assigner_read_placement`, `assigner_read_seat_holders`,
`assigner_read_wants`, `assigner_read_findings`, `assigner_read_asks` and `assigner_read_differences`. These are
the nine buckets of the indexer and the six of the assigner the 19 read (§ 12.6), `modules`, `wants` and
`placement` among them for the status view; the indexer's tenth, `verdicts`, is read by the grants part alone,
through its first-view tool. **They are ordinary module tools**: granted, as any tool is, to every
reader that declares it in `invokes`, and callable by a session; the `callers` list of ADR 0621 §2, ADR 0622 §1 and ADR 0631 §1 covers
only the seven first-view tools. **The status view keeps all its tools**, their names, arguments and answers
unchanged, its `indexer_*` and `assigner_*` tools included: as a reader it answers them by asking the owners' read
tools instead of reading their buckets. Checked live after the delivery: a session's call of each status view tool
answers, and `mesh_search` finds each owner's read tool by its bucket's words.
- **Only the owner reaches it** (ADR 0596 §2). The membership grants the owner's account its own entries and no
other module's: the new form has no readers. What another module needs of that data it asks through the owner's
tools or seat verbs, and what the owner lets others hear is its declared events, subscribed to through
`consumes` (ADR 0363, unchanged). A part watches only its own state, and hears another's events (ADR 0596 §4).
### 12.3 The seat's verbs, events and fill
The seat `mesh-broker`, until now a seat with no verbs, gains these (ADR 0595 §5, §6). It is one of the ten seats
defined whole in the milestone of ADR 0612, its contract a package of its own in `mesh-contracts` (ADR 0613 §1,
ADR 0627 §1, ADR 0625 §1; design 26 *A seat's definition*), which settles where its definition lives (ADR 0595 §12). An entry's arguments are the module, its local
name, its storage type, its template, its options and its configuration; what is held answers the settings held and
the fill (count, bytes, the cap); `drop` and `recreate` carry the warrant and why.
| Verb | Does | Who may call it |
|---|---|---|
| `list` | every entry, its template, its settings, its fill against its cap | any caller whose role grants it (ADR 0386) |
| `show` | one entry, the same | the same |
| `check` | compares what the bus holds with what was asked, entry by entry | the same |
| `orphans` | entries the bus holds that no manifest asks for; never one whose manifest the manifest validator refused or outgrew, nor one of the foundation's old sections | the same |
| `create` | creates one entry now, for an entry an accepted manifest asks for | an act of the bus module's process alone, answered on no subject |
| `apply` | applies a difference that only grows: a larger cap, a longer age | the same |
| `recreate` | rebuilds an entry whose settings differ in a way that cannot grow, from its data where the storage type allows | the same, on the operator's warrant, which the process verifies itself |
| `drop` | removes one orphan | the same, on the operator's warrant, which the process verifies itself |
`create`, `apply`, `recreate` and `drop` stay in the seat's definition as acts its holder's process alone performs,
answered on no subject (ADR 0626 §4, ADR 0436 §3): they are not verbs whose subjects a credential is granted, but
acts on the bus's own create, update and delete subjects, which only the process's account holds, so no module can
ask the bus for them. A person asks for them through the bus module's tools, which hand the request to the process
through its own state (below); `recreate` and `drop` act only on the operator's warrant (ADR 0259 §6), asked
through the operator channel. Before `recreate` or `drop` acts, the process takes a stream snapshot of the entry
(ADR 0595 §5), and it acts only once the snapshot is whole. A drifted entry is said, never quietly fixed: only a
difference that grows is applied without a person.
**Events**: `created`, `refused` (with why), `drifted` and `drift-cleared`, `full` and `full-cleared`, `orphaned`
(cleared by a `drop`, or by a manifest asking for the entry again, which emits `created` for it), `recreated` and
`dropped` (ADR 0595 §6, as its progressive insight of 2026-11-03 has it, waiting for the operator's Approve);
each carries the module, the local name, the bus name, the template, and the settings asked against those held.
**They follow every seat's events**: each is said on `mesh.seat.mesh-broker.event.<event>`, on the shared stream
`EVENTS` (§ 3), kept 7 days, and heard through `consumes` `mesh-broker.<event>` like any seat's event. **The bus
module keeps their history apart**, because a drift or an orphan is read back days later by a person: a stream of
its own in its old `streams` section, on its module subjects, kept 30 days (ADR 0363's bound), created by the grants
part as today, with a cap of 256 MiB, to which the process appends every event it says. Its own kept data stays on
the old sections (ADR 0595 §10), because it cannot wait for its own process to create it. That stream drops its
oldest when full, which loses no live state, because every drift, orphan and fill stands as a condition until it
clears; the conditions hold the live state. The tool `history` of one entry reads it back: every event the bus
module said of the entry, in order. This settles where they are kept, which ADR 0595 §12 left to the design, and
needs no new manifest form: a module's stream never takes a seat's subjects, and every seat keeps one form for its
events.
**Fill is the bus module's one periodic read** (ADR 0595 §6): every minute, for every entry that refuses when full,
because the bus says nothing as a store fills. The condition `full` is raised at a raise mark and cleared below a
lower clear mark, so an entry sitting at the mark does not flap; the marks are settings of the bus module, by default
a raise at 75 percent of the cap and a clear below 70 (ADR 0595 §6, as its progressive insight of 2026-11-03 has it).
The grants part's `mesh-grants.history-full` moves to it: the new condition is raised first, then the old key cleared;
no manifest on main asks for a history stream or objects today, so the move loses nothing. A bare stream that refuses
when full is read the same way. **The conditions**, each naming the module and the entry: `full` (cleared below the
clear mark), `drifted` (cleared by `drift-cleared`), `orphaned` (cleared by a
`drop` or by a manifest asking for it again) and a refused create (cleared by a later `created`).
**Its tools** (tools-are-how-a-module-is-spoken-to): the four reading verbs, marked reads, answer every entry the
bus module created and its fill, and `history` its events for one entry; `create` and `apply`, marked acts, and
`recreate` and `drop`, marked dangerous and so granted only by name (ADR 0386 §6, ADR 0387), are its tools for a
person. **A person's request reaches the process through the process's own state** (ADR 0626 §4), because a process
account answers no tool call (ADR 0436 §3). Such a tool, in the bus module's bundle, holds no grant of the bus's
create, update or delete subjects, and neither does the tool runner's account: it writes the request into the bus
module's state `requests` (an old `state` section, as `asked` is, ADR 0595 §10), stamped with the caller the tool
runner proved and recorded (ADR 0386 §8), and watches that key for the process's answer, never asking again on a
timer. The process watches its own state `requests` (ADR 0596 §4) and takes a request up when it is written. The stamp names
who asked, for the record; it is no boundary, because every session on a machine reaches the bus as the tool
runner's account (ADR 0436 §9). **So the process verifies the operator's warrant itself**: for `recreate` and `drop`
it trusts nothing in the request, reads the operator channel's record of the ask under its own account and checks
that the warrant binds exactly this act on this entry (ADR 0259 §6), as the mail sender does (ADR 0436 §9); a request
without a valid warrant it refuses, deleting nothing and writing why into its answer; with one, it takes the entry's
stream snapshot first. **The process wakes on the warrant, never on a timer**: a warrant is said on the operator
channel's `decided` for the asker that raised the ask (ADR 0259 §6; ADR 0620 §2), here the bus module itself, which
hears its own `decided` on a durable consumer of the process, whose filter the grants part derives from the
process's `uses` of the operator channel seat (not from a `consumes`, which a process has none of), and runs its pass over the waiting `recreate` and
`drop` requests when one arrives, so a warranted `drop` acts within seconds of the operator's answer, and one given
while the process was down is heard when it is back. **A warrant acts once** (ADR 0259 §6): before it acts, the
process claims the warrant's ask id by writing the key `acted.<ask id>` in the state `spent` of the process's own bus state, which the tools bundle is not given (a person's request and the
record of what it spent are never in one bucket, and no tool can write the record), created by compare-and-set after the snapshot and before any deletion, and a request naming an ask it already
acted on is refused, deleting nothing and saying which act that warrant already performed. **An ask lasts 12 hours**,
shorter than the 24 hours `requests` keeps a request: a request whose ask ended unanswered is ended and said, and a
warrant is not acted on once its ask ended; the tool raises the ask before it hands the request over. A request
that cannot act yet says why in its field Waiting, and a hand act the process could not record is written on the
request in its field Unrecorded. The tool `nats_requests` reads the waiting and ended requests, and the warrants
spent from `spent`, which is declared `read-only: true` (design 32 § 4) so that the tool reads it and holds no publish on it, and drops a waiting request by hand, recorded as a hand act. `create` and `apply` need no warrant, since they only make or grow what an accepted manifest
asks for, and the process refuses either for an entry no accepted manifest asks for. A tool acts only for a caller
whose role grants it; the process records each act as a hand act (`mesh-hand-acts`) and answers what it did. What
stands between the tool runner's account and the process's delete power is that check of the warrant, so its tests
are this section's central check (§ 12.7). **The mesh deletes kept data only through `drop` and `recreate`** (ADR 0595 §4, as its
progressive insight of 2026-11-03 has it): the bus module's older remove tools (its stream, bucket and consumer
removals, ADR 0564) refuse an entry of the new form, naming `drop`'s tool, and keep working for the old sections. The bus seat's `apply` (a difference that grows) and the
ledger's `apply` (§ 12.4) are two verbs on two subjects, granted apart; this design writes them qualified. Its health
names the process's last pass, the entries it could not create, and the entries past the raise mark.
### 12.4 The ledger
A **ledger** is a storage type whose state is derived from its history (ADR 0597). It is built on a stream and
key-value buckets, and asked for with the template ledger.
```
module ──apply(instance, operation)──▶ tool runner on its own machine
│ 1 checks the operation against the type
│ 2 reads the history's last sequence for the instance; replays if the state is behind
│ 3 appends to the history, that sequence expected ──▶ history (stream, a subject per instance)
│ 4 derives the state by the operation's effect ──▶ state (key-value, updated at the revision read)
│ 5 writes the instance's snapshot ──▶ snapshot subject (latest only, never expires)
▼
answers: taken, with its sequence (and "state behind" when the state bucket is full)
module's process: a watcher keeps each declared index (a second key-value bucket) from the history
```
- **A type** is a schema and the operations allowed on it; an **instance** of a type is named by a path
(`listing/a17`, `decision/0290`); every change is an **operation**, kept in the instance's history.
- **A ledger effect** is what an operation does to the state, one of a fixed set: set fields, set an entry of a
map, append to a list, remove (ADR 0597 §2). An effect copies only the fields its type names, and may add the
operation's sequence and author. Where a type pairs a text field with its digest, `apply` refuses an operation
whose digest does not match. A module's own rules are checked by the module before it calls `apply`, never by the
effects. Only the owning module calls `apply` on its ledger, for every effect, remove among them; a person
removes through the owner's tools. This settles who may call the remove effect, which ADR 0597 §13 left to the
design.
- **One writer wins, and the state never goes back** (§5): the expected sequence makes the bus refuse a racing
writer; the state is written as an update of the revision read, never a plain put; when that update loses to
another writer, `apply` answers success with its operation's sequence and re-reads, never appending again.
- **A full state bucket loses no operation** (§6): `apply` answers that the operation is taken, with its sequence,
and that the state is behind because the bucket is full; the next `apply` after room is made catches it up.
- **The author is proven** (§7): `apply` stamps the owning module's authenticated account and the account that
called the owner's tool. The owner passes that author on by naming the tool call it serves: the tool runner, which
served that call and proved its caller, stamps the caller it proved, so the module cannot set the author to words of
its own; an `apply` naming no call in flight carries the owner's account alone, and one naming another module's
call is refused. A copy of old records (design 55 § 8.1) is stamped with the owner's account and keeps the old
author as a separate field marked imported, never as the proven author.
- **Every instance has a snapshot** (§8): the latest state on a subject of its own, never expiring, written by every
`apply`. It makes a kept ledger's rebuild fast and an expiring ledger's possible; a repair that finds it behind
the state rewrites it.
- **Only the tool runner's ledger credential publishes on a ledger's history** (§9): one per module assignment, that
is per node the module runs on, the account `ledger.<node>.<module>` in the tool runner's own secret `ledgers` on
that node (ADR 0597 §9, as its progressive insight of 2026-11-03 has it, waiting for the operator's Approve), held by
the tool runner and never by the module's own code, whose account may read its ledger and not publish on it. A
person's `rotate` of that secret is refused by the vault, as a bus account's is. The grants part issues it with the
module's membership on that machine, scoped to the subjects of that module's ledger
histories and snapshots, and removes it when the module is unassigned from the machine. Only the
owner's account reads the history, the snapshot and the state.
- **An index** is a second key-value bucket kept by a watcher in the owning module's process, rebuildable from the
history; it raises a condition when it lags behind (§4).
### 12.5 The calls, and where they live
The new calls live in a small mesh-sdk package of their own, served by the tool runner and imported only by the
modules that use them; the module library, which 112 modules import, does not change (ADR 0595 §9). They are the
object store's (put, get, list, delete, info, watch); a stream's read-back and fill; a work queue's size and dead
letters; and the ledger's: `apply`, `get`, `history`, `watch`, `query`, and its repairs `rebuild` (the state from
the history or the snapshot), `reindex`, `snapshot` and `lag` (ADR 0597 §10). The tool runner serves the repairs as
tools of every module that owns a ledger, limited to its own ledgers, so no owner declares them and the next ledger's
owner has them on its first day. mesh-sdk's catalog and membership
packages change once for the typed `requires` entry and the binding, rebuilding about 30 modules.
### 12.6 The move, and the order of the build
**The build, each step undoable on its own** (ADR 0595 §10): (1) mesh-sdk's typed `requires`, binding and calls
package; (2) the manifest validator's rules; (3) the bus module's process, verbs and events, creating only for the new form, with the grants part's writing of
the binding on `created`; (4) the tool runner's calls, the ledger's `apply` and its ledger credential among them; (5)
modules move, each owner first. Each undo: undoing step 2 refuses the new form again, which no module uses before
step 5; undoing step 3 leaves what it created in place, and a module moved after it fails loudly on its missing
binding; undoing step 4 leaves a moved module without its calls, which the placement holds off that machine. The
placement holds a moved module off a machine whose tool runner does not serve the typed binding and the calls the
module asks for (a ledger's `apply` among them), and says so; where the placement reads what a tool runner serves is
left to the build of step 4 and tracked on issue 1966.
**A module moves when it is next touched** (ADR 0595 §11), keeping its local names and carrying its old settings
as options, so its bus objects and data stay and no drift is raised. It asks for each entry once: an old section
and the new form are never both used for one local name. During the move the grants part keeps creating the old
sections' entries, so each entry has one creator. The indexer counts the modules on each old section, a tool reads
the count, and the manifest validator refuses a section once its count is 0. The foundation's own kept data (the bus module's,
the grants part's and the indexer's) stays on the old sections for good and is not counted: those modules cannot
wait for a process that needs them first. ADR 0619, extending ADR 0595 §10's foundation, adds the parts the bus module's own composition, placement, build or delivery needs before it can answer:
the assigner, the tool runner, the builder, the checker, mesh-config and the vault; their new state and streams of
this milestone go on their old sections too (§ 12.2; design 51 *The move*; design 32).
**Readers move after their owners** (ADR 0596 §3). Today 195 `reads` entries in 37 modules read another module's
bus state (counted 2026-11-03 02:00). **A correction of fact**: the indexer's and the assigner's state, counted again
from every manifest on main later that day, is read by 23 modules, 10 buckets of the indexer (`verdicts`, read by the grants part, among them) and 6 of the assigner,
not by the 19 the first plan counted; and 151 state entries were counted, not 158 (the template table of § 12.1 keeps
the first count, which chose the templates). The owners first gain their events, first-view tools and read tools: the
indexer a read tool per bucket its readers read, the assigner the same (§ 12.2 lists them). Then all 23 readers move in the milestone of ADR 0612, the
four foundation readers included, by the first view of § 12.2 (ADR 0615 §1), the grants part's of the indexer's
`verdicts` by its first-view tool `verdicts` (ADR 0622) and then `manifest-accepted` and `manifest-refused`, and the
assigner's of the indexer's `graph` by its first-view tool `graph` (ADR 0631) and then `indexer.views`; their order is the module graph's, not
a review reading an owner delivered (ADR 0615 §2). The status view's move keeps every tool it serves, each answered by
asking the owners' read tools of § 12.2. **The node-engine's reads granted in code stay**: the grants part's code
grants each node's node-engine, with no manifest declaring them (`CandidateReads` and the composed reads beside it),
the indexer's `candidates` and `verdicts` and its own node's keys of the assigner's `wants`. They are not among the
23 readers of ADR 0615 §1 and move in the next milestone, queued on issue 1966. The manifest validator still refuses a moved module's subscription to an event
its owner does not declare. Every other reader moves when its module is next touched. These moves are tracked by
issue 1966.
### 12.7 How it is checked ### 12.7 How it is checked
@@ -1067,6 +738,7 @@ issue 1966.
| A module asking bus state waits for the bus module | the module graph's table test: a manifest with a ledger entry has a requirement edge to the module providing `bus-state`, one without has none (ADR 0614 §5) | | A module asking bus state waits for the bus module | the module graph's table test: a manifest with a ledger entry has a requirement edge to the module providing `bus-state`, one without has none (ADR 0614 §5) |
| A foundation part's first view | unit tests of each owner and reader (ADR 0615's How it is checked), as table cases, and these cases of the whole-module integration tests that exist today (mesh-delivery is over its integration budget, so no new test function): the indexer's (`modules/mesh-delivery-indexer`, its one whole-module test, which the milestone's packages test merges into), the assigner's (`modules/mesh-delivery-assigner`) and the grants part's (`modules/mesh-grants`), and the tool runner's in mesh-tools: the owner answers its verb at once from what it holds, with its revision, before its own first view; the reader asks once at start, then only on a gap in revisions; a reader whose owner is down raises its condition only past the bound and never reads the owner's state, and the owner's answer clears it; an empty answer at revision 0 drops no login; an owner's event carries the changed value whole, and a reader applies a put and a delete from the events alone and never asks the verb; a revision lower than the one held makes the reader ask its first view again; an existing waiter on `manifest-accepted` or on the assigner's `put` does not wake on the new families; for the indexer's `verdicts` (ADR 0622), called by the grants part while the node's tool runner is stopped, it answers, and from any other account, the indexer's own included, it is refused; a missed revision on `manifest-refused` makes the grants part ask `verdicts` once more; the grants part and the indexer started at the same moment, `verdicts` answers within the bound with its revision; with the indexer down past the bound the grants part raises the condition naming the indexer and `verdicts` and never reads the indexer's state, and the answer clears it; for the indexer's `graph` (ADR 0631), called by the assigner it answers with its revision, and from any other account, the indexer's own included, it is refused; a missed revision of the bucket `graph` on `indexer.views` makes the assigner ask `graph` once more; started with the indexer down, the assigner raises past the bound the first-view condition naming the indexer and `graph` and reads nothing of the indexer's state, and the answer clears it; the read tool shows the revision each reader holds, and the re-read tool asks once and records the hand act; in the assigner's whole-module test, the assigner starts before any new-form bus state exists, with no binding of the bus module, and the indexer, the grants part and the tool runner each get their first view of it. Live, after the delivery: the indexer's `indexer_old_sections`, which the milestone extends to count the `reads` entries on each owner's state, reads 0 declared `reads` entries on the indexer's state and 0 on the assigner's, and says the node-engine's reads granted in the grants part's code remain (§ 12.6); and each owner's read tool of § 12.2 answers what its bucket holds, each status view tool answers a session's call as before the move, and `mesh_search` finds each owner's read tool | | A foundation part's first view | unit tests of each owner and reader (ADR 0615's How it is checked), as table cases, and these cases of the whole-module integration tests that exist today (mesh-delivery is over its integration budget, so no new test function): the indexer's (`modules/mesh-delivery-indexer`, its one whole-module test, which the milestone's packages test merges into), the assigner's (`modules/mesh-delivery-assigner`) and the grants part's (`modules/mesh-grants`), and the tool runner's in mesh-tools: the owner answers its verb at once from what it holds, with its revision, before its own first view; the reader asks once at start, then only on a gap in revisions; a reader whose owner is down raises its condition only past the bound and never reads the owner's state, and the owner's answer clears it; an empty answer at revision 0 drops no login; an owner's event carries the changed value whole, and a reader applies a put and a delete from the events alone and never asks the verb; a revision lower than the one held makes the reader ask its first view again; an existing waiter on `manifest-accepted` or on the assigner's `put` does not wake on the new families; for the indexer's `verdicts` (ADR 0622), called by the grants part while the node's tool runner is stopped, it answers, and from any other account, the indexer's own included, it is refused; a missed revision on `manifest-refused` makes the grants part ask `verdicts` once more; the grants part and the indexer started at the same moment, `verdicts` answers within the bound with its revision; with the indexer down past the bound the grants part raises the condition naming the indexer and `verdicts` and never reads the indexer's state, and the answer clears it; for the indexer's `graph` (ADR 0631), called by the assigner it answers with its revision, and from any other account, the indexer's own included, it is refused; a missed revision of the bucket `graph` on `indexer.views` makes the assigner ask `graph` once more; started with the indexer down, the assigner raises past the bound the first-view condition naming the indexer and `graph` and reads nothing of the indexer's state, and the answer clears it; the read tool shows the revision each reader holds, and the re-read tool asks once and records the hand act; in the assigner's whole-module test, the assigner starts before any new-form bus state exists, with no binding of the bus module, and the indexer, the grants part and the tool runner each get their first view of it. Live, after the delivery: the indexer's `indexer_old_sections`, which the milestone extends to count the `reads` entries on each owner's state, reads 0 declared `reads` entries on the indexer's state and 0 on the assigner's, and says the node-engine's reads granted in the grants part's code remain (§ 12.6); and each owner's read tool of § 12.2 answers what its bucket holds, each status view tool answers a session's call as before the move, and `mesh_search` finds each owner's read tool |
| The owners' first views are their own tools, in a runtime of their own (ADR 0621) | cases of the indexer's and the assigner's whole-module integration tests: each first-view tool, called from a reader's account while the node's tool runner is stopped, answers (§1); each answers its granted readers and refuses every other account, the owner's own included (§2); the two runtimes started at the same moment, each asking the other's first view at start, both answer within the first-view bound with their revisions (§3); and the milestone's group passes its merge check with no change to the manifest validator's known fields for these verbs (§4) | | The owners' first views are their own tools, in a runtime of their own (ADR 0621) | cases of the indexer's and the assigner's whole-module integration tests: each first-view tool, called from a reader's account while the node's tool runner is stopped, answers (§1); each answers its granted readers and refuses every other account, the owner's own included (§2); the two runtimes started at the same moment, each asking the other's first view at start, both answer within the first-view bound with their revisions (§3); and the milestone's group passes its merge check with no change to the manifest validator's known fields for these verbs (§4) |
| The grants part's first view of the bindings (design 25 bindings-first-view) | the cases of design 25 bindings-first-view §8: a case in the grants part's existing whole-module integration test, where a binding whose `created` was never said is written after a start; unit rows of the bus module's tools and the grants part; live, the next start after the delivery |
| A foundation part's first view comes within its bound | live, after the delivery: each reader's read tool (`grants_first_views`, the mesh MCP server's `mesh_first_views`, `indexer_first_views`, `assigner_first_views`) shows, for the indexer's and the assigner's verbs, the time from its ask to the owner's answer under the two-minute bound, and no first-view condition raised by the delivery | | A foundation part's first view comes within its bound | live, after the delivery: each reader's read tool (`grants_first_views`, the mesh MCP server's `mesh_first_views`, `indexer_first_views`, `assigner_first_views`) shows, for the indexer's and the assigner's verbs, the time from its ask to the owner's answer under the two-minute bound, and no first-view condition raised by the delivery |
| The bus module's first view | cases in the nats module's one integration test: a replayed verdict older than the first view is not applied; an `indexer_bus_state` that does not answer turns the health unhealthy naming it and is asked again on the backoff; a set-aside manifest missing from the first view keeps its `asked` key, an empty `manifest-accepted` removes it; a deleted module's entry, after its empty `manifest-accepted`, is listed by `orphans` and raises `orphaned`, and a set-aside module's entry is neither; `nats_first_view_again` asks at once and records the hand act | | The bus module's first view | cases in the nats module's one integration test: a replayed verdict older than the first view is not applied; an `indexer_bus_state` that does not answer turns the health unhealthy naming it and is asked again on the backoff; a set-aside manifest missing from the first view keeps its `asked` key, an empty `manifest-accepted` removes it; a deleted module's entry, after its empty `manifest-accepted`, is listed by `orphans` and raises `orphaned`, and a set-aside module's entry is neither; `nats_first_view_again` asks at once and records the hand act |
| `manifest-accepted` is never lost silently | unit tests of the indexer: a failed publish is published again, and one still failing writes the finding; `indexer_say_accepted` says it again for one module from the verdict held, and refuses a module with no accepted verdict | | `manifest-accepted` is never lost silently | unit tests of the indexer: a failed publish is published again, and one still failing writes the finding; `indexer_say_accepted` says it again for one module from the verdict held, and refuses a module with no accepted verdict |
@@ -1093,6 +765,21 @@ prepare step of issue 1966, and a repository over it merges tests to stay within
settings and its fill; a module that moved keeps its data; a drift or an orphan reaches the operator as a condition settings and its fill; a module that moved keeps its data; a drift or an orphan reaches the operator as a condition
and is changed only on their word; and the knowledge base keeps its records as a ledger (design 55 § 10). and is changed only on their word; and the knowledge base keeps its records as a ledger (design 55 § 10).
## Milestones
Each milestone not yet delivered has its part in [25-milestones/](25-milestones/), holding every line it builds
(playbook 02, *The shape of a design*); § 1 to § 11 and the five steps of § 9 stay in this file.
- **bus-state** — § 12.2 to § 12.6, built under issue 1966: the bus module creates every module's bus state, the
seat `mesh-broker` gains its verbs and events, the grants part writes the bindings, the ledger, the calls and the
move. Its part: [25-milestones/bus-state.md](25-milestones/bus-state.md).
- **bindings-first-view** — a recovery fix under issue 1966: the grants part takes a first view of every binding
from the seat `mesh-broker`'s `list` at every start and when asked again by hand, so a binding whose `created` it
never heard is written all the same. Its part:
[25-milestones/bindings-first-view.md](25-milestones/bindings-first-view.md). Its acceptance: a module whose
bus-state entry the bus holds, and whose `created` the grants part never heard, finds the entry's binding in its
membership after the grants part's next start, with nobody's hand.
## Reviewed by architectural domain ## Reviewed by architectural domain
The amendment of 2026-11-03 to ADR 0595, ADR 0596 and ADR 0597 (designs 25 § 12, 32 § 4 and 55 together) was The amendment of 2026-11-03 to ADR 0595, ADR 0596 and ADR 0597 (designs 25 § 12, 32 § 4 and 55 together) was
@@ -121,7 +121,13 @@ record says so in its Why. Round 3 of the review by architectural domain (2026-1
`minio` and `nfs-server` in mesh-catalog; `mesh-delivery-assigner`, `mesh-delivery-builder`, `mesh-delivery-checker` (until its `minio` and `nfs-server` in mesh-catalog; `mesh-delivery-assigner`, `mesh-delivery-builder`, `mesh-delivery-checker` (until its
removal, ADR 0667 §12), `mesh-delivery-indexer`, `mesh-delivery-teller`, `mesh-delivery-view`, `mesh-delivery-watchman` and removal, ADR 0667 §12), `mesh-delivery-indexer`, `mesh-delivery-teller`, `mesh-delivery-view`, `mesh-delivery-watchman` and
`mesh-enrolment` in mesh-delivery; `mesh-host`, the node-engine; `node-tools`, the tool runner, in mesh-tools; and the modules that gain `mesh-enrolment` in mesh-delivery; `mesh-host`, the node-engine; `node-tools`, the tool runner, in mesh-tools; and the modules that gain
findings in G, the runner (`mesh-delivery-pipeline`, §355) and the forge bridge (`mesh-delivery-forge-bridge`, for `branch-unprotected`, §17). The manifest findings in G, the runner (`mesh-delivery-pipeline`, §355), the forge bridge (`mesh-delivery-forge-bridge`, for `branch-unprotected`, §17) and
the grants part (`mesh-grants`). The grants part, a foundation part, gains in G its state `findings` and its finding stream in its old
`state` and `streams` sections (ADR 0595 §10), and writes there, instead of raising them through the operator channel, its findings
about what it waits for: `view-waits` (§200), `first-view-unanswered`, `first-view-empty` and `binding-unlisted` (design 25
bindings-first-view §4, §5), each naming what it named as a condition; its other conditions stay as they are. Why the grants part:
§200 already has it write `view-waits` as a finding, and a part that says what it waits for only from its process's
memory is the kind §384 ends (lead-47, 2026-11-11, taking the design writer's recommendation). The manifest
validator refuses a manifest declaring the state `findings` without that stream and that `consumes`. Why: an validator refuses a manifest declaring the state `findings` without that stream and that `consumes`. Why: an
observer whose stream is missing says nothing the keeper hears; the list is read from main on 2026-11-11 and the rule observer whose stream is missing says nothing the keeper hears; the list is read from main on 2026-11-11 and the rule
is the design's choice. How it is checked: unit (manifest validator), a manifest with `findings` and no finding is the design's choice. How it is checked: unit (manifest validator), a manifest with `findings` and no finding
@@ -92,7 +92,9 @@ line says so.*
fixtures. fixtures.
- §399 **A binding names its objects and its sent store.** The binding of a state, as the seat `mesh-broker` answers - §399 **A binding names its objects and its sent store.** The binding of a state, as the seat `mesh-broker` answers
it, gains `objects`, the bus name of its `<state>-objects` (§58), and a module's sent store is a binding entry it, gains `objects`, the bus name of its `<state>-objects` (§58), and a module's sent store is a binding entry
`sent-objects` (§59), so a module learns both names from its bindings. Why: the design's choice (Q7). How it is `sent-objects` (§59), so a module learns both names from its bindings. The binding is one form wherever the seat
answers it: in its events and in `list`'s answer, from which the grants part takes its first view of every binding
(design 25 bindings-first-view §1, §2). Why: the design's choice (Q7). How it is
checked: unit (mesh-broker's contract), a binding with both read. checked: unit (mesh-broker's contract), a binding with both read.
- §400 **No new verb is optional.** A verb G adds is not `optional`: the order of its holder's delivery is §298's - §400 **No new verb is optional.** A verb G adds is not `optional`: the order of its holder's delivery is §298's
edges, and a claim refused meanwhile is a gap of the delivery system built within G (§93). Why: the design's choice edges, and a claim refused meanwhile is a gap of the delivery system built within G (§93). Why: the design's choice