Files
hq/02-DECISIONS/0154-the-meshs-own-verbs-are-the-controller-seats-tools.md
T
jschoubben 37b46d5349 ADR 0153 and ADR 0154: the record is read by a module, and the mesh's verbs are its seat's tools
Design 33 in progress against ADR 0154 (the twelve verbs, the prerequisites built); design 35 for
the records module under ADR 0153, extending 0025; as-is 07 rewritten to a mesh that keeps no store;
as-is 12 and 13 updated; issue 006 built and waiting on its live check.
2026-09-30 17:47:49 +02:00

8.8 KiB

topic, status, date, deciders, reconstructed, extends
topic status date deciders reconstructed extends
the mesh accepted 2026-09-30 jochen false 02-DECISIONS/0132-a-seat-carries-the-tools-its-holder-must-serve.md

154. The mesh's own verbs are the mesh-controller seat's tools, and which verbs those are

Context

ADR 0132 decided that a seat's protocol carries its tools in full, that holding a seat means serving them, and that the mesh's own verbs are the mesh-controller seat's. It named three prerequisites, none in place: the protocol in the store rather than in compiled defaults; a protocol richer than a list of verbs; a node-scoped seat's subject carrying the node. And it left one thing to a decision per seat: which verbs each seat serves, because a seat's tools bind every future holder.

The console shipped the same day (ADR 0152) and made the gap visible from the operator's chair: a person on a workstation could call every tool a module serves and none of the mesh's own. What a node runs, what is assigned, whether a push applied — the questions issue 147 opened with — still meant a shell on the control node. The console's own handshake said so.

The control plane already answers every one of those questions, as commands: status --json, node show, plan --json, assign, push. ADR 0035 says a surface is an adapter over those with no decisions in it, and the api verb proves the shape: every route calls the function the command line calls.

Considered Options

1. Leave the mesh's verbs to the shell until an identity provider authenticates the HTTP API. Rejected. The authenticated network surface is for a browser on another machine; the console is already behind the machine's login (0152), and the bus already carries every other tool call under an account whose permission list says what it may ask. Waiting would keep the one surface the mesh has from answering the mesh's own questions, for a reason that does not apply to it.

2. Serve the verbs as the mesh-controller module's tools, mesh.mod.mesh-controller.tool.<verb>. Rejected; 0132 rejected it already. The controller holds a seat, and the verbs must keep their address while the control plane is being replaced, which is the moment they are most needed. A module's name would change with the implementation; the seat's does not.

3. Call each command's function inside the serving process. Rejected on two facts: the commands print, to the process's standard output, and two calls answered at once would read each other's words; and each command opens and closes its own stores, which the serving process holds open. Making every command return a value is the larger refactor, and it would give the tools a second code path to keep in step with the command line — the thing ADR 0035 forbids.

4. The holder of the seat runs the command it names, in its own binary, and answers what it printed. Chosen.

Decision

The mesh-controller seat serves twelve verbs, and these are its interface, additive within a version (33 — The tools the mesh answers §7):

verb answers with takes
tools every seat's tools, from the mesh's records nothing
status what is wrong, quiet, behind, waiting — status --json nothing
nodes every machine and its mode nothing
node what one machine reported, what it is assigned, why node
modules every module, its version, commit and machines nothing
seats every seat, what it delivers, who holds it — seats --json nothing
builds what was built lately and what came of it module (optional)
plan the declaration a machine would be sent — plan --json node
assign, unassign the mesh's own words, refusal included node, module
push that it was sent; status says what the machine did node (optional: every machine behind)
build that the build machine was asked; builds says what came of it repository, path, ref

Each verb runs the command it names, in the controller's own binary, and answers what the command printed — the output, whether it succeeded, and, where the command speaks JSON, the same as data. A refusal is the command's refusal in the command's words, because it is the same output. A verb takes only the arguments its schema names; nothing reaches a flag the schema did not declare. push and build are sent and not waited for: a call that blocked for a whole apply would time out on every machine that takes a minute and say nothing about the others.

The three prerequisites are built. A seat's protocol is three columns on its row, seeded from the compiled defaults where a row had none and additively thereafter, so a verb a release adds joins the row and nothing an operator wrote is taken away. A served verb is its name, what it does, and the schema of its arguments and answer; a manifest may still write a bare name. A node-scoped seat's tool carries the node it is asked of, as the last token of its subject; a mesh-scoped seat's stays flat.

Holding a mesh seat requires serving its verbs, judged where the store's set is loaded, and the refusal names the missing verbs. The controller's own manifest lists the twelve under tools.

Discovery reads the records, through the seat. tools is one of the twelve because the console cannot read the store and should not: the mesh answers for its own records through the role that owns them, and the answer is true while any other holder restarts. It is not true while the control plane itself restarts, and the console says so rather than hiding the modules' tools with it.

A grant of * reaches a role's tools; seat:<seat>.<verb> grants one. The console's * needed no change to reach the mesh's verbs, which is what a grant meaning every tool should mean.

Consequences

  • The console answers the mesh's own questions. Issue 147's first paragraph closes: what a node runs, what is assigned, whether a push applied, from the machine the person sits at, over the bus, under an account whose permission list says so.
  • Whoever may call mesh-controller.push may change the mesh. That is the console's * on a machine whose login owns the mesh (0152), and a person's account only if operator issue says so. A grant reviewer reads * and seat:mesh-controller. with the same care.
  • A verb here binds every future controller. Twelve is deliberate: what an operator asks weekly, and nothing that is still finding its shape (take, converge, settings, secret stay commands).
  • A command's text is the answer, and text changes. The three verbs that speak JSON carry it as data; the rest are read by a person or an agent, not parsed. Anything that needs a shape asks for --json to be added to the command first, which is the right order.
  • What got harder: the mesh-controller seat's row now carries a protocol an operator could edit, and a verb removed from the row is a verb the controller stops serving without a build. That is ADR 0122's arrangement applied to tools, and seats shows the row.

How this is checked

Rule Checked by
Every declared verb is one the binary can run, with the arguments its schema names a test walks the table and derives a command line for each
A verb missing a required argument is refused in its own words, before anything runs a test per shape
A holder that does not serve a mesh seat's verbs cannot hold it, and the refusal names them a catalogue test against a seat with two verbs and a holder with one
A node-scoped seat's tool carries the node; a mesh seat's does not the bus composition test: two nodes derive two addresses
The controller subscribes its seat's tools and may answer the composition test, and the golden user list
* reaches a role's tools; seat: grants one and refuses a name with no verb the composition test
The protocol is seeded into the row and widened additively the store-backed seat test
Live: the console lists mesh-controller.status and a call answers what status --json prints the rollout of this record

References