The work order's group-3 question answered: an ordinary module the mesh assigns to the machine a person sits at, holding a minted credential, calling tools under a manifest grant (invokes), serving MCP on loopback. Design 34; pointers in 33, 25 and 0095; module check designed into 12 (issue 148); README stops claiming an indexing nothing provides (issue 006).
12 KiB
topic, status, date, deciders, reconstructed, extends
| topic | status | date | deciders | reconstructed | extends |
|---|---|---|---|---|---|
| what runs on it | accepted | 2026-09-30 | jochen | false | 02-DECISIONS/0095-the-control-plane-is-the-way-to-ask-a-module.md |
152. The operator's surface is a module the mesh assigns: the console
Context
Since the bus moved, nobody can ask the mesh anything without opening a shell on a machine. Every tool call an operator's assistant makes fails, on every machine including the one the operator sits at, with AMQP not connected (issue 147). The program answering is the predecessor's tool server, started on the workstation by hand, with the predecessor's broker address written into the assistant's own configuration. It has no manifest, no assignment, no account on the bus, and the mesh has never known it exists. Nothing regressed: the mesh removed a transport that a program outside the mesh still dials.
The mesh has a tool model, and it works. A module serves each tool on its own subject and its
account may serve nothing else (ADR 0047);
a person is issued an account whose only permission is to publish the tool subjects named at issue
(25 — The bus on NATS §7); a client on the
runtime repository's main branch speaks that account as a command line and as an MCP server. Measured
on the live mesh on 2026-09-28: a call to the forge's gitea_list_repos answered with real
repositories; the tool list came back empty, because it asks the catalogue for a tool nothing serves
(ADR 0132).
Two records have already said where the surface belongs. ADR 0132's consequences: the MCP surface belongs inside the mesh — a module the mesh assigns to the machine where the agent sits, with a credential the mesh minted and authority derived from what it may call, not a program started by hand with a credential printed to a terminal. Design 33 §6 says the same. Neither is a decision about the surface: 0132 decided where a seat's tools live, and named the surface in passing.
And one record says the opposite, in the letter. ADR 0095 made the control plane the way to ask a module, deferred "calling is a grant" until something asked for it, and recorded that nothing outside the control plane can. A person's account (design 25 §7, built 2026-09-28) is exactly that grant, minted for a person. So 0095's exclusivity has already been widened once without a record saying so; a module that calls tools widens it a second time, and this record is where that is said.
What a module's account may do today, counted from the composition (internal/broker): publish
its own events, publish the accept subjects of seats it uses, subscribe its own tools and what it
consumes. No module principal may publish another module's tool subject. Of 72 modules in the
catalogue, 45 serve tools and 0 may call one.
The question the work order asks before any code: does the mesh grow its own operator surface, or is the surface an ordinary module that happens to serve tools?
Considered Options
1. The control plane serves the agent protocol itself — a listener on the controller, or a verb
its binary runs. Rejected. It puts a surface for tools the control plane does not implement on the one
component that must stay answerable while it is itself being replaced, which is the reason 0132
rejected the control plane as the answer to discovery. A person on a workstation would reach it over
the network, and the networked surface ADR 0035
reserves for that authenticates through an OAuth2 provider that is not configured — so the controller's
api verb correctly serves nothing today, and this option would either wait for it or bypass it.
2. A program a person installs and starts by hand with a printed credential — what exists on the
runtime repository's main. Rejected as the end state. It is outside the mesh in every way issue 147
names: no assignment, no declaration, no seat, no check that it reaches anything, revoked only by a
person remembering to. It is the predecessor's arrangement one bus later, and it fails the same way
the next time an address moves. It stays as the recovery path, the way the command line does
(ADR 0035): a credential from operator issue and the mesh client work with no console assigned.
3. An ordinary module, assigned to the machine the person sits at, holding a credential the mesh minted, serving the mesh's tools on that machine's loopback. Chosen.
Decision
The console is a module. mesh-console is built by the mesh, registered like any module,
assigned to a machine, and given a bus credential sealed to that machine. It serves the mesh's tools to
whoever is on that machine: to an agent over MCP, and to a person through the same endpoint. Assigning
it to a machine is what makes the mesh reachable from there; unassigning it revokes that, at the next
composition, with nothing on the machine to remember to remove.
A grant to call is a manifest word: invokes. A module declares the tools it calls, each as
<module>.<tool>, or the single entry * for every tool on the mesh. The bus grants exactly that
publish side and nothing beside it — no event, no subscription, no seat. This is ADR 0095's deferred
first option, taken now that a consumer asks for it; a person's account already has this shape, and
the same composition derives both. The control plane's ask stands, and 0095's audit point with it:
every call still passes one account whose permission list says what it may ask.
Authority is the machine's login. The console listens on the machine's loopback only, declared
from: machine, so whoever can open a socket on the machine is whoever owns the machine, and the
account that installed the host owns the mesh on that node
(ADR 0034). Anything on a machine may call anything on it,
and that is the whole of local (ADR 0144).
The mesh knows no person: what the audit sees is which console asked, under the account
<node>.mesh-console. How a person's identity reaches a session is the question design 15 leaves
open, and this record does not close it.
What the console lists is asked of the modules. Design 33 §5: a module's own tools are answered by
the module, from the code that defines them. The tool runtime therefore answers one reserved verb for
every module it serves — tools, the module's tool names, descriptions and argument schemas — and the
console assembles its list by asking the catalogue which modules the mesh holds and each module what
it answers. A module that is not running is absent from the list and says so; a tool an agent already
knows the name of can be called whether or not it was listed. A module may not name a tool of its own
tools; the runtime refuses the collision at load rather than letting one shadow the other. A
role's tools, and the mesh's own verbs, join the list when the mesh-controller seat serves them
(design 33 §1, third family) — the console reads whatever the mesh can say about itself, and grows as
that does.
The console holds one credential and one grant: *. It is the operator's surface on a machine the
operator owns; narrowing what it may call is a setting on its assignment, which
ADR 0046 already provides for
and nothing here builds.
Consequences
- The way a person drives the mesh is inside the mesh. It is declared, delivered, replaced and
revoked by the same machinery as everything else, and
statussays whether the machine carrying it has applied. Issue 147's shape — a surface kept alive by an address in a file — cannot recur, because there is no file: the console's credential names the bus the mesh is on, and moves when it does. - A module may now call tools, which ADR 0095 had reserved to the control plane. The grant is
explicit, per tool or
*, and derived by the same composition that grants everything else. A module that declares noinvokesgains nothing. What got harder: a manifest reviewer has one more field to read for authority, and*in it deserves the reader's attention every time. - A new manifest word ships one release before any manifest uses it, and must reach both parsers: the build machine's and the running controller's. The console's manifest cannot be registered until the controller and the builder that packages it have been rebuilt with the word.
- Discovery costs a fan-out per list. One request per module the mesh holds, answered at once by the bus for every module nothing serves, so the cost is bounded by the modules that are up. The list is cached briefly in the console; a module assigned a moment ago appears at the next refresh.
- Every tool runtime must be rebuilt once to answer
tools. Until a module is, it is callable and not listed, and the console says which modules did not answer. - The mesh's own verbs are not in the console yet.
status,push,assignare themesh-controllerseat's tools under 0132, and the three prerequisites 0132 names are still not in place. A person asking what a node runs still opens a shell for that question, and that gap is design 33's to close, not this record's — recorded here so nobody reads the console as the whole of 147. - The person's credential is not retired.
operator issueand themeshclient remain the path when no console is assigned, and the path an operator uses to bring a mesh up far enough to assign one.
How this is checked
| Rule | Checked by |
|---|---|
A module's invokes becomes exactly that publish grant, and nothing else |
the bus user composition test: a module invoking shop.price may publish that subject and no other tool's; * may publish every tool subject; neither may publish an event or subscribe anything it did not consume |
A malformed invokes entry is refused at registration |
a parser test: an entry naming no tool is a problem named in the manifest's words |
The runtime answers tools for every module it serves |
the runtime's test: a module registering two tools answers three names, and a module naming one of its own tools is refused at load |
| The console's list is what the modules answer | the client's test against a real bus: two modules up, a third the catalogue holds and nothing serves, and the list carries the two and names the third as not answering |
| A call from the console reaches a module over the bus | the same test, and the live mesh: the console assigned to a workstation answers tools/list on its loopback and a call to the forge returns repositories |
| The console listens on loopback and nowhere else | its manifest declares from: machine, and the filter composed for the machine opens nothing for it |
References
- issue 147 — the surface outside the mesh
- ADR 0095 — extended: a grant to call, for a module as for a person
- ADR 0132 — where a seat's tools live, and the sentence that named the surface
- ADR 0035 — three surfaces over one implementation
- ADR 0034, ADR 0144 — why loopback is the authority boundary
- 33 — The tools the mesh answers §5, §6 — discovery, and what serves it to an agent
- 34 — The console — the design this record authorises
- mesh-tools
src/client.ts,src/mcp.ts,src/mesh.ts— the client this makes a module of