From ee17cddb74ea7beada8401bc8ef672fc6feb6d53 Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 3 Oct 2026 15:11:08 +0200 Subject: [PATCH] Research 020: what a bundled tool is given; design 38 WP4: fail2ban followed, proven live --- .../00-overview.md | 34 ++++++++ .../01-what-the-containers-are-given.md | 86 +++++++++++++++++++ .../38-building-the-operators-machine.md | 10 ++- 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 01-RESEARCH/020-what-a-bundled-tool-is-given/00-overview.md create mode 100644 01-RESEARCH/020-what-a-bundled-tool-is-given/01-what-the-containers-are-given.md diff --git a/01-RESEARCH/020-what-a-bundled-tool-is-given/00-overview.md b/01-RESEARCH/020-what-a-bundled-tool-is-given/00-overview.md new file mode 100644 index 0000000..07fa74a --- /dev/null +++ b/01-RESEARCH/020-what-a-bundled-tool-is-given/00-overview.md @@ -0,0 +1,34 @@ +--- +status: active +initiated: 2026-10-03 +touches: [the tool runtime, the catalogue's tool bundles, the controller's declaration composer, settings, own secrets, 03-DESIGN/01-to-be/38-building-the-operators-machine.md] +became: [] +--- + +# 020 — What a bundled tool is given + +## What is being investigated + +How a module's tools, once they are a bundle the node's runtime loads +([ADR 0175](../../02-DECISIONS/0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md), +[ADR 0188](../../02-DECISIONS/0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md)), +learn the things their container used to be handed: where the module's configuration file is, where +its token or password is, which port the service listens on, where a provision's address is written. +A container is given these as an environment and mounts, composed by the mesh per module per machine. +A bundle has no environment of its own: the runtime's process carries four words for every bundle it +loads, and nothing per module ([design 38](../../03-DESIGN/01-to-be/38-building-the-operators-machine.md) WP4). + +## Why + +Two holders moved on 2026-10-03 — the packet filter and the intrusion prevention — and both could, +because neither needs anything but a fixed path and root. Of the thirty-three modules whose tools +still run as containers on the runtime's image, thirty-one are not like that: their environment +names a configuration file, a credential file, a service address, a grants directory. Moving them +one by one without a rule for this would give the mesh thirty-one answers to one question. The +measurement and the options are in [01](01-what-the-containers-are-given.md). + +## What it touches + +The runtime (which hands a bundle what it is given), the composer (which resolves `${dir:…}` and +`${port:…}` for a container today and would for a bundle), the manifest (where a bundle would say +what it needs), and design 38, which records the gap and must say the rule once there is one. diff --git a/01-RESEARCH/020-what-a-bundled-tool-is-given/01-what-the-containers-are-given.md b/01-RESEARCH/020-what-a-bundled-tool-is-given/01-what-the-containers-are-given.md new file mode 100644 index 0000000..65e62e0 --- /dev/null +++ b/01-RESEARCH/020-what-a-bundled-tool-is-given/01-what-the-containers-are-given.md @@ -0,0 +1,86 @@ +# What the tool containers are given, measured + +Counted 2026-10-03 in the catalogue, after the two holders moved. + +| | | +|---|---| +| modules whose tools still run as a container on the runtime's image | 33 | +| tool containers among them (two modules run two) | 36 | +| modules whose container's environment carries only the bus credential | 1 (the intrusion prevention, now moved) | +| modules whose container's environment carries more | 32 — 31 still containers | + +## What "more" is + +Every value a container is given is one of five shapes. The reference kinds the composer resolves +in those values, over the 36 containers: a module directory (`${dir:…}`) in all 36, a mesh-chosen +port (`${port:…}`) in 12, a seat and an access grant once each. + +1. **A file the mesh already places on the host, mounted in.** The module's configuration as + JSON (`…_CONFIG_FILE`), its own secret (`…_TOKEN_FILE`, `…_PASSWORD_FILE`, `MESH_BROKER_FILE`), + a provision's address and secret written for it. Every one is a path under one of the module's + directories — its mesh state, its state, its grants, what it has written — mounted at a path of + the container's choosing and named to the tool through the environment. **The file is on the + host already; only the name under which the tool finds it is the container's.** +2. **The service's address, with the port the mesh chose:** `http://127.0.0.1:${port:3000}`. The + port is the composer's; the rest is the manifest's constant. +3. **A provision's address as a constant string** (a database's URL on the module's own network + name), paired with a mounted secret file from shape 1. +4. **A directory of grants** (`MESH_RECEIVES`): shape 1 again, a directory rather than a file. +5. **Literals the image needs:** a time zone, a user id, a memory limit. These belong to the + service's container where one exists; a tool bundle needs none of them. + +So the whole of what a bundled tool needs is: the paths of its module's directories on this +machine, the ports the mesh chose for its module here, and the constants its own manifest wrote. +Nothing a container had that a bundle cannot have; the mesh composes all three for the container +today, per module per machine. + +## What the runtime already has for it + +- The SDK's tool contributor is `(env) => tools`, and `collectTools(env)` takes the environment to + hand each contributor. The runtime calls it without one, so every contributor reads the process's + — the four words. The hook for a per-module environment exists and is unused. +- A launched bundle ([ADR 0188](../../02-DECISIONS/0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md)) + is spawned with the runtime's environment; the launch takes an environment argument. +- The composer resolves `${dir:…}` and `${port:…}` for a container's `env` and `volumes`; the same + resolution over a bundle's declaration is the same code. + +## Options + +**A. The bundle declares its environment on its artifact, and the mesh composes it as a +container's.** The manifest's tools artifact gains `env`, resolved with the same references; +values that were mount targets become the host-side paths directly (`${dir:mesh-state}/config.json` +rather than `/run/config/config.json`). The controller composes one environment per bundle per +machine into the runtime's declaration; the runtime hands it to the bundle's contributor and to a +launched child, and to nothing else. *For:* the tool code does not change — it reads the same +names; the conversion of the thirty-one is a mechanical move of the container's `env` with the +mounts folded in; one rule, one place. *Against:* the runtime's process carries thirty-one +environments in its declaration, and a bundle's environment is visible to the other bundles in the +process unless the runtime keeps them apart, which it must — a tool that reads `process.env` +instead of the environment it was handed would see its neighbours' paths. + +**B. The runtime derives the environment from the module's placed manifest.** No new field: the +runtime reads, for each module it serves, where that module's directories and ports are, and hands +a conventional set of words. *For:* nothing to declare. *Against:* a convention the tool code must +be rewritten to, thirty-one times; the runtime learns the composer's job; a module that names its +file `config.json` and one that names it `settings.json` need different words anyway. + +**C. Tools read their module's files through the bus** — ask the controller. *Against:* a tool +that cannot start without the bus answering a question is a tool that fails in the one case the +tools exist for, and a secret crossing the bus to reach a file already on the machine is a +disclosure for nothing. + +A is the one that keeps the tool code and the composer's vocabulary as they are, and names the one +thing the runtime must add: an environment per bundle, kept apart. The thing to decide beside it: +whether a bundle's environment may name a secret file at all, or whether secrets stay mounts in +spirit — a path the tool reads, never a value in the environment — which is what every container +does today and what A keeps if the rule says *paths, not values*. + +## What a decision would have to say + +- Where a bundle says what it is given (the artifact, option A), and that values are paths and + constants, never a secret's content. +- That the composer resolves it with the references it already has, per module per machine. +- That the runtime hands each bundle its own environment and nothing of another's, and how that is + checked: a test loading two bundles whose environments differ and asserting each sees only its own. +- That the thirty-one move in one mechanical change after the rule lands, each proven by its tools + answering from the runtime, and the registration gate then refuses the container shape for all. diff --git a/03-DESIGN/01-to-be/38-building-the-operators-machine.md b/03-DESIGN/01-to-be/38-building-the-operators-machine.md index 2a717d5..f49a943 100644 --- a/03-DESIGN/01-to-be/38-building-the-operators-machine.md +++ b/03-DESIGN/01-to-be/38-building-the-operators-machine.md @@ -215,7 +215,15 @@ file and answers with the table, `remove` refuses the mesh's own table by name `mesh-nftables` on any, the container's credential is gone with it, and `status` is well. One thing the step found is issue [210](../../04-ISSUES/210-the-host-re-creates-the-nodes-runtime-on-every-reconcile/00-report.md): -the host re-creates the runtime's process on every reconcile. +the host re-creates the runtime's process on every reconcile (resolved the same day, mesh-host #80). +*fail2ban followed 2026-10-03* (mesh-catalog `aa5bf7d`), the same shape: container, base images, +credential and state directory gone, the client through `sudo` since the daemon's socket is root's; +proven on all four machines — `status`, `banned` and the module's own `fail2ban_settings` answer from +the runtime, no `mesh-fail2ban` container, the runtime serving both bundles. Two holders moved; of the +thirty-three tool containers the catalogue held, thirty-one remain, and all but these two carried their +module's configuration and secrets in the container's environment, which a bundle does not have — the +question research [020](../../01-RESEARCH/020-what-a-bundled-tool-is-given/00-overview.md) opens +before the rest move. ## WP5 — The shell, on a server first -- 2.54.0