Files
hq/02-DECISIONS/0150-a-modules-own-code-runs-as-supervised-processes-under-one-account.md
T
jochen bf39baf104 Research 018 graduates: ADRs 0173–0177 and to-be 37, the operator's machine
Every configurable thing on a node is a module, the home included, and a
module is whatever it declares (0173, extending 0040). A node varies a module
only through a setting rendered into the file or a kept region, never an edit
(0174, extending 0011; issue 168 first). One tool runtime per node serves every
module's tools on the host side, never in a container; the console is its
serving mode, renamed node-tools (0175, extending 0150; 0047/0150/0152 carry
dated notes). The login shell is a node seat held by one shell module with
`execute` as its contract (0176). A unit may be user-scoped and the service
manager is a node seat held by systemd (0177).

To-be 37 is handed off in-progress to mesh-host, mesh-controller, mesh-tools
and mesh-catalog, with the build in order: the account on every node, the
runtime, zsh, systemd, then the graphical stack. To-be 29 keeps ~/.ssh and
points at 37; 33 §6 and 34 are amended; the glossary gains node tools, bundle,
kept region, installed/holding, and retires flavor.
2026-10-02 16:34:57 +02:00

7.8 KiB

topic, status, date, deciders, reconstructed, extends
topic status date deciders reconstructed extends
what runs on it accepted 2026-09-30 jochen false 02-DECISIONS/0047-a-module-runs-its-code-as-its-own-process-with-its-own-account.md

150. A module's own code runs as supervised processes under the module's one account

The mechanism changed — 2026-10-02, by ADR 0175. For a module's tools, read that record: one runtime per node, the node's one account, bundles loaded from the memberships. This record still governs a module's long-lived processes — a daemon, a provisioner, a scheduled ingest — and the account invariant for them.

Context

The repository answers "what runs a module's own code" two ways and reconciles them nowhere (issue 117).

ADR 0047 is accepted and says a container — "the tool runtime carrying that module's compiled code" — and "one module, one process, one account". Two proposed design documents say a process resource running an argv, supervised by the machine, and one of them declares four of them for a single module and presents four as the point. Neither design document names 0047 in its decisions:, and the string process as a resource type appears in no decision record at all. The thing as built is the container.

Two things have happened since 0047 was written that bear on it directly.

ADR 0142 decided that the mesh's own components are binaries on the machine rather than container images, and issue 114 was closed by it. That settled the mesh's components and deliberately said nothing about a module's.

And the standing definition of a module hardened: a module is software that delivers one or more services, and a module is not a container. It may deliver them as a container, an installed package with a unit, a binary, or configuration files; 61 of 73 happen to use a container and 11 do not, including the resolver, sshd and fail2ban. A rule that a module's own code must be a container makes the one kind of module the mesh writes itself the only kind that has no choice.

Considered Options

1. Hold 0047 as written: a container. Rejected. Its own reasoning does not require one. What 0047 argued for was a runtime per module rather than one for the whole node, because a node-wide runtime could not hold a per-module broker account and per-module runtimes competing on one tool key would each be handed calls for tools they do not have. A supervised unit per module satisfies that argument exactly — it is per module, and a unit runs as an account. The container was the mechanism to hand, not the conclusion.

2. Let each design document choose. Rejected; that is the present state and it is what issue 117 reports. A module author reading the guide writes four processes; a module author reading the record writes a container; nothing tells either that the other exists.

3. Settle the hosting form as a supervised process, and settle the count separately. Chosen.

Decision

A module's own code runs as one or more supervised processes on the machine, under the module's single account. Where ADR 0047 says "a container, the tool runtime carrying that module's compiled code", read this record. Everything else 0047 decided stands untouched: a tool is served on its own key, only the module that serves it answers, and the module's account is scoped to exactly its tool keys.

The invariant is the account, not the process count. 0047's "one module, one process, one account" carried its weight in the last clause. Its stated worry about a second process was "not a second one to scope and seal" — a second identity to grant, seal a secret to, and scope on the bus. Several processes sharing the module's one account create no second identity, so nothing further is scoped or sealed, and a module may therefore declare as many as its work has shapes: events, tools, a provisioner, a scheduled ingest. What a module may not have is two accounts.

A module that delivers its service as a container still does. This record is about the code the module itself carries — its tools, its events, its provisioner — and not about the software it delivers. A module wrapping a third-party image wraps a third-party image.

Why supervised by the machine rather than by the mesh: it is the same answer ADR 0142 gave for the mesh's own components, for the same reason. A unit the machine restarts needs no image, no registry pull and no runtime to be up before the mesh's own code can run — which matters most for exactly the modules whose code the mesh cannot start any other way.

How this is checked

  • No design document describes a hosting form for a module's own code without citing this record. Designs 18 and 20 name it in decisions:; this is the gap issue 117's third point reports, and cycle.py already enforces that a to-be design names its decisions.
  • A module declaring several processes resolves to one account. A test composes a module with more than one process resource and asserts the mesh mints exactly one broker account for it, scoped to that module's tool keys and nothing else — which is 0047's invariant stated as an assertion rather than a sentence.
  • A module's own code does not require the container runtime. A machine with no container runtime can still run a module whose code is its own, which is the claim that separates this from option 1 and is checkable on a machine that has one by asserting the declaration names no image for it.

Consequences

  • The sidecar port stops being needed. ADR 0029 records that "anything that is a service plus a sidecar currently has to publish a port to talk to itself", and the host's network shape exists partly for it. A process beside the service on the same machine reaches it without publishing anything, so that pressure goes.
  • Something must supervise, and it is the machine. This adds a unit per module's code to what the host writes and owns. The mesh already writes and owns units — nftables proves a module can write one and run it — so the mechanism exists; the count grows.
  • A module's code is delivered, not pulled, which puts it behind the same gap as the host's own delivery (ADR 0141, not built): nothing yet delivers a version of a module's binary to a machine. A container's code arrives by docker pull, and this does not. This is the cost of the decision and it is not paid; until delivery exists, a module whose code is its own is a module somebody places by hand.
  • Issue 117 is answered and its three disagreements close differently: container-or-unit is decided here; one-process-or-several is decided here as several under one account; and whether the record was consulted is fixed by designs 18 and 20 naming this one.

References

  • issue 117 — the contradiction this answers
  • ADR 0047 — extended; its "a container" clause is settled here
  • ADR 0142 — the same answer for the mesh's own components
  • ADR 0141 — the delivery this depends on and which is not built
  • ADR 0029 — the sidecar port this relieves