ADR 0152: the operator's surface is a module, the console
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).
This commit is contained in:
@@ -75,6 +75,18 @@ another — and a mesh you cannot name precisely is a mesh two people describe d
|
|||||||
is a role you occupy, and the two meet where a seat delivers a provision: occupying the seat is
|
is a role you occupy, and the two meet where a seat delivers a provision: occupying the seat is
|
||||||
what makes a module *the* provider of it.
|
what makes a module *the* provider of it.
|
||||||
|
|
||||||
|
## The surfaces
|
||||||
|
|
||||||
|
- **console** — the module (`mesh-console`) that puts the mesh's tools in front of whoever is on a
|
||||||
|
machine: an MCP endpoint on the machine's loopback for an agent, the same endpoint for a person. It
|
||||||
|
is assigned like any module, holds a credential the mesh minted, and calls tools under a grant its
|
||||||
|
manifest declares (`invokes`). Loopback is the authority boundary: whoever is on the machine owns the
|
||||||
|
mesh there ([ADR 0152](../02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md)).
|
||||||
|
Not "the tool bridge", "the brain" or "the MCP server" — those name the predecessor's program or a
|
||||||
|
protocol, and the console is a module.
|
||||||
|
- **invokes** — the manifest word for the tools a module calls, `<module>.<tool>` each or `*` for
|
||||||
|
every one. A grant on the publish side and nothing else; a module that declares none calls nothing.
|
||||||
|
|
||||||
## How this page is kept
|
## How this page is kept
|
||||||
|
|
||||||
A new name for an existing thing lands here first, in the same change that introduces it in code. A
|
A new name for an existing thing lands here first, in the same change that introduces it in code. A
|
||||||
|
|||||||
@@ -47,6 +47,14 @@ Anything with the control plane in reach can ask any module anything it serves.
|
|||||||
harder: nothing outside the control plane can, and the control plane's connection is one more
|
harder: nothing outside the control plane can, and the control plane's connection is one more
|
||||||
thing on the path of every question — a cost accepted for the audit it buys.
|
thing on the path of every question — a cost accepted for the audit it buys.
|
||||||
|
|
||||||
|
> **The mechanism changed — 2026-09-30, by ADR 0152.** What stands: `ask` on the control plane, and
|
||||||
|
> that every call passes an account whose permission list says what it may ask. What moved: "nothing
|
||||||
|
> outside the control plane can" stopped being true when a person's account gained a publish grant per
|
||||||
|
> tool (design 25 §7, 2026-09-28), and [ADR 0152](0152-the-operators-surface-is-a-module-the-console.md)
|
||||||
|
> takes the first option above for a module as well — a manifest declares `invokes`, and the bus grants
|
||||||
|
> exactly that publish side. The audit the second option bought is the bus's permission list, which
|
||||||
|
> derives both.
|
||||||
|
|
||||||
## How it is checked
|
## How it is checked
|
||||||
|
|
||||||
A tools-only bed asks a served tool through the control plane and asserts an answer arrived —
|
A tools-only bed asks a served tool through the control plane and asserts an answer arrived —
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
---
|
||||||
|
topic: what runs on it
|
||||||
|
status: accepted
|
||||||
|
date: 2026-09-30
|
||||||
|
deciders: jochen
|
||||||
|
reconstructed: false
|
||||||
|
extends: 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](../04-ISSUES/147-the-operators-tools-still-dial-the-bus-that-was-removed/00-report.md)).
|
||||||
|
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](0047-a-module-runs-its-code-as-its-own-process-with-its-own-account.md));
|
||||||
|
a person is issued an account whose only permission is to publish the tool subjects named at issue
|
||||||
|
([25 — The bus on NATS](../03-DESIGN/01-to-be/25-the-bus-on-nats.md) §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](0132-a-seat-carries-the-tools-its-holder-must-serve.md)).
|
||||||
|
|
||||||
|
**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](../03-DESIGN/01-to-be/33-the-tools-the-mesh-answers.md)
|
||||||
|
§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](0095-the-control-plane-is-the-way-to-ask-a-module.md)
|
||||||
|
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](0035-one-implementation-several-surfaces.md)
|
||||||
|
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](0034-the-local-account-owns-the-mesh.md)). Anything on a machine may call anything on it,
|
||||||
|
and that is the whole of local ([ADR 0144](0144-anything-on-a-machine-may-call-anything-on-it.md)).
|
||||||
|
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](0046-a-module-configuration-is-its-assignments-not-its-manifest.md) 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 `status` says 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 no `invokes` gains 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`, `assign` are the
|
||||||
|
`mesh-controller` seat'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 issue` and the `mesh` client 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](../04-ISSUES/147-the-operators-tools-still-dial-the-bus-that-was-removed/00-report.md) — the surface outside the mesh
|
||||||
|
- [ADR 0095](0095-the-control-plane-is-the-way-to-ask-a-module.md) — extended: a grant to call, for a module as for a person
|
||||||
|
- [ADR 0132](0132-a-seat-carries-the-tools-its-holder-must-serve.md) — where a seat's tools live, and the sentence that named the surface
|
||||||
|
- [ADR 0035](0035-one-implementation-several-surfaces.md) — three surfaces over one implementation
|
||||||
|
- [ADR 0034](0034-the-local-account-owns-the-mesh.md), [ADR 0144](0144-anything-on-a-machine-may-call-anything-on-it.md) — why loopback is the authority boundary
|
||||||
|
- [33 — The tools the mesh answers](../03-DESIGN/01-to-be/33-the-tools-the-mesh-answers.md) §5, §6 — discovery, and what serves it to an agent
|
||||||
|
- [34 — The console](../03-DESIGN/01-to-be/34-the-console.md) — the design this record authorises
|
||||||
|
- mesh-tools `src/client.ts`, `src/mcp.ts`, `src/mesh.ts` — the client this makes a module of
|
||||||
@@ -256,6 +256,7 @@ python3 00-META/checks/index.py fail if stale
|
|||||||
- **0146** — [Connectivity is checked by name, per hosting form, with a valid certificate](0146-connectivity-is-checked-by-name-per-hosting-form.md)
|
- **0146** — [Connectivity is checked by name, per hosting form, with a valid certificate](0146-connectivity-is-checked-by-name-per-hosting-form.md)
|
||||||
- **0147** — [A module anchors the mesh's authority on a machine, and takes it away again](0147-a-module-anchors-the-meshs-authority.md)
|
- **0147** — [A module anchors the mesh's authority on a machine, and takes it away again](0147-a-module-anchors-the-meshs-authority.md)
|
||||||
- **0150** — [A module's own code runs as supervised processes under the module's one account](0150-a-modules-own-code-runs-as-supervised-processes-under-one-account.md)
|
- **0150** — [A module's own code runs as supervised processes under the module's one account](0150-a-modules-own-code-runs-as-supervised-processes-under-one-account.md)
|
||||||
|
- **0152** — [The operator's surface is a module the mesh assigns: the console](0152-the-operators-surface-is-a-module-the-console.md)
|
||||||
|
|
||||||
### How it is built
|
### How it is built
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ code:
|
|||||||
- mesh-controller internal/catalogue/build.go
|
- mesh-controller internal/catalogue/build.go
|
||||||
- mesh-controller internal/inventory/secrets.go
|
- mesh-controller internal/inventory/secrets.go
|
||||||
- mesh-controller cmd/mesh-builder
|
- mesh-controller cmd/mesh-builder
|
||||||
updated: 2026-09-12
|
updated: 2026-09-30
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0069-a-module-is-a-repository-and-a-path.md
|
- 02-DECISIONS/0069-a-module-is-a-repository-and-a-path.md
|
||||||
|
- 02-DECISIONS/0037-where-a-module-lives.md
|
||||||
- 02-DECISIONS/0009-modules-and-the-graph.md
|
- 02-DECISIONS/0009-modules-and-the-graph.md
|
||||||
- 02-DECISIONS/0010-delivery.md
|
- 02-DECISIONS/0010-delivery.md
|
||||||
- 02-DECISIONS/0005-the-node-host.md
|
- 02-DECISIONS/0005-the-node-host.md
|
||||||
@@ -60,6 +61,32 @@ module from a repository and a path, and the root-only reading left every existi
|
|||||||
unbuildable — pointed at the catalogue the builder finds no manifest, pointed at a module's source
|
unbuildable — pointed at the catalogue the builder finds no manifest, pointed at a module's source
|
||||||
it finds no manifest either.*
|
it finds no manifest either.*
|
||||||
|
|
||||||
|
## A manifest is checked where it is written
|
||||||
|
|
||||||
|
*Added 2026-09-30, from [issue 148](../../04-ISSUES/148-a-manifest-outside-this-catalogue-has-no-check/00-report.md).*
|
||||||
|
|
||||||
|
The check the mesh applies at registration — the manifest parses strictly, every name in it is a
|
||||||
|
usable one, its routes and events and seats are well formed, and no two manifests given together
|
||||||
|
declare one seat — is a verb on the controller's binary, `module check <manifest>…`, and it needs no
|
||||||
|
mesh. It reads the files it is given, runs the same functions registration runs, prints every problem
|
||||||
|
in the manifest's own words, and exits non-zero if there was one. Somebody describing their own
|
||||||
|
application in their own repository — the case [ADR 0037](../../02-DECISIONS/0037-where-a-module-lives.md)
|
||||||
|
calls the one that matters most — runs it before pushing, and finds out there rather than when a
|
||||||
|
running mesh refuses the registration, or later, when a machine applies something that resolved and
|
||||||
|
should not have.
|
||||||
|
|
||||||
|
**What it cannot know, it says.** A seat another module declares elsewhere is unknown to a check that
|
||||||
|
was not handed that module's manifest, and the output says so rather than refusing: pass the other
|
||||||
|
manifest too. The mesh's own seats it knows from the binary, which is the one place that set may be
|
||||||
|
read without a store ([ADR 0122](../../02-DECISIONS/0122-a-seat-is-data-a-rename-is-a-database-update.md)
|
||||||
|
keeps the store authoritative, so a claim on a mesh seat is judged fully only at registration, and the
|
||||||
|
check says that too).
|
||||||
|
|
||||||
|
*How it is checked:* the controller's test runs the check over the catalogue checkout beside it and
|
||||||
|
over a manifest with a known fault, and asserts the first passes and the second names the fault; the
|
||||||
|
test that used to be the only check, `TestEveryCatalogueManifestParses`, now stands beside a command
|
||||||
|
anybody can run.
|
||||||
|
|
||||||
## The manifest in the repository is not the manifest the mesh holds
|
## The manifest in the repository is not the manifest the mesh holds
|
||||||
|
|
||||||
A resource names an artifact:
|
A resource names an artifact:
|
||||||
|
|||||||
@@ -361,6 +361,13 @@ bridged. It is three things:
|
|||||||
|
|
||||||
Nothing is built of this before §10's bed passes; the MCP surface is a thin adapter over (2).
|
Nothing is built of this before §10's bed passes; the MCP surface is a thin adapter over (2).
|
||||||
|
|
||||||
|
*Built, and then made a module — 2026-09-30.* (1) and (2) exist: `operator issue` and the `mesh`
|
||||||
|
client. What (2) describes as a program on the workstation is now the recovery path; the surface an
|
||||||
|
operator uses is a module the mesh assigns to the machine, holding a credential the mesh minted —
|
||||||
|
[ADR 0152](../../02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md),
|
||||||
|
[34 — The console](34-the-console.md). The tool list it asks for is no longer
|
||||||
|
`catalog_tools`, which nothing served: each runtime answers `tools` for its own module.
|
||||||
|
|
||||||
## 8. What a module sees, and what the wire does
|
## 8. What a module sees, and what the wire does
|
||||||
|
|
||||||
**The contract a module is written against does not change.** `publish` on an envelope becomes a
|
**The contract a module is written against does not change.** `publish` on an envelope becomes a
|
||||||
|
|||||||
@@ -102,6 +102,12 @@ being something a person carries and becomes something the mesh runs, on a node,
|
|||||||
An agent's authority can then be role-shaped: *the forge's tools*, rather than a list of
|
An agent's authority can then be role-shaped: *the forge's tools*, rather than a list of
|
||||||
module-specific names that changes the day the forge is replaced.
|
module-specific names that changes the day the forge is replaced.
|
||||||
|
|
||||||
|
*Decided and designed on 2026-09-30:* the module is the console —
|
||||||
|
[ADR 0152](../../02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md),
|
||||||
|
[34 — The console](34-the-console.md). It builds the second half of §5 now (a module's tools are
|
||||||
|
asked of the module, through a `tools` verb every runtime answers) and lists a role's tools when the
|
||||||
|
records carry them.
|
||||||
|
|
||||||
## 7. Versioning
|
## 7. Versioning
|
||||||
|
|
||||||
A seat's tools are an interface and change like one. Additive within a version. A change that would
|
A seat's tools are an interface and change like one. Additive within a version. A change that would
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
---
|
||||||
|
layer: to-be
|
||||||
|
status: in-progress
|
||||||
|
code: [mesh-catalog, mesh-tools, mesh-controller]
|
||||||
|
updated: 2026-09-30
|
||||||
|
decisions:
|
||||||
|
- 02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md
|
||||||
|
- 02-DECISIONS/0132-a-seat-carries-the-tools-its-holder-must-serve.md
|
||||||
|
- 02-DECISIONS/0095-the-control-plane-is-the-way-to-ask-a-module.md
|
||||||
|
- 02-DECISIONS/0035-one-implementation-several-surfaces.md
|
||||||
|
- 02-DECISIONS/0034-the-local-account-owns-the-mesh.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# 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](../../02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md)).
|
||||||
|
|
||||||
|
## 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;
|
||||||
|
- `listens` on a port `from: machine` — the filter opens nothing for it, because loopback is not outside;
|
||||||
|
- no `emits`, no `consumes`, no `tools` — 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](../../02-DECISIONS/0034-the-local-account-owns-the-mesh.md),
|
||||||
|
[ADR 0144](../../02-DECISIONS/0144-anything-on-a-machine-may-call-anything-on-it.md)). 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](33-the-tools-the-mesh-answers.md) §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](25-the-bus-on-nats.md) §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 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](../../02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md) — the decision
|
||||||
|
- [33 — The tools the mesh answers](33-the-tools-the-mesh-answers.md) — what the console lists
|
||||||
|
- [25 — The bus on NATS](25-the-bus-on-nats.md) §7 — the person's client this makes a module of
|
||||||
|
- [issue 147](../../04-ISSUES/147-the-operators-tools-still-dial-the-bus-that-was-removed/00-report.md) — the symptom
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
status: located
|
status: located
|
||||||
opened: 2026-08-23
|
opened: 2026-08-23
|
||||||
located-in: [hal, hq]
|
located-in: [hq README.md, and the agent ADR 0025 names — not built]
|
||||||
fixed-by:
|
fixed-by:
|
||||||
amended-design: 02-DECISIONS/0025-the-design-record-is-read-not-copied.md
|
amended-design: 02-DECISIONS/0025-the-design-record-is-read-not-copied.md
|
||||||
---
|
---
|
||||||
@@ -169,3 +169,16 @@ them into. The record stays open, and its answer is no longer "index this reposi
|
|||||||
is whatever the mesh grows as its own knowledge surface, if it grows one. Until then the honest fix
|
is whatever the mesh grows as its own knowledge surface, if it grows one. Until then the honest fix
|
||||||
is the README, which should stop claiming a property nothing provides.
|
is the README, which should stop claiming a property nothing provides.
|
||||||
|
|
||||||
|
|
||||||
|
## Where this stands, 2026-09-30
|
||||||
|
|
||||||
|
The README no longer claims a property nothing provides: it says the indexing never existed, that the
|
||||||
|
store it named is unreachable since the cut-over, and that ADR 0025's answer — read, not copied, by an
|
||||||
|
agent that consults this repository — is decided and not built. That was the honest fix the previous
|
||||||
|
note asked for, and it is done.
|
||||||
|
|
||||||
|
The record stays open on ADR 0025's build, and on nothing else. The mesh's own operator surface is now
|
||||||
|
a module ([ADR 0152](../../02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md)); the
|
||||||
|
reader ADR 0025 describes is the mesh session of design 15, which would answer through that surface
|
||||||
|
like any tool. What closes this is still the check 0025 names: search for a phrase that appears only in
|
||||||
|
a design document here, and get it back.
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
---
|
---
|
||||||
status: located
|
status: located
|
||||||
opened: 2026-09-29
|
opened: 2026-09-29
|
||||||
located-in: [nothing in the mesh — the surface in use is the predecessor's, installed on the workstation]
|
located-in: [mesh-catalog modules/mesh-console, mesh-tools src/mesh.ts, mesh-controller internal/broker]
|
||||||
|
fixed-by:
|
||||||
|
amended-design: 03-DESIGN/01-to-be/34-the-console.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# 147 — the operator's tools still dial the bus that was removed
|
# 147 — the operator's tools still dial the bus that was removed
|
||||||
@@ -73,3 +75,21 @@ outside the mesh.
|
|||||||
- It fails identically for the local machine, which rules out reachability and points at the
|
- It fails identically for the local machine, which rules out reachability and points at the
|
||||||
transport alone.
|
transport alone.
|
||||||
- The mesh's own traffic over the new bus is unaffected: nodes report, declarations apply.
|
- The mesh's own traffic over the new bus is unaffected: nodes report, declarations apply.
|
||||||
|
|
||||||
|
## Answered, 2026-09-30
|
||||||
|
|
||||||
|
The work order's question — does the mesh grow its own operator surface, or is the surface an
|
||||||
|
ordinary module — is answered by [ADR 0152](../../02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md):
|
||||||
|
**the console is a module.** `mesh-console` is assigned to the machine a person sits at, holds a
|
||||||
|
credential the mesh minted, calls tools under a grant its manifest declares (`invokes`), and serves the
|
||||||
|
mesh's tools on that machine's loopback to an agent over MCP and to a person through the same endpoint.
|
||||||
|
Designed in [34 — The console](../../03-DESIGN/01-to-be/34-the-console.md).
|
||||||
|
|
||||||
|
What that leaves, said plainly so nobody reads this record as closed on the whole of its first
|
||||||
|
paragraph: the console reaches every tool a *module* serves. The mesh's own questions — what a node
|
||||||
|
runs, what is assigned — are the `mesh-controller` seat's tools under ADR 0132 and are not on the bus
|
||||||
|
yet; for those a shell is still the way, and design 33 is where that closes.
|
||||||
|
|
||||||
|
The predecessor's program on the workstation is not replaced by the mesh; it is left where it is and
|
||||||
|
the assistant is pointed at the console beside it. The `hal` entry in the assistant's configuration
|
||||||
|
still names things that are not the mesh's.
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
---
|
---
|
||||||
status: open
|
status: located
|
||||||
opened: 2026-09-29
|
opened: 2026-09-29
|
||||||
located-in: [mesh-controller cmd/mesh-controller]
|
located-in: [mesh-controller cmd/mesh-controller, mesh-controller internal/catalogue]
|
||||||
|
fixed-by:
|
||||||
|
amended-design: 03-DESIGN/01-to-be/12-a-module-repository.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# 148 — a manifest outside this catalogue has no check
|
# 148 — a manifest outside this catalogue has no check
|
||||||
@@ -35,3 +37,14 @@ Nothing prevents this; it was noticed and left. ADR 0037 named it on 2026-09-01
|
|||||||
take a path from `MESH_CATALOG`, so the mechanism is already path-driven and not repository-bound.
|
take a path from `MESH_CATALOG`, so the mechanism is already path-driven and not repository-bound.
|
||||||
- `mesh-controller module add` refuses a bad manifest at registration, which is the same check far
|
- `mesh-controller module add` refuses a bad manifest at registration, which is the same check far
|
||||||
too late: by then it is in a running mesh's records.
|
too late: by then it is in a running mesh's records.
|
||||||
|
|
||||||
|
## Built, 2026-09-30
|
||||||
|
|
||||||
|
`mesh-controller module check <manifest>…` runs what registration runs — the strict parse, the
|
||||||
|
per-manifest problems, and the cross-manifest rules over every manifest given — with no store and no
|
||||||
|
mesh, prints every problem in the manifest's words, and exits non-zero on any. What it cannot judge
|
||||||
|
without a store it says: a claim on one of the mesh's own seats is judged fully only at registration
|
||||||
|
([ADR 0122](../../02-DECISIONS/0122-a-seat-is-data-a-rename-is-a-database-update.md)), and a seat
|
||||||
|
declared by a module whose manifest was not passed reads as unknown. Written into
|
||||||
|
[12 — A module repository](../../03-DESIGN/01-to-be/12-a-module-repository.md) as the section *a
|
||||||
|
manifest is checked where it is written*. The console's own manifest was the first checked with it.
|
||||||
|
|||||||
@@ -87,12 +87,15 @@ rather than location** — that these documents would be indexed into the knowle
|
|||||||
symptom search returns them beside everything else. One source, many surfaces. Where the source
|
symptom search returns them beside everything else. One source, many surfaces. Where the source
|
||||||
is authored is then a separate question.
|
is authored is then a separate question.
|
||||||
|
|
||||||
**That indexing does not exist.** It was checked on 2026-08-23 and returns nothing; it appears
|
**That indexing never existed, and the store it would have indexed into is gone.** It was checked
|
||||||
never to have existed. Until it does, the objection stands unanswered and this repository is
|
on 2026-08-23 and returned nothing; the knowledge base it named was the predecessor's, and since the
|
||||||
the fourth knowledge system it was argued not to be. Recorded as
|
mesh moved to its own bus on 2026-09-28 nothing can reach it at all. The claim is recorded as
|
||||||
[`04-ISSUES/006`](04-ISSUES/006-hq-is-not-indexed-into-the-knowledge-base/00-report.md), and
|
[`04-ISSUES/006`](04-ISSUES/006-hq-is-not-indexed-into-the-knowledge-base/00-report.md) and answered
|
||||||
left standing here rather than quietly reworded, because a claim that held up a decision and
|
by [ADR 0025](02-DECISIONS/0025-the-design-record-is-read-not-copied.md): these documents are
|
||||||
was never checked is precisely the failure this repository exists to name.
|
**read, not copied** — an agent reads this repository and a search consults it — and that agent is
|
||||||
|
not built. So today this repository is reachable by whoever knows to open it and surfaces to nobody
|
||||||
|
else. Said here rather than quietly reworded, because a claim that held up a decision and was never
|
||||||
|
checked is precisely the failure this repository exists to name.
|
||||||
|
|
||||||
Answered separately, a repository of its own is the better home:
|
Answered separately, a repository of its own is the better home:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user