**0114 accepted.** The separation it draws — a consumer's resource and a credential that reaches it are different things — is a data-loss rule, and a record naming one should not sit unresolved while the code that could hit it is written. Not built, and accepting it schedules nothing; accepted-and-not-built is where 0141 and 0142 already are. **0068 superseded by 0149, the live mesh is the test bed.** Never built, and contradicted by practice that was written down nowhere but a handoff note. The faults that cost the most are faults of a mesh that already exists — bound consumers, containers made against an older roster, an adopted machine — and a bed is by construction a mesh that does not. Issue 156 settles it: that change was verified honestly, on the only path where it cannot fail. The lab is not retired; raising a mesh from bare is now its whole job. **117 answered by 0150.** A module's own code runs as supervised processes under the module's one account. 0047's argument was for a runtime per module, not for a container — a unit satisfies it and runs as an account. The invariant is the account, not the process count: its worry was a second identity to scope and seal, and processes sharing one account create none. Designs 18 and 20 now cite it and 0047 carries a dated pointer, which closes the third disagreement — that neither design knew the record existed.
7.4 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
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, andcycle.pyalready 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
networkshape 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 —
nftablesproves 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.