From 769f0724caea5f6ed464dbb6d0f3bae115be2f72 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 30 Sep 2026 16:20:06 +0200 Subject: [PATCH] mesh-console: the mesh's tools on the machine a person sits at The tool runtime's own client, mesh serve, started by the mesh on the credential it sealed to the machine (novox/hq ADR 0152, design 34): invokes every tool, listens from the machine only, holds no state. Checked with module check before it was ever registered (hq issue 148). --- modules/mesh-console/Dockerfile | 13 +++++++ modules/mesh-console/README.md | 38 +++++++++++++++++++ modules/mesh-console/module.json | 64 ++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 modules/mesh-console/Dockerfile create mode 100644 modules/mesh-console/README.md create mode 100644 modules/mesh-console/module.json diff --git a/modules/mesh-console/Dockerfile b/modules/mesh-console/Dockerfile new file mode 100644 index 0000000..23ba413 --- /dev/null +++ b/modules/mesh-console/Dockerfile @@ -0,0 +1,13 @@ +# The console (novox/hq ADR 0152, design 34): the mesh's tools for whoever is on a machine, served +# over MCP on that machine's loopback. +# +# **Nothing is compiled here.** The console is the tool runtime's own client — `mesh serve` — which +# the runtime image already carries beside the runtime it runs modules with. This recipe changes the +# program the image starts and nothing else, so the console is exactly the client a person can run by +# hand, started by the mesh instead, on the credential the mesh sealed to the machine. +# +# One base, named rather than pinned: the mesh answers with the copy it holds (novox/hq issue 044). +ARG RUNTIME_BASE + +FROM ${RUNTIME_BASE} +ENTRYPOINT ["node", "dist/mesh.js"] diff --git a/modules/mesh-console/README.md b/modules/mesh-console/README.md new file mode 100644 index 0000000..3410750 --- /dev/null +++ b/modules/mesh-console/README.md @@ -0,0 +1,38 @@ +# mesh-console + +The mesh's tools, on the machine a person sits at, served by a module the mesh assigned there +(novox/hq [ADR 0152](https://git.novox.be/novox/hq), design 34). + +Assign it to a machine and an agent on that machine has the mesh's tools at +`http://127.0.0.1:/mcp` — MCP over HTTP, `initialize`, `tools/list`, `tools/call`. A person at +a terminal reaches the same endpoint with `mesh tools --console http://127.0.0.1:` and +`mesh call . --console …`, with no credential of their own: the console holds it. + +## What it is + +The tool runtime's own client, `mesh serve`, started by the mesh on the credential it sealed to the +machine for `.mesh-console`. The 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; +- a listener `from: machine` — loopback only, and the filter opens nothing for it; +- no `emits`, no `consumes`, no `tools` — nothing on the bus can address it. + +**Loopback is the authority boundary.** 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 and no login, +and `mesh serve` refuses to bind anything but a loopback address. + +## What it lists + +What the running modules answer: every tool runtime serves a `tools` verb for its module, and the +console asks the catalogue which modules the mesh holds and each module what it serves. A module that +did not answer — not assigned, not up, or built before the runtime answered `tools` — is named in the +list's `_meta.notAnswering` and can still be called by `.`. + +The mesh's own verbs (`status`, `push`, `assign`) are the `mesh-controller` seat's tools under +ADR 0132 and are not served on the bus yet; they appear here when they are. + +## Port + +The manifest declares port 4270 and the mesh assigns the machine port as it does for any listener; +the console binds `127.0.0.1:${port:4270}`. `node show ` says which port a machine was given. diff --git a/modules/mesh-console/module.json b/modules/mesh-console/module.json new file mode 100644 index 0000000..3228816 --- /dev/null +++ b/modules/mesh-console/module.json @@ -0,0 +1,64 @@ +{ + "module": "mesh-console", + "version": "1", + "slug": "console", + "capabilities": [ + "container-runtime" + ], + "invokes": [ + "*" + ], + "own-secrets": { + "broker": "/var/lib/mesh/mesh-console/broker" + }, + "listens": [ + { + "name": "mcp", + "port": 4270, + "protocol": "tcp", + "from": "machine", + "why": "the mesh's tools for whoever is on this machine, over MCP on loopback; the machine's login is the authority (novox/hq ADR 0152)" + } + ], + "resources": [ + { + "id": "mesh-state", + "type": "directory", + "path": "/var/lib/mesh/mesh-console", + "mode": "0700" + }, + { + "id": "server", + "type": "container", + "name": "mesh-console", + "network": "host", + "args": [ + "serve" + ], + "env": { + "MESH_BROKER_FILE": "/run/secrets/broker", + "MESH_CONSOLE_LISTEN": "127.0.0.1:${port:4270}" + }, + "volumes": [ + "/var/lib/mesh/mesh-console/broker:/run/secrets/broker:ro" + ], + "artifact": "runtime" + } + ], + "build": { + "on": [ + { + "arg": "RUNTIME_BASE", + "module": "mesh-tools", + "artifact": "runtime" + } + ], + "artifacts": [ + { + "name": "runtime", + "kind": "image", + "from": "Dockerfile" + } + ] + } +}