The predecessor's agent module was retired and its six files stayed on both workstations telling every session to use tools that no longer exist. Before a successor module is written, the design needs the decisions it rests on and nothing in the record stated them: - ADR 0169 (reconstructed) records what the controller shipped on 2026-09-27 without a record: the operator account is a node fact stated by the operator, the home is derived unless stated, a resource may be placed under it owned by the account, and a node with no account refuses one. - ADR 0170 generalises to-be 29 §3's found-vs-owned boundary to every directory under a home: the module owns the directory and the files it places, writes into the tool's own files for its few keys, never declares a credential's content, and holds everything else as found — a predecessor's leftovers included, which the operator removes once. - ADR 0171 draws the licence line the operator asked to have drawn rather than assumed: the mesh binds and delivers (to-be 14 and 15 stand), the module alone writes the credential file, refresh stays central (ADR 0050), a switch is the binding changed through a controller seat verb asked for via the console, and the token-carrying shell helper is retired. The controller learns nothing about the agent; that is what "no part" means. To-be 36 is the module's design: the ownership map per path, the fate of the six predecessor files, what the three instruction documents say, the licence tools and skill, the console as a node-scoped provision, the package gap stated honestly, and the order of the build. To-be 14, 29 and 34 carry dated notes; the glossary gains "operator account".
119 lines
7.7 KiB
Markdown
119 lines
7.7 KiB
Markdown
---
|
|
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 <node>` 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: <name>` 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)
|