--- topic: the mesh status: accepted date: 2026-09-29 deciders: jochen reconstructed: false extends: 02-DECISIONS/0141-the-host-delivers-its-own-successor.md --- # 142. The mesh delivers its own components as binaries, not as container images ## Context Measured on the control-node, 2026-09-29: | what | how it runs | publishes | |---|---|---| | host | a binary on the machine | — | | controller, catalogue, builder, vault | containers | nothing | | store, registry, broker | containers | ports | **The mesh's own software is delivered two ways, and the difference is not a property of the software.** The host and the controller are both written in the same language, both the mesh's own, both doing the mesh's own work. One is an image fetched from a registry. The other is a file somebody copied to four machines, owned by no package, built by nothing ([issue 142](../04-ISSUES/142-the-host-is-the-one-thing-the-mesh-does-not-deliver/00-report.md)). **The reason is not a judgement about either, it is that images are the only delivery that works.** There is no way to put a binary on a machine. The host is hand-copied because of that, and the controller is an image because of that. Neither was chosen on its merits. What it costs, all of it measured rather than argued: - **Genesis must raise a container runtime before the control plane can exist.** The bundle carries three images and one of them is the controller, *"in the bundle for the same reason they are: there is nothing to fetch it with yet"* ([design 07](../03-DESIGN/01-to-be/07-the-foundation.md)). So the hardest moment in the mesh's life has a prerequisite that the thing being started does not need. - **Updating the control plane depends on the control plane.** Its image is fetched from the registry, which is a container the controller manages. - **A change to the host cannot be rolled out at all.** Every machine here runs a byte-identical hand-copied binary. A change merged yesterday reached none of them. - **Compiling the language the mesh is written in is not a capability of the builder.** The bundle toolchains are typescript — real, with a registered base module — and python, which is named in the list and absent from the catalogue. The controller is built as an image from a Dockerfile, which is the per-repository incantation the bundle toolchain exists to abolish ([design 18](../03-DESIGN/01-to-be/18-building-a-module.md)). The half that *receives* a binary safely is already built and tested ([ADR 0141](0141-the-host-delivers-its-own-successor.md)): versions side by side in directories named for them, the newest run, the running one standing aside between reconciles, retirement keeping the predecessor, and a rollback that chooses a directory. What is missing is everything that puts one there. ## Considered Options 1. **Leave it as it is.** Rejected: it is not a design, it is the reach of one mechanism. And it is what makes a host change undeliverable. 2. **Containerise the host too**, so everything is delivered one way. Rejected: the host is what starts the container runtime and what applies containers. A host in a container is the bootstrap problem made total, and the machine would have no way back from a bad one. 3. **Deliver the mesh's components as operating-system packages.** Rejected for the reason [ADR 0141](0141-the-host-delivers-its-own-successor.md) rejected it for the host: a package and a trusted repository per operating system, three of each, and the `package` resource asserts presence and deliberately never a version. 4. **Binaries for the mesh's own components, containers for third-party software.** Adopted. ## Decision **The mesh's own components are delivered as binaries on the machine.** The host, the controller, the catalogue, the builder, the vault — the software this project writes. They are delivered by the mechanism [ADR 0141](0141-the-host-delivers-its-own-successor.md) built: an archive, fetched by digest, unpacked into a directory named for its version, with the running one standing aside between reconciles and a rollback that chooses the predecessor. **Third-party software stays a container.** The store, the registry, the broker. They are somebody else's build, they are already adopted as modules ([ADR 0078](0078-the-store-and-broker-are-modules.md)), and an image is the right way to carry somebody else's software. **The container runtime remains required** — modules use it — so this removes a dependency from the control plane, not from the machine. **The builder compiles the languages the mesh is written in.** A toolchain for Go, with a base module providing the compiler, exactly as typescript has. The obligation the toolchain list warns about — an SDK carrying the broker client, the envelope and tool serving — attaches to a *module* written in a language, not to the language being compilable. None of these components is a module in that sense; the host is what applies modules. **An artifact says what it targets.** A compiled binary is per operating system, pinned at link time ([ADR 0005](0005-the-node-host.md)), and a toolchain deliberately takes nothing from the module, because anything a module could override there it would be writing a Dockerfile to override. So the target is a property of the artifact rather than of the recipe, and one artifact declared per target is one build each. **A component's version comes from where it sits, not from its linker.** It is unpacked into a directory named for its version, so it can read its own version from its path. The stamp goes, and with it the need for a build to know what it will be called. **Genesis carries a binary reference where it carried an image reference.** The principle does not change — the bundle names a thing by digest and the host fetches it, pinned because nothing can resolve a version when no mesh exists — and the container runtime stops being a prerequisite for the control plane. It stays a prerequisite for the store and the broker, which is where it belongs. **The order is staged, and each step stands alone.** Compiling Go; an artifact naming its target; delivering a binary; the host as the first component delivered; the controller, catalogue, builder and vault out of their containers; genesis last. Genesis is last for the reason it is always last: it matters for a machine nobody has yet, and every earlier step is provable on a mesh that exists. ## Consequences - **One delivery for the mesh's own software**, so a change to the host ships the way a change to the controller does, and neither is copied by hand. - **The control plane stops depending on a container runtime and on its own registry.** Both remain on the machine for other reasons; neither gates the control plane's own life any more. - **`Replaced()`, the known-good record and the launcher's rollback stop being dead code.** They were written for this and have been called by nothing but their tests. - **Four more components gain a rollback they do not have.** Today a bad controller image is recovered by an operator; under this it is recovered the way a bad host is. - **Two versions of each component occupy disk.** Around nine megabytes each. The predecessor is what a rollback needs. - **Genesis gets smaller, not larger.** One fewer image to carry and one fewer runtime to raise before the control plane. - **This does not make the components smaller or simpler.** They are the same programs; what changes is how they arrive. A reader expecting the containers to have been hiding complexity will not find any. - **What got harder:** the builder gains a language, artifacts gain a target, and the mesh gains a second kind of thing it must deliver correctly — one where getting it wrong takes the control plane down rather than a module. That is why the host is first: it is the component whose recovery is already built and tested. ## How it is checked - **A component is delivered and runs, with nothing copied by hand.** A bed builds the host from its repository, delivers it to a machine running an older one, and the machine reports the new version. This fails today at the first step, because nothing builds it. - **Each target is built once and only the matching one is delivered.** Asserted by declaring an artifact per operating system and checking that a machine is offered the one it can run — a host built for another is what ADR 0005's link-time pin exists to refuse. - **A component reads its version from its path**, asserted by unpacking the same bytes into two differently named directories and seeing each report its own. - **A bad component is rolled back without an operator**, for the host first: a version that will not start is replaced by its predecessor once, and the second failure halts naming the machine. - **The control plane comes up with no registry reachable**, which is the dependency this removes — asserted by raising it with the registry stopped. - **Genesis raises a control plane with no container runtime running**, and raises the store and the broker afterwards. Last, and on a machine with nothing on it. - **A published port count that does not change.** The mesh's own components publish nothing today, so moving them out of containers must not open anything — asserted on the machine's reachable set before and after, which the converge preview already reads. ## References - [ADR 0141](0141-the-host-delivers-its-own-successor.md) — the receiving half, already built - [ADR 0005](0005-the-node-host.md) — the host, its supervision, and one binary per operating system - [ADR 0078](0078-the-store-and-broker-are-modules.md) — why third-party software stays a container - [ADR 0006](0006-the-substrate-and-the-control-plane.md) — what genesis must raise, and in what order - [issue 142](../04-ISSUES/142-the-host-is-the-one-thing-the-mesh-does-not-deliver/00-report.md) — the measurement that started this - [design 07](../03-DESIGN/01-to-be/07-the-foundation.md) — the bundle's three images, one of them the controller