ADR 0272: the review's answers — every line a call, the socket grants nothing, the terminal does not follow assign, each claim and how it is checked
mesh/merge-gate pass: the change touches no module of the mesh's graph
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request

- §3: the node-engine vouches and decides nothing; deadline first, other
  namespaces refused, each account bounded and answered busy; the subject
  is the machine's own in the writers table.
- §4: phase 1 refuses the agent account (phase 2 answers it); an ordinary
  settings line takes the settings verb's path; the controller's module is
  assigned at the terminal only, so the terminal does not follow assign;
  'no wider than today' holds only while its two measured facts hold.
- §5: every line is a call, followed to its answer; 'nothing ran' only of
  a line nobody took, otherwise 'not known' with the call; the answer is
  its asker's alone.
- §7: after the four merge, the operator assigns mesh-cli to every node.
- How it is checked: each claim with its test or measurement, the
  MESH_VERB refusal in mesh-cli named a courtesy, and the sudo and
  agent-account facts read on the control-node today.
This commit is contained in:
jochen
2026-10-09 12:56:44 +02:00
parent 41060224e6
commit bac7feca77
@@ -109,20 +109,28 @@ their refusals already use: `mesh-cli settings set zsh '{"execute":"withhold"}'
`mesh-cli` holds no credential and reads no file of the mesh. It does one thing: it asks the node-engine on its own
machine over a local socket, and prints what comes back. A process that carries `MESH_VERB` (a verb's own process)
is refused at once, and told why: a verb is never a person.
is refused at once, and told why. **That refusal is a courtesy, not the gate**: a process can unset a variable.
The gate is the controller's judgement of the account the node-engine vouches for (§4).
### 3. The node-engine vouches for the account
### 3. The node-engine vouches for the account; the socket grants nothing
The node-engine serves a socket every local account may connect to. For each connection it:
**The socket grants nothing; the controller judges.** What the node-engine adds is a fact only it can read, the
asking account, and it decides nothing with it. The node-engine serves a socket every local account may connect
to. For each connection it:
1. reads the connecting process's user id from the kernel (`SO_PEERCRED`), and names the account from the
machine's user database;
2. sends the line, the account and the user id to the controller on `mesh.control.<node>.cli`, as a request on
core NATS. Only that node's engine may publish under `mesh.control.<node>.`: its bus account is granted that
prefix and nothing of any other node's, so **the node is a fact the bus server enforces and the account is a
fact the kernel gives**. The CONTROL stream names its own subjects, so the request is never persisted;
3. answers the program with what the controller answered, or says that the controller did not answer within its
call bound.
1. sets the connection's deadline before anything else, and reads the connecting process's user id and process id
from the kernel (`SO_PEERCRED`);
2. refuses a process whose mount or user namespace is not the engine's own: a container running with the
operator's user id shares the number, not the machine;
3. takes one of at most two lines at once for that user id and eight for the machine, and answers *busy* at once
when there is none, so one account cannot hold the socket from another and accepting never waits;
4. names the account from the machine's user database, reads the line, and sends the line, the account and the
user id to the controller on `mesh.control.<node>.cli`, as a request on core NATS. Only that node's engine may
publish under `mesh.control.<node>.`: its bus account is granted that prefix and nothing of any other node's,
and the writers table names the subject as the machine's own, so **the node is a fact the bus server enforces
and the account is a fact the kernel gives**. The CONTROL stream names its own subjects, so the request is
never persisted;
5. follows the line to what came of it (§5) and answers the program.
Whether the node's agents run under an account of their own is not in the request. The node-engine already
says it: its judgement of the agent account is in its health statement (ADR 0266 §3, §4), and the controller reads
@@ -135,45 +143,64 @@ the terminal is already the operator's, and §4 makes no other node the terminal
rule stands. The socket is a file under the node-engine's runtime directory with no network address, so nothing
dials it from another machine. Nothing sent to it acts on the node: the node-engine runs nothing it receives and
changes nothing; it adds what the kernel says and sends the request out over its own link, to a controller that
judges it like any other caller. Only the node's operator account and its agent account are answered, and both
already reach the mesh's verbs through the mesh MCP server (§4).
judges it like any other caller. Only the node's operator account is answered in phase 1 (§4), and it already
reaches the mesh's verbs through the mesh MCP server.
### 4. The controller judges, and runs the line
A line from node N, vouched as account A, **runs as the controller's terminal** when A is N's operator account
([ADR 0181](0181-the-operator-account-is-a-node-fact-and-a-home-is-a-placement-root.md)) and either:
- N is the control-node; or
- N names an agent account of its own (ADR 0266 §1), and the controller's own record of the engine's judgement,
heard within ADR 0266 §4's fifteen minutes, says it cannot become root without a person.
- N is the control-node: the one node the controller's module is assigned to. Two or more, and nobody is the
terminal; or
- (phase 2) N names an agent account of its own (ADR 0266 §1), and the controller's own record of the engine's
judgement, heard within ADR 0266 §4's fifteen minutes, says it cannot become root without a person.
The controller runs it as it runs a verb's line, in a fresh process of its own binary with its own environment, but
without `MESH_VERB`, and records the caller as *A through mesh-cli on N*.
**A line from the node's operator account or its agent account that is not the terminal is an ordinary
call.** It goes through exactly what the `command` verb does with a line: its refusals today, and its list of
**Which node is the control-node does not follow a verb.** Whoever chooses where the controller's module runs
chooses the terminal, so the module is assigned and unassigned at the controller's terminal only; `assign` and
`unassign` naming it through a verb are refused with the reason.
**A line from N's operator account that is not the terminal is an ordinary call.** A `settings` line is composed as
the settings verb composes its own and meets that verb's refusals, the terminal-only keys among them; any other
line goes through exactly what the generic `command` verb does with a line: its refusals today, and its list of
reads once ADR 0266 §7 builds one. It runs with `MESH_VERB=mesh-cli`, so every terminal-only change is refused
with the controller's own reason. The answer adds one sentence saying why this account on this node is not the
terminal: it is the agent account, or the node's agents may run as its operator. These two accounts already
reach the mesh's verbs through the mesh MCP server, so this widens nothing.
terminal: the node's agents may run as its operator. That account already reaches the mesh's verbs through the
mesh MCP server, so this widens nothing.
**A line from any other account is refused**, root and every service account included, and the answer says
which account asked and that `mesh-cli` answers the node's operator and agent accounts only. Run as yourself,
not under `sudo`.
**A line from any other account is refused, and runs nothing**: root, every service account, and **in phase 1 the
agent account too**. The answer says which account asked and that `mesh-cli` answers the node's operator account.
Phase 2 answers a node's agent account as an ordinary call (§7).
**The control-node rule is no wider than today.** On the control-node the operator's account holds `sudo`, and
until ADR 0266 is switched on so do the agents running as it: either could run the controller's binary as root
with its environment. `mesh-cli` gives that account the same reach and no more. Once agents there run as `agent`,
`agent` is never the terminal.
**The control-node rule is no wider than today, while today lasts.** On the control-node the operator's account
holds passwordless `sudo`, and until ADR 0266 is switched on agents run as that account: either could run the
controller's binary as root with its environment, which is the terminal. `mesh-cli` gives that account the same
reach and no more. Once agents there run as `agent`, `agent` is refused. The claim rests on two measured facts and
fails if one changes alone: if the operator's account lost its passwordless `sudo` while agents still ran as it,
`mesh-cli` would give those agents a terminal they no longer had (§ How it is checked).
### 5. What comes back
**Every line is a call**, run, recorded and answered as a seat's verb is: the bus lets a request be answered for a
minute only, so a line answered when it ended lost every answer after that and was said as nothing having run. The
controller answers within ten seconds that a line is still running, with its call; the node-engine follows the
call, as the same account on the same node, until the line ends. The call's record keeps the line's command word
and who asked, never the rest of the line, and the controller's journal says each line, its call, who asked where
and how it ran. **The answer is its asker's alone**: kept in the memory of the controller that ran it for the asker
to follow, never on the bus, and never shown by `calls`, which answers anyone who may call the seat. Lines running
at once are bounded (eight); one over is answered busy, and nothing runs.
The answer is the command's standard output, its standard error, its exit status, whether it ran as the terminal,
and why or why not. `mesh-cli` prints the two streams and exits with the status. An answer larger than one bus
message is cut there and the cut is said; it is never silently short. Standard input is not carried: a command
that reads it gets nothing and fails saying so. Streaming the output and carrying the input are later steps.
A path in a line names a file where the controller runs, read by the controller's account, never a file beside
`mesh-cli`: settings are given inline, `"$(cat settings.json)"` in the operator's own shell.
and why or why not. `mesh-cli` prints the two streams and exits with the status. It says one of three things,
never confused: what the line printed; that **nothing ran**, and why, only of a line no controller took; or that
**what came of it is not known** — no answer in time, so it may have run — naming the call to look at. An answer
larger than one bus message is cut there and the cut is said; it is never silently short. Standard input is not
carried: a command that reads it gets nothing and fails saying so. Streaming the output and carrying the input
are later steps. A path in a line names a file where the controller runs, read by the controller's account, never
a file beside `mesh-cli`: settings are given inline, `"$(cat settings.json)"` in the operator's own shell.
### 6. What is shared, and where it lives
@@ -191,11 +218,13 @@ A path in a line names a file where the controller runs, read by the controller'
### 7. In what order it is built
1. **Phase 1.** The SDK package; the node-engine's socket and its request; the controller's answer, with the
control-node as the terminal for its operator account; the `mesh-cli` module on every node. From then on the
operator types `nox …` on the control-node, and gets the ordinary calls on every other node. An agent account
is answered as soon as a node names one (ADR 0266 §1), whichever phase is live.
2. **Phase 2.** The agent-account route of §4 on a node other than the control-node, once ADR 0266 runs there
(on the operator's own machines after the follow-up decision ADR 0266 names).
control-node as the terminal for its operator account; the `mesh-cli` module. **After the four merge, the
operator assigns `mesh-cli` to every node** at the terminal; until then it is on no node. From then on the
operator types `nox …` on the control-node, and gets the ordinary calls on every other node. The agent account
is refused.
2. **Phase 2.** The agent account: answered as an ordinary call on a node that names one (ADR 0266 §1), and the
agent-account route of §4 to the terminal on a node other than the control-node, once ADR 0266 runs there (on
the operator's own machines after the follow-up decision ADR 0266 names). Its tests are written with it.
3. **Later.** Streamed output and standard input; a second factor for a node whose agents must share the
operator's account.
@@ -214,29 +243,57 @@ A path in a line names a file where the controller runs, read by the controller'
- The alias hides any other `nox` in interactive zsh (Arch's `python-nox` installs `/usr/bin/nox`);
`command nox` still reaches it.
- The node-engine gains a local socket every account on the machine may reach. It accepts one line per
connection, bounded in size and in time, runs nothing itself, and is never reachable from another machine
(ADR 0004 stands, §3).
connection, bounded in size, in time and per account, runs nothing itself, and is never reachable from another
machine (ADR 0004 stands, §3).
- Assigning the controller's module moves to the terminal: an agent, or any caller of `assign`, can no longer move
the controller, and so the terminal, to a node of its choosing.
- The controller's own binary is still never on `PATH`. A record that says to type
`mesh-controller <command>` at the terminal means `mesh-cli <command>` on the control-node from this record on.
## How it is checked
- **The SDK:** the request and the answer survive a round trip, and a request over the bound is refused.
- **The bus request:** a test in the node-engine and one in the controller hold the same field names.
- **The node-engine:** a test connects to the socket as the test's own process and the request carries that
process's user id and account; a connection whose credentials cannot be read is refused, never sent as
anyone; a line over the bound is refused.
- **The controller:** tests hold that a line from the control-node's operator account runs without `MESH_VERB`;
that a line from the agent account, or from another node's operator account without a judged agent account,
runs with `MESH_VERB=mesh-cli` and a terminal-only change is refused with the reason; that a line from any
other account, root included, is refused and runs nothing; that a judgement older than fifteen minutes is not
one; and that an ordinary line meets every refusal of the `command` verb.
- **The catalogue:** the module's check passes, its archive places one file, and its shell contribution is the
alias.
- **Live, at acceptance (the operator's view):** on the control-node, in the operator's own shell,
`nox settings show zsh --node <node>` answers as the terminal, and a terminal-only `nox settings set …` is taken.
On another node, the same `settings set` is refused and says why. From `agent`, once that account exists, it is
refused too.
Each claim, and what holds it:
- **The account is the kernel's** (§3): the node-engine's test connects as the test's own process and the request
carries that process's user id and account; credentials that cannot be read are refused and never sent as
anyone (mesh-host `internal/meshcli`).
- **The socket grants nothing; the controller judges** (§3, §4): the node-engine's package for it runs no command
and changes no file — its one act is the forward, which review holds, and its tests hold every refusal before
it. The controller's judge is tested case by case: the control-node's operator
account runs without `MESH_VERB` (the test reads the variable in the process it ran); another node's operator
account runs with `MESH_VERB=mesh-cli`; every other account, root and the agent account included, is refused
and runs nothing; two control-nodes make nobody the terminal (mesh-controller `cmd/mesh-controller`, the
mesh-cli tests).
- **The node is the bus server's fact**: the writers table names `mesh.control.*.cli` as the machine's own, and
its test refuses another machine or a module granted it (mesh-controller `internal/broker`).
- **Another namespace is refused, and one account cannot hold the socket**: the node-engine's tests refuse a peer
whose namespaces differ, and answer a second line of a busy account *busy* at once while the first is still
asked.
- **An ordinary `settings` line takes the settings verb's path**: the controller's test composes each form as the
verb does and refuses any other; an ordinary repair without `--why` meets the generic verb's refusal.
- **The control-node does not follow a verb**: the controller's test refuses assigning or unassigning its module
with `MESH_VERB` set, and lets the terminal do it.
- **A line is never lost, and never said as nothing having run when it may have**: the controller's test follows a
line that outlasts the answer window to its answer, over a bus, and refuses a follow by another account or
node; the node-engine's tests follow a running line to its answer, say *not known* with the call when no
answer comes, and say *nothing ran* only of a request nobody took. A line's record keeps neither its line's
values nor its answer on the bus, and `calls` never shows its answer (mesh-controller `internal/link`).
- **The `MESH_VERB` refusal in `mesh-cli` is a courtesy**: its test holds that the program refuses before asking;
nothing rests on it, since the controller judges the vouched account whatever the program does.
- **The module places one file**: the catalogue's test holds `mesh-cli` to one archive resource unpacking one
compiled Go bundle into `/usr/local/bin`; its shell contribution is the alias.
- **No wider than today, while today lasts** (§4), measured on the control-node, not assumed: the `sudo` module's
own check lists the operator account's rules (on 2026-10-08 it found every command as root without a password,
ADR 0266's context), and `mesh-controller.node` for the control-node shows whether an agent account is named.
Both are read before phase 1 is delivered and said in its pull request. Read on 2026-10-09: the `sudo` module's
`sudo_rules` on the control-node answered `passwordless_all: true` for the operator account (two rules, every
command as root, no password), and `mesh-controller.node` named no agent account there. Neither is checked by the controller at
each line: if the operator account's passwordless `sudo` goes while agents still run as it, this claim is false,
and the remedy is ADR 0266's agent account, not a check here.
- **Live, at acceptance (the operator's view):** after `mesh-cli` is assigned to every node, on the control-node,
in the operator's own shell, `nox settings show zsh --node <node>` answers as the terminal, and a terminal-only
`nox settings set …` is taken. On another node, the same `settings set` is refused and says why. A line that
runs longer than a minute (`nox push <node>`) is followed to its answer.
## References