The predecessor is retired on every node and what it still owned on the two workstations — some thirty modules of dotfiles, user units and /etc files — is owned by nothing. To-be 29 covers one directory under the home; the operator wants the whole machine, system folders and home alike, as modules: one default configuration each, varied per node by settings or a kept region, never an edit; roles the machine has once as node-scoped seats with tool contracts; the graphical stack gated by a capability so the same catalogue serves the servers. Four documents: the intended behaviour in the mesh's words; the predecessor's desktop measured (34 modules, one with 88 files, 4 flavors and ~90 theme variables) against what the records already give and what is missing (the account is empty on every node, no user-scoped units, settings leak, tools run in a container per module per node); the direction the operator set for where tools run — one executor per node, host-side, module-agnostic, the console renamed and moved out of its container, superseding 0047/0150 for tools; and the candidate seats of the environment with first verbs, the shell first.
89 lines
7.1 KiB
Markdown
89 lines
7.1 KiB
Markdown
# 03 — One tool executor per node
|
|
|
|
*The direction the operator set on 2026-10-02, the evidence it rests on, and what it supersedes.
|
|
A direction, not yet a decision: the record is written when this effort graduates.*
|
|
|
|
## Where tools are served today
|
|
|
|
[To-be 33](../../03-DESIGN/01-to-be/33-the-tools-the-mesh-answers.md) names three families — a
|
|
role's tools on the seat, a module's own tools on the module, the mesh's own verbs on the
|
|
controller seat — and [ADR 0160](../../02-DECISIONS/0160-the-mesh-issues-an-assignments-subjects-and-a-runtime-serves-what-it-is-issued.md)
|
|
says a runtime serves the subjects its membership issues. What *runs* that runtime is
|
|
[ADR 0150](../../02-DECISIONS/0150-a-modules-own-code-runs-as-supervised-processes-under-one-account.md):
|
|
one supervised process per module, under the module's own account, carrying that module's
|
|
compiled tools. Measured on the live mesh:
|
|
|
|
| who answers | how it runs | count |
|
|
|---|---|---|
|
|
| the mesh's own verbs | the controller binary, on its node | 17 verbs |
|
|
| the store seat | the store's own runtime | 2 verbs |
|
|
| the packet-filter seat | **a container per node**, built on the tool-runtime base image, with the network namespace and `NET_ADMIN`, on all four nodes | 3 verbs and 1 own tool |
|
|
| every module's own tools | the module's container, one per node it runs on | 67 tools across the catalogue |
|
|
| the console | a container per node, loopback MCP, `invokes: *` | serves none, calls all |
|
|
| the host | — | serves nothing; answers no question about the machine |
|
|
|
|
**The packet-filter holder is the case to look at.** The module is a package, three files and a
|
|
system service. To serve three verbs it also declares a built image and a container on every
|
|
node whose only job is to answer them. Scaled to the environment — a shell, a prompt, a launcher,
|
|
a notifier, a compositor, a login manager, a service manager, a boot loader, a downloads folder —
|
|
that is one container per module per node for software that is itself not a container, and the
|
|
operator's judgement is that tools should not run inside a container at all.
|
|
|
|
## The direction
|
|
|
|
**One tool executor per node, on the host side.** A process the host supervises, the way the
|
|
launcher supervises the host ([ADR 0005](../../02-DECISIONS/0005-the-node-host.md)): not a
|
|
container, one bus credential for the node, module-agnostic. It loads the tool code of every
|
|
module assigned to the node and serves each module's tools and each held seat's verbs on the
|
|
subjects the membership issues — nothing changes in what [ADR 0159](../../02-DECISIONS/0159-a-tool-call-names-the-machine-and-a-holder-serves-its-seats-verbs.md)
|
|
and [ADR 0160](../../02-DECISIONS/0160-the-mesh-issues-an-assignments-subjects-and-a-runtime-serves-what-it-is-issued.md)
|
|
say about subjects, grants and memberships; what changes is that one process subscribes for the
|
|
node instead of one per module.
|
|
|
|
- **A module brings its tools as a built artifact**, a bundle the pipeline produces, never an
|
|
image. The executor knows bundles and subjects; it knows nothing of zsh or nftables.
|
|
- **A tool is code the module wrote**, one function behind an MCP verb. `execute` on the shell
|
|
seat is a function with a string argument. The executor does not declare, template or
|
|
interpret tools; it runs them.
|
|
- **Root is the module's concern.** A tool that must change the packet filter or rebuild boot
|
|
images escalates itself. The executor does not run as root for everyone, and the caller does
|
|
not know.
|
|
- **Any node may call any tool on any node.** The executor's credential may call everything,
|
|
as the console's already does; per-module grants on the calling side are not kept.
|
|
- **The mesh's own verbs stay with the controller** ([ADR 0154](../../02-DECISIONS/0154-the-meshs-own-verbs-are-the-controller-seats-tools.md)),
|
|
and a mesh-scoped seat's verbs run on the node that holds it
|
|
([ADR 0121](../../02-DECISIONS/0121-a-system-seat-is-named-for-its-scope-and-modules-define-their-own.md)).
|
|
No hub is added; the controller's node is already one.
|
|
|
|
**The console is the executor, renamed.** It already runs on every node with a credential that
|
|
may call everything, and it already serves the mesh's tools to whoever is on the machine over
|
|
MCP on loopback ([ADR 0152](../../02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md)).
|
|
It moves out of its container into the host's process tree, gains the serving half, and takes a
|
|
name that says what it is — *the node's tool runtime* or simply *node tools*; "console" names
|
|
the operator's half only.
|
|
|
|
## What it supersedes, and what it keeps
|
|
|
|
| record | effect |
|
|
|---|---|
|
|
| [ADR 0047](../../02-DECISIONS/0047-a-module-runs-its-code-as-its-own-process-with-its-own-account.md), [ADR 0150](../../02-DECISIONS/0150-a-modules-own-code-runs-as-supervised-processes-under-one-account.md) | superseded *for tools*: one process per node runs every module's tool code, under one account. A module's long-running service — a daemon, a container — is untouched; the executor runs tools, not services. The record must say why one account for every module's tools is acceptable: every tool may be called from every node anyway, and root is taken by the tool, not granted to the process |
|
|
| [ADR 0152](../../02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md) | kept in substance — a module, assigned per node, loopback MCP, the machine's login is the authority — changed in form: host-side, not a container; serves as well as calls; renamed |
|
|
| [to-be 33](../../03-DESIGN/01-to-be/33-the-tools-the-mesh-answers.md) §6, [to-be 34](../../03-DESIGN/01-to-be/34-the-console.md) | amended the same way |
|
|
| [ADR 0170](../../02-DECISIONS/0170-the-firewall-seat-serves-its-verbs.md) §3, *a container may ask for a capability* | moot for that holder: the verbs run on the host side and escalate as they need |
|
|
| the container-runtime seat, proposed in an open change: *the holder runs as a supervised process and serves the verbs locally to the host and on the bus* | consistent — a supervised process serving verbs is what the executor is; the open question is whether that holder keeps its own process or serves through the executor like everyone else |
|
|
| the tool-runtime base image | no longer the way tools reach a node; may remain the way a module's *service* is built |
|
|
|
|
## What stays open
|
|
|
|
- **The executor's language.** The host is a static Go binary and loads no plugins, so the
|
|
executor is a sibling process, and its language decides the language of every tool bundle.
|
|
One decision, taken once.
|
|
- **How a bundle reaches the node.** An artifact of the module's build, delivered as the host
|
|
delivers everything else; whether it is a file resource in the declaration or a thing the
|
|
executor fetches by digest.
|
|
- **Reload.** A push that adds or upgrades a module's bundle reaches a running executor as a
|
|
reload, not a restart, or every tool on the node blinks on every push.
|
|
- **The host's own questions.** [Issue 160](../../04-ISSUES/160-a-machine-says-little-about-itself-and-only-when-asked/00-report.md)
|
|
wants a machine to say more about itself. With an executor on every node, "what is this
|
|
machine made of" is a seat verb like any other, served there.
|