Design 58: milestones 11 to 14 from research 057 with their issues, and as-is 17 on today's accounts (playbook 04)
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
---
|
||||
layer: as-is
|
||||
status: implemented
|
||||
code: [mesh-controller, mesh-catalog modules/claude-code, mesh-catalog modules/gitea, mesh-catalog modules/ssh-client]
|
||||
updated: 2026-10-17
|
||||
decisions:
|
||||
- "ADR 0266"
|
||||
- "ADR 0399"
|
||||
- "ADR 0409"
|
||||
- "ADR 0410"
|
||||
---
|
||||
|
||||
# Accounts on a machine, as they run
|
||||
|
||||
**A node record names at most two logins: the operator account, always, and an agent account, on one node of
|
||||
four. Where a node names no agent account, agents run as the operator account. Nothing ties an identity to a
|
||||
login, and a module that needs one declares it itself.** Read on 2026-10-17 from the controller's and the
|
||||
catalogue's code on main (both local clones were a few commits behind the forge, so each claim below was
|
||||
also checked against a live read) and from the live node records. The change planned in ADR 0411 to 0414
|
||||
replaces this document when it is built.
|
||||
|
||||
## The node record
|
||||
|
||||
- **The operator account is stated at the controller's terminal** with `node account <node> <account> [home]`.
|
||||
It is the person's login on that machine. The controller refuses to set it to the node's agent account.
|
||||
- **The agent account is stated at the controller's terminal only** with `node agent-account <node> <account>
|
||||
[home]`, and cleared with `--clear`. It is no verb and no setting, so an agent cannot name itself one
|
||||
(ADR 0266 §1). The controller refuses root, a malformed login, a well-known service account and the
|
||||
operator account.
|
||||
- **Five machine facts come from the record.** `account` and `account-home` are the operator's, absent when
|
||||
the node has no operator account. `agent-account` is the agent account, or the operator account where none is
|
||||
named; `agent-home` is its home (`/home/<account>` when no home is stated); `agent-root` is `never` only where
|
||||
the node names an agent account of its own, otherwise empty. Modules read them as `${machine:...}` words.
|
||||
- **One node of four names an agent account today**, the control node. The workstation, the laptop and the
|
||||
home server name none: `mesh-controller.node` says there "agents run as the operator account; no agent
|
||||
account is named", and the plan for the workstation declares the `claude-code` account resource with the
|
||||
operator's name and no `root` field. The tool runner also receives `MESH_AGENT_ACCOUNT` and `MESH_AGENT_HOME`.
|
||||
- **Not verified:** whether the mesh-config snapshot carries either account. The controller's code reads both
|
||||
from its own node table (columns for account, account home, agent account and agent home); no second place
|
||||
that states them was found.
|
||||
|
||||
## What claude-code does with the agent account
|
||||
|
||||
- **It declares a `user` resource** named `${machine:agent-account}` with home `${machine:agent-home}` and
|
||||
`root: ${machine:agent-root}`, and a `~/.claude` directory under that home owned by it, mode 0700. On the
|
||||
control node the plan holds the resource with `root: never`; elsewhere it holds the operator's account.
|
||||
Where the account is new, the mesh creates it with a home, its own group and no password (ADR 0266 §2).
|
||||
- **`claude-agent`** is a file the module writes at `/usr/local/bin/claude-agent`. Run as the agent account
|
||||
it starts `claude`; run as any other account it runs `sudo -iu <agent account> claude` through that
|
||||
account's own sudo, carrying the operator's guard override only if it is set in that shell. The file
|
||||
`/etc/claude-code/agent-account` holds the account's name.
|
||||
- **The guard of ADR 0266 §28** is the module's hook on every tool call. On a node with an agent account
|
||||
of its own it refuses each call of a session that runs as another account, with no override, and tells the
|
||||
session to start with `claude-agent`. Its record goes to the machine's journal. The same guard also refuses
|
||||
ssh to a mesh machine and writes to `/etc/hosts` and the resolver file (ADR 0245).
|
||||
- **Not verified live:** this rests on the guard's code and README, not on a refusal seen on a running machine.
|
||||
|
||||
## The judgement that the agent account cannot become root
|
||||
|
||||
- **The node-engine judges `root: never` on every look**, by reading only: uid 0, a group that grants root,
|
||||
a sudo, doas or polkit rule, a container runtime's socket or API port, a secret the mesh placed for another
|
||||
account, a setuid program no package owns, and kernel link protection off (ADR 0266 §12 to §16). A way found,
|
||||
or a question unanswered, makes the account resource unhealthy. It does not prevent any way to root.
|
||||
- **The controller's self-check holds** that a node naming an agent account has it judged, in a verdict heard
|
||||
within 15 minutes, as unable to become root (ADR 0266 §21 to §22). Otherwise it raises the urgent condition
|
||||
`agent-can-become-root`. `mesh-controller.node` shows the verdict: for the control node it reads "cannot
|
||||
become root without a person", judged the same day.
|
||||
- **On the three nodes without an agent account** the check does not apply. The controller instead holds an
|
||||
urgent condition `root-not-free` on each machine that runs reads or the mail notifier. Two of the three are
|
||||
silenced until 2026-10-23 on the operator's decision; the home server's is open and not silenced.
|
||||
|
||||
## The accounts agents push as on the forge
|
||||
|
||||
- **Today the forge has a user per machine**, made by the gitea module's `forge-account` provision and
|
||||
holding that machine's key, plus a write token the module still mints for each (ADR 0398, ADR 0400). ADR 0410
|
||||
decides one forge user, `nox-agent`, in the agents' write team, each machine's key titled by machine. No tool
|
||||
reads the forge's user list, so whether `nox-agent` exists yet is **not verified**.
|
||||
- **The forge key is meant for the agent account** (ADR 0409 §2), but a module cannot run as that account:
|
||||
the controller refuses it (issue 989, open). So the `ssh-client` module's key keeper still runs as the
|
||||
operator account.
|
||||
- **An ordering deadlock** between `ssh-client`, which wants `forge-account`, and `gitea`, which provides it,
|
||||
stopped every node's declaration from composing on 2026-10-17 at 00:35 until gitea was registered three
|
||||
minutes later (issue 986). The four urgent conditions it raised were not observed again but stay raised,
|
||||
because the controller's declaration check times out and never clears them (issue 992).
|
||||
Git over ssh was proven from the laptop only; the proof on the other three, and retiring the forge token the
|
||||
module still places, wait on issues 989 and 991 (the latter in `fixing`).
|
||||
- **Main stays closed**: a push to the main branch is refused, and a merge waits for a person (ADR 0398).
|
||||
|
||||
## Logins on the bus
|
||||
|
||||
Not here: see [`16-logins-on-the-bus.md`](16-logins-on-the-bus.md).
|
||||
|
||||
## What it cannot do
|
||||
|
||||
- **No identity carries a login.** An agent, a person and a module are not tied to an account on a machine;
|
||||
the node record names a login per node and nothing else.
|
||||
- **A module declares a login itself.** Each module that needs an account writes a `user` resource of its own,
|
||||
and nothing checks that two modules agree or that a login exists on every machine that needs it.
|
||||
- **Agent and operator share a login on three of four machines.** There the agent runs as the operator account,
|
||||
which may become root; the guard's account rule does not apply, and the exposure is a held condition, not a fix.
|
||||
- **No per-agent bus password.** Every session on a machine calls through that machine's tool-runner login
|
||||
(document 16), so one agent's calls cannot be told from another's.
|
||||
- **Detects, does not prevent.** The judgement above lists the ways to root it looks for; a way it does not list
|
||||
is not seen (ADR 0266 §17).
|
||||
|
||||
## How it is checked
|
||||
|
||||
- `mesh-controller.node` with a node name: "agents run as" line, the verdict and when it was judged, the open
|
||||
conditions (`agent-can-become-root`, `root-not-free`, `uncomposable`).
|
||||
- `mesh-controller.plan` with a node name: the `claude-code.agent-account` resource and its `root` field; with
|
||||
`files` set, the files given, including `claude-agent` and `agent-account`.
|
||||
- `mesh-controller.conditions` for the held conditions; `claude_code_guard` for the guard's record, `claude-code`
|
||||
tools for its status.
|
||||
- `knowledge-base.get` with `kind` decision and number 266, 409 or 410; `issue-tracker.get` with 989 or 991.
|
||||
- The code: the controller's node-record and agent-account files, and the `claude-code` module's manifest and
|
||||
guard in the catalogue. Their unit tests hold the node record's refusals and the guard's account rule.
|
||||
@@ -25,6 +25,7 @@ Where the two disagree, the implementation wins and the disagreement is stated.
|
||||
| [`14-a-modules-state.md`](14-a-modules-state.md) | A module's current state on the bus: what the controller creates, the runtime serves, and the first live use showed |
|
||||
| [`15-the-agent-and-its-licences.md`](15-the-agent-and-its-licences.md) | The operator's agent on every machine and the licence manager that keeps its licences |
|
||||
| [`16-logins-on-the-bus.md`](16-logins-on-the-bus.md) | The bus's one list of logins, a login per module assignment, persons limited to their tools, sessions calling through the tool runner's login, and no effect on any tool |
|
||||
| [`17-accounts-on-a-machine.md`](17-accounts-on-a-machine.md) | The operator and agent accounts on the node record, claude-code's own login and guard, the judgement that the agent account cannot become root, and the forge accounts agents push as |
|
||||
|
||||
## What these documents are not
|
||||
|
||||
|
||||
@@ -279,7 +279,8 @@ From research 057 (ADR 0411 to ADR 0414):
|
||||
Each milestone is one feature step (playbook 07), usable as soon as its delivery plan is done, and starts when
|
||||
the previous one's is. What it replaces is in the as-is documents
|
||||
[15 — The agent and its licences](../00-as-is/15-the-agent-and-its-licences.md) and
|
||||
[16 — Logins on the bus](../00-as-is/16-logins-on-the-bus.md). The issues are opened once the operator confirms
|
||||
[16 — Logins on the bus](../00-as-is/16-logins-on-the-bus.md), and for milestones 11 to 14
|
||||
[17 — Accounts on a machine](../00-as-is/17-accounts-on-a-machine.md). The issues are opened once the operator confirms
|
||||
the milestones, and their numbers written here.
|
||||
|
||||
**Confirmed by the operator on 2026-10-16.** One issue per milestone: 1 is issue 878, 2 is 879, 3 is 880, 4 is
|
||||
@@ -304,3 +305,18 @@ and the issue tracker takes only registered owners. The building waits until the
|
||||
|
||||
The front ends' later parts, the inference seat and the other agent harnesses are tracked by issue 850; the
|
||||
seat-sense "role" in older designs by issue 872.
|
||||
|
||||
**Milestones 11 to 14, from research 057, confirmed by the operator on 2026-10-17.** One issue per repository
|
||||
part, opened at the confirmation, so a fixing agent per repository can start when its step does. Milestone 11
|
||||
waits for nothing; 12 waits for milestone 6.
|
||||
|
||||
| # | Outcome, in one sentence | Proves | Repositories and their parts (issues) | Depends on |
|
||||
|---|---|---|---|---|
|
||||
| 11 | The agent's forge account is `nox`, with its keys and its place in the agents' write team kept | test 12 | mesh-catalog gitea: the rename, keys registered on `nox`, every `forge-account` entry (993) | — |
|
||||
| 12 | The mesh makes the operator's logins and nox's on every machine from the identities, and judges nox unable to become root everywhere | tests 6 to 9 | identity module: logins, machines, kept logins, the agent account mark, the operator's verb (994); the node-engine (`mesh-host`): existing logins adopted, every agent login judged (995); mesh-controller: machine accounts composed, node accounts read from the identities, the terminal lines retired (996); mesh-catalog: claude-code declares no login, the catalogue's check refuses one (997) | 6 |
|
||||
| 13 | The operator and nox each reach the bus with their own password, held to their role; the operator's only where no agent runs as the operator | test 11 | mesh-controller: `person.jochen` and `agent.nox`, secrets at 0600, the fifteen-minute verdict (999); identity module: nox's role refuses approvals and dangerous tools (998) | 12 |
|
||||
| 14 | The operator starts a claude-code session for nox or for themself, and nox pushes over SSH with its own key | test 10 | mesh-controller: run for, assignments keyed by identity, the refusals (1001); mesh-sdk: calls as the identity, a start goes down only (1002); claude-code: started for nox or jochen (1003); ssh-client and gitea: nox's own key, closing issue 989 (1004) | 11, 13 |
|
||||
|
||||
The agent's and the operator's accounts in Keycloak and mail (ADR 0414 §2 and §3) follow milestone 13 and are
|
||||
tracked by issue 1000, which needs a design section first. A second agent identity is made only when a rule
|
||||
needs another role (ADR 0414 §1); no milestone makes one.
|
||||
|
||||
Reference in New Issue
Block a user