Files
hq/02-DECISIONS/0192-a-tools-bundle-declares-what-it-is-given-and-the-runtime-hands-it-to-that-bundle-alone.md

123 lines
8.2 KiB
Markdown

---
topic: what runs on it
status: accepted
date: 2026-10-03
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md
---
# 192. A tools bundle declares what it is given, and the runtime hands it to that bundle alone
## Context
[ADR 0175](0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md) put one
runtime on every node serving every module's tools from a bundle, and
[ADR 0188](0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md)
said a module's own code is bundles and never an image. The two holders that moved first
([to-be 38](../03-DESIGN/01-to-be/38-building-the-operators-machine.md) WP4) needed nothing a
bundle does not have: a fixed path, and root. Research
[020](../01-RESEARCH/020-what-a-bundled-tool-is-given/00-overview.md) measured the rest before
they move: thirty-three modules still run their tools as a container on the runtime's image, and
thirty-one of them are handed, through the container's environment and mounts, things a bundle
has no way to receive — the module's configuration file, its own secret as a file, the service's
address with the port the mesh chose, a provision's address, a directory of grants. Every one of
those is a file the mesh already places on the machine or a value the controller already composes
for the container, per module per machine, from references the manifest writes: a placed
directory, a chosen port. And the SDK's tool contributor is a function of an environment that the
runtime calls without one, so every bundle reads the process's four words.
Without a rule, each of the thirty-one would answer the question its own way, and the runtime's
process would be the one place where every module's paths meet.
> **Progressive insight — 2026-10-03.** The context above calls the thirty-one remaining containers
> tool containers handed what a bundle cannot receive. Measured the same day while building this
> record: nine of them run only tools; three run a main of their own; twenty import, beside their
> tools, the module's own long-running code — event handlers that subscribe on the bus and
> provisioners that act on grants — under the module's own bus identity, and some reach their
> service by a container network name or need a package the image installed. That code is not a
> tool and is not this record's to move: under
> [ADR 0188](0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md)
> §3 it is a `process` bundle, and how it is given its credential, its words and its reach is the
> open question of [design 38](../03-DESIGN/01-to-be/38-building-the-operators-machine.md) WP4c.
> Decision 4 applies to these containers' tools; the containers themselves go when their other
> code has moved. The decision and its options stand.
## Considered Options
1. **The bundle declares its environment on its artifact, and the mesh composes it as a
container's.** Chosen. The tools artifact gains `env`: names to values, the values written with
the references the composer already resolves for a container — `${dir:…}`, `${port:…}` — and
what was a mount target becomes the host path itself. The controller composes one environment
per bundle per machine into the runtime's declaration. The runtime hands it to that bundle's
contributor, or to the child it launches, and to nothing else. The tool code reads the names it
read before.
2. **The runtime derives it from the module's placed manifest** — a conventional word per
directory and port, no new field. Rejected: a convention the thirty-one tools must be rewritten
to, the runtime learning the composer's job, and a module that names its file one way and a
module that names it another needing different words regardless.
3. **The tool asks the controller over the bus.** Rejected: a tool that cannot start until the
bus answers fails in the one case tools exist for, and a secret crossing the bus to reach a file
already on the machine is a disclosure for nothing.
4. **Leave each module to its own device.** Rejected by the measurement: thirty-one modules, one
question.
## Decision
**1. A tools bundle says what it is given, on its artifact.** `build.artifacts[].env` names the
words the bundle reads and their values. A value is a path or a constant, composed with the
references a container's environment may use; **never a secret's content.** A secret reaches a
tool the way it reaches a container: as a file the mesh places, whose path the environment names.
A bundle that declares no `env` is given nothing beyond the runtime's own words, which is what the
two holders that moved have.
**2. The mesh composes it, per bundle per machine, as it composes a container's.** The same
references, resolved the same way, to the host's own paths. The composed environment travels in
the node's declaration beside the bundle's archive; a change to it is a change to the bundle for
the purpose of `restart-on`.
**3. The runtime hands each bundle its own environment, and nothing of another's.** A bundle
imported into the runtime's process receives it as the argument its contributor is written to
take; a bundle launched as a child ([ADR 0188](0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md) §2)
receives it as the child's environment, over the runtime's own words. The runtime's process
environment is not where a module's words go, and a tool that reads the process's environment
rather than the one it was handed finds the runtime's four words and no module's.
**4. The remaining tool containers move in one change** after this is built, each proven by its
tools answering from the runtime, and the registration gate of
[to-be 38](../03-DESIGN/01-to-be/38-building-the-operators-machine.md) WP2 then refuses the
container shape for every module, as ADR 0188 already provides.
## Consequences
- The manifest gains one field on one artifact kind; the composer gains one more thing to resolve
with references it has; the runtime gains the hand-off and the separation. The thirty-one modules'
tool code does not change, and their conversion is the move of a container's `env` with its
mounts folded into host paths.
- A tool's inputs become legible in the manifest where its container hid them in mounts: what a
module's tools read is declared beside what the module writes.
- What got harder: the runtime must keep thirty-one environments apart in one process, and a
bundle's author must not reach for the process's environment. The separation is a rule the
runtime's test holds, not a property of the language.
- `MESH_BROKER_FILE` is not a bundle's to declare: the runtime speaks with the node's credential
([ADR 0175](0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md)), and
a module's own bus credential went with its container.
## How it is checked
| Rule | Checked by |
|---|---|
| A value in a bundle's `env` is a path or a constant, never a secret's content | the catalogue's manifest check refuses a `${secret:…}` reference in a bundle's `env`, naming this record |
| The composer resolves a bundle's `env` as a container's | the controller's composition test: one module, one bundle with `${dir:…}` and `${port:…}` in its `env`, the declaration carrying the host paths and the chosen port |
| Each bundle sees its own environment and no other's | the runtime's test: two bundles with different `env`, loaded in one runtime, each answering with its own words and none of the other's; the same for a launched bundle |
| A change to a bundle's environment restarts the runtime | the composition test above, with `restart-on` naming the bundle |
| Live | a module whose tools read a configuration file and a token file answers from the runtime on one machine with no container |
## References
- [ADR 0175](0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md),
[ADR 0188](0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md)
- Research [020](../01-RESEARCH/020-what-a-bundled-tool-is-given/00-overview.md) — the measurement
and the options
- [to-be 38](../03-DESIGN/01-to-be/38-building-the-operators-machine.md) — where the work is listed