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.
81 lines
4.8 KiB
Markdown
81 lines
4.8 KiB
Markdown
---
|
|
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@<node>` 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)
|