From 64bbdc30c187a25373e0978fce243f8b4a3971ea Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 26 Sep 2026 23:53:13 +0200 Subject: [PATCH] to-be 29: a node has operator accounts, and the mesh owns what lives under a home MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mesh models machines but not the people on them — a node record holds no username, and no module places anything under a home. So who you are on each node (jochens/ace/jochen) is unknown to the mesh, and nothing owns ~/.ssh, dotfiles or ~/.config. HAL knew it; the nox mesh dropped it. Proposes the account as a node fact and a home-scoped resource class (the ~/ mirror of ADR 0112's /var/lib placement), with the login key staying the operator's (ADR 0051). Not urgent — HAL's generators still run — load-bearing at node-by-node retirement. Found generating ~/.ssh/config from HAL's registry, which nox has no equivalent for. --- .../29-a-node-has-operator-accounts.md | 84 +++++++++++++++++++ 03-DESIGN/01-to-be/README.md | 1 + 2 files changed, 85 insertions(+) create mode 100644 03-DESIGN/01-to-be/29-a-node-has-operator-accounts.md diff --git a/03-DESIGN/01-to-be/29-a-node-has-operator-accounts.md b/03-DESIGN/01-to-be/29-a-node-has-operator-accounts.md new file mode 100644 index 0000000..f038a5d --- /dev/null +++ b/03-DESIGN/01-to-be/29-a-node-has-operator-accounts.md @@ -0,0 +1,84 @@ +--- +layer: to-be +status: proposed +code: [] +updated: 2026-09-27 +decisions: + - 02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md + - 02-DECISIONS/0051-shared-data-is-the-operators.md +--- + +# 29 — A node has operator accounts, and the mesh owns what lives under a home + +**The mesh models machines but not the people on them.** A node record holds its name, its +address, its mode — and nothing about *who a person is* on it: `jochens` on novox, `ace` on ace, +`jochen` on shanks and g14. That username is not incidental. It decides who a file under `~` is +owned by, who a user service runs as, and — the case that surfaced this — which account `ssh +` logs in as. The predecessor knew it (its per-node `user:`, and the modules that wrote a +person's `~/.ssh/config`, `~/.zshrc`, `~/.config`); the mesh, taking those over, kept the machine +facts and dropped the human one. + +Two things are missing, and they are one idea: + +## 1. The account is a node fact + +A node has one or more **operator accounts**: the human logins on it. At minimum a name; the +mesh already knows the node and its address, so `@` is then a complete answer to +"who am I, where." It is the mesh's to hold because everything below is derived from it, and +because it is exactly the fact that was silently lost — `ssh ace` failed to `ace` because nothing +in the mesh said ace's account is `ace`. + +It is **not** a credential. The account names a login; the key that authorises it is the +operator's, placed as a secret or an operator-owned file, never minted by the mesh (ADR 0051). + +## 2. A resource may live under a home, owned by its account + +[ADR 0112](../../02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md) placed a +module's *system* data — `/`, owned by the module. It has no analog for the other +half of the filesystem: the things that belong under a person's home and are owned by that +person. `~/.ssh/config`, `~/.ssh/config.d/mesh`, `~/.zshrc`, `~/.config/hal` — every one of these +is a resource the mesh should be able to place and own, resolved against **the account's home** +rather than a system root, and chowned to **the account** rather than to root or a module uid. + +This is the same move as `${dir:…}`, one level over: a resource says `home: ` (or names +an account requirement), and the mesh resolves the home directory and the owning uid on the node +that account lives on. A module that writes operator config — the eventual replacements for +`hal/terminal`, `hal/claude-code`, `hal/secrets` — declares its files this way and names no +`/home/...` path, exactly as a system module now names no `/var/lib` path. + +## Why now, and why not yet + +**Why it matters:** when HAL retires, the generators that keep `~/.ssh/config`, shell config and +the operator's `~/.config/hal` current retire with it. Without this, adding a node stops adding +its ssh alias, and a fresh machine has no operator dotfiles at all — the mesh would run every +service and leave the human unable to work on the box. The account is also load-bearing for +correctness already: `ssh ` (issue 122's cousin), user-scoped systemd units, and any file a +person rather than a daemon must own. + +**Why not build it reflexively:** it is a real addition to the node model and the resource model, +and it must be gotten right, not smuggled in beside a firewall fix. Open questions to settle +first: + +- **One account or several per node?** A workstation has one human; a shared box might have more. + The model should allow more than one without forcing the common case to name it. +- **Where the login key lives.** An operator-owned file (ADR 0051) or an accepted secret — never + minted. The account fact and the key that authorises it are separate, and only the first is the + mesh's to generate. +- **The boundary with `sshd`.** The `sshd` module (server side) already exists. This is the + *client* and *identity* side: the account a node offers, and the home-scoped files an operator + needs. They meet at the account but are not the same module. +- **Multi-operator.** Today there is one human. The model should not assume it, but the first + cut may serve one and leave the shape open. + +**Not urgent, not blocking.** ssh and dotfiles work today because HAL's generators still run as +the substrate. This becomes load-bearing in the node-by-node retirement phase, not before — which +is the right time to build it, once the account model is decided here. + +## References + +- The gap was found generating `~/.ssh/config` from the *HAL* registry (`hal/terminal`'s + postConfigure hook), which the nox mesh has no equivalent for. +- [ADR 0112](../../02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md) — the + system-path placement this mirrors for home paths. +- [ADR 0051](../../02-DECISIONS/0051-shared-data-is-the-operators.md) — why the login key stays + the operator's, never minted. diff --git a/03-DESIGN/01-to-be/README.md b/03-DESIGN/01-to-be/README.md index 584c25b..bae51da 100644 --- a/03-DESIGN/01-to-be/README.md +++ b/03-DESIGN/01-to-be/README.md @@ -38,6 +38,7 @@ document is written and this one's status becomes `implemented`. | [`26-the-seats.md`](26-the-seats.md) | **Proposed.** What a mesh can have one of, who fills each, and a seat's holder answering for the provision it delivers — including the `git` seat a build's source can live on | [ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md), [ADR 0111](../../02-DECISIONS/0111-a-build-source-is-on-the-git-seat-or-external.md), [ADR 0109](../../02-DECISIONS/0109-a-package-registry-seat-is-one-per-ecosystem.md) | | [`27-a-module-requires-the-mesh-resolves.md`](27-a-module-requires-the-mesh-resolves.md) | **Proposed.** One concept for everything a module needs: a requirement with a contract, answered by one of four kinds of provider, resolved at assignment or refused. Retires settings, placeholders, facts and paths in definitions | [ADR 0112](../../02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md), [ADR 0113](../../02-DECISIONS/0113-the-vault-makes-every-secret.md), [ADR 0114](../../02-DECISIONS/0114-a-shared-credential-rotates-over-two-credentials.md), [ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md) | | [`28-building-the-bus.md`](28-building-the-bus.md) | **Proposed.** The five steps of the bus work in the order their dependencies allow, each ending at a bed — with the surface measured, so no step's size is a guess | [ADR 0116](../../02-DECISIONS/0116-the-bus-is-built-in-five-steps.md), [ADR 0106](../../02-DECISIONS/0106-the-bus-is-nats.md), [ADR 0074](../../02-DECISIONS/0074-the-wire-is-specified-not-the-types.md) | +| [`29-a-node-has-operator-accounts.md`](29-a-node-has-operator-accounts.md) | **Proposed.** The mesh models machines but not the humans on them: a node gains operator accounts, and a resource may live under a home owned by its account — what would own ~/.ssh, dotfiles and ~/.config when HAL retires | [ADR 0112](../../02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md), [ADR 0051](../../02-DECISIONS/0051-shared-data-is-the-operators.md) | ## Not yet written