--- topic: what runs on it status: accepted date: 2026-10-02 deciders: jochen reconstructed: false extends: 02-DECISIONS/0121-a-system-seat-is-named-for-its-scope-and-modules-define-their-own.md --- # 177. A unit may be user-scoped, and the service manager is a node seat whose holder answers for the units ## Context The host's `service` shape puts a system unit into a state. It has no user scope. [To-be 29](../03-DESIGN/01-to-be/29-a-node-has-operator-accounts.md) states the gap: *a workstation's per-user daemons have no form the mesh can send.* Four of the predecessor's environment modules ship user units — the desktop's reload watcher and bar watchdog, the audio module's masks, the power module's memory guard, the thermal daemon's profile switcher — and the predecessor needed a hook to enable them because *shipping a unit file does not run it*; one unit was deployed for months and ran on one machine only. [ADR 0040](0040-what-a-module-is.md) says the host hardcodes no supervisor, and a swappable machine mechanism is a module implementing a capability — which is what the nftables module is for the packet filter ([ADR 0170](0170-the-firewall-seat-serves-its-verbs.md)). The service manager is reported today as a capability, `service-manager`, and held by nobody. The operator's proposal: a systemd module that holds the seat and serves the tools about units, system and user. ## Considered Options 1. **Keep user units as a module concern** — each module runs `systemctl --user` in a hook. Rejected: that is the hook that silently never ran, and an action over the link is refused. 2. **The service-manager module applies units** on behalf of others, as a provision. Rejected by the operator: provisioning is for resources a provider creates for a consumer; a unit is declared state the host applies, as every resource is. 3. **The host's `service` shape gains a user scope; a systemd module holds the service-manager seat and serves the verbs about units.** Chosen. ## Decision **1. The `service` shape gains `scope`: `system` (the default) or `user`.** A user-scoped unit is applied as the operator account through the account's own service manager: enabled, started, stopped, reloaded on its triggers, exactly as a system unit is, and refused on a node with no account, naming the fact. The host applies it; no module does. **2. `node-service-manager` is a seat of the mesh's own, node-scoped**, seeded by the controller under this record, as ADR 0121 requires of a `node-*` name. The `systemd` module claims it and is assigned to every machine whose profile reports `service-manager`. **3. The seat's verbs answer for every unit on the machine**, each taking an optional `scope`: `units`, `status`, `start`, `stop`, `restart`, `enable`, `disable`, `journal`. The host applies what is declared; the holder answers questions and operator acts about it, and says, for a mesh-held unit, that the host will restore what its declaration says. ## Consequences - The host's vocabulary grows by one field on one shape, asserted by its count test ([to-be 05](../03-DESIGN/01-to-be/05-the-node-host.md)); an older host refuses a declaration carrying it, so the host rolls before the first module that uses it. - The predecessor's four user-unit modules become declarable without a hook. - The seat's holder is the first system seat held by a module that runs nothing of its own: its verbs are served by the node tools runtime ([ADR 0175](0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md)). - What got harder: `journal` and `status` on a user unit need the account's manager reachable from the runtime's process, which runs as the node's account; the holder's tool escalates or switches user as it needs, which is ADR 0175 §4 applied. ## How it is checked | Rule | Checked by | |---|---| | A `service` with `scope: user` is enabled and started under the account, and refused with no account | the host's tests with a fake service manager | | The seat declares its verbs; a claim serving fewer is refused by name | the catalogue's seat tests | | The verbs act on a named unit in the named scope and name the unit's holder when the mesh declares it | the module's tests over a fake runner | | Live | the desktop's reload watcher declared `scope: user` on a workstation; `node-service-manager.status@` reports it active | ## References - [Research 018](../01-RESEARCH/018-the-operators-machine-as-modules/04-the-seats-of-the-environment.md) - [ADR 0121](0121-a-system-seat-is-named-for-its-scope-and-modules-define-their-own.md), [ADR 0132](0132-a-seat-carries-the-tools-its-holder-must-serve.md), [ADR 0040](0040-what-a-module-is.md), [ADR 0170](0170-the-firewall-seat-serves-its-verbs.md) - [To-be 05](../03-DESIGN/01-to-be/05-the-node-host.md), [to-be 29](../03-DESIGN/01-to-be/29-a-node-has-operator-accounts.md)