--- topic: what runs on it status: accepted date: 2026-10-03 deciders: jochen reconstructed: false extends: 02-DECISIONS/0193-every-bundle-the-runtime-serves-is-launched-and-the-runtime-knows-no-language.md --- # 198. A module's long-running code is launched by the node's runtime, and reaches the bus through it ## Context [ADR 0193](0193-every-bundle-the-runtime-serves-is-launched-and-the-runtime-knows-no-language.md) made every tools bundle a child the node's runtime launches, speaking MCP over stdio, and gave the channel one bus verb: a tool's emit, published by the runtime as the module. Twenty-three modules still run the rest of their own code — event handlers, provisioners, a preparation step, three mains — in a container on the runtime's image, because that code needs what a container gave it: a bus connection that can *subscribe*, and its module's words. Research [022](../01-RESEARCH/022-where-a-modules-long-running-code-runs/00-overview.md) measured what it uses. [ADR 0188](0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md) §1 says a module's own code is never an image. ## Considered Options 1. **The runtime launches it and is its bus.** Chosen. 2. A process per module with its own bus client and credential. Rejected for the reasons ADR 0188 rejected it for tools: a transport in every language's SDK, a credential per module on disk, and a bus change rebuilding every module. 3. Keep the containers for it. Rejected: ADR 0188's rule stays broken for most of the catalogue. ## Decision **1. A module's long-running code is a bundle the node's runtime launches and supervises,** exactly as its tools are: an executable entrypoint, given the runtime's words and its module's ([ADR 0192](0192-a-tools-bundle-declares-what-it-is-given-and-the-runtime-hands-it-to-that-bundle-alone.md)), started at the runtime's start and again when it exits. A bundle may serve tools, run long, or both. **2. The runtime is its bus.** The stdio channel carries, beside MCP, the mesh's verbs a module's code uses: `mesh/publish` (ADR 0193), **`mesh/subscribe`** — the runtime binds that module's durable consumer, as the module's own runtime did, and delivers each event to the child as a `mesh/event` request, acknowledging it on the bus only when the child has answered — and **`mesh/ask`**, a tool call made on the module's behalf. The runtime's account is granted what each module it carries consumes, and the consumer keeps the module's name, so no event is lost or replayed in the move. **3. A preparation step is a run-once process the host runs before the runtime starts the module,** with its module's words and no bus — what it already was. **4. What a container reached by its network is reached on the machine.** A service by its published port (`${port:…}`) on loopback; a backend's command-line client as a package of the machine's system, or, where the system has none, the backend's own driver inside the bundle. ## Consequences - The per-module containers go, and with them the runtime image as a way module code runs; ADR 0188's registration rule can then refuse a module's own image without exception. - One bus connection per machine carries every module's events; a module's handler is a function of the events it is handed, in any language, with no bus client of its own. - What got harder: the runtime holds every carried module's consumer and must not acknowledge an event before the child has handled it — a child that dies mid-event leaves it unacknowledged, and it is delivered again. The runtime's grants widen to what its modules consume. - Two modules need code before they can move: their backends' clients exist on no machine's system, so they talk to the backend through a driver instead. ## How it is checked | Rule | Checked by | |---|---| | A subscribed event reaches the child and is acknowledged only after it answered | the runtime's test over a real bus: a child that answers is acknowledged once; one that dies mid-event is delivered again | | The module's consumer keeps its name | the composer's test: the durable consumer the runtime binds is the one the module's own runtime bound | | No module's own code is an image | the catalogue's registration check, without exception, once the last container has moved | | Live | every moved module's provisioner and handlers act, on their machines, from the node's runtime; `docker ps` shows no runtime-image container on any machine | ## References - [ADR 0188](0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md), [ADR 0192](0192-a-tools-bundle-declares-what-it-is-given-and-the-runtime-hands-it-to-that-bundle-alone.md), [ADR 0193](0193-every-bundle-the-runtime-serves-is-launched-and-the-runtime-knows-no-language.md) - Research [022](../01-RESEARCH/022-where-a-modules-long-running-code-runs/00-overview.md) - [to-be 38](../03-DESIGN/01-to-be/38-building-the-operators-machine.md) WP4c