--- topic: what runs on it status: proposed date: 2026-10-01 deciders: jochen reconstructed: false extends: 02-DECISIONS/0161-what-deserves-a-seat.md --- # 165. `container-runtime` is what a machine can run; that a runtime is running is its holder's health ## Context A capability is a requirement a module places on a machine, detected by the host and renewed with every report ([ADR 0161](0161-what-deserves-a-seat.md)). The host's `container-runtime` asks the daemon for its version: *a running daemon, not an installed client*. It was made that way by [issue 007](../04-ISSUES/007-an-installed-package-is-not-a-capability/00-report.md), where an installed package was believed to be a working service, and [design 05](../03-DESIGN/01-to-be/05-the-node-host.md)'s table says the same: *a runtime is running*. The installer's preflight borrows the same detector to wait for the runtime the foundation bundle installs, so there is one answer to "is there a runtime here". The mesh is now to have a module for the runtime itself — its packages, its configuration, its service — on every machine ([ADR 0166](0166-the-container-runtime-is-a-node-seat-and-the-host-creates-containers-through-its-holder.md)). That module cannot declare `container-runtime` as defined: it would require the very thing it installs, the cycle [research 011](../01-RESEARCH/011-the-module-graph/cases.md)'s case 12 names ("something the mesh installs that then becomes a node capability"). The operator defined the word for it: **`container-runtime` means the machine is able, at the kernel level, to install a runtime and execute containers** — not that one is installed, and not that one is running. The host already draws this line once. `seat` is hardware, a display server *could* run here; `graphical-session` is state, one *is* running; the detector's own comment says "assignment needs the first". A machine without a display has no seat however much software is installed, and a machine with one has a seat before anything is. Fifty-four catalogue modules declare `container-runtime` today, counted on the catalogue's main branch on the day of deciding: every module that delivers a container. Each relies on the current meaning to keep it off a machine with no running runtime. ## Considered Options 1. **Keep the meaning; let the runtime's module declare nothing.** Rejected: a module that installs the runtime has requirements on the machine — the kernel features without which installing it is pointless — and would state none of them. The cycle stays, only hidden. 2. **Two capabilities, "can run" and "is running".** Rejected: the second is made true by assigning a module, so it is the module's state, not a fact of the machine; a capability the mesh itself flips by its own assignment is case 12's cycle with an extra name. 3. **The capability is the kernel's; whether a runtime runs is the runtime module's health, and a module that delivers a container needs the runtime's seat held.** Adopted. ## Decision **`container-runtime` is detected from what the kernel offers**, as `seat` is: the namespaces a container needs, a control-group hierarchy the runtime can manage, and an overlay filesystem the running kernel has or can load. Present when all three are; absent naming the missing one. Nothing is run and no runtime is asked. The verdict's detail names what was found, not a runtime's version. **"A runtime is running and answers" is one probe, owned by the host and used twice:** by the installer's preflight, which waits for the runtime the foundation installs, and as the runtime module's health. It asks the daemon, as issue 007 requires. The preflight stops borrowing the capability's detector, and there is still one answer to "is a runtime running here". **The runtime's module declares `container-runtime`**, with `package-manager`, `service-manager` and `privileged`, like any module that manages machine software. **A module that delivers a container needs the runtime seat held on its machine**, and is refused otherwise, naming the seat and the modules that could hold it — the refusal design 27 already lists for an unheld seat. That requirement is derived from the container resource and needs no manifest field ([ADR 0166](0166-the-container-runtime-is-a-node-seat-and-the-host-creates-containers-through-its-holder.md)). The fifty-four existing declarations of the capability stay valid and become redundant; a catalogue test lists them, and they retire when the list is empty. **The order is fixed, not preferred.** The detector changes only once the seat requirement is enforced. In between, a machine with the kernel and no running runtime would read as able to run every containerised module, which is issue 007 again. ## Consequences - Design 05's capability table changes its `container-runtime` row from *a runtime is running* to *the kernel can run containers*, and names the runtime module's health as where "running" is now asked. - The node listing stops showing the runtime's version beside the capability. The version moves to the runtime module's health and its seat's verbs. - A fresh machine with no runtime reads as able to run one, so it can be assigned the runtime's module, which is what makes the mesh able to install the runtime instead of the bootstrap alone. - **What got harder:** "is this machine running containers" is no longer one glance at the profile; it is the runtime seat's holder and its health. The node's listing should show both side by side. ## How this is checked | Rule | Checked by | |---|---| | The capability is the kernel's | Host detector tests over a fixture `/proc` and `/sys`: all three present → present; each one missing → absent naming it; no runtime binary on the fixture machine changes nothing | | One probe asks whether a runtime runs | A host test that the preflight and the runtime module's health call the same probe, and that the probe fails against a stopped daemon with an installed client (issue 007's shape) | | A containerised module needs the runtime seat held | A resolution test: a module with a container resource on a machine whose runtime seat is unheld is refused, naming the seat and its candidate holders | | The order holds | The host release that changes the detector is gated on the controller release that enforces the seat requirement — stated in both changes' descriptions and checked at review | | Live | Every machine's profile shows `container-runtime` present with the kernel's features as its detail; a machine with no runtime installed reads present too | ## References - [ADR 0161](0161-what-deserves-a-seat.md) — the profile renewed by every report; a capability that names a dialect - [ADR 0166](0166-the-container-runtime-is-a-node-seat-and-the-host-creates-containers-through-its-holder.md) — the seat and its holder - [Issue 007](../04-ISSUES/007-an-installed-package-is-not-a-capability/00-report.md), [research 011](../01-RESEARCH/011-the-module-graph/cases.md) cases 12–13 - [Design 05 — the node host](../03-DESIGN/01-to-be/05-the-node-host.md) - mesh-host `internal/profile/detectors.go` (the runtime detector), `internal/profile/seat.go` (the hardware/state split), `internal/bootstrap/preflight.go` (the preflight that borrows it)