8.4 KiB
layer, status, code, updated, decisions
| layer | status | code | updated | decisions | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| to-be | implemented |
|
2026-09-30 |
|
34 — The console
The mesh's tools, on the machine a person sits at, served by a module the mesh assigned there. An agent reaches them over MCP on the machine's loopback; a person reaches the same endpoint. Nothing is installed by hand, nothing is configured with an address, and the mesh knows the surface exists because it put it there (ADR 0152).
1. What it is
A module, mesh-console, in the catalogue. Its image is the tool runtime's own — the client that
already speaks the bus as a command line and as an MCP server — started in a mode that reads the
module's credential and listens on loopback. It has no state, no provision, no seat. What it needs is
the bus, which it gets the way every module does: a credential the mesh minted for <node>.mesh-console,
sealed to the machine, delivered as the module's own secret.
Its manifest says three things nothing else in the catalogue says together:
invokes: ["*"]— it calls every tool on the mesh, and the bus grants exactly that publish side;listenson a portfrom: machine— the filter opens nothing for it, because loopback is not outside;- no
emits, noconsumes, notools— it answers nothing on the bus and nobody can address it there.
2. What it serves, and to whom
One endpoint, POST /mcp on the machine's loopback, speaking MCP over HTTP: initialize,
tools/list, tools/call. An agent on the machine is pointed at it once — the address is the machine's
own and never changes — and sees every tool the mesh can say it has. A person at a terminal uses the
same endpoint through the mesh client, or through anything that can make an HTTP request; the client
needs no credential, because the console holds it.
The endpoint is the machine's login. It binds 127.0.0.1 and nothing else. Whoever can connect is
on the machine, and whoever is on the machine is the account that owns the mesh there
(ADR 0034,
ADR 0144). There is no
token, no login page and no second identity, on purpose: a credential a person had to carry to reach
their own machine's console would be the arrangement this replaces, moved one hop.
3. How it knows what the mesh can do
Design 33 §5 splits discovery in two: a role's tools are read from the mesh's records, a module's own are asked of the module. The console builds the second half now and reads the first when it exists.
Every tool runtime answers tools. The runtime that serves a module's tools also serves one verb of
its own under that module's name, mesh.mod.<module>.tool.tools, answering the module's tool names,
descriptions and argument schemas — the definitions from the code that answers them, and from nowhere
else. A module may not name a tool of its own tools; the runtime refuses the collision at load.
The console asks the catalogue which modules the mesh holds, then asks each. catalog_modules
answers the roster; one tools request per module, in parallel, answers the list. The bus refuses at
once a request nothing serves, so a module that is not running costs nothing and is named in the answer
as not answering, rather than silently absent — silence and success must never look alike. The list is
kept for a short while and refreshed, so an agent asking on every turn does not fan out on every turn.
A tool that was not listed can still be called. Listing is discovery; calling is the grant. An agent
that knows a tool's name asks for it by <module>.<tool> and the module answers or the bus says why not.
What is missing from the list, and until when. A role's tools and the mesh's own verbs — status,
push, assign — are the mesh-controller seat's under ADR 0132 and are not served yet; their three
prerequisites are listed in that record. When the seat serves them, the console lists them beside the
modules' own, and the person stops opening a shell for the mesh's own questions. Until then the console
says so in its handshake.
4. Where it runs
On whichever machines an operator sits at, by assignment. It is not on the control node by default and does not need to be: it reaches the bus like any module, from anywhere in the mesh. A machine that is not a node cannot have it, which is the right refusal — the mesh reaches what it declares, and a workstation that wants the console joins first.
The person's credential and the mesh client (design 25 §7) remain the path
for a machine that is not a node, and the path to a mesh not yet far enough along to assign anything.
5. Removing it
Unassigning the console from a machine revokes its bus account at the next composition and stops the container; nothing is left on the machine that could still connect. An agent pointed at the loopback address gets a refused connection, which is the truthful answer.
How it is checked
| Check | Defends |
|---|---|
a module invoking one tool may publish that subject and no other tool's; * may publish every one; neither may publish an event or subscribe what it did not consume |
ADR 0152, the grant |
a module registering two tools answers three names to tools, with schemas; a module naming its own tools is refused at load |
ADR 0152, discovery |
| against a real bus: two modules up, a third held and not running — the console lists the two and names the third as not answering | ADR 0152, silence is not success |
| a call through the console's endpoint reaches a module over the bus and the answer is the module's own, unshaped | ADR 0035, a surface decides nothing |
on the live mesh: the console assigned to a workstation answers tools/list on loopback and a call to the forge returns repositories |
the exit of work-order step 3 |
| the composed filter for a machine carrying the console opens no port for it | ADR 0144 |
What shipped, 2026-09-30
Everything above, the same day: mesh-controller PR 164 (invokes, module check), mesh-tools PR 20
(mesh serve, the tools verb), mesh-catalog PR 181 (mesh-console). Verified on the live mesh: the
console assigned to a workstation answered tools/list on its loopback with 62 tools from the modules
whose runtimes had been rebuilt to answer tools, named 36 modules as not answering (modules that serve
no tools, and modules whose new runtime the mesh records rather than rolls out), and a tools/call of
the forge's gitea_list_repos returned repositories. An agent on that machine reaches it as an HTTP
MCP server and reports it connected. The mesh assigned the declared port unchanged, which is what a
machine with nothing else on it does; the console binds whatever it is given.
Two things shipped bent, both stated in 00-as-is/13-the-console.md:
the person's client through the console (--console) exists and was exercised in the test suite, not
on the live mesh; and a module registered from the catalogue by hand recorded its source as a URL rather
than as a path on the git seat, because --self takes the forge path form — the rebuild-on-merge still
matched it by URL.
What this does not settle
- Narrowing a console's grant per assignment. ADR 0046 makes it a setting; nothing reads one yet.
- The mesh's own verbs on the bus. Design 33's third family; this document only says where they appear once they exist.
- A person's identity behind the console. The mesh sees the console's account; design 15 keeps the question open.
References
- ADR 0152 — the decision
- 33 — The tools the mesh answers — what the console lists
- 25 — The bus on NATS §7 — the person's client this makes a module of
- issue 147 — the symptom