162 lines
13 KiB
Markdown
162 lines
13 KiB
Markdown
---
|
|
topic: what runs on it
|
|
status: proposed
|
|
date: 2026-10-01
|
|
deciders: jochen
|
|
reconstructed: false
|
|
extends: 02-DECISIONS/0161-what-deserves-a-seat.md
|
|
---
|
|
|
|
# 166. The container runtime is a node seat, and the host creates containers through its holder
|
|
|
|
## Context
|
|
|
|
Every container the mesh runs on a machine is created by the host, which looks for a runtime
|
|
(`docker info`, then `podman info`) and drives that runtime's command line itself: run, inspect,
|
|
remove, exec. Research 012 called this "detected rather than declared": the host takes over whatever
|
|
runtime it finds. Nothing in the mesh owns the runtime. Its package came from the foundation bundle
|
|
or was already on the machine. Its configuration file was written by hand, differs on each of the
|
|
four machines, and is also written into by two modules that are not the runtime's
|
|
([issue 190](../04-ISSUES/190-the-runtimes-configuration-is-written-by-modules-that-are-not-the-runtime/00-report.md)).
|
|
Its service is declared by those same two.
|
|
|
|
The operator set the direction:
|
|
|
|
- a module for the runtime, on every machine, owning "what is needed to run containers here": its
|
|
packages, its configuration and its service;
|
|
- that module holds a node seat for the runtime, so a second runtime (podman) can later compete
|
|
for the seat;
|
|
- the host stops speaking to the runtime directly and uses the seat's holder. The host stays the one
|
|
that decides, and the holder becomes the one that executes;
|
|
- every container on the machine is in scope, not only the mesh's. A development environment started
|
|
by hand, or a test database a tool runs, is legitimate. The host already calls these *strays*: 3,
|
|
8 and 25 on three of the machines on the day of deciding;
|
|
- the runtime's events and verbs are subjects on the bus, and the mesh's own interface is built on
|
|
them. The third-party interface run until now was removed by hand.
|
|
|
|
[ADR 0161](0161-what-deserves-a-seat.md)'s test for a seat is whether the mesh's own code finds it by
|
|
name. Here it does: the host would look up the holder on its own machine. A singular role of a module
|
|
held once per machine is a `node-*` seat ([ADR 0121](0121-a-system-seat-is-named-for-its-scope-and-modules-define-their-own.md)),
|
|
in the controller's seed.
|
|
|
|
The constraint that decides most of this record is a cycle. The bus runs in containers. On the
|
|
broker's machine, the broker's own container is created by the host. A holder's code served from a
|
|
container cannot create the container that runs it. On a first machine, before the controller exists,
|
|
nothing holds anything.
|
|
|
|
## Considered Options
|
|
|
|
1. **The host calls the holder's verbs over the bus.** Rejected: with the broker down, no machine can
|
|
create any container, including the broker's. The mesh would be unable to restart its own
|
|
transport.
|
|
2. **The holder picks a dialect that the host speaks itself, as with the uplink.** Rejected: the
|
|
host would still drive the runtime, and the module would drive it too for every other caller.
|
|
That is two programs speaking to one daemon, and they come to disagree about the same machine
|
|
(the installer's preflight already exists to avoid this).
|
|
3. **The holder's code runs as a supervised process on the machine and serves the seat's verbs
|
|
twice: locally to the host, on the bus to everyone else.** Adopted.
|
|
|
|
## Decision
|
|
|
|
**`node-container-runtime` is a seat of the mesh's own, node-scoped,** in the controller's seed under
|
|
this record. It delivers no provision; what it carries is its role's protocol: verbs its holder must
|
|
serve ([ADR 0132](0132-a-seat-carries-the-tools-its-holder-must-serve.md)) and events its holder emits
|
|
([ADR 0129](0129-a-seat-carries-the-protocol-of-its-role.md)). The runtime's module, `docker`, claims
|
|
it and is assigned to every machine. A podman module may claim it later; one machine runs one.
|
|
|
|
**The seat's verbs cover every container on the machine:** list, inspect, logs, stats, start, stop,
|
|
restart, create and remove. A mesh-held container is marked by the host's label and says which
|
|
assignment holds it. **A container the runtime runs can be root on the machine** — privileged, a host
|
|
path mounted, the host's network or process namespace, the runtime's own socket — so a caller other
|
|
than the host may not create one that is any of these; only a declaration the mesh composed may ask
|
|
for them. And the verbs that change anything are granted by name, never by a wildcard: a grant of
|
|
every tool (the console's today) reaches the reading verbs only. Issue 193 is what a verb that trusts
|
|
its caller costs. **Creating or removing a mesh-held container is the host's alone.** Any other
|
|
caller is refused naming the assignment, because the host would undo it at its next apply. Starting,
|
|
stopping or restarting one is allowed, and the answer says the host will restore what its
|
|
declaration says. A container the mesh does not hold is the caller's to do anything with.
|
|
|
|
**The seat's events are the runtime's own** — a container created, started, stopped, died, removed,
|
|
its health changed. They are emitted on the seat's subjects, so every holder emits the same events and
|
|
no reader depends on which runtime holds the seat. As
|
|
[ADR 0160](0160-the-mesh-issues-an-assignments-subjects-and-a-runtime-serves-what-it-is-issued.md)
|
|
decides, the subjects are issued by the controller, not composed by the module.
|
|
|
|
**The holder's code is a supervised process, not a container** ([ADR 0150](0150-a-modules-own-code-runs-as-supervised-processes-under-one-account.md)).
|
|
A runtime cannot be run by the thing it runs. The process serves the seat's verbs on the bus to the
|
|
console, to tools and to the mesh's interface. The same verbs are served on a local socket on the
|
|
machine, which only the host may use. **The host creates, inspects and removes its containers
|
|
through that socket and nothing else.** If the holder does not answer, the host creates nothing. It
|
|
says so in its report, naming the seat. It never falls back to the command line.
|
|
|
|
**A container needs the seat held on its machine.** An assignment that delivers a container on a
|
|
machine whose runtime seat is unheld is refused, naming the seat and its candidates
|
|
([ADR 0165](0165-container-runtime-is-what-a-machine-can-run-and-a-running-runtime-is-its-holders-health.md)).
|
|
Mounting the runtime's socket into a container is granted by the seat, not by the capability. The
|
|
socket's path is the holder's to state, because podman's is not docker's.
|
|
|
|
**The runtime module owns the runtime's configuration.** Its settings are declared with defaults
|
|
([ADR 0164](0164-a-setting-is-declared-with-its-default-its-meaning-and-what-changing-it-costs.md)):
|
|
the resolver containers use, the registries it trusts, log rotation, and keeping containers through a
|
|
daemon restart. The module is given the resolver's address and the mesh's registry as values; no other
|
|
module writes the runtime's file or declares its service.
|
|
|
|
**The first machine is bootstrapped with the holder, and adopted afterwards.** The foundation bundle
|
|
already installs the runtime's package and service. It also carries the holder's process, delivered as
|
|
a binary the way the host is ([ADR 0142](0142-the-mesh-delivers-its-own-components-as-binaries.md)).
|
|
When the runtime module is assigned, it adopts what the bundle made, as the store and broker modules
|
|
adopt theirs ([ADR 0078](0078-the-store-and-broker-are-modules.md)).
|
|
|
|
## Consequences
|
|
|
|
- **The migration on the running mesh has a fixed order:**
|
|
1. Each machine's hand-written configuration is read, because the module's defaults replace what
|
|
differs.
|
|
2. In one push per machine: the resolver module and the private network stop writing the
|
|
runtime's file ([issue 190](../04-ISSUES/190-the-runtimes-configuration-is-written-by-modules-that-are-not-the-runtime/00-report.md)),
|
|
and the runtime module is assigned and adopts the runtime, its file and its service. Split in
|
|
two, either the controller refuses two modules declaring one path, or a machine is left with
|
|
nothing setting `dns` and `live-restore`.
|
|
3. The controller seeds the seat and enforces the container requirement.
|
|
4. The host releases the version that uses the holder.
|
|
5. The host's command-line path is removed in the release after every machine's holder answers.
|
|
Until then, the host reports per machine which path it used.
|
|
- **Every container the host makes depends on the holder's process.** A crash-looping holder stops
|
|
new containers on its machine. Running containers are unaffected. The host's report names the cause.
|
|
- The process form of a module's own code must serve tools on the live mesh before this ships. Only the
|
|
showcase declares it, and [issue 117](../04-ISSUES/117-a-modules-own-code-is-a-container-and-a-process/01-diagnosis.md)
|
|
found the showcase's tools declared in a form nothing runs. The runtime module is the first whose
|
|
tools cannot fall back to a container.
|
|
- A user interface subscribing to events directly does not exist. Today a reader of events is a module
|
|
that consumes them. The mesh's container view is a module, or waits for that path.
|
|
- [ADR 0005](0005-the-node-host.md) ("a container runtime is detected, not chosen") and
|
|
[ADR 0006](0006-the-substrate-and-the-control-plane.md)'s matching line describe the mechanism this replaces: on
|
|
acceptance, each gets a dated note saying the runtime is now a seat's holder, as the decision
|
|
records' rule for a moved mechanism requires. Design 05 and design 26 are amended after acceptance.
|
|
- The operator's decision to remove the third-party interface by hand needs no mechanism. No
|
|
module-retires-module rule is introduced.
|
|
- **What got harder:** the host gains a dependency it did not have, and a first machine's bundle gains
|
|
a component. The direct path was simpler and is what makes a runtime a black box to the rest of the
|
|
mesh.
|
|
|
|
## How this is checked
|
|
|
|
| Rule | Checked by |
|
|
|---|---|
|
|
| The seat is the mesh's own, node-scoped, with its verbs and events | A catalogue test on the default seats; registration refuses a claimant that does not serve every verb (design 33's existing check) |
|
|
| Creating or removing a mesh-held container is the host's alone | A test of the runtime module's verbs: create or remove of a container carrying the host's label, from any caller but the host's socket, is refused naming the assignment; the same verbs on an unlabelled container succeed |
|
|
| No caller but the host creates a container that is root on the machine | A test of `create` from the bus: privileged, a host path, the host's namespaces and the runtime's socket are each refused; the same request on the host's socket is accepted. A broker test: a grant of every tool does not reach a changing verb |
|
|
| The host uses the holder and never the command line | A host test with a fake holder on the local socket: every container operation goes to it, and with the holder absent the apply creates nothing and reports the seat; after step 5, the host carries no command-line runtime code (checked by build: the package is gone) |
|
|
| A container needs the seat held | A resolution test refusing a containerised assignment on a machine with the seat unheld, naming the seat |
|
|
| Socket mounts are granted by the seat | A catalogue test: a module mounting the runtime's socket on a machine whose holder states a different path is refused |
|
|
| No other module writes the runtime's file | The existing collision check, once the private network's computed resources are inside it ([issue 190](../04-ISSUES/190-the-runtimes-configuration-is-written-by-modules-that-are-not-the-runtime/00-report.md)) |
|
|
| Live | `seats` lists `node-container-runtime` held on every machine; the node listing shows each machine's containers, strays included, from the seat's `list` verb; a container started by hand appears as an event on the bus |
|
|
|
|
## References
|
|
|
|
- [ADR 0121](0121-a-system-seat-is-named-for-its-scope-and-modules-define-their-own.md), [ADR 0129](0129-a-seat-carries-the-protocol-of-its-role.md), [ADR 0132](0132-a-seat-carries-the-tools-its-holder-must-serve.md), [ADR 0159](0159-a-tool-call-names-the-machine-and-a-holder-serves-its-seats-verbs.md), [ADR 0160](0160-the-mesh-issues-an-assignments-subjects-and-a-runtime-serves-what-it-is-issued.md), [ADR 0161](0161-what-deserves-a-seat.md)
|
|
- [ADR 0150](0150-a-modules-own-code-runs-as-supervised-processes-under-one-account.md), [ADR 0142](0142-the-mesh-delivers-its-own-components-as-binaries.md), [ADR 0078](0078-the-store-and-broker-are-modules.md), [ADR 0005](0005-the-node-host.md)
|
|
- [ADR 0164](0164-a-setting-is-declared-with-its-default-its-meaning-and-what-changing-it-costs.md), [ADR 0165](0165-container-runtime-is-what-a-machine-can-run-and-a-running-runtime-is-its-holders-health.md), [issue 190](../04-ISSUES/190-the-runtimes-configuration-is-written-by-modules-that-are-not-the-runtime/00-report.md)
|
|
- [Design 26 — the seats](../03-DESIGN/01-to-be/26-the-seats.md), [design 33 — the tools the mesh answers](../03-DESIGN/01-to-be/33-the-tools-the-mesh-answers.md)
|
|
- mesh-host `internal/apply/apply.go` (the runtime lookup and the command line it drives)
|