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
- §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:
+112
-55
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user