Files
hq/02-DESIGN/00-as-is/05-runtime-and-installation.md
T
jschoubben f05e4a0dce Follow papa-hq's research convention; the mesh links nothing
Research efforts move from status.md to 00-overview.md with active /
graduated / abandoned, matching papa-hq so the two repositories read the
same way. Playbooks, skills, README and the ledger follow.

Reverses yesterday's withdrawal of the symlink note in GENESIS. The note
was right and the withdrawal was wrong: the intent is that the mesh
creates no symlinks at all, so a founding document listing "symlinks, not
copies" as a design principle does point the opposite way from where this
is going, and that is a contradiction rather than a stale detail.

ADR 0018 records the position, proposed. ADR 0011 stays as it is — it is
the historical decision and the incident behind it is why anyone believes
either record — and is superseded in intent, not edited. Its one
editorial line, which called the wider reading false, is corrected to
state what is actually true: centralising who may link narrowed the
incident class without closing it, because a link the installer makes
resolves exactly like one made by hand.

The argument that kept linking was staleness. ADR 0004 removed it: every
managed file is already derived and reconciled, so a copy is the natural
form and a pointer into source is the shape the mesh's own model forbids
everywhere else. What is not settled, and is marked open, is how
staleness gets detected — which is the decision that makes or breaks it.
2026-08-23 09:29:09 +02:00

5.0 KiB

layer, status, code, updated, decisions
layer status code updated decisions
as-is implemented
hal
2026-08-23
adr/0002-everything-is-a-module.md
adr/0011-the-installer-owns-linking.md

The node runtime, and how a node comes into being

Every node runs the same runtime. What differs is its assignment.

Two modes, coexisting

The runtime runs in two modes at once on any node that needs both.

Daemon mode is a headless consumer: it connects to the broker, consumes the node's request queue, routes each request to a local capability, and emits the node's lifecycle events. This is what makes a node a participant — it is reachable whether or not anyone is logged in.

Interactive mode exposes the node's capabilities to a session on that machine over a local protocol. Its capability surface is larger, because it includes stand-ins for every capability discovered on peers.

The two are the same code with the same catalogue. A capability is written once and is available to both.

Anatomy naming, and what it obscures

The runtime's components are named after brain anatomy: an entry point that bootstraps, a headless listener, an interactive surface, an installer daemon, and a provisioner daemon.

This is the mesh's most-cited naming problem and it belongs in the as-is layer because it is what a reader will actually encounter. The names are evocative and describe nothing: the most suggestive word in the system names the node runtime, and the component whose manifest says "mesh messaging" is documented elsewhere as the interactive runtime. Anatomy makes attractive names and poor boundaries.

ADR 0015 replaces this with names taken from what each part owns. Until then, this is the vocabulary in the code.

Starting a module

For each assigned module the runtime, at startup:

  1. Reads the manifest, if there is one — a flag module has nothing to read.
  2. Skips a module's capabilities if a variable they require is unset. This is quiet by design and hard to distinguish from a module that has no capabilities.
  3. Loads the capabilities the module carries.
  4. For a service module, ensures it is installed and running.

Installation is idempotent and does the unglamorous work: ensure the runtime directory, reconcile the link from the catalogue's definition into it, generate the environment, run any outstanding local migrations, create data directories with the right ownership, then start the service under supervision.

The installer is the only thing that creates a link (ADR 0011). It reconciles rather than assumes: a missing link is created, a stale one repointed, and a real file found where a link belongs is adopted into the node's override area and replaced. Nothing else — not a hook, not a fix, not a person debugging — creates one.

That is the as-is. The intent is to remove linking altogether and derive a real file instead, which the reconciliation machinery already makes possible (ADR 0018, proposed). What is described above is what runs today.

Supervision

Services run under the host's init system via a templated unit, one instance per module. It is a thin layer: the unit starts and stops a container group.

Whether the mesh keeps this, drops the per-module layer, containerises the daemons, or writes its own supervisor is open — costed in research effort 003 and deliberately undecided. It no longer gates anything.

Two failures worth knowing about, both in the shape of "the change did not apply". A per-instance copy of the unit template shadows the template, so edits to the template do nothing. And a session-scoped one-shot job loses the environment it was given, because the import is one-time and not persisted.

How a node comes into being

Three bootstrap scripts, and which one runs depends on the situation:

  • First node. Nothing exists yet, so the script stands up the database the rest of the mesh reads from, publishes the catalogue, and starts the mesh. This resolves the circularity of a mesh whose source of truth is itself a provisioned module.
  • Joining. The node registers, takes its assignment from the database, and syncs.
  • Rescue. A node that cannot reach the mesh is brought back far enough to.

Bringing a node into being is therefore a database operation with a script attached, not a checkout. There is no per-node content in the repository to copy.

Adoption of a pre-existing machine's configuration was the original path and is now a legacy one, explicitly out of scope for the lab (01-to-be/01-end-to-end-testing.md).

Node identity

Each node carries an identity text in its own record, which the daemon writes onto the node at startup so that a session on that machine knows which node it is on and how that node presents itself. It carries identity only; shared rules live separately.

Like everything else derived onto a node, it is generated and not edited there.