Merge pull request 'Designs and glossary follow ADR 0634, 0635 and 0636: shared code lives with its owner, the seat list from the bus (issue 1966)' (#520) from docs/domain-sdk into main

This commit was merged in pull request #520.
This commit is contained in:
2026-11-05 13:01:50 +00:00
7 changed files with 435 additions and 203 deletions
+5 -3
View File
@@ -19,9 +19,11 @@ against the records, never limits on what you check: a ruling that contradicts a
- it contradicts a decision in force, or does not do what its record's clause says;
- a new exported type, field, constant, package or module placed where the designs do not put it: check each
one yourself against the design section *Where a seat's code goes* (ask the knowledge base where a seat's code
goes): a seat's types in its contract in its domain's contracts module, or in the seat-set package while the seat
is not defined whole; logic over a seat in its client package; mesh-sdk only the runtime and seat-neutral parts,
never a seat-specific type. Check it whatever the handover says;
goes): a seat's types and state layouts in its contract in its domain's contracts module; logic over a seat in its
client package; what one module owns in that module's own package; what one domain's modules share in its domain
SDK, imported only by that domain's modules and its seats' holders and callers; mesh-sdk only the runtime and what
more than one domain uses, never a seat-specific type and never an import of a contracts module. Check it whatever
the handover says;
- an interaction that is not a mesh path: one module naming another, reading another's internals or being
special-cased in another part; a module's name in shared code; state only in memory; a poll or a timer where
an event exists; a new state without a tool that reads it or a new behaviour without a tool that steers it;
+4 -3
View File
@@ -21,9 +21,10 @@ event, which provision (requires and provides), which state on the bus (key-valu
and which module owns it, which field of a module's own manifest. A module that names another module, reads
another's internals, or is special-cased in another part is not a mesh path: name it as a finding and give the
mesh-shaped alternative. Where each new piece of code goes is the design section *Where a seat's code goes* (ask the knowledge base where a
seat's code goes): a seat's types in its contract in its domain's contracts module, or in the seat-set package while
the seat is not defined whole; logic over a seat in its client package; mesh-sdk only the runtime every module uses
and seat-neutral parts, never a seat-specific type and never a module's name. Name, for each new exported type,
seat's code goes): a seat's types and state layouts in its contract in its domain's contracts module; logic over a
seat in its client package; what one module owns in that module's own package; what one domain's modules share in
its domain SDK; mesh-sdk only the runtime every module uses and what more than one domain uses, never a
seat-specific type and never a module's name. Name, for each new exported type,
field, constant, package or module, the line of that section that places it.
**Then the work**, as playbook 07's prepare step says: per repository every file and test the change touches,
+65 -34
View File
@@ -59,8 +59,12 @@ is listed under [Homonyms](#homonyms), with the qualified form each domain uses.
A node seat may also name **the paths its holder owns**, such as `/etc/hosts`: a claim whose module does
not declare them is refused, and only a claimant declares them (ADR 0287).
- **seat definition** — the one language-neutral file (a schema with conformance fixtures) that names a seat's
verbs and their messages, its events, its state and the forms of its provisions; its Go and TypeScript types
verbs and their messages, its events, its states and the forms of its provisions; its Go and TypeScript types
are generated from it. The indexer keeps every trunk's and open pull request's as bus state (ADR 0608 §1, §2).
Every seat has one, naming its domain, written by hand in its domain's contracts module, either whole or at the seat list's grain
(its name, scope, verbs and provision), and every reader of the list of seats learns it from the bus, never from a
compiled package (ADR 0635 §1, §2).
*Not:* ~~seat-set package~~ (hq)
- **operator** — the person who runs the mesh, and the one the mesh talks to.
*Not:* ~~master~~ (hq)
- **person** — any human, as against the mesh acting unattended: *a person's word* releases a held
@@ -72,7 +76,7 @@ is listed under [Homonyms](#homonyms), with the qualified form each domain uses.
**Purpose.** To say what one module is, in the one document every other domain reads.
**Recorded in** the catalog, one manifest per module. **Upstream of** every other domain; its words
are a published vocabulary, fixed by the manifest's schema. **Decided by** ADR 0040, 0126, 0174, 0188,
0207, 0236, 0262.
0207, 0236, 0262, 0634, 0635.
**Uses:** module, seat, config value (Config), provision (Provisioning), data class (Data), health (Health
and repair).
@@ -99,25 +103,32 @@ and repair).
seat's **verb** is the same thing carried by a seat rather than a module.
- **mesh-sdk** — the library a module's own code is written against, including the SDK harness that serves a
tools bundle to the tool runner. The SDK harness is part of it, not a library of its own. It keeps the runtime
every module uses, and the seat-neutral parts two or more seats' clients or holders share; its seat contracts and
the logic it held over seats move into the contracts modules, and its seat list into the seat-set package
(ADR 0608 §5, ADR 0625 §2, §9, ADR 0627 §2).
every module uses, and what modules or seats of more than one domain share; its seat contracts and the logic it
held over seats move into the contracts modules, what one domain's modules share into its domain SDK, and what
one module owns into that module's own package. It imports no contracts module and reads the seat list from the
bus (ADR 0608 §5, ADR 0625 §9, ADR 0634 §1, §4, §5, ADR 0635 §1, §4).
*Not:* ~~tools-sdk~~
- **contracts module** — a module whose work is to publish the contract packages of seats (their verbs, events,
the layouts of their states, their messages), one per seat defined whole and per language, the seat-set package,
and the seats' client packages; every file but a client's is generated. `mesh-contracts` for the Core domain's
seats and every domain that has none yet, `mesh-<domain>-contracts` for a domain's, named after its heading on
this page; a seat defined outside the mesh's own contracts has one of its own. A seat's holder implements the
contract; the contracts module holds no seat (ADR 0608 §5, ADR 0625 §1, §5 to §7).
the layouts of their states, their messages), one per seat defined whole and per language, with the definitions
of its seats at every grain and the seats' client packages; every file but a definition's, a fixture's and a
client's is generated. `mesh-contracts` for the Core domain's seats, `mesh-config` and every domain that has none
yet, `mesh-<domain>-contracts` for a domain's, named after its heading on this page; a seat defined outside the
mesh's own contracts has one of its own. A package-only module found by its manifest, at a repository's root or in
a folder. A seat's holder implements the contract; the contracts module holds no seat (ADR 0608 §5, ADR 0625 §1,
§5 to §7, ADR 0634 §7, §8, ADR 0635 §2).
*Not:* ~~SDK module~~, ~~domain package~~
- **client package** — the one hand-written package a seat may carry beside its contract, in the seat's contracts
module and never in a holder: the logic two or more callers would otherwise each write over that seat. It uses
only its own seat's contract and mesh-sdk's runtime, never another seat's client, and names no holder, no module's
tool and no module's subject (ADR 0625 §4, ADR 0627 §1, §3). Bare "client" only where the sense is plain (see
module and never in a holder: the logic two or more callers or holders would otherwise each write over that seat.
It uses only its own seat's contract, mesh-sdk's runtime and its own domain SDK, never another seat's client, and
names no holder, no module's tool and no module's subject (ADR 0625 §4, ADR 0627 §1, §3, ADR 0634 §2). Bare "client" only where the sense is plain (see
[Homonyms](#homonyms)).
- **seat-set package** — the generated package in `mesh-contracts` holding every seat not yet defined whole at the
seat list's grain (its name, scope, verbs and provision); mesh-sdk's `catalogue` is its one importer in mesh-sdk
(ADR 0625 §2, §3).
- **domain SDK** — a package-only module, `mesh-<domain>-sdk`, named after a domain's heading on this page, holding
the logic and forms that one domain's modules and seats share and that no seat carries and no one module owns
(`mesh-delivery-sdk` the first). Its importers are its domain's modules and the holders and callers of its domain's
seats; it imports only mesh-sdk and contract packages, names no module's tool or subject, and holds no seat's type
or state layout and no state layout one module owns. Not a contracts module, and not mesh-sdk, which keeps what
more than one domain uses (ADR 0634 §1, §2, §8).
*Not:* ~~domain library~~ (hq)
- **contracts marker** — the field `generated-from` of a contracts module's package artifact, naming the directory
of the seat definitions its files are generated from; the manifest validator refuses a module carrying it that claims a seat,
and the build modules' test step regenerates and compares every module carrying it (design 26).
@@ -278,22 +289,27 @@ every other domain has something to run on. **Recorded by** the controller and t
## Placement — what runs on which node
**Purpose.** To decide, send and apply what each node runs, and to say why.
**Recorded by** the controller's `inventory` database; from ADR 0567, by the seat `mesh-placement`, in its states on
**Recorded by** the controller's `inventory` database; from ADR 0567, by the seat `mesh-module-assigner`, in its states on
the bus. **Upstream of** Provisioning, Connectivity, Data
and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 0252, 0261, 0411, 0539, 0549, 0559, 0567, 0571.
and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 0252, 0261, 0411, 0539, 0549, 0559, 0565, 0567, 0571, 0634.
**Uses:** node, machine, module, seat, manifest (Module), config value (Config), proposal (Operator and conversation).
- **assignment** — a module put on a node. Its config values on that node (module-assignment config,
Config) are `mesh-config`'s, never part of what the controller records (ADR 0348 §9).
- **placement** — which module runs on which node, with its pins, host ports and bindings, and what a person
decided of the node, as one key per node in the state `placement` of the seat `mesh-placement`. It changes only by
decided of the node, as one key per node in the state `placement` of the seat `mesh-module-assigner`. It changes only by
a placement proposal the operator approves, and no repository holds it or names a node (ADR 0567). Until ADR
0547's third milestone the controller's assignments are kept equal to it by the holder.
*Not:* ~~placement file~~ (hq)
- **mesh-placement** — the seat, held once for the mesh by the assigner, that keeps placement: its states
- **mesh-module-assigner** — the seat, held once for the mesh by the assigner, that keeps placement: its states
`placement`, `proposals` and `candidates`, and its seven verbs `placement`, `proposals`, `propose`, `withdraw`,
`again`, `adopt` and `hand-back`. Only the holder writes its states (ADR 0567).
- **seat holders** — the state `seat-holders` of the seat `mesh-placement`, written by its holder, the assigner: one
`again`, `adopt` and `hand-back`. Only the holder writes its states (ADR 0567, renamed by ADR 0634 §9). Its
contract is in `mesh-placement-contracts` (ADR 0634 §7).
*Not:* ~~mesh-placement~~
- **mesh-wants** — the seat, held once for the mesh by the assigner, that keeps what each node should run, one want
per node and module (`want.<node>.<module>`), and a person's upgrade policy, served as its verb `upgrade`
(ADR 0565 §7, ADR 0571 §8). Its contract is in `mesh-placement-contracts` (ADR 0634 §6, §7).
- **seat holders** — the state `seat-holders` of the seat `mesh-module-assigner`, written by its holder, the assigner: one
key per seat naming each holder and its node, composed from placement and the manifests' claims as the indexer
holds them, and rewritten only when a key differs. The tool runner and every other reader of who holds a seat
read it there, never the controller (ADR 0571 §6, §7).
@@ -301,11 +317,11 @@ and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 02
of each key they were laid over, the nodes' answers, the verdict and the ask. It takes effect only on the operator's
Approve, and is discarded if declined, expired or withdrawn. A kind of *proposal* (Operator and conversation).
- **placement candidate** — one node's placement as a placement proposal lays it, kept in the state `candidates`
of the seat `mesh-placement` under `<proposal>.<node>` while the proposal is open. The node composes it in
of the seat `mesh-module-assigner` under `<proposal>.<node>` while the proposal is open. The node composes it in
**shadow** (composed and compared, never applied) and answers in its own have whether what it would run is the
same; the verdict fails only on a refusal or a node that has not answered (ADR 0567). Not the indexer's candidates of
a group, which are fingerprints (Delivery).
- **hand-back** — the operator's verb `hand-back` of the seat `mesh-placement`, answered until the lease write: it
- **hand-back** — the operator's verb `hand-back` of the seat `mesh-module-assigner`, answered until the lease write: it
deletes every placement key, and every node returns to the compositions the controller sends. A hand act (ADR 0567).
- **pending assignment** — an assignment of a module whose build is still running, kept, and made by the
controller when that build registers the module, or ended with why when it does not
@@ -341,7 +357,7 @@ and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 02
composition `<node>.node`. Its digest, with the controller's judgement of the move from what the node runs
now, is kept under the same key in `mesh-controller_composed`; a want names that digest. Derived state, kept
while its fingerprint is among the module's five newest or a want names it, and no link of the chain
(ADR 0539 §1 to §4). From release 6. Not mesh-sdk's composition, the function that composes it (see
(ADR 0539 §1 to §4). From release 6. Not the composition of `mesh-delivery-sdk`, the function that composes it (see
[Homonyms](#homonyms)), nor the **composition check** (Delivery).
- **send** — the controller giving a node its declaration, until release 6 of ADR 0429. The controller's verb
`push` asked for a send (of one node, or of every node); prose says send. From release 6 nothing is sent: the
@@ -431,13 +447,19 @@ and vault (Identity and access).
**Purpose.** To issue, hold, rotate and check the credentials and grants by which modules, nodes, agents
and the operator act. **Recorded by** the controller's `identity` and `licences` databases and the vault.
**Upstream of** Provisioning, Delivery, and Operator and conversation.
**Decided by** ADR 0085, 0113, 0183, 0225, 0234, 0386, 0387, 0388, 0411, 0413, 0414, 0550, 0571.
**Decided by** ADR 0085, 0113, 0183, 0225, 0234, 0386, 0387, 0388, 0411, 0413, 0414, 0550, 0571, 0587, 0634.
**Uses:** module, node, seat, tool (Module), operator, consumer and provision (Provisioning), agent and ask (Operator and conversation).
- **credential** — anything that proves an identity: a password, a key, a token. A **pair credential**
is the two ends of one credential, the consumer's and the provider's.
- **secret** — a credential or other value the vault keeps sealed and the node-engine unseals into a
file at apply (ADR 0085).
- **secret service** — a node's store of a person's secrets that the programs of a desktop session ask, the node seat
`node-secret-service`, of this domain.
- **mesh-node-enroller** — the seat, held once for the mesh by the module `mesh-enrolment`, through which a new
machine joins: a one-time token issued on the operator's Approve, redeemed once by the machine's node-engine, the
node's account minted and signed by the vault (ADR 0587 §1, renamed by ADR 0634 §9). The module keeps its name;
only the seat's changed. Its contract is in `mesh-identity-and-access-contracts` (ADR 0634 §7).
- **secret family** — a module's own secrets declared as one name ending in `-*`; each member is the prefix
and a name (`smb-password-<source>`, one per source the module's config values name), given by its full name
and never made by the mesh
@@ -508,7 +530,7 @@ controller (the planner, the rollout) until release 6 of ADR 0429; from each rel
§1, §2, ADR 0614). Nothing stores the order.
*Not:* ~~build graph~~
- **composition check** — whether the machines a change touches still compose with it, run by the assigner
from the modules the indexer and the builder hold, with mesh-sdk's composition, the one the controller
from the modules the indexer and the builder hold, with the composition of `mesh-delivery-sdk`, the one the controller
composes with
(ADR 0446). It replaces the controller's check of that kind, which composed every machine from release 4 of
ADR 0429 back (issue 1303).
@@ -730,6 +752,9 @@ controller (the planner, the rollout) until release 6 of ADR 0429; from each rel
judged (a witness, ADR 0236).
- **the lab**, **scenario**, **replay** — a real mesh raised to run a change against before it reaches
nodes; what a lab run declares; and a recorded failure run again to prove a fix (ADR 0016, ADR 0237).
- **git seat** — the seat `git`, held once for the mesh by the forge, delivering the provision `git`: the
repositories a build comes from ([26 — The seats](../03-DESIGN/01-to-be/26-the-seats.md) *The git seat*). A seat of
this domain.
## Health and repair — what is wrong, and putting it right
@@ -845,19 +870,24 @@ becomes a message). **Decided by** ADR 0227, 0231, 0240, 0252, 0283, 0564.
**Purpose.** To make every node and module reachable by name where it should be, and unreachable where
it should not. **Recorded by** the controller. **Upstream of** Provisioning and Health and repair.
**Decided by** ADR 0007, 0117, 0138, 0223, 0226, 0247.
**Decided by** ADR 0007, 0117, 0138, 0223, 0226, 0247, 0634.
**Uses:** node, machine, assignment and endpoint.
- **private network** — the mesh's own encrypted network between its nodes, on which every node has an
address and a mesh name (ADR 0226).
The network the machine sits on without it is the **underlay**.
*Not:* ~~overlay~~ (hq)
- **the-private-network** — the node seat of the private network, held by the module `mesh-wireguard`, whose
provision is the value a module reads to reach the private network. Its contract, holding that value's layout and
no key material, is in `mesh-connectivity-contracts`, a folder module of the mesh-networking repository beside its
holder; it has a client only where its callers share logic, and the private network's manifest and peer generator
are the holder's own (ADR 0634 §6, ADR 0636 §6).
- **anchor** and **hub** — the roles a node plays for the private network: the anchor is reachable from
outside and every node reaches it; a hub relays for nodes that cannot reach each other directly.
- **resolver** — a seat holder that answers the mesh's names; a node lists only the mesh's resolvers
(ADR 0223), or, where it holds one, its own resolver, which asks them. **uplink** — a node's
connection to the outside network, a seat (ADR 0117). **hostname** — a node's own name,
a seat.
- **resolver** — a seat holder that answers the mesh's names, of the replicated seat `mesh-dns-resolver`; a node
lists only the mesh's resolvers (ADR 0223), or, where it holds one, its own resolver, which asks them. **uplink** — a
node's connection to the outside network, the node seat `node-uplink` (ADR 0117). **hostname** — a node's own name,
the node seat `node-hostname`. All three seats are of this domain.
- **split DNS** — resolving names by domain on one machine: a VPN's domains through the VPN's servers
over its link, every other name through the mesh's resolvers. The provision `split-dns`, provided by
the holder of the node seat `node-resolver`, the machine's **own resolver**, which exists only where
@@ -867,7 +897,8 @@ it should not. **Recorded by** the controller. **Upstream of** Provisioning and
- **packet filter** — what the mesh enforces on a node about which packets pass, the
`node-packet-filter` seat and its verb `rules`. A **found firewall** is a program the mesh found on a
machine and keeps retired or in force; *firewall* says that, and a wrapped program's firewall is its own.
- **intrusion prevention** and **ban** — refusing a source that misbehaved, and the refusal itself.
- **intrusion prevention** and **ban** — refusing a source that misbehaved, and the refusal itself; the node seat
`node-intrusion-prevention` is of this domain.
- **reach** — how far an assignment's endpoint may be reached from: the node, the private network, or
the outside (ADR 0138).
@@ -1173,7 +1204,7 @@ renaming one sense, the old sense moves to a *Not:* line and becomes mechanical.
| | the sending of one batch of commits across nodes, until release 6 | **rollout** (verb `plans`) | Delivery |
| | a step a person starts, like the bus's | **planned step** | Core |
| composition | one module's resources for one node at one fingerprint, signed, from release 6 | **composition** | Placement |
| | the function in mesh-sdk that composes them, which the controller and the assigner both run | **mesh-sdk's composition** | Placement |
| | the function in `mesh-delivery-sdk` that composes them, which the node agents, the assigner and the grants part run (ADR 0635 §4) | **the composition of `mesh-delivery-sdk`** | Placement |
| | whether the machines a change touches still compose | **composition check** | Delivery |
| membership | the subjects a bus account is issued | **membership** | Provisioning |
| binding | the names on the bus of a module's bus state, in its membership | **binding** of bus state | Data |
+16 -7
View File
@@ -83,21 +83,30 @@ Three questions, answered from the machine:
thing works: the credential authenticates, a wrong one is refused, the containers reach each
other, the data survives a restart.
## A module that calls or holds a seat
## A module that calls or holds a seat, or shares code
*Added 2026-11-04* (ADR 0625, ADR 0627). Where each piece of a seat's code goes is
[design 20](../../03-DESIGN/01-to-be/20-writing-a-module.md) *Where a seat's code goes*; in short:
*Added 2026-11-04, widened 2026-11-05* (ADR 0625, ADR 0627, ADR 0634, ADR 0635). Where each piece of shared code goes
is [design 20](../../03-DESIGN/01-to-be/20-writing-a-module.md) *Where a seat's code goes*: shared code lives with its
owner. In short:
- **Import the seat's contract and, where it has one, its client package**, from the seat's contracts module
(`mesh-contracts` today), never the holder module and never a copy of the seat's types in your own code.
- **Import the seat's contract and, where it has one, its client package**, from the seat's domain's contracts module
(`mesh-<domain>-contracts`, or `mesh-contracts` for the Core domain and a domain with none), never the holder module
and never a copy of the seat's types in your own code. The list of seats your module needs is read from the bus,
never compiled in.
- **Logic you would write over a seat that another caller would write too** belongs in that seat's client package,
through a change of the contracts module; it never goes into mesh-sdk, and never into the holder.
- **Logic and state layouts only your module owns**, where it holds no seat for them, are your module's own package;
another repository's module imports it only by a published version, so your manifest declares it as a package
artifact.
- **Logic your domain's modules share, owned by no one module and carried by no seat**, is your domain's SDK's
(`mesh-<domain>-sdk`); only what modules of more than one domain use goes into mesh-sdk.
- **Holding a seat** means implementing its contract in your module. A seat you define yourself gets a contracts
module of its own (it may share your repository); a role no other module could ever take over is your module's
own tools and events, not a seat.
The review of the pull request checks this; the checks design 26 lists refuse a stray file in a contracts module,
a seat named in mesh-sdk and a client reaching another seat's client.
The review of the pull request checks the home of every new exported name; the checks design 26 lists refuse a stray
file in a contracts module, a seat named in mesh-sdk, an import of a contracts module from mesh-sdk, and a client
reaching another seat's client.
## What the first port actually cost
+26 -13
View File
@@ -6,10 +6,16 @@ code:
- mesh-controller internal/builder
- mesh-sdk src
- mesh-contracts
updated: 2026-11-04
updated: 2026-11-05
decisions:
- "ADR 0634"
- "ADR 0635"
- "ADR 0625"
- "ADR 0627"
- "ADR 0573"
- "ADR 0606"
- "ADR 0609"
- "ADR 0593"
- "ADR 0150"
- "ADR 0099"
- "ADR 0053"
@@ -55,30 +61,37 @@ Split the way the protocol is split:
- **No storage, no HTTP framework, no logging library.** Use the language's.
If you find yourself wanting to add something to the SDK, the test is ADR 0039's: **does editing it
recompile unrelated modules, and does it change often?** Both, and it does not belong.
recompile unrelated modules, and does it change often?** Both, and it does not belong. It goes to
its owner instead: a seat's contract or client, the module that owns it, or its domain's SDK (*Where a seat's code
goes*, below; ADR 0634 §1).
## Where a seat's code goes
*Added 2026-11-04* (ADR 0625, ADR 0627; [design 26](26-the-seats.md) *Where a seat's code goes* is the
whole rule). A module that calls or holds a seat meets three places, and each piece of code has one:
*Added 2026-11-04, widened 2026-11-05* (ADR 0625, ADR 0627, ADR 0634, ADR 0635; [design 26](26-the-seats.md) *Where
a seat's code goes* is the whole rule). Shared code lives with its owner (ADR 0634 §1): a module that calls or holds
a seat, or shares code with other modules, meets these places, and each piece of code has one, the first that fits:
| the code | lives in | written |
|---|---|---|
| a seat's types, subjects and a typed call per verb or accept | the seat's **contract**: a package of its own per language in the seat's contracts module (`mesh-contracts`, or `mesh-<domain>-contracts` once its domain moves), or the seat-set package while the seat is not yet defined whole | generated from the seat's definition, never by hand |
| logic two or more callers would otherwise each write over that seat (an ask and its awaited answer, publishing a file in parts to a registry) | the seat's **client package**, beside its contract, owned by the contracts module | by hand; it uses only its own seat's contract and mesh-sdk's runtime, never another seat's client, and names no holder, no module's tool and no module's subject |
| what every module needs whatever the seat (the bus connection, the tools bundle's runtime, state), and a part several seats' clients or holders share that names no seat, verb or seat's type (moving a file in parts) | **mesh-sdk** | by hand; it takes no seat-specific type |
| a seat's types, subjects, the layouts of its states and a typed call per verb or accept | the seat's **contract**: a package of its own per language in its domain's contracts module (`mesh-<domain>-contracts`, or `mesh-contracts` for the Core domain and a domain with none), for a seat defined whole; a seat at the seat list's grain has only its definition, which every reader reads from the bus (ADR 0635 §1, §2) | generated from the seat's definition, never by hand |
| logic two or more callers or holders would otherwise each write over that seat (an ask and its awaited answer, publishing a file in parts to a registry) | the seat's **client package**, beside its contract, owned by the contracts module | by hand; it uses only its own seat's contract, mesh-sdk's runtime and its own domain's SDK, never another seat's client, and names no holder, no module's tool and no module's subject |
| logic and state layouts one module owns, where it holds no seat for them | that module's **own package**, which other modules import from it: by path in the same repository (ADR 0609 §2), by a published version from another (ADR 0573 §1), so its manifest declares it as a package artifact; its fingerprint counts only that package's files (ADR 0593 §2), so its importers rebuild only when it changes | by hand, in the owner module |
| logic and forms no seat carries and no one module owns, used by one domain's modules or seats only (moving a file in parts, the build steps' shared logic) | that domain's **domain SDK**, `mesh-<domain>-sdk`, a package-only module (ADR 0634 §2) | by hand; it imports only mesh-sdk and contract packages, names no module's tool or subject, and holds no seat's type or state layout |
| what every module needs whatever the seat (the bus connection, the tools bundle's runtime, state), and a part modules or seats of more than one domain share that names no seat, verb or seat's type | **mesh-sdk** | by hand; it takes no seat-specific type, imports no contracts module, and reads the seat list from the bus |
| the seat's implementation | the **holder** module | by hand, against the contract; never the place of the contract or a client |
**So a module imports the contracts and clients of the seats it uses, and mesh-sdk.** Its users rebuild when a
seat they use changes, not when any seat does. A seat you define yourself, outside the mesh's own contracts, gets a
**So a module imports the contracts and clients of the seats it uses, the packages of the modules whose state it
reads, its domain's SDK where it uses it, and mesh-sdk.** Its users rebuild when something they use changes, not when
any seat or any domain does. A seat you define yourself, outside the mesh's own contracts, gets a
contracts module of its own, which may share your holder module's repository (ADR 0625 §7): a second holder then
imports only the contract, never your holder. And a role no other module could ever take over is no seat at all:
it is your module's own tools and events (ADR 0625 §8).
imports only the contract, never your holder. A contracts module and a domain SDK are modules like any other, found by
their manifests in folders of a repository and delivered to no node (ADR 0634 §8). And a role no other module could
ever take over is no seat at all: it is your module's own tools and events (ADR 0625 §8).
*How it is checked:* by the checks design 26 lists: the generators' tests, mesh-sdk's import and package-list tests,
the build modules' regenerate-and-compare step, the catalog check of a client and the manifest validator's refusal of a
contracts module that claims a seat; a role made a seat, or a seat left as one module's tools, by the review of the
pull request that adds it.
contracts module that claims a seat; the home of each new exported name, a domain SDK's importers included, and a role
made a seat or a seat left as one module's tools, by the review of the pull request that adds it (ADR 0634 §10).
## A module with four capabilities, in four languages
+278 -112
View File
@@ -15,9 +15,18 @@ code:
- mesh-catalog modules/gitea/module.json
- mesh-controller internal/inventory/migrations/0063-a-machines-names-are-one-seats.sql
- mesh-controller internal/inventory/migrations/0064-the-resolver-file-is-the-uplinks.sql
updated: 2026-11-04
updated: 2026-11-05
decisions:
- "ADR 0636"
- "ADR 0634"
- "ADR 0635"
- "ADR 0596"
- "ADR 0593"
- "ADR 0625"
- "ADR 0573"
- "ADR 0606"
- "ADR 0609"
- "ADR 0595"
- "ADR 0627"
- "ADR 0608"
- "ADR 0613"
@@ -151,18 +160,18 @@ convention, which later seats departed from.
| `mesh-vault` | — | mesh | `secret` | the vault (in the seed since ADR 0161; the earlier *reserved* named an effect no rule produced) |
| `container-image-registry` | `mesh-artifact-store` for images (ADR 0605; the old name readable until every node-engine reads the new one) | mesh | `artifact-store` | the artifact registry (`distribution`) |
| `oci-artifact-registry` | `mesh-artifact-store` for bundles and archives (ADR 0605) | mesh | `artifact-store` | the artifact registry (`distribution`) |
| `mesh-catalog` | `the-catalogue` | mesh | — | the catalog |
| `mesh-catalog` | `the-catalogue` | mesh | — | the indexer |
| `npm-package-registry` | — (its rename to `mesh-*` stays deferred, ADR 0156 §8) | mesh | `npm-package-registry` | the forge |
| `go-package-registry` | — (ADR 0605) | mesh | `go-package-registry` | the forge |
| `mesh-git` | `git` | mesh | `git` | the forge |
| `git` | — | mesh | `git` | the forge |
| `mesh-build-machine` | `the-build-machine` | node | — | a builder |
| `mesh-resolver` | — | mesh | — | the mesh's resolvers, each holding every node's internal domain (ADR 0194); replicated, held on the anchor and the home server (ADR 0223) |
| `mesh-dns-resolver` | — | mesh | — | the mesh's resolvers, each holding every node's internal domain (ADR 0194); replicated, held on the anchor and the home server (ADR 0223) |
| ~~`mesh-dns-port`~~ | `the-dns-port` | node | — | retired by ADR 0194: the local resolver became the mesh's one; deleted from the set, and from the store's table, once nothing claimed it (ADR 0220) |
| `node-hostname` | `node-hosts-file` (renamed by ADR 0223; the former name an alias) | node | — | owns a machine's names: `/etc/hostname`, written from its holder's `hostname` setting with no default and taking effect at the next boot, and in `/etc/hosts` the machine's own lines and the operator's kept region, changed through its verbs `entries`, `add`, `remove` (ADR 0199). Held by the module `hostname`, formerly `hosts` |
| `node-resolver` | — | node | — | a machine's own resolver, held only where something requires `split-dns`: it writes `/etc/resolv.conf` there (the uplink's holder steps back from it) and routes a VPN's domains to the VPN's servers over its link through its verbs `routes`, `route`, `unroute` (ADR 0247). Held by the module `systemd-resolved` |
| `mesh-intrusion-prevention` | `the-intrusion-prevention` | node | — | an intrusion-prevention service |
| `mesh-packet-filter` | `the-packet-filter` | node | — | the packet filter |
| `mesh-private-network` | `the-private-network` | node | — | the private network the mesh runs over |
| `the-private-network` | — (it keeps its name, ADR 0636 §6) | node | — | `mesh-wireguard`, the private network the mesh runs over |
| ~~`mesh-resolver-configuration`~~ | `the-resolver-configuration` | node | — | retired by ADR 0223: `/etc/resolv.conf` is written by the holder of `node-uplink`, the program that would otherwise rewrite it, and the need of the uplink beside it (ADR 0220) goes with it; deleted from the set, and from the store's table, once nothing claims it |
| `mesh-showcase` | `the-showcase` | node | — | the showcase module |
@@ -178,24 +187,49 @@ merges.
## A seat's definition (from 2026-11-03)
**A seat is defined once, in one language-neutral file, and every part learns it from the bus** (ADR 0608, from
research 081). The registry seats (design 51), `mesh-broker` (design 25 § 12.3), the four seats the milestone
adds verbs or events to, and `node-engine` are the first ten defined whole this way, in the milestone of ADR 0612
(ADR 0613 §1, ADR 0620, ADR 0627 §1).
research 081). Every seat has its definition file in its domain's contracts module, written by hand, whether it is
defined whole or at the seat list's grain (ADR 0635 §2). In the milestone of ADR 0612, 25 seats are defined whole
(ADR 0613 §1, ADR 0620, ADR 0627 §1, ADR 0634 §6, ADR 0635 §5); every other seat is defined at the seat list's grain.
- **The file** is a schema with conformance fixtures: the seat's verbs and their messages, its events and their
bodies, its state and the forms of its provisions. It lives in the contracts module of its domain (*Where a seat's
bodies, its states and the forms of its provisions, and names its glossary domain (ADR 0636 §5). It lives in the
contracts module of its domain (*Where a seat's
code goes*, below). Its Go and
TypeScript types are generated from it and committed beside it; nobody writes them by hand. The format is JSON, its
messages and bodies JSON Schema, as the seat set's verb inputs already are, with the format's own schema checked in;
the fixtures are data, never functions.
- **What is defined whole now** (ADR 0613; ADR 0620, on the
operator's answer of 2026-11-04 to move them all). Every seat this milestone changes is defined whole and its
contract moves into `mesh-contracts`: `npm-package-registry`, `go-package-registry`, `container-image-registry`,
`oci-artifact-registry` (one common part of their shared verbs, written once, so the four cannot drift) and
`mesh-broker`; and the four seats it adds a verb or an event to, each with every verb its holder's manifest on the
trunk declares today, plus the new ones. **No event moves subject in this milestone** (ADR 0620): a seat's own events are only those published on its seat subject today,
and a holder's own events stay under the holder's own subjects, listed beside the seat they belong with, never moved
onto it:
- **A definition names its states** (ADR 0608 §1; ADR 0634 §6 leaves the file's format to this design). A `states`
entry is what any holder of the seat keeps and what the seat's verbs answer from: its local name, its template of
ADR 0595 (state, events, work queue, log, objects or ledger), who writes it, each form of its keys with the schema
of the value under it, how long it keeps its history, and its size cap. The holder writes every state; only a state
of the events or log template may name each caller as the writer of its own keys, granted on keys under the
caller's own name and no other. No entry names a holder module, so another module can hold the seat by importing
the contract alone (ADR 0625 §7). The generated package carries, per state, its local name, a builder and a parser
per key form, and the value types, decoded strictly; the fixtures hold one value per key form.
`operator-channel`'s `records` becomes one `states` entry in the same change, so the format has one form for a
state. The format's own schema refuses an entry with an unknown template or a key form without a value, naming it.
**A state is the holder's, asked through the seat's verbs** (ADR 0596 §2): the module that reads another module's
bucket directly today keeps doing so only as a reader in transition, through its own manifest's `reads`, until its
module moves to the owner's verbs or events (ADR 0596 §3, issue 1966). The bucket's name stays
`<holder>_<state>`, the holder's own, so nothing moves under a seat's rename. **How it is checked**: the manifest
validator refuses a holder whose `requires` does not declare each of its seat's states with the same local name and
template, naming the state; the format's own schema refuses a caller-written state of another template and a state
with no size cap.
- **What is defined whole.** Ten seats came first (ADR 0613; ADR 0620, on the operator's answer of 2026-11-04 to move
them all; ADR 0627 §1): `npm-package-registry`, `go-package-registry`, `container-image-registry` and
`oci-artifact-registry` (one common part of their shared verbs, written once, so the four cannot drift),
`mesh-broker`, the four seats the milestone adds a verb or an event to, each with every verb its holder's manifest
on the trunk declares today, plus the new ones, and `node-engine`. Fifteen more are defined whole in the same
milestone because a name leaving mesh-sdk (*Where a seat's code goes*) is their type, state layout or message
(ADR 0634 §6, ADR 0635 §5), each with its verbs, events, messages and the layouts of its states:
`mesh-module-assigner`, `mesh-wants`, `mesh-node-enroller`, `mesh-catalog`, `node-build-go`, `node-build-image`,
`node-build-node`, `mesh-hand-acts`, `mesh-provisioner`, `mesh-conditions`, `mesh-self-check`, `mesh-healers`,
`mesh-watchdogs`, `the-private-network` and `mesh-delivery`. Five seats already whole gain the state layouts
mesh-sdk held for them: `node-engine`, `mesh-vault`, `mesh-delivery-checker`, `container-image-registry` and
`oci-artifact-registry` (ADR 0634 §6). **No event moves subject in this milestone** (ADR 0620), but those of the
two renamed seats (*Two seats renamed*, below): a seat's own events are only those published on its seat subject
today, and a holder's own events stay under the holder's own subjects, listed beside the seat they belong with,
never moved onto it. The four seats the milestone adds to:
- `mesh-delivery-checker` (held by the checker): the verbs `try`, `try-read` and `try-end`; no seat event. The
holder's own, beside it: its family `checker.asks`, new, with `tag-wanted` (design 51 *Tagged versions*) and
`verdict` (*Out of step is learned from events*, below); and its stream `checks`, which already declares
@@ -207,35 +241,92 @@ adds verbs or events to, and `node-engine` are the first ten defined whole this
`own`, `issue-account`, `sign`, `enrolment-token`, `node-account`, `rotate-node`, `node-account-confirm`,
`abandon` and, new, `retire`; no seat event. The holder's own, beside it: `provisioned`, `rotated`,
`secret-changed`, `account-hashed`, `own-replaced`, `account-issued`, `signed`, `rotation-begun`,
`node-account-ready`, `rotation-done` and, new, `own-retired` (design 32). The seat list mesh-sdk compiles
today also names `accept` and `move-asks`, which no holder serves and nothing calls: the same mesh-sdk change
drops them from the seat, and its `vault_seat_test.go` with them.
`node-account-ready`, `rotation-done` and, new, `own-retired` (design 32). The seat list mesh-sdk compiled
also names `accept` and `move-asks`, which no holder serves and nothing calls: the definition drops them.
- `operator-channel` (held by the router, the messenger module): the verbs `open`, `history`, `notify`, `tell`
and `asks`; the asks it accepts, `ask`, `cancel` and `condition`; its seat event `decided`; `by-caller` for
`ask`, `cancel`, `condition` and `decided`; and `records` `asks`, as the holder's seat entry has them. The
`ask`, `cancel`, `condition` and `decided`; and its state `asks`, as the holder's seat entry has them. The
holder's own, beside it: `refused`, and `offered` and `closed`, new, on its restricted stream (design 55 § 11).
mesh-sdk's seat list holds no entry for it today, so its definition is written from the holder's seat entry.
- `node-engine` (held by the node-engine on every node): the verb `manifest-reads` and its seat event
`manifest-reads-changed`, which the milestone gives it (ADR 0618 §3, §4; design 51), so it is defined whole too
(ADR 0627 §1).
Its definition is written from the holder's seat entry.
And `node-engine` (held by the node-engine on every node) has the verb `manifest-reads` and its seat event
`manifest-reads-changed`, which the milestone gives it (ADR 0618 §3, §4; design 51). The 25, by the contracts
module that holds each (ADR 0634 §7, ADR 0635 §5):
| contracts module | domain | seats | held by |
|---|---|---|---|
| `mesh-delivery-contracts` | Delivery | `mesh-catalog`, `mesh-delivery-checker`, `mesh-delivery`, `node-build-go`, `node-build-image`, `node-build-node`, `npm-package-registry`, `go-package-registry`, `container-image-registry`, `oci-artifact-registry` | the indexer, the checker, the gate part, the three build modules, the forge (two), `distribution` (two) |
| `mesh-placement-contracts` | Placement | `mesh-module-assigner`, `mesh-wants` | the assigner |
| `mesh-health-and-repair-contracts` | Health and repair | `mesh-conditions`, `mesh-self-check`, `mesh-healers`, `mesh-watchdogs`, `mesh-hand-acts` | the condition keeper, mesh-self-check, mesh-healers, the watchman, mesh-hand-acts |
| `mesh-provisioning-contracts` | Provisioning | `mesh-provisioner` | mesh-provisioner |
| `mesh-identity-and-access-contracts` | Identity and access | `mesh-vault`, `mesh-node-enroller` | the vault, the module `mesh-enrolment` |
| `mesh-connectivity-contracts` (mesh-networking repository, ADR 0636 §6) | Connectivity | `the-private-network` | `mesh-wireguard` |
| `mesh-operator-and-conversation-contracts` | Operator and conversation | `operator-channel` | the router |
| `mesh-contracts` | Core, and Config | `node-engine`, `mesh-broker`, `mesh-config` | the node-engine, the bus module, mesh-config |
**How it is checked**: each of these definitions' conformance fixtures names every event's subject, the seat's and
the holder's own, and a case of the holder's whole-module test takes a fixture from the running holder (an event
it publishes) and matches it, subject and body, against the definition. The builder and the indexer hold no seat:
it publishes) and matches it, subject and body, against the definition; the indexer's generator tests cover each
of the 25 (ADR 0634 §10, ADR 0635 §6). The builder and the indexer's own events hold no seat:
the builder's `numbered` and `old-form-closed`, the indexer's `manifest-accepted` and its family `indexer.views`
are those modules' own, declared in their own manifests, which
ADR 0613 does not govern, and no seat definition carries them. Every other seat, the foundation's included, has its definition on the bus in the same milestone, all
at once (ADR 0608 §2), holding the seat list's grain: its name, whether it is held once or on every node, its verbs
with their inputs and its provision, generated from the seat list mesh-sdk compiles today, so the move changes
nothing a reader sees (ADR 0613 §2). Its messages, events and state layouts are added to
a seat's definition when its domain's contract next changes, which moves that domain's seats whole into its contracts
module in that change (ADR 0613 §3); which domain moves when is tracked by issue 167 (ADR 0613 §4).
- **The seat set has one source.** The manifest validator, the node-engine, the tool runner and claude-code each compile a copy of
the seat list today; all four import the seat-set package generated in `mesh-contracts` instead, which is where
mesh-sdk's seat list moves (ADR 0625 §2), and mesh-sdk's `catalogue` reads the seat set from there. The tool runner's descriptions
and claude-code keep that compiled set, equal to the trunk's definition, in this milestone, so neither depends on
the indexer at its start; they move to `indexer_seat_definition` and its event when their next change moves them,
tracked on issue 1966.
with their inputs and its provision. Its file is written by hand in its domain's contracts module, or in
`mesh-contracts` where its domain has none; the files the registry branch generated once from mesh-sdk's seat list
are their starting text, and nothing generates them again (ADR 0635 §2). Its messages, events and state layouts are
added to a seat's definition when its domain's contract next changes, which moves that domain's seats whole into its
contracts module in that change (ADR 0613 §3); which domain moves when is tracked by issue 167 (ADR 0613 §4).
- **Two seats renamed** (ADR 0634 §9): `mesh-placement` becomes `mesh-module-assigner`, and `mesh-enrolment`
becomes `mesh-node-enroller`; the module `mesh-enrolment` keeps its name. Their definitions, the subjects of their
verbs and events, every holder's claim, every caller's `invokes` and grant, the claude-code instructions' text, the
mesh MCP server's description and the glossary take the new names in the same delivery group, and nothing answers
to the old ones: a call on an old subject fails loudly, naming the seat. **No state moves**: a state's bucket is
the holder's (`<holder>_<state>`), so placement keeps every revision. The old subjects' events keep only their
stream's own retention. **The order is the module graph's**: the grants of the new subjects are an ordering edge
that comes before the holders move, and the callers come after the holder that serves the renamed verbs; an older
node-engine takes its new build through `mesh-wants`, which is not renamed. **A machine enrolling with an installer
built before the rename** asks the old enrolment seat's subject, which no holder answers: its enrolment fails
loudly, naming the seat, and nothing joins; it enrols with an installer of the new build. **How it is checked**: the
manifest validator refuses a claim, an `invokes`, a `consumes` or a grant naming a seat no definition names, so an
old name fails
(ADR 0634 §10); the acceptance after the delivery is that `mesh-module-assigner.placement` answers and
`mesh-placement.placement` is not found, placement's revisions are those it had before, and no `seat-holders` key,
finding or condition stays under an old name.
- **Every part learns the seat list from the bus, through the seat `mesh-catalog`** (ADR 0635 §1, as ADR 0636 §4
changes it). The node-engine, the tool runner, claude-code, the manifest validator and every other reader of the
list of seats call, through mesh-sdk's runtime, the verb `seat-definitions` of the seat `mesh-catalog`: without a
seat it answers every trunk's seat with its digest, with a seat that seat's definition. They hear the seat's events
`seat-definition-changed` and `seat-definition-removed`, said for the trunks' definitions. A reader names no
module: it calls the seat, never its holder, the indexer, by name. A running part acts on what is merged, so a
reader takes no definition from a pull request's head; the manifest validator inside a delivery group's check is
the one reader that reads the members' heads. No package carries the seat list, and mesh-sdk imports no contracts
module.
**A reader without the list acts on nothing new, and keeps running what it runs** (ADR 0636 §1, changing ADR 0635
§3). A reader that starts without the list applies, composes, grants and judges nothing new, except for the
bootstrapped modules below; it keeps running what it already runs, says in its health what it waits for, and a
node-engine after a reboot starts again the compositions it last applied. Each node writes one finding when its
readers have waited five minutes (ADR 0564 §2); the condition keeper raises it as a condition, which reaches the
operator as every condition does, and clears it at the first full read. A reader away longer than the events'
retention reads the whole list again. A check that cannot read the definitions it needs fails, naming them, rather
than passing without them.
**The bootstrapped definitions** (ADR 0636 §2, changing ADR 0608 §2) are the foundation's seats (research 075) and
`mesh-catalog`, `mesh-conditions` and `operator-channel`: every reader carries them from its start and acts on their
holders without the list, so after a full restart the indexer comes back and serves the list, and the condition
keeper and the router, with the channels it sends through (ADR 0259), carry a wait's condition to the operator.
**A missing or refused definition blocks new claims only** (ADR 0636 §3): when a seat's definition is missing,
removed or refused, readers refuse a new claim, `invokes` or grant of that seat, and never stop or remove a module
that already runs holding or calling it.
**Each reader's list has two tools** in mesh-sdk's runtime bundle (ADR 0636 §7): one reads which definitions a
reader holds, their source and digests and what it waits for; the other, granted to the operator and the healers,
makes it read the list again now.
**How it is checked** (ADR 0635 §6, ADR 0636 §8; *Acceptance tests*, below): the node-engine's whole-module test,
against a stand-in of the seat, acts on nothing but the bootstrapped modules without the list and on all once it
answers, and starts its last compositions after a restart; the indexer's whole-module test starts with only the
bootstrapped seats and answers `seat-definitions`; with the indexer kept down, the node's finding is written after
five minutes and the condition keeper raises it to the operator; a refused definition stops no running module; a
reader refuses a pull request head's definition; mesh-sdk's unit test refuses a module's name in the runtime; both
tools answer.
- **The generator** is each language's build module's: the Go build module's package for Go, the Node and TypeScript
build module's for TypeScript (the logic is the module's whose work it is). The generator's version is a build input
of every contracts module, so a changed generator rebuilds each contracts module once (ADR 0603 §3). Each build
@@ -249,84 +340,136 @@ adds verbs or events to, and `node-engine` are the first ten defined whole this
contracts module runs then. The indexer consumes both and writes the trunk's finding.
- **On the bus**: the indexer reads every seat definition from every trunk and open pull request, from the forge
bridge's messages, and keeps them in its own state `seat-definitions`, keyed by seat and source (the trunk, or a
pull request's head), as their only writer; a definition is at most 256 KiB. It emits `seat-definition-changed`
(seat, source, digest), kept seven days, only when a digest changed. **Only the trunk's entry is the seat's
contract**: the readers (the contracts modules' builds, the review, and later the tool runner's descriptions) ask
`indexer_seat_definition`, which answers the trunk's unless a pull request's head is named, and says which it
answered. A pull request's entries are removed on the forge bridge's event that it closed or merged. A definition
pull request's head), as their only writer; a definition is at most 256 KiB. As the holder of `mesh-catalog` it says
`seat-definition-changed` (seat, source, digest), kept seven days, only when a digest changed, and
`seat-definition-removed` when a trunk's definition goes, both as events of the seat (ADR 0636 §4, changing ADR 0608
§3). **Only the trunk's entry is the seat's contract**: the readers of the seat list read the trunks' through
`seat-definitions`; the contracts modules' builds and the review ask `indexer_seat_definition`, which answers the
trunk's unless a pull request's head is named, and says which it answered. **The manifest validator inside the
delivery group's one check reads the seat definitions at every member's head** (ADR 0636 §4), so a group that
changes a definition is judged by what it merges. A
pull request's entries are removed on the forge bridge's event that it closed or merged. **A seat of the mesh's own
contracts is defined only in its domain's contracts module, and a move there is a handover** (ADR 0636 §5, changing
ADR 0608 §2): a definition names its glossary domain; the indexer takes a seat of the mesh's
own contracts only from that domain's contracts module, `mesh-contracts` for a domain with none, and refuses a
definition of it from any other module, with a finding naming both. A definition moving from `mesh-contracts` to its
domain's contracts module is a handover: the indexer keeps the last trunk definition until the new one is merged,
in either order of the two trunks. A seat defined outside the mesh's own contracts is taken from its own contracts
module (ADR 0625 §7). A definition
that does not parse fails its pull request's check; the trunk's stays. The merge is the only approval. The
foundation's own definitions are bootstrapped before the bus exists, as the bootstrap of a fresh mesh has it
(research 075).
- **Findings**, written by the indexer, from which the condition keeper raises and clears the conditions (ADR 0564): a
trunk's seat definition that does not parse, `seat-definition-unparsed` (a pull request's is its check's verdict, not
a condition), cleared by a definition that parses; generated types on a trunk out of step with their definition,
`generated-types-out-of-step`, cleared only by a clean build end of the same contracts module. **Tools** of the indexer: `indexer_seat_definition`
`generated-types-out-of-step`, cleared only by a clean build end of the same contracts module, per module and not per repository, since several
contracts modules share one; a seat defined outside its domain's contracts module, naming both modules (ADR 0636 §5). **Tools** of the indexer: `indexer_seat_definition`
(read one), `indexer_seat_definitions_again` (read a trunk's or a pull request's again now) and `indexer_out_of_step`
(the generated types out of step).
- **Contracts modules.** A seat's contract is published by a contracts module, which holds the seats' contracts and
their client packages and no seat (ADR 0625 §4, §5); the seat's holder implements the contract. **The contracts marker**: a contracts
- **Contracts modules.** A seat's contract is published by a contracts module, which holds the seats' definitions,
their contracts and their client packages and no seat (ADR 0625 §4, §5); the seat's holder implements the contract.
**The contracts marker**: a contracts
module's package artifact names the directory of its definitions in the field `generated-from`, and the manifest validator
refuses a module carrying it that claims a seat, naming the seat. `mesh-contracts` declares its packages in Go and
TypeScript, cut per seat as *Where a seat's code goes* says, in package series `0.1`, published through the two
package seats (design 51) and taken by its users by version, or inside one delivery group by the group's head. No
`mesh-<domain>-contracts` is created in this milestone: a domain's contracts module is created by the change that
next moves its domain (ADR 0625 §6). mesh-sdk keeps its name and the shared runtime, and takes no new seat-specific
type (ADR 0613 §5): a unit test of mesh-sdk refuses an exported identifier or string constant naming a seat the
definitions define. The two checks every change already runs, the
refuses a module carrying it that claims a seat, naming the seat. **Each domain whose seats the milestone defines
whole has a contracts module of its own, created in the milestone** (ADR 0634 §7, ADR 0635 §5; the table above);
`mesh-contracts` keeps the Core domain's seats, `mesh-config`, and the definitions of every seat whose domain has
none yet. Each declares its packages in Go and TypeScript, cut per seat as *Where a seat's code goes* says, in
package series `0.1`, published through the two package seats (design 51) and taken by its users by version, or
inside one delivery group by the group's head.
- **A contracts module and a domain SDK are modules like any other, found by their manifests** (ADR 0634 §8).
`mesh-delivery-contracts` and the Delivery domain's SDK are folders of the mesh-delivery repository, and the other
domains' contracts modules folders of the mesh-contracts repository, but the Connectivity domain's:
`mesh-connectivity-contracts` is a folder of the mesh-networking repository, beside `mesh-wireguard`, which holds
`the-private-network` with its manifest and peer generator its own (ADR 0636 §6, changing ADR 0634 §8). Each is a package-only module, as mesh-sdk long
is: published from its trunk, rebuilding the modules that use it like any package (ADR 0573 §2, §3), and delivered
to no node. A step of the build or the delivery that treats a module in a folder unlike one at a repository's root
is a gap of the build and delivery system, fixed in the milestone, never worked around. **How it is checked**: the
delivery group's one check builds and publishes every new module from its folder, and the build modules'
regenerate-and-compare step runs on every contracts module the milestone creates (ADR 0634 §10).
- **mesh-sdk takes no seat-specific type** (ADR 0613 §5): a unit test of mesh-sdk refuses an exported identifier or
string constant naming a seat the definitions define. The two checks every change already runs, the
build modules' compare step and that test, are where a stray file and a stray type are refused; mesh-catalog's own
checks cannot see another repository.
- **A contracts cycle holds nothing.** `mesh-contracts` is published through the forge module's package seats and
built by the Go and Node build modules, and the forge module, `distribution` and the Go build module import it. A
- **A contracts cycle holds nothing.** `mesh-delivery-contracts` is published through the forge module's package seats
and built by the Go and Node build modules, and the forge module, `distribution` and the Go build module import it. A
package edge and a build module's edge are triggers, never waits, so that cycle starts and is not refused (ADR 0614
§6), and a package is numbered per source revision, so the triggers end (design 51 *The versions*).
- **Older readers keep working**: a moved contract keeps its subjects, keys and message shapes, and a reader ignores
a field it does not know.
a field it does not know. The two renamed seats are the exception, and every one of their users moves in the same
delivery group (ADR 0634 §9, as it changes ADR 0608 §6).
- **A vendor-specific** provision or contribution lives in its module's own extension package, published by version
like any package; a later holder of the seat may publish its own the same way. A seat's definition stays
vendor-neutral, which the seams review checks.
### Where a seat's code goes
*Added 2026-11-04* (ADR 0625, ADR 0627; the operator, 2026-11-04: "many small sdk's instead of 1 large sdk"). A
module is rebuilt only for the seats it uses, so a seat's code is cut per seat, in the contracts module of its
domain; mesh-sdk keeps what every module shares.
*Added 2026-11-04, widened 2026-11-05* (ADR 0625, ADR 0627, ADR 0634, ADR 0635; the operator, 2026-11-04: "many small
sdk's instead of 1 large sdk", and shared logic lives with its owner). A module is rebuilt only for what it uses, so
shared code lives with its owner: a seat's code is cut per seat, in the contracts module of its domain; what one
module owns is that module's own package; what one domain's modules and seats share is that domain's SDK; mesh-sdk
keeps only what modules or seats of more than one domain use.
```
contracts module (mesh-contracts, or mesh-<domain>-contracts)
├── definitions and fixtures written by hand, the generation's source, outside the published packages
├── one package per seat defined whole, per language generated: types, subjects, a typed call per verb
│ └── its client package (where callers share logic over the seat) written by hand
└── the seat-set package generated: every seat not yet defined whole, at the seat list's grain
contracts module of a domain (mesh-<domain>-contracts; mesh-contracts for Core, mesh-config, and a domain with none)
├── definitions and fixtures written by hand, every seat's at its grain, the generation's source
└── one package per seat defined whole, per language generated: types, subjects, state layouts, a typed call per verb
└── its client package (where callers or holders share logic over the seat) written by hand
mesh-sdk the runtime every module uses, and seat-neutral parts that several seats' clients or holders share
imports of a contracts module: only `catalogue`'s, of the seat-set package
domain SDK (mesh-<domain>-sdk) written by hand: logic and forms one domain's modules and seats share, owned by no module
a module's own package written by hand: logic and state layouts one module owns, where it holds no seat for them
mesh-sdk the runtime every module uses, and what modules or seats of more than one domain share;
it imports no contracts module and reads the seat list from the bus
```
- **Shared code lives with its owner** (ADR 0634 §1), and the first home that fits is the one: a seat's types, the
layouts of its states and the messages of its verbs and events are its contract's, and the logic its callers or
holders share is its client's; logic and state layouts one module owns, where that module holds no seat for them,
are that module's own package, which other modules import from it; logic and forms no seat carries and no one
module owns, the seat-neutral parts seats' clients or holders share included, are their domain SDK's when only one
domain's modules or seats use them, and mesh-sdk's when more than one domain's do or every module does.
- **Each seat defined whole has a package of its own, per language** (ADR 0625 §1, ADR 0627 §1): its generated
contract, and a module imports only the packages of the seats it uses, so a change of that seat rebuilds only its
users. Where the fingerprint counts a language's module rather than its package, each such package is a module of
its own in the same repository. In the milestone of ADR 0612 these are the ten seats above.
- **Every other seat is in one seat-set package** in `mesh-contracts` (ADR 0625 §2), holding each seat's name, scope,
verbs and provision at the seat list's grain. A change of any of them rebuilds every importer of that package,
mesh-sdk's importers included, until its domain moves and it is defined whole.
- **The imports run one way per package** (ADR 0625 §3): a generated package imports nothing of mesh-sdk; mesh-sdk's
only import of a contracts module is `catalogue`'s, of the seat-set package; a client imports its own seat's
contract and mesh-sdk's runtime. Where a language's module is the unit of import, the clients are a module of
their own in the repository, so the two never form a cycle.
- **A client package** (ADR 0625 §4) is the one hand-written package a seat may carry beside its contract, owned by
the seat's contracts module, never by a holder. It holds the logic two or more callers would otherwise each write
over that seat, and only where such logic exists; it uses only its own seat's contract and mesh-sdk's runtime,
never another seat's client, and never names a holder, a module's tool or a module's subject. A seat's types and
typed calls are its contract's, never its client's.
- **Seven clients come in the milestone of ADR 0612** (ADR 0627 §1), for the seats whose shared logic mesh-sdk holds
today, each moved out of mesh-sdk with every importer in the same group, mesh-sdk keeping no copy and no alias:
`operator-channel` (an ask and its awaited answer, mesh-sdk's `asks` today); `mesh-vault` (the calls its callers
share; mesh-sdk's hand-written vault types are replaced by the vault contract's, not moved into the client);
`node-engine` (the checks of its compose-candidate ask and answer); and `npm-package-registry`,
`go-package-registry`, `container-image-registry` and `oci-artifact-registry` (publishing a file in parts, one
client per seat, each binding mesh-sdk's seat-neutral parts transfer to its own seat's verbs and types).
- **Moving a file in parts stays seat-neutral in mesh-sdk, on both sides** (ADR 0627 §2): the sending side and the
going on from what a holder already holds, and the holder's side (taking the parts into the holder's own objects,
its own in the same folder. In the milestone of ADR 0612 these are the 25 seats above.
- **A seat at the seat list's grain has its definition and no package** (ADR 0635 §1, §2): its readers learn it
from the bus, and a change of it rebuilds nothing.
- **The imports run one way per package** (ADR 0625 §3, as ADR 0634 §2 and ADR 0635 §1 change it): a generated
package imports nothing of mesh-sdk; mesh-sdk imports no contracts module; a domain SDK imports only mesh-sdk and
contract packages; a client imports its own seat's contract, mesh-sdk's runtime and its own domain's SDK. Where a
language's module is the unit of import, the clients are a module of their own in the folder, so the two never
form a cycle.
- **A client package** (ADR 0625 §4, as ADR 0634 §2 changes it) is the one hand-written package a seat may carry
beside its contract, owned by the seat's contracts module, never by a holder. It holds the logic two or more
callers or holders would otherwise each write over that seat, and only where such logic exists; it uses only its
own seat's contract, mesh-sdk's runtime and its own domain's SDK, never another seat's client, and never names a
holder, a module's tool or a module's subject. A seat's types and typed calls are its contract's, never its client's.
- **A domain SDK** (ADR 0634 §2) is a package-only module, `mesh-<domain>-sdk`, named after the domain's heading in
the glossary, which may sit in a repository with that domain's modules. Its importers are its domain's modules and
the holders and callers of its domain's seats; it imports only mesh-sdk and contract packages, names no module's
tool or subject, and holds no seat's type or state layout and no state layout one module owns. The first is
`mesh-delivery-sdk`, in the milestone of ADR 0612: the moving of a file in parts (below), the build steps' and the
composer's shared logic, each made seat-neutral over its callers' types (ADR 0635 §4), and the rules the Delivery
domain's modules share.
- **A module's own package** (ADR 0634 §1) holds what that module owns and no seat carries: its state layouts that
other modules read and the logic over them. Its fingerprint counts only that package's files (ADR 0593 §2), so a
change elsewhere in the owner module rebuilds none of the package's importers. Another module of the same repository uses it by path, from the same
commit (ADR 0609 §2); a module of another repository uses it only by its name and a published version (ADR 0573
§1), so the owner's manifest declares it as a package artifact (ADR 0606 §4).
- **The clients of the milestone of ADR 0612** (ADR 0627 §1, ADR 0634 §4, ADR 0635 §4), each moved out of mesh-sdk
with every importer in the same delivery group, mesh-sdk keeping no copy and no alias: `operator-channel` (an ask
and its awaited answer, mesh-sdk's `asks` and `asker`); `mesh-vault` (the calls its callers share; mesh-sdk's
hand-written vault types are replaced by the vault contract's, not moved into the client); `node-engine` (the
checks of its compose-candidate ask and answer); `npm-package-registry`, `go-package-registry`,
`container-image-registry` and `oci-artifact-registry` (publishing a file in parts, one client per seat, each
binding the Delivery domain's SDK's transfer to its own seat's verbs and types); `mesh-hand-acts` (recording a
hand act); and `mesh-provisioner` (mesh-sdk's provider runtime and its harness, which only providers use).
`the-private-network` has none: the layout of the value it provides, which holds no key material, is the seat's
contract in `mesh-connectivity-contracts` (ADR 0634 §1), the private network's manifest and peer generator are
`mesh-wireguard`'s own, and the seat has a client only where its callers share logic (ADR 0636 §6, changing ADR 0635
§4).
- **Moving a file in parts is seat-neutral, in the Delivery domain's SDK, on both sides** (ADR 0627 §2, as ADR 0634
§3 changes it): it serves only the four registry seats, all of the Delivery domain. The sending side and the going
on from what a holder already holds, and the holder's side (taking the parts into the holder's own objects,
checking their hashes, dropping an upload left idle, saying when its objects fill), name no seat, verb or seat's
type and take subjects, shapes and checks from their caller. Each registry seat's client binds the sending side,
and each holder (the distribution module, the forge's two package seats) binds the receiving side to the seat it
@@ -334,10 +477,10 @@ domain; mesh-sdk keeps what every module shares.
- **In a contracts module's published packages every file outside a client is generated** (ADR 0625 §5): the build
modules' regenerate-and-compare step fails any other hand-written file there, naming it. The definitions and their
fixtures sit outside the published packages and are not judged by that step.
- **The contracts modules follow the glossary's domains** (ADR 0625 §6): a domain's seats move into
- **The contracts modules follow the glossary's domains** (ADR 0625 §6, ADR 0634 §7): a domain's seats move into
`mesh-<domain>-contracts`, named after the domain's heading in the glossary, when that domain's seats move; a seat
of the Core domain, or of a domain with no contracts module yet, is in `mesh-contracts`. The order of the domains
stays issue 167's (ADR 0613 §4).
of the Core domain, or of a domain with no contracts module yet, is in `mesh-contracts`. The seven the milestone of
ADR 0612 creates are in *A seat's definition*, above; the order of the other domains stays issue 167's (ADR 0613 §4).
- **A seat defined outside the mesh's own contracts has a contracts module of its own** (ADR 0625 §7), which may
share a repository with its first holder; its definition, contract and client never live in the holder module, so
another module can hold the seat by importing only the contract, and the holder can be retired while the contract
@@ -345,33 +488,51 @@ domain; mesh-sdk keeps what every module shares.
- **A role only one module could ever play is no seat** (ADR 0625 §8): it is that module's own tools and events,
reached by the module's name (ADR 0596 §2). The test is whether another module could take the role over, not how
many hold it today, so every seat in force stays a seat, `mesh-broker`, `node-engine` and `route-proxy` included.
- **mesh-sdk takes only the runtime every module uses** (ADR 0625 §9, as ADR 0627 §2 widens it): the bus connection,
the tools bundle's runtime, state and its helpers, and a seat-neutral part two or more seats' clients or holders
share, as long as it names no seat, verb or seat's type. After the milestone it holds no package, type or function
over the seven seats above (ADR 0627 §4); the value layouts of seats not defined whole stay in it until their domain
moves, as ADR 0613 §3 has it.
- **mesh-sdk takes only what modules of more than one domain use** (ADR 0625 §9, as ADR 0627 §2, ADR 0634 §1, §4, §5
and ADR 0635 §4 change it): the bus connection, the tools bundle's runtime, state and its helpers, the answer of a
module's health tool, the `Membership` type with the names its runtime reads to learn its own membership, the
package `views` (which takes its hand-act recorder and its finding writer from its caller), `outward`, the parts of
`forgekey` that modules of more than one domain use, and a seat-neutral part seats of more than one domain share,
as long as it names no seat, verb or seat's type. In the milestone of ADR 0612 its `go/delivery` is moved whole and
deleted, each of its names to the home this section gives it, and every other part naming a seat or a holder's
state leaves for its owner, every importer moving in the same delivery group; after it, mesh-sdk holds no package,
type or function over a seat defined whole, and no seat list (ADR 0627 §4, ADR 0634 §4, ADR 0635 §1).
**How it is checked** (ADR 0625 §11, ADR 0627 §3, both built in the milestone of ADR 0612):
**How it is checked** (ADR 0625 §11, ADR 0627 §3, ADR 0634 §10 and ADR 0635 §6, all built in the milestone of ADR 0612):
| Rule | Checked by |
|---|---|
| One package per seat defined whole, one seat-set package | a unit test of the generator (one program in the Go build module, emitting both languages and run by the Node build module too) emitting one package per seat defined whole and one seat-set package; the generator refuses a seat marked whole in the seat set without its own package; the manifest validator refuses a contracts module whose package holds the generated files of two seats defined whole, naming them |
| The imports run one way | a mesh-sdk unit test that no package but `catalogue` imports a contracts module, and `catalogue` only the seat-set package; the generator tests check no generated package imports mesh-sdk |
| mesh-sdk holds no logic over the seven seats | mesh-sdk's package-list test refuses by name `asks`, the vault's types and its shared calls, the compose-candidate checks and any seat-named publish code, and refuses the seven seats' names in mesh-sdk outside `catalogue`, so a copy kept without importing a contract fails too |
| A client uses only its own seat's contract | the catalog check of a client: its own test refuses a client importing another seat's client and one naming a module's tool or subject, and passes the seven clients |
| The parts transfer is seat-neutral | its unit test runs both sides against two different subjects and shapes the caller gives, and the seat-name check holds it seat-neutral; the review refuses a holder that takes parts in without it |
| Only a client is written by hand in a published package | each build module's regenerate-and-compare test fails on a hand-written file outside a client package and passes a contracts module holding its definitions and fixtures |
| One package per seat defined whole | a unit test of the generator (one program in the Go build module, emitting both languages and run by the Node build module too) emitting one package per seat defined whole, covering each of the 25; the manifest validator refuses a contracts module whose package holds the generated files of two seats defined whole, naming them |
| The imports run one way | a mesh-sdk unit test that no package of mesh-sdk imports a contracts module; the generator tests check no generated package imports mesh-sdk; a domain SDK's importers and imports by the review below |
| mesh-sdk holds no logic over seats, and no part that left it | mesh-sdk's package-list test refuses by name `go/delivery`, `asks`, `asker`, the vault's types and its shared calls, the compose-candidate checks, the provider runtime, and every other package or part ADR 0634 §4 and ADR 0635 §4 move; its seat-naming test refuses the defined seats' names in mesh-sdk, so a copy kept without importing a contract fails too |
| A client uses only its own seat's contract and its domain's SDK | the catalog check of a client: its own test refuses a client importing another seat's client or another domain's SDK, and one naming a module's tool or subject, and passes every client of the milestone |
| The parts transfer is seat-neutral | its unit test, in the Delivery domain's SDK, runs both sides against two different subjects and shapes the caller gives, and the seat-name check holds it seat-neutral; the review refuses a holder that takes parts in without it |
| Only a client is written by hand in a published package | each build module's regenerate-and-compare test fails on a hand-written file outside a client package and passes a contracts module holding its definitions and fixtures; the step runs on every contracts module |
| A module in a folder is built and published as one at a root | the delivery group's one check builds and publishes every new contracts module and the domain SDK from its folder, and `mesh-wireguard` and `mesh-connectivity-contracts` from the mesh-networking repository |
| A seat is defined only in its domain's contracts module | the indexer refuses a second module's definition of a seat, with its finding naming both, and a move from `mesh-contracts` holds in either merge order (ADR 0636 §5) |
| A holder keeps its seat's states | the manifest validator refuses a holder whose `requires` does not declare each of its seat's states with the same local name and template |
| A contracts module holds no seat; a role only one module could ever play has none | the manifest validator's refusal of a contracts module that claims a seat; review both ways: a new seat definition's pull request is asked whether another module could take the role over, and a module's pull request is asked the same of each new tool or event family other modules call |
| Each new exported name sits where this section puts it | the review of every pull request (playbooks 07 and 08, the code reviewer's definition) asks the home of each new exported type, field, constant, package or module against this section, a domain SDK's importers included |
Acceptance tests: research 081's 14, 21, 25 and 26; a unit test that the generated seat set equals the seat list
mesh-sdk compiled before the move, entry for entry; mesh-sdk's seat-naming test; each build module's compare step
failing on a differing, a missing and an unwritten file, naming each; the checker's `verdict` naming out-of-step
files; and cases of the indexer's whole-module integration test: a trunk
definition that does not parse writes its finding and a fixed one clears it; a trunk build end naming out-of-step types
writes the finding, a clean build end of another contracts module leaves it, and a clean one of the same module clears
it; `indexer_seat_definition` answers the trunk's entry while a pull request
holds another; a pull request's entries go when it closes; a definition over 256 KiB is refused; each of the three
tools answers.
**Acceptance tests**, each at its level (unit, or a case of the module's one whole-module integration test against the
real bus and store) and in its repository. The move raises no repository's test count, since mesh-delivery is already
over its budget: each case below replaces or joins an existing one.
| Test | Level | Repository |
|---|---|---|
| research 081's 14, 21, 25 and 26 | as research 081 sets them | as research 081 sets them |
| mesh-sdk's seat-naming, import and package-list tests; no module's name in the runtime | unit | mesh-sdk |
| a reader of the seat list, against a bus the test gives: a reader away longer than the events' retention reads the whole list again; a reader refuses a pull request head's definition; both list tools answer, the re-read one only for the operator and the healers | unit | mesh-sdk |
| the node-engine, against a stand-in of the seat `mesh-catalog`: without the list it acts on nothing but the bootstrapped modules, then on all once the stand-in answers; after a restart it starts its last applied compositions again; a refused definition stops no running module | a case of the node-engine's whole-module test | `mesh-host` (the node-engine's repository) |
| the indexer starts with only the bootstrapped seats and answers `seat-definitions`, without a seat (names and digests) and with one; with the indexer kept down, the node's finding is written after five minutes and the condition keeper raises it to the operator | cases of the indexer's whole-module test | mesh-delivery |
| a trunk definition that does not parse writes its finding and a fixed one clears it; a trunk build end naming out-of-step types writes the finding, a clean build end of another contracts module of the same repository leaves it, and a clean one of the same module clears it | a case of the indexer's whole-module test | mesh-delivery |
| `indexer_seat_definition` answers the trunk's entry while a pull request holds another; a pull request's entries go when it closes; a definition over 256 KiB is refused; a seat-list-grain definition is read from a contracts module like a whole one; a removed definition says `seat-definition-removed` and its seat takes no new claim afterwards; a second module defining a seat is refused with its finding, and a definition moving from `mesh-contracts` to its domain's contracts module holds in either merge order; each of the indexer's tools answers | cases of the indexer's whole-module test | mesh-delivery |
| the manifest validator refuses a claim, an `invokes`, a `consumes` or a grant naming `mesh-placement` or the seat `mesh-enrolment`, and a holder missing one of its seat's states in `requires` | unit | mesh-delivery |
| the checker's `verdict` names out-of-step files; the group's check reads seat definitions at every member's head | a case of the checker's whole-module test | mesh-delivery |
| each build module's compare step fails on a differing, a missing and an unwritten file, naming each, per contracts module | unit | mesh-catalog |
| a change in one folder module rebuilds and publishes only it and its importers; a module with both a deployed and a package artifact (the grants part or disk-load) is built, delivered and published | a case of the Go build module's whole-module test | mesh-catalog |
| after the delivery, `mesh-module-assigner.placement` answers and `mesh-placement.placement` is not found; placement keeps its revisions; no `seat-holders` key, finding or condition stays under an old name | live, read through the mesh's tools | the mesh |
| `mesh-wireguard` is built and delivered from the mesh-networking repository | the group's one check, then live | mesh-networking |
## The foundation's seats
@@ -567,5 +728,10 @@ condition keeper (health), a pull request's entries are removed when it closes (
The milestone's amendment (ADR 0612, ADR 0613, ADR 0614, ADR 0620: the seats defined whole, the rest at the seat
list's grain, the contracts marker, out of step learned from the checker's `verdict` and the builder's build end, the
seat set's one source), with the amendment of 2026-11-04 (ADR 0625, ADR 0627: ten seats defined whole, a package per
seat, the seat-set package in `mesh-contracts`, the seven clients and the seat-neutral parts transfer, *Where a
seat's code goes*), awaits its own review by architectural domain, which rewrites this section.
seat, a package for the seats at the seat list's grain in `mesh-contracts`, the seven clients and the seat-neutral parts transfer, *Where a
seat's code goes*), and the amendment of 2026-11-05 (ADR 0634, ADR 0635: shared code with its owner, the domain
SDK, a module's own package, a contracts module per domain the milestone moves, 25 seats defined whole with their
states, the seat list read from the bus, two seats renamed), with ADR 0636 (the seat list through the seat
`mesh-catalog`, a reader without it acting on nothing new, a seat defined only in its domain's contracts module, the
Connectivity domain in the mesh-networking repository; accepted at its version 15) and the lead's rulings on the first review by architectural
domain written in, await their final review, which rewrites this section.
@@ -2,8 +2,11 @@
layer: to-be
status: in-progress
code: [mesh-controller, mesh-catalog modules/mesh-delivery-gate, mesh-delivery, mesh-host, mesh-sdk, mesh-vault, hq 00-META/checks]
updated: 2026-11-04
updated: 2026-11-05
decisions:
- "ADR 0636"
- "ADR 0635"
- "ADR 0634"
- "ADR 0594"
- "ADR 0572"
- "ADR 0571"
@@ -168,17 +171,17 @@ source revision is derived from the keys: every **want** equals its module's **f
node's own want `want.<node>.node`, and never deletes a want (ADR 0408 §4). A move a person must allow is judged
by the node agent, which waits; the assigner raises the ask and on a yes writes the want again naming it
(ADR 0547 §3). It reads which modules run on which node from its own state `placement`, held as the seat
`mesh-placement` (ADR 0567); until ADR 0547's third milestone it also reads the controller's assignments, writes no want for
`mesh-module-assigner` (named `mesh-placement` until ADR 0634 §9; ADR 0567); until ADR 0547's third milestone it also reads the controller's assignments, writes no want for
a module on a node that the two do not both name, and raises a condition naming the pair while they differ.
It also runs the **composition check**: whether the machines a change touches still compose with it, read
from the keys the indexer and the builder hold and from the machines, settings and grants it reads, with
mesh-sdk's composition, the one every node agent composes with (ADR 0446, ADR 0547 §2). Its module was made in
the composition of `mesh-delivery-sdk`, the one every node agent composes with (ADR 0446, ADR 0547 §2, ADR 0635 §4). Its module was made in
R4g with release 5; from release 6 it writes no difference (ADR 0524 §4, ADR 0528, ADR 0539 §8). The check composes
no placement and waits for no node's answer: a pull request changes no placement, since placement is bus state
changed by a proposal (ADR 0567; ADR 0446 §3 read as written). Its own composition stands in for every sealed value, so only a node's answer to a proposal can say that
what the node would run is the same.
- **The node agent**, the node-engine's own process, follows its node's wants, **composes each wanted module for
its own node** with mesh-sdk's composition from the manifest by digest, the module's resolved values from
its own node** with the composition of `mesh-delivery-sdk` from the manifest by digest, the module's resolved values from
mesh-config, its secrets and bus credential asked of the vault sealed to the node's key, its licences and what
its providers contribute, applies one module at a time and only what differs from what runs, backs up a data
module first, and writes its haves with the fingerprint and the digest of the composition it runs (ADR 0547 §2,
@@ -186,7 +189,8 @@ source revision is derived from the keys: every **want** equals its module's **f
public records and the manifests wanted on them. On the node holding `mesh-broker` it also writes the bus's user
list, alone and with no want (*The bus's user list*, below).
- **The grants part** (module `mesh-grants`, holding the seat `mesh-grants`) composes who may say what on the bus
with mesh-sdk's bus composer: each module's membership, the buckets, logs and streams a manifest declares, the
with mesh-sdk's bus composer, whose parts that name a seat or a holder's state are the grants part's own package
(ADR 0635 §4): each module's membership, the buckets, logs and streams a manifest declares, the
bus's user list, and `grants.<module>.<fingerprint>`, written from L7's delivery once the list the bus loaded
carries that build's accounts (ADR 0547 §5, §6; ADR 0571 §9). It keeps each module's membership in its own state
`memberships`, which the runtime reads, and publishes memberships from the first milestone (ADR 0571 §10); until the operator's `take-over` it keeps the bus's user list, the
@@ -225,7 +229,7 @@ there is three milestones, one issuer of each key at every moment (ADR 0547 §11
elsewhere but the bus's user list, which the bus node keeps able to follow until the third milestone so `hand-back` has a list to return to.
3. **Nothing is left with the controller** (issue 774; what remains of issue 1126, the homes of the controller's
other jobs, carries into it by a note on 1126 and is not one of its issues): the vault mints bus accounts, the grants part
writes grants, the try is the checker's own verb, placement is read from the seat `mesh-placement` alone (ADR 0567; ADR 0549 §5,
writes grants, the try is the checker's own verb, placement is read from the seat `mesh-module-assigner` alone (ADR 0567; ADR 0549 §5,
§6), the vault signs with the mesh's signing key (ADR 0550 §2), each node-engine holds the node seat `node-engine`
and asks the operator itself for a hand-over or an access act (ADR 0563), and the controller's lease is handed to a
holder that is no controller once every other job it keeps has a home by its own decision (research 072): health
@@ -268,13 +272,13 @@ The target (ADR 0547):
- **The controller** is no part of build and delivery (ADR 0547 §1). The jobs it still keeps outside them
(nodes and adoption, identities, secrets and given values, licences, config moves, conditions and facts, ADR
0429 §9) each move by a decision of their own under research 072 before it loses its lease. All are decided:
placement lives on the bus, held by the seat `mesh-placement` (ADR 0567); the mesh's signing key in the vault
placement lives on the bus, held by the seat `mesh-module-assigner` (ADR 0567); the mesh's signing key in the vault
(ADR 0550 §2); a hand-over, an access act and the root-free judgement on each node's own seat `node-engine`
(ADR 0563); health with the condition keeper, the watchman, the self-check and the healers (ADR 0564); the
control records with mesh-config, the node agents, the assigner and the hand-act log's seat `mesh-hand-acts`
(ADR 0565); provisioning with the seat `mesh-provisioner` (ADR 0570); and build history with the builder's
build log, the artifact sweep with the seat `mesh-artifact-sweep`, disk readings with disk-load, and who holds
which seat with the seat `mesh-placement`'s state `seat-holders` (ADR 0571).
which seat with the seat `mesh-module-assigner`'s state `seat-holders` (ADR 0571).
- **The vault** keeps every secret the mesh keeps (ADR 0550 §1): the mesh's signing key, licence grants and
tokens, bus account passwords and module secrets; a holder gets a value only sealed to its own key, or asks the
vault to act with it. It makes the mesh's signing key and signs enrolments and node certificates on request, by
@@ -591,8 +595,8 @@ From its delivery, a check is this:
composes only the machines that run or could run a module the change moves or adds, or whose grants or
providers it alters; for a group, once over every member's change, read from the indexer's candidates of the
group, and while the indexer holds none the group's check waits and says why. It composes with the function
every node agent composes with, mesh-sdk's composition, so the two agree for one mesh-sdk version; its verdict
names the mesh-sdk version it composed with. No composing program is built from a change: a change to the
every node agent composes with, the composition of `mesh-delivery-sdk`, so the two agree for one version of that
package; its verdict names the version it composed with (ADR 0635 §4). No composing program is built from a change: a change to the
composition is proved by its own tests, a field only a newer composition reads goes out as optional first, and
the running composition refuses a manifest that uses a field it does not read, naming the field. What it fails on, and its warning past twelve rebuilt modules, are the composition check's, as they were the controller's composition check's (ADR 0446, ADR 0237 §2).
The assigner writes the verdict under the checker's hash of the heads it judged, in its own bucket, and the checker reads it into the
@@ -687,21 +691,23 @@ backup holder backs up nothing, and the have says so.
*ADR 0360 §8, §9, ADR 0377, ADR 0429 §4, §6, ADR 0539 §9, §14 to §16, ADR 0547 §2 to §9, ADR 0549, ADR 0555.*
- **Each node composes its own.** For each want `want.<node>.<module>` the node agent composes that module for
its own node with mesh-sdk's composition from the manifest by digest the indexer wrote, the module's resolved
its own node with the composition of `mesh-delivery-sdk` from the manifest by digest the indexer wrote, the module's resolved
values read from mesh-config's resolved state filtered to its node, its secrets and bus credential asked of the
vault sealed to the node's key (ADR 0349 §11), its licences, and what its providers contribute; and the node's
own composition (adoption, firewall openings, accounts, the resolver, identities and groups) from the same
inputs. Of other nodes it reads only their public records and the manifests wanted on them, which its firewall
and resolver need. Nothing composed crosses the bus, so nothing composed is signed (ADR 0547 §2).
- **What a node composes from, and who writes it.** One composer, mesh-sdk's, has three callers: the node agent for
- **What a node composes from, and who writes it.** One composer, `mesh-delivery-sdk`'s, made seat-neutral over its
callers' types (ADR 0635 §4), has three callers: the node agent for
its own node, the assigner's composition check for every node, and the grants part. Each fills the composition's
input from the bus with its own credential and nothing else (ADR 0547 §2, ADR 0549 §3):
- **placement**, the state `placement` of the seat `mesh-placement`, one key `placement.<node>`, written by the
- **placement**, the state `placement` of the seat `mesh-module-assigner`, one key `placement.<node>`, written by the
assigner's credential alone on an approved proposal and said by `placement-changed` (ADR 0567);
- **the node records**, each in its node's own have key `<node>.node`, written by that node alone and heard as
the key's change (ADR 0555 §3);
- **the manifests**, the indexer's `modules.<module>` and its manifests by digest, or the digest the composing
node's want names where it runs another build; the private network's manifest comes with mesh-sdk;
node's want names where it runs another build; the private network's manifest is its holder `mesh-wireguard`'s
own (ADR 0636 §4);
- **the config values**, mesh-config's resolved state, the composing node's slice alone for a node agent; a module
in the settings form (its manifest declares no config, so mesh-config holds none for it) has its values in the
placement key instead;
@@ -710,9 +716,10 @@ backup holder backs up nothing, and the have says so.
While both sources stand (ADR 0549 §5), mesh-config resolves values for a module either the placement or the
controller's assignments name on a node, and the grants part composes both, holding a seat held once for the mesh
where the placement says.
- **How placement is changed.** The seat `mesh-placement`, held once for the mesh by the assigner, keeps three
- **How placement is changed.** The seat `mesh-module-assigner`, held once for the mesh by the assigner, keeps three
states: `placement`, one key `placement.<node>` per node; `proposals`; and `candidates`. Only the holder's
credential writes them, and the contract (the states' names among it) is mesh-sdk's. No repository holds
credential writes them, and the contract (the states' layouts among it) is the seat's, in
`mesh-placement-contracts` (ADR 0634 §6, §7). No repository holds
placement and none names a node: what differs between nodes is bus state, and a build module's capacity and
build hours are values on each node's layer of mesh-config. A key holds what a person decides of the node and
nothing a machine observes:
@@ -732,7 +739,7 @@ backup holder backs up nothing, and the have says so.
changes (place a module, take one off, pin a provider, hold a seat, set a decided field of a node, set a
module's config placement or values); who proposes is the caller as the runtime proves it. The holder lays the
changes over the current keys, names the revision of each, and refuses at once, naming every finding, what
mesh-sdk's placement check refuses: a module the indexer does not hold or a node not enrolled, two modules of one
the placement check, the assigner's own package since only the assigner uses it, refuses (ADR 0634 §1): a module the indexer does not hold or a node not enrolled, two modules of one
node on one host port, a pin, binding or holding naming a provider not placed where it says, a seat held once
for the mesh held twice, two nodes with one address and more than one hub. Otherwise it writes the proposal and
one candidate for each node whose key it changes, and the nodes answer. `withdraw` deletes both. Until the lease
@@ -756,7 +763,9 @@ backup holder backs up nothing, and the have says so.
holder hears that key's change; nothing polls. The nodes that must answer are each node whose key the proposal
changes and each node whose composition the holder finds changed with the current inputs on both sides, not every
placed node, so a node gone for good or asleep blocks only a proposal that touches it. The contract (the state,
its key, the answer, the composition with a given placement and the comparison) is mesh-sdk's (ADR 0567).
its key, the answer) is the seat's, in `mesh-placement-contracts` (ADR 0567, ADR 0634 §6, §7); the composition with
a given placement is the composition of `mesh-delivery-sdk` (ADR 0635 §4), and the comparison is the node-engine's
own package (ADR 0634 §1).
- **The holder judges the answers and the operator approves.** The holder computes, by node, the modules the
proposal changes and reads every answer. A proposal names no difference of its own, and the verdict fails on no
difference: each difference, and each one the nodes answered that the holder did not compute, is shown by node in
@@ -777,7 +786,7 @@ backup holder backs up nothing, and the have says so.
- **What a node's account reads to compose.** The grants part's composer gives every node's account these reads,
each read only, and the controller's frozen copy none of them (ADR 0555):
- every node's `<node>.node` of the node-engine's have bucket: the other nodes' records;
- every key of the state `placement` of the seat `mesh-placement`: where modules run and what a person decided of each node;
- every key of the state `placement` of the seat `mesh-module-assigner`: where modules run and what a person decided of each node;
- every key of the indexer's `modules` and `manifests`: the manifests it composes from;
- every key of the builder's `builds`: the manifest of a build its want names when that is not the trunk's;
- mesh-config's `resolved` values of its own node alone, `<node>.>`.
@@ -795,7 +804,7 @@ backup holder backs up nothing, and the have says so.
the node composing it keeps what runs until the vault holds it. Likewise a consumer of `model-access` answered by a
licence record, placed before the vault serves licences (ADR 0550 §3, issue 569), is refused by the same guard and
its node keeps what runs; none is placed today.
- **The order a node takes to composing its own.** One delivery group carries every part: the seat `mesh-placement`
- **The order a node takes to composing its own.** One delivery group carries every part: the seat `mesh-module-assigner`
on the assigner, the controller's verb `placement-export` (as it is) and the controller's pin only, the node-engine
(its record and its own composer), the vault's `own`, the grants part's reads, and mesh-config's reading of
placement. The grants part's new reads reach the nodes before the node-engine watches the seat's states. Nothing
@@ -815,7 +824,8 @@ backup holder backs up nothing, and the have says so.
rule reads, is written by the grants part from ADR 0547's third milestone; its account is minted by the vault
(ADR 0547 §4, §5, §9). Until that milestone the issuer ADR 0539 §7 names writes both, so a want never waits on a
key nobody writes.
- **The bus's user list.** The grants part composes the list with mesh-sdk's bus composer when an input changes,
- **The bus's user list.** The grants part composes the list with mesh-sdk's bus composer, its seat-named parts in
the grants part's own package (ADR 0635 §4), when an input changes,
writes it as an object sealed to the bus node's key and writes the key `user-list` with its digest, revision,
user count and the users left out; it says `user-list-changed`. The node agent of the bus node writes the bus
module's user-list file alone, mode 0600, only when its digest differs, and the bus reloads it in place; no want
@@ -852,8 +862,8 @@ backup holder backs up nothing, and the have says so.
accounts and secrets, its tools) while it keeps the server's container as it runs, with no record ask. Only a want
that carries the operator's warrant (a put after the streams' snapshot, or the operator's hand write) replaces the
server; until then the bus module's have names the fingerprint applied and the one the server runs, and the view
says the server waits. mesh-sdk names the resources the bus's
server holds, so the assigner and the node agent read one list; the assigner writes the bus module's want only to a
says the server waits. The contract of the seat `mesh-broker` names the resources the bus's
server holds (ADR 0634 §1), so the assigner and the node agent read one list; the assigner writes the bus module's want only to a
node agent whose have says it keeps the server (the version it reports), so the node agent's keeping of the server
is delivered before the assigner's change. Until
then nothing writes the bus module's process accounts, and its tools that write to the bus fail (issue 774, the
@@ -934,7 +944,7 @@ and `delivered` events.
*ADR 0408, ADR 0429 §17, ADR 0539 §4, §12, §13.*
From release 6, module, source revision, build, want and have are one chain, each link answerable from the next
(ADR 0429 §17). Placement is bus state of the seat `mesh-placement`, held by the assigner, and no repository holds it (ADR 0567); the controller's
(ADR 0429 §17). Placement is bus state of the seat `mesh-module-assigner`, held by the assigner, and no repository holds it (ADR 0567); the controller's
assignments bucket is kept equal to it by the holder until ADR 0547's third milestone and is a source of nothing from then
(ADR 0549 §6). The rollouts bucket is kept read-only with its history: no rollout is written after release
5's last plan (ADR 0539 §12).
@@ -944,7 +954,7 @@ assignments bucket is kept equal to it by the holder until ADR 0547's third mile
| build | its module, repository and commit, its reuse key, what it published: an artifact by digest or a package release by name and version | the controller | `mesh-controller_builds` |
| want | the fingerprint a node should run of a module, and the digest of the one composition it allows; the node's own want names its node composition | the assigner | `mesh-delivery-assigner_wants` |
| have | what the node runs of a module: the fingerprint, the composition's digest, the phase and the health | the node agent | `node-engine_have` |
| placement | each node's modules with their pins, host ports and bindings, and what a person decided of the node, as one key per node | the assigner, the holder of the seat `mesh-placement`, on the operator's Approve of a proposal (ADR 0567) | the seat's state `placement` |
| placement | each node's modules with their pins, host ports and bindings, and what a person decided of the node, as one key per node | the assigner, the holder of the seat `mesh-module-assigner`, on the operator's Approve of a proposal (ADR 0567) | the seat's state `placement` |
| placement proposal | while a change is open: its changes, the revision of each key it was laid over, the nodes' answers, the verdict and the ask, beside the chain and never composed into what runs | the assigner | the seat's state `proposals` |
| placement candidate | while a proposal is open: one node's placement as the proposal lays it, which that node composes in shadow and answers | the assigner | the seat's state `candidates` |
| assignment | until ADR 0547's third milestone: the module and node, pinned or not, ended or not, written by `assign` and `unassign` after an approved placement change, so that it equals the placement (ADR 0567) | the controller, called by the holder | `mesh-controller_assignments` |
@@ -963,7 +973,7 @@ at its first start of release 6 (ADR 0539 §13).
controller's store holds builds, rollouts, batches or assignments, so no record mirror,
audit or healer exists for them; the nodes, settings, declared-config and predefined-keys mirrors stay as
ADR 0289 and ADR 0356 have them. A module reads a bucket by naming it in `reads`. The assigner, the grants part,
mesh-config and the node agents read placement from the seat `mesh-placement`'s state, on its watch; from ADR 0547's second
mesh-config and the node agents read placement from the seat `mesh-module-assigner`'s state, on its watch; from ADR 0547's second
milestone no part but the controller reads the assignments bucket, and the assigner until the third
(ADR 0567).
- **No key of the chain is removed.** Each bucket keeps up to 64 earlier revisions of each key as its history. A
@@ -1106,7 +1116,7 @@ What the build proves before this design is implemented (research 055's directio
newer fingerprint once its rules hold, a fingerprint nobody wants any more is never applied, and nothing is
released by a person or held in the gate.
12. No table in the controller's store holds builds, deliveries or assignments: builds are the builder's keys,
wants the assigner's, haves the node agents', and placement the seat `mesh-placement`'s, held by the assigner,
wants the assigner's, haves the node agents', and placement the seat `mesh-module-assigner`'s, held by the assigner,
with the controller's assignments bucket kept equal to it by the holder until ADR 0547's third milestone
(ADR 0567).
@@ -1200,7 +1210,7 @@ From ADR 0548 and ADR 0551 (issue 568):
move carries a verdict by its package digests.
- Placement written only by the controller's `assign` and `unassign` (ADR 0539 §12, as ADR 0549 §1 changes it):
from ADR 0547's third milestone its tables and verbs are a source of nothing (ADR 0549 §6). The seat
`mesh-placement` is the only writer of placement (ADR 0567).
`mesh-module-assigner` is the only writer of placement (ADR 0567).
- A group's order: `after:` lines, the ranked rules and the order of the repositories' names (ADR 0419, which
supersedes ADR 0249).
- Delivery member by member, a stop of a member after a failed one, and the own path's separate batch for a
@@ -1270,13 +1280,13 @@ release reports the controller's code lines against research 063's baseline (acc
| R4g | The last milestone of release 4 and release 5 as one, with one switch-over and no rehearsal (ADR 0524 §1): builds and checks are asks on streams that each build node pulls when it has a free slot, deliveries first, with nothing holding checks and no controller queue (ADR 0525); the assigner is made with its composition check, writes each node's wants compared with what the controller sends and every difference, and the view answers the asks that need no proof of who answered (ADR 0528, ADR 0437 §10); no module's fingerprint takes the builder (ADR 0530); the scripts of the repositories a module is built from, build-agent with its socket and Dockerfile code, the toolchain images and the controller's build code go, and the builder delivers its own module, the build modules and the controller from its keys (ADR 0453 clauses 5, 8, 10; ADR 0524 §5, §7); the ways to deliver without the builder are recorded (ADR 0526); hq's four checks are Go tests (ADR 0527); each repository's script goes (ADR 0529); the word for the composition check's predecessor is struck. Its pull requests are opened only after five readings, the revert path read, not shown on a test merge (ADR 0524 §3) | 4 (from R4g mesh-build-image, rootless, is the only part that builds an image: ADR 0487 §7), 17, 24 (no `merge-check.sh` in a repository a module is built from) | mesh-sdk, mesh-controller (build-agent removed), mesh-delivery, mesh-catalog, mesh-tools, mesh-tools-go, the node-engine (`mesh-host`), mesh-media-catalog, okioki, hq | 1887 (the flow), 1297 (the assigner), 1295 (`merge-check.sh` goes), 979 (closed with the toolchain images), 1296 and 1303 (hq's check, the old word struck), 1299, 1305, 1294, 1318 (the controller's deletions) |
| R6 | Each node applies from its wants the compositions its own node agent composes (ADR 0547 §2; as released it applied the controller's signed ones), a new version reaches the module's canary first and the rest once it is healthy there, a move a person must allow is asked before its want, the view says what is moving, settled, stuck and delivered, and the plans are deleted from the code; switched over by release 5's last plan (ADR 0539) | 18, 19, 20 | mesh-sdk, the node-engine (`mesh-host`: the node agent), mesh-controller (plans deleted; as released also compositions and credentials, which ADR 0547 moves to the node agents, the grants part and the vault), mesh-delivery (assigner, watchman, view), mesh-catalog (the gate, the messenger, the issue tracker, the backup holder), hq | 1942 (the planner puts the node-engine and the controller in one tier for the switch; delivered first), the bus's user list granting what release 6 uses (delivered first, under L1's issue), 1943 (mesh-sdk: the composition's layout and signature, the want's digest, `mesh-hand-compose`, the verb tables without the plan verbs), 1944 (the node agent), 1946 (the gate keeps the merge's states; the messenger and the issue tracker hear the view), 1906 (the assigner's seventh rule and its asks, the watchman's pulses for wants, the view's wants and events), 1130 (hq) |
| L1 | The controller leaves build and delivery, first milestone: the bus node follows the user list alone, the grants part composes in shadow and the vault holds the bus accounts' hashes, so the operator's take-over can make the grants part's list the one loaded (ADR 0547 §8, §11; ADR 0550 §4) | — | mesh-controller, mesh-delivery, mesh-catalog (the vault, the grants part), the node-engine (`mesh-host`), mesh-sdk | 1945 |
| L2 | Nodes compose: each node agent composes its own modules, and no node applies a composition made elsewhere but the bus's user list (ADR 0547 §11); where modules run is bus state held by the seat `mesh-placement` on the assigner, changed by a proposal the nodes answer and the operator approves, and no repository names a node or holds what one installation decides (ADR 0567). Built as release sdk1 (ADR 0593) and delivered to every machine on 2026-11-02, the controller's code emptied (mesh-controller #491) and its repository archived; done once a check has composed a candidate on every machine | — | the node-engine (`mesh-host`), mesh-sdk, mesh-delivery (the assigner as the seat's holder), mesh-catalog, mesh-controller (its pin only) | 1126 |
| L3 | Nothing is left with the controller: the vault mints bus accounts and signs, the grants part writes grants, the try is the checker's own verb, placement is read from the seat `mesh-placement` alone, each node-engine holds the node seat `node-engine`, judges `root-free` for its node and asks the operator itself for a hand-over or an access act, acting on the warrant with nothing signed (ADR 0563), and, once L4 to L7 are delivered, the lease write passes the lease to a holder that is no controller, made right after the delivery that drops every listed read of the controller but the three ADR 0571 §13 keeps to the write itself (ADR 0547 §11, ADR 0549 §5, §6, ADR 0550 §2, ADR 0571 §13); the rest of the controller's code lines is the issue of L2. Since the controller left (2026-11-02), nothing writes the bus module's own process accounts while the bus module has no want on the broker node, so its tools that write the bus fail (issue 774) | — | mesh-controller, mesh-sdk, the node-engine (`mesh-host`), mesh-catalog (the vault, the grants part, the messenger), mesh-delivery | 774 |
| L2 | Nodes compose: each node agent composes its own modules, and no node applies a composition made elsewhere but the bus's user list (ADR 0547 §11); where modules run is bus state held by the seat `mesh-placement` (now `mesh-module-assigner`) on the assigner, changed by a proposal the nodes answer and the operator approves, and no repository names a node or holds what one installation decides (ADR 0567). Built as release sdk1 (ADR 0593) and delivered to every machine on 2026-11-02, the controller's code emptied (mesh-controller #491) and its repository archived; done once a check has composed a candidate on every machine | — | the node-engine (`mesh-host`), mesh-sdk, mesh-delivery (the assigner as the seat's holder), mesh-catalog, mesh-controller (its pin only) | 1126 |
| L3 | Nothing is left with the controller: the vault mints bus accounts and signs, the grants part writes grants, the try is the checker's own verb, placement is read from the seat `mesh-placement` (now `mesh-module-assigner`) alone, each node-engine holds the node seat `node-engine`, judges `root-free` for its node and asks the operator itself for a hand-over or an access act, acting on the warrant with nothing signed (ADR 0563), and, once L4 to L7 are delivered, the lease write passes the lease to a holder that is no controller, made right after the delivery that drops every listed read of the controller but the three ADR 0571 §13 keeps to the write itself (ADR 0547 §11, ADR 0549 §5, §6, ADR 0550 §2, ADR 0571 §13); the rest of the controller's code lines is the issue of L2. Since the controller left (2026-11-02), nothing writes the bus module's own process accounts while the bus module has no want on the broker node, so its tools that write the bus fail (issue 774) | — | mesh-controller, mesh-sdk, the node-engine (`mesh-host`), mesh-catalog (the vault, the grants part, the messenger), mesh-delivery | 774 |
| M1 | A milestone is a record of the issue tracker: each row of a design's milestone table is a milestone record its issues are linked to by `issue-tracker.link`, an issue to at most one, and pull requests and commits to issues the same way; the knowledge base's `progress` reads the milestones from the tracker, not from this table (ADR 0493 §1 to §4) | — (ADR 0493 §13 in part) | hq, mesh-catalog (mesh-issues, mesh-knowledge) | 1781 |
| L4 | Health leaves the controller (ADR 0564): the condition keeper holds the seat `mesh-conditions` and keeps every open condition from the observers' findings, saying today's condition events; the watchman holds `mesh-watchdogs`, its watchdogs pulses, reads each node-engine's node health report, and keeps the dead letters from the lease write; `mesh-self-check` runs the probes, `root-free` among them from L7 (ADR 0571 §11), and says `checked`; of the probes still deferred, D13, D8, D-fill and DA are built and the rest are not, their kinds ending with the controller at the lease write (ADR 0572); `mesh-healers` heals on events and acts only once the lease names no controller; `status` is the view's derivation; built dark, compared, then switched by the operator's take-over; the bus module gains a tool that removes one named consumer, which the lease write uses (ADR 0547 §11) | — | mesh-sdk, mesh-catalog (mesh-conditions, mesh-self-check, mesh-healers, the router, mesh-watcher, nats), mesh-delivery (watchman, view, the grants part) | 1952 |
| L5 | Control records leave the controller, and the controller is not changed (ADR 0565): mesh-config keeps each module's declared config in its state `declared` and publishes the predefined keys, which the indexer reads from the forge bridge's messages with each open pull request's config candidate; config switches to that source only on the operator's take-over, refused while `compare` shows a difference; each node agent writes its node's declared packages (the node seat `node-engine`, verb `packages`); the assigner holds the new seat `mesh-wants` with the upgrade policy a person chose and the bus step; the hand-act log (`hand-act`, `hand-acts`, `warranted`) is the stream of the new seat `mesh-hand-acts`, held by a new module in mesh-catalog, which every recorder calls from this delivery with no switch; the checker and the assigner's composition check read the held values of their pull requests from mesh-config's state `kept`; from the lease write a trusted config value is set only on the operator's Approve; `settings`, `move-config` and `move-placement` end with the lease and nothing replaces them. each person's six policy choices are set through `mesh-wants.upgrade` and recorded as hand acts, `upgrade compare` and the assigner's finding `policy-split` show any split, and the lease write waits for `compare` to be empty (ADR 0565 §8). One delivery group, built before the lease write of L3, which waits for the group delivered, the six choices set, mesh-config's `compare` empty and its take-over done, `mesh-wants.upgrade compare` empty, and the readings of ADR 0565 §12 (ADR 0565 §14; ADR 0348 §9, ADR 0502 §2, ADR 0539 §14, §15, ADR 0547 §11) | — | mesh-sdk, mesh-catalog (mesh-config, mesh-hand-acts, pacman, the forge module, claude-code, mesh-knowledge, mesh-conditions, mesh-healers), mesh-delivery (indexer, assigner, checker, grants part, forge bridge), the node-engine (`mesh-host`); mesh-controller is not a member | 1953 |
| L6 | Provisioning leaves the controller (ADR 0570): every provision is one key of the seat `mesh-provisioner`, held by mesh-provisioner, holding a reference and never a value, and each placed module's provisioning plan a key of its state `plans`, both composed from placement, the wants and the manifests on change events; each provider acts once all a provision depends on is provided; the assigner moves a want only once its build's provisions are provided; a provision no want needs is retiring, and its account or credential is removed only on the operator's Approve, its data never; the vault mints each provision's secret sealed to the consumer's and the provider's node, and the consumer's node-engine writes it; a wait for a person is a provision's state, which the watchman reads; bus permissions stay with the grants part (ADR 0349, ADR 0230, ADR 0547 §11) | — | mesh-sdk, the node-engine (`mesh-host`), mesh-tools (the tool runner's provisioner client), mesh-catalog (mesh-provisioner, the vault, the provisioner client, postgres, keycloak, gitea, route-adapter), mesh-delivery (assigner, watchman) | 1954 |
| L7 | History and records leave the controller (ADR 0571): the builder keeps its build log, one entry at a build's start and one at its end, from which build history and durations are read and the module mesh-catalog asks the builds it missed; the artifact sweep is the seat `mesh-artifact-sweep`, held by artifact-store-tools beside the artifact store, its keep set from the store's own listing, the wants and the build log, sweeping at each build's end from the lease write and recording each deletion in the hand-act log; disk readings are disk-load's own state on every node; who holds which seat is the state `seat-holders` of the seat `mesh-placement`, and tools are found by the tool runners' announcements; what a node runs is its want and its have; the grants part writes `grants.<module>.<fingerprint>` and keeps memberships in its own state; root-free is a probe of the self-check; no manifest but the controller's reads, invokes or consumes `mesh-controller`, but a named list kept to the lease write, so the lease write's check passes (ADR 0189, ADR 0547 §11, ADR 0549 §6) | — | mesh-sdk, mesh-controller (its pin only), mesh-delivery (builder, indexer, assigner, grants part, view), mesh-catalog (artifact-store-tools, disk-load, the backup seat's holder, mounts, nfs-server, minio, mesh-watcher, the messenger, the self-check, the module mesh-catalog, docker), mesh-tools (the tool runner) | 1955 |
| L7 | History and records leave the controller (ADR 0571): the builder keeps its build log, one entry at a build's start and one at its end, from which build history and durations are read and the module mesh-catalog asks the builds it missed; the artifact sweep is the seat `mesh-artifact-sweep`, held by artifact-store-tools beside the artifact store, its keep set from the store's own listing, the wants and the build log, sweeping at each build's end from the lease write and recording each deletion in the hand-act log; disk readings are disk-load's own state on every node; who holds which seat is the state `seat-holders` of the seat `mesh-placement` (now `mesh-module-assigner`), and tools are found by the tool runners' announcements; what a node runs is its want and its have; the grants part writes `grants.<module>.<fingerprint>` and keeps memberships in its own state; root-free is a probe of the self-check; no manifest but the controller's reads, invokes or consumes `mesh-controller`, but a named list kept to the lease write, so the lease write's check passes (ADR 0189, ADR 0547 §11, ADR 0549 §6) | — | mesh-sdk, mesh-controller (its pin only), mesh-delivery (builder, indexer, assigner, grants part, view), mesh-catalog (artifact-store-tools, disk-load, the backup seat's holder, mounts, nfs-server, minio, mesh-watcher, the messenger, the self-check, the module mesh-catalog, docker), mesh-tools (the tool runner) | 1955 |
An issue is in one row only (ADR 0493 §2): where a row named an issue another row names too, the last row keeps it and the
earlier row says so in words (the operator, 2026-10-26). Milestone M1 makes each row a record of the issue tracker