Files
hq/02-DECISIONS/0057-the-host-is-a-root-service-installed-as-a-package.md
T
jschoubben 3ab11c96ef Say what the host process is: a root service, installed as a package
The design described what the host does and never what it is at runtime. The
words daemon, long-running, interval, poll and heartbeat appeared nowhere in it
or in the relevant decisions. What exists is a command that runs and exits;
what the design needs is a process holding a link. Nobody had written down that
those differ, so several questions had no answer.

0057 settles them. It runs on every node -- the host is what makes a machine
managed, so a machine without one is not a node. Root, because no useful subset
of the job is unprivileged. A systemd unit, because something must survive a
reboot to hold the link.

It never manages its own unit. The temptation is obvious and it ends with a
host stopping itself half way through an apply, leaving a machine with nothing
running to fix it. The installation owns the host; the host owns everything
else.

Installed as a package, with a tarball as the floor. The package carries the
unit file, the state directory and an upgrade path, which a bare binary does
not. But the mesh's package repository is hosted on the mesh, so any route that
needs the mesh to install the thing that joins the mesh is a circle -- the
tarball is the path that must never acquire a dependency.

Reconciles on start, on a declaration, on a timer and on reconnect. The timer
is the one easy to leave out, and without it `owned` reports what the host
applied rather than what is there -- ADR 0035 violated by omission.

The records checker caught this commit on its first attempt: 05 listed 0057 in
its frontmatter while 0057 is still proposed, and a to-be document may not rest
on an unaccepted record. The section now says so in the body instead.
2026-08-27 21:06:00 +02:00

6.7 KiB

status, date, deciders, reconstructed, extends
status date deciders reconstructed extends
proposed 2026-08-27 jochen false 0041-the-host-depends-on-nothing.md

57. The host is a root service, installed as a package, that never manages itself

Context

05-the-node-host.md describes what the host does and never says what it is at runtime. Searched: the words daemon, long-running, interval, poll and heartbeat appear in none of it, nor in ADR 0038 or ADR 0039.

What exists today is a command that runs and exits — mesh-host apply FILE. What the design requires is a process holding an outbound link to the control plane. Nobody wrote down that these are different things, so several questions have no answer: does it reconcile on a timer, what happens when the link drops, and who installs the unit that starts it — given that the host is the thing that installs units.

Decision

It runs on every node, and that is what a node is

ADR 0036 defines a node as a managed machine. The host is what makes it managed, so a machine without one is not a node with a missing component; it is not a node. There is no partial mode, no agentless node, and no second way in.

It is a root service

Root, because there is no useful subset of its job that is not privileged: it writes under /etc, installs packages, manages units, and runs containers. A host that dropped privilege could apply almost nothing, and the almost is where the confusion would live.

A service rather than a command, because it holds the link, and something must survive a reboot to hold it. The command-line entry points remain — they are how a person inspects and rescues a machine — but the ordinary case is a unit that is always up.

It never manages its own unit

The host's own service file is not a resource the host applies. The temptation is obvious — it manages units, and its own unit is a unit — and it ends with a host stopping itself half way through an apply, leaving a machine in a state nothing is running to fix.

So the boundary is: the installation owns the host; the host owns everything else. A declaration that names the host's own unit is refused rather than obeyed.

It is installed as a package, and a tarball is the floor

Two mechanisms, and the second is not a fallback for the first failing — it is what makes the first possible.

package — pacman -S nox-mesh-host the ordinary path. Carries the binary, the unit file, the state directory, and an upgrade path
tarball — curl … | tar -xz the floor. One static binary, no repository, no distribution assumed

Why a package rather than only a binary. ADR 0041 says copying the binary onto a machine is the whole installation, and that remains true of the binary. But a unit file, a state directory and an upgrade path are real, and something has to own them. A package that installs one statically linked binary plus a unit file adds no runtime dependency — 0041 is about what must already be present for the host to work, not about how the bytes arrived.

Why the tarball must keep working. The package lives in a repository, and the mesh's own repository is hosted on the mesh. A first node cannot fetch from a mesh that does not exist yet, and neither can a node whose mesh is down — which is exactly when somebody is trying to fix it. Any path that requires the mesh to install the thing that joins the mesh is a circle, so the tarball is the path that is never allowed to acquire a dependency.

The mesh does not upgrade the host

A running process replacing its own binary and restarting mid-apply is the self-management problem wearing a different hat. Upgrading the host is an act on the machine, by the package manager, not a declaration the host applies to itself.

Recorded as a limit rather than a plan: it means a fleet-wide host upgrade is not currently a mesh operation, and that will be felt.

It reconciles on four triggers

Trigger Why
start the machine may have changed while nothing was running
a declaration arrives the ordinary path
a timer drift. Something other than the host changed the machine — a person, a package upgrade replacing a config file
reconnect it may have missed declarations while disconnected (ADR 0036)

The timer is what makes the store's claim true. Without it a machine that drifted stays drifted until somebody changes a declaration, and owned reports what the host applied rather than what is there — which is ADR 0035 violated by omission.

Ten minutes, configurable. Short enough that drift is bounded by something a person would notice anyway, long enough that a fleet is not doing constant work. The reconcile is cheap: it asks the package database, the service manager and the container runtime about resources the host already knows it owns.

Consequences

  • Adoption becomes two concrete steps, which is the point of writing this down: install the package, then hand it a token. Nothing else.
  • The host gains a mode it does not have, and it is the larger half of stage 3. Today every entry point runs and exits.
  • Refusing to manage its own unit needs enforcing, not just stating. A declaration naming the host's unit must be refused by name, and that refusal is a test.
  • A host that cannot reach the mesh keeps reconciling from its store, which is ADR 0036 made operational rather than aspirational: a disconnected node is not merely tolerated, it is actively holding its machine in the last state it was told to hold.
  • The timer makes drift visible and also makes it loud. A resource the host cannot apply will now fail every ten minutes rather than once. That is correct and it needs somewhere to go other than a log nobody reads — which is observability's, and it does not exist yet.
  • Host upgrades are outside the mesh, so a fleet-wide upgrade is currently manual. Nothing here solves it and it should not be solved by giving the host a self-upgrade path.

References

  • ADR 0041 — the property the package must not break.
  • ADR 0036 — what a node is, which this makes operational.
  • ADR 0039 — the link the process exists to hold.
  • ADR 0051 — the second of the two adoption steps.