Files
hq/02-DECISIONS/0207-a-module-depends-on-the-node-seats-that-apply-its-resources.md
T

108 lines
5.7 KiB
Markdown

---
topic: the mesh
status: accepted
date: 2026-10-04
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0177-a-unit-may-be-user-scoped-and-the-service-manager-is-a-node-seat.md
---
# 207. A module depends on the node seats that apply its resources
## Context
A module declares resources: packages, services, containers, files. Some of those are applied
through software on the machine that is itself a module:
- a service through the service manager;
- a package through the package manager;
- a container through the container runtime.
Until now nothing said so. A module carried a *capability* such as `service-manager` or
`package-manager`, which the host detects on the machine. A capability says the software is
installed. It does not say that a module of the mesh holds the role and answers for it.
The cost showed on 2026-10-04:
- **A networking module declared the service manager's own package.** The controller allows one
declaration of a resource per node, so the module that *is* the service manager could not declare
its package and had been written without it. The networking module's real relation to the service
manager, that it needs one held on its node, was nowhere.
- **The container runtime** has had this decided for its own case since ADR 0165 and ADR 0166
(proposed): a module that delivers a container needs the runtime seat held on its machine, derived
from the container resource, with no manifest field.
- **The operator's order for building the machines' modules** (to-be 42) is *the most core first*.
That is an order the mesh should enforce, not one a person should remember.
## Considered Options
1. **Keep capabilities as the only gate.** Rejected: a capability is a fact about the machine, not
about the mesh. Software installed by hand satisfies it, and nothing then answers for it.
2. **A manifest field per module naming the seats it needs.** Rejected: a module would restate what
its resources already say, and a module that adds a service but forgets the field passes.
3. **Derive the dependency from the resources,** as ADR 0165 already does for containers, and refuse
an assignment whose seats are not held on the node. Chosen.
## Decision
**1. Three node seats apply resources,** each in the mesh's own set:
| resource | applied through | seat | first holder |
|---|---|---|---|
| `service` | the service manager | `node-service-manager` (ADR 0177) | `systemd` |
| `package` | the package manager | `node-package-manager` (new) | `pacman` |
| `container` | the container runtime | `node-container-runtime` (ADR 0166) | `docker` |
`node-package-manager` is new and has no verbs yet. `node-container-runtime` is seeded now as ADR 0166
names it. Its verbs, and the host creating containers through its holder, stay with that record's
acceptance.
**2. A module depends on each seat its resources need.** The controller derives this from the
resource types the module declares. A module never states it.
**3. A dependency is met when any module assigned to the same node holds the seat,** the module
itself included. The holders of these seats declare resources of each other's kinds: the service
manager's package needs the package manager, and the package manager's timer needs the service
manager. They are therefore judged as the node's whole set of assignments, never one at a time.
**4. Where it is checked:**
- **At `assign`,** an assignment whose dependencies are unmet by the node's assignments, including the
new one, is refused. The refusal names each seat and the modules in the catalogue that can hold it.
- **At composition,** an unmet dependency on a node is **reported** in `status` until the three holders
are assigned to every node. Then it is **refused** like any unresolved requirement. The switch is one
line in the controller, made when `status` reports none.
**5. The mesh's own foundation is exempt.** These are the pieces genesis lays before any module exists:
the host, the private network and the bootstrap runtime. Their declarations are the installation's,
not a module's.
## Consequences
- The order of to-be 42 becomes the mesh's: `systemd`, `pacman` and `docker` on a node before
anything that installs, runs or contains.
- **Two modules no longer declare one shared package to say they need it.** A component's module
(networkd's) declares what it configures and depends on the seat. The component's own package
belongs to the module that holds the seat.
- Capabilities stay what they are, facts about the machine, used where a module needs the machine to
be able to do something.
- **What got harder:** a module can no longer be tried on a node that lacks the core three. That is
the point.
## How it is checked
| Rule | Checked by |
|---|---|
| The dependency is derived from resources: a service, a package and a container each need their seat | the controller's resolve tests |
| A node whose assignments hold the seats passes; one missing a holder is refused at `assign`, naming the seat and its possible holders | the same tests, and `assign` live |
| Mutual dependencies among the holders resolve when they are assigned together | the same tests |
| Until the switch, an unmet dependency is reported in `status` and does not refuse a push | the controller's status test |
| Foundation declarations are exempt | the composition test with genesis's declarations |
## References
- [ADR 0165](0165-container-runtime-is-what-a-machine-can-run-and-a-running-runtime-is-its-holders-health.md),
[ADR 0166](0166-the-container-runtime-is-a-node-seat-and-the-host-creates-containers-through-its-holder.md),
[ADR 0177](0177-a-unit-may-be-user-scoped-and-the-service-manager-is-a-node-seat.md)
- [To-be 42](../03-DESIGN/01-to-be/42-the-machines-modules-in-order.md)