--- topic: what runs on it status: accepted date: 2026-09-27 deciders: jochen reconstructed: true extends: 02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md --- # 176. The operator account is a node fact, and a home is a placement root *Reconstructed. The controller shipped this on 2026-09-27 and [to-be 29](../03-DESIGN/01-to-be/29-a-node-has-operator-accounts.md) recorded it as built without a decision behind it. This record states what was decided, from the code and the design, and adds the two rules the code left implicit — what an empty account means for a module, and that the account is stated rather than discovered. Written 2026-10-02.* ## Context [ADR 0112](0112-a-module-definition-names-no-node-mesh-or-path.md) took every host path out of a module definition and gave a module's *system* data a place: a directory the mesh resolves under the node's root, owned by the module. It said nothing about the other half of a filesystem — the files that belong under a person's home and are owned by that person. The predecessor wrote several of those: the ssh client configuration, the shell's configuration, an agent's instruction files. It knew whose home it was writing into because each of its node records carried a login name. The mesh took the machine facts over and dropped the human one. The loss was found the ordinary way: `ssh ` logged into the home-server under the workstation's own login name, because nothing in the mesh said the home-server's account was a different one ([to-be 29](../03-DESIGN/01-to-be/29-a-node-has-operator-accounts.md), [issue 172](../04-ISSUES/172-the-ssh-client-block-matches-one-spelling-of-a-machine/00-report.md)). What the controller does since 2026-09-27: a node record carries an operator account and, optionally, its home; the account and its home are machine facts a definition may name in a resource's path, owner and content; a roster file may say it lives under the home, and is then rendered per node, placed under that node's account's home, owned by the account, and left out on a node with no account. On 2026-10-02 **all four nodes of the live mesh carry an empty account**: the fact exists and nobody has stated it, so no home-scoped resource can land anywhere yet. ## Considered Options 1. **The definition names the login.** `owner: ` in the module. Rejected: it is the installation written into a definition, which ADR 0112 forbids and [ADR 0155](0155-a-definition-names-no-installation-and-how-that-is-checked.md) checks for, and it is wrong on the first machine whose login differs — which is exactly the machine that surfaced this. 2. **The host discovers the account.** The first non-system user, or whoever ran the enrolment. Rejected: a guess. A shared machine has several people on it, a server may have none, and a host deciding whose files these are is a decision the mesh then cannot see, state or correct. 3. **The account is a fact the operator states on the node record, and the home is derived from it unless stated.** Chosen. ## Decision **A node has an operator account: the login name of the person who works on it.** It is stated by the operator on the node record, the way a node's address or mode is held there, and it is empty for a machine nobody logs into. Empty is a real state, not a missing value. The mesh holds the fact because everything below derives from it, and because it is precisely the fact that was lost when the predecessor's records were not carried over. **The account's home is derived unless stated.** The superuser's home for the superuser, the distribution's conventional per-user home otherwise; a node whose account lives elsewhere states its home. One place computes the default, so a fact and the record cannot disagree about it. **A resource may be placed under the home, owned by the account.** This is ADR 0112's move one level over: as a module's system directory is resolved under the node's root, a file under a person's home is resolved against the account's home, and owned by the account rather than by root or a module's own account. A definition names the account and its home as machine facts, never as a path; a roster fact may say it is a home file and is then placed and owned the same way. The controller resolves both at composition, and the host chowns what it creates. **A node with no account cannot carry a home-scoped resource, and says so.** A roster fact that lives under the home is left out of that node's declaration rather than written to nowhere. A resource naming the account fact on such a node is refused at composition, naming the fact the machine does not have. A module that writes a person's files is thereby unassignable to a machine with no person on it, which is the right refusal. **One account per node is what this record decides.** Several people on one machine is left open, with the constraint that allowing it must not force the common case — one workstation, one person — to name anything. ## Consequences - **The operator states the account before any home-scoped module lands.** Today none is stated, so the first assignment of such a module begins with four node records. - The roster carries each node's account, so a composed ssh configuration logs in as the right person on every machine — the gap that surfaced this, closed by the same fact. - A family of modules becomes writable: everything the predecessor placed under a home — ssh client, shell, the agent's instruction files — is now a module naming a fact rather than a path ([to-be 29 §2](../03-DESIGN/01-to-be/29-a-node-has-operator-accounts.md)). - **What got harder:** a definition cannot say "my user's home" without the mesh knowing who the user is, so a module of this family is refused on a freshly enrolled machine until a person is named on it. That is a prompt, not an obstacle. - **Not decided here:** several accounts per node; a service unit running as the account rather than as root or a module; a one-off step run as the account. Each is a record of its own. ## How it is checked | Rule | Checked by | |---|---| | A resource's path and owner resolve the account and its home | controller tests on machine-fact resolution: a file naming the account facts lands under the account's home, owned by the account | | The home is derived unless stated | a controller test: the superuser's home for the superuser, the conventional home otherwise, the stated home when one is stored | | A home roster fact is left out on a node with no account | a controller test on roster composition: the file is absent from that node's declaration and present on a node with an account | | A resource naming the account on a node with no account is refused by name | a controller test on machine-fact resolution: the refusal names `account` and lists the facts the machine does have | | No definition names a home path | ADR 0112's catalogue test on host paths, which a `/home` or `/root` literal fails | ## References - [to-be 29](../03-DESIGN/01-to-be/29-a-node-has-operator-accounts.md) — the design this record gives a foundation to, and its "what has shipped" section - [ADR 0112](0112-a-module-definition-names-no-node-mesh-or-path.md) — the system-path placement this mirrors; [ADR 0155](0155-a-definition-names-no-installation-and-how-that-is-checked.md) — why a login name may not be in a definition - [ADR 0120](0120-a-roster-fact-carries-its-format-as-a-template.md) — the roster fact a home file may be - [ADR 0177](0177-inside-a-home-the-mesh-owns-what-it-places-and-holds-the-rest-as-found.md) — what the mesh may and may not do inside the home this record lets it reach - mesh-controller `internal/inventory/nodes.go` (the account and its home on the node record), `internal/catalogue/machine_into_files.go` and `roster.go` (resolution and the home fact)