Files
hq/02-DECISIONS/0177-a-unit-may-be-user-scoped-and-the-service-manager-is-a-node-seat.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

4.8 KiB

topic, status, date, deciders, reconstructed, extends
topic status date deciders reconstructed extends
what runs on it accepted 2026-10-02 jochen false 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 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 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). 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); 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).
  • 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