From 95d78f7011cccea6ad0e975043b65e03bd6af8d4 Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 9 Oct 2026 11:59:34 +0200 Subject: [PATCH] ADR 0272 (proposed) and issue 343: the controller's terminal has no command The records send the operator to "the controller's terminal", and on the control-node `mesh-controller` is not a command: the controller is a mesh process with its own account and environment, and nothing puts anything on PATH. The record proposes mesh-cli, a module on every node whose program asks the node-engine over a local socket; the node-engine vouches for the account from the kernel and asks the controller on its node's own subject; the controller runs the line as the terminal only for the operator's account where no agent can be that account. The glossary gains mesh-cli, nox (its zsh alias) and the controller's terminal. --- 00-META/glossary.md | 15 +- ...nly-where-no-agent-runs-as-its-operator.md | 226 ++++++++++++++++++ .../00-report.md | 62 +++++ .../01-diagnosis.md | 75 ++++++ 4 files changed, 377 insertions(+), 1 deletion(-) create mode 100644 02-DECISIONS/0272-the-operator-reaches-the-controller-with-mesh-cli-on-every-node-and-a-node-is-the-terminal-only-where-no-agent-runs-as-its-operator.md create mode 100644 04-ISSUES/343-the-controllers-terminal-has-no-command/00-report.md create mode 100644 04-ISSUES/343-the-controllers-terminal-has-no-command/01-diagnosis.md diff --git a/00-META/glossary.md b/00-META/glossary.md index 9062b7fa..2a7dd534 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -272,6 +272,11 @@ and the operator act. **Recorded by** the controller's `identity` and `licences` `release`, an identifier to rename. - **proof** — what an authorising answer carries: a verified sender, a one-time code, a security key's touch (ADR 0234). How much proof a kind of ask needs is its **assurance level**. +- **controller's terminal** — a command line the controller runs as typed by its operator, not asked through a + verb, so a change that is the terminal's alone is taken (issue 339, issue 340). In the controller's code it is a + line run without `MESH_VERB`. The operator reaches it with `mesh-cli` from their own account on a node where no + agent can run as that account: the control-node, and a node whose agents run under an account of their own + ([ADR 0272](../02-DECISIONS/0272-the-operator-reaches-the-controller-with-mesh-cli-on-every-node-and-a-node-is-the-terminal-only-where-no-agent-runs-as-its-operator.md)). ## Change and delivery — a commit on its way to the nodes @@ -437,7 +442,7 @@ it should not. **Recorded by** the controller. **Upstream of** Provisioning and trust. **Recorded by** the router and the controller (authorising asks). **Downstream of** Health and repair, Change and delivery, and Identity and access. An authorising answer acts in whichever domain asked, through that domain's own verb; the conversation owns the -asking, never the act. **Decided by** ADR 0152, 0175, 0234. +asking, never the act. **Decided by** ADR 0152, 0175, 0234, 0272. **Uses:** operator, person, proof (Identity and access), release (Change and delivery). - **mesh MCP server** — the endpoint on a node's loopback through which every agent and person on that @@ -446,6 +451,14 @@ asking, never the act. **Decided by** ADR 0152, 0175, 0234. not a module of its own. "Console" suggested a terminal or a shell, and the module `mesh-console` it once named no longer exists; "the tool bridge" and "the brain" named the predecessor's program. *Not:* ~~console~~ (hq), ~~mesh-console~~, ~~tool bridge~~ +- **mesh-cli** — the module on every node that places the operator's command, and the command itself: + `/usr/local/bin/mesh-cli ` asks the controller to run one of its command lines. It asks the + node-engine on its own machine, which says to the controller which account is asking; the controller runs the + line as the **controller's terminal** or as an ordinary call + ([ADR 0272](../02-DECISIONS/0272-the-operator-reaches-the-controller-with-mesh-cli-on-every-node-and-a-node-is-the-terminal-only-where-no-agent-runs-as-its-operator.md)). +- **nox** — `mesh-cli`'s alias, in interactive zsh only. A script, `sudo` and a document meant to work everywhere + say `mesh-cli`. The operator also calls the mesh itself *nox mesh*; on this page and in the records *nox* is the + alias alone. - **channel / intake** — the two kinded benches the mesh talks to its operator through: `channel` sends, `intake` turns what arrives into one envelope. A holder of either declares **capabilities** from the fixed vocabulary `channel-capabilities/1`. Not *notifier* (that is the desktop's node seat, one diff --git a/02-DECISIONS/0272-the-operator-reaches-the-controller-with-mesh-cli-on-every-node-and-a-node-is-the-terminal-only-where-no-agent-runs-as-its-operator.md b/02-DECISIONS/0272-the-operator-reaches-the-controller-with-mesh-cli-on-every-node-and-a-node-is-the-terminal-only-where-no-agent-runs-as-its-operator.md new file mode 100644 index 00000000..172377fd --- /dev/null +++ b/02-DECISIONS/0272-the-operator-reaches-the-controller-with-mesh-cli-on-every-node-and-a-node-is-the-terminal-only-where-no-agent-runs-as-its-operator.md @@ -0,0 +1,226 @@ +--- +topic: the tiers +status: proposed +date: 2026-10-09 +deciders: jochen +reconstructed: false +extends: 02-DECISIONS/0181-the-operator-account-is-a-node-fact-and-a-home-is-a-placement-root.md +--- + +# 272. The operator reaches the controller with `mesh-cli` on every node, and a node is the controller's terminal only where no agent runs as its operator + +## Context + +Some changes to the mesh are made **at the controller's terminal** only: + +- the places and accesses a module's settings name, and the keys a provider's trust or a trusted file reads + ([issue 339](../04-ISSUES/339-a-verb-can-place-a-directory-at-the-machines-own/00-report.md)); +- the agent module's managed settings, tool servers and shared text + ([issue 340](../04-ISSUES/340-an-agent-can-plant-a-hook-in-every-agent-session-on-the-mesh/00-report.md)); +- a node's agent account (ADR 0266, hq pull request 225, not yet merged), typed as + `mesh-controller node agent-account `. + +In the controller's code "the terminal" is a negative: a command line that did not come through a verb. The +serving controller names the verb in the environment of every command it runs for one (`MESH_VERB`, issue 327), +and a terminal-only change is refused when that variable is set. Nothing says who the terminal is, and nothing +lets the operator reach it. + +On 2026-10-09 the operator typed `mesh-controller …` on the control-node and the shell answered +`zsh: command not found: mesh-controller` +([issue 343](../04-ISSUES/343-the-controllers-terminal-has-no-command/00-report.md)). The controller runs as a +mesh process: the node-engine unpacks its bundle under the mesh's own directory, and writes a unit that runs it as +the `mesh-controller` account with an environment composed from the controller's module (its stores' credential +files under a directory only that account may enter, the stores' and the bus's ports). Nothing puts a command on +any `PATH`, and running the binary by hand would mean rebuilding that environment under `sudo`. + +The operator's directions of 2026-10-09 shape what follows: no wrapper script and no link (the mesh creates no +symbolic links); the operator's command is a real program, its own module, on every node; it reaches the +controller over the mesh, not by running the controller's binary; and the person is proven by the account the +command runs from, vouched for by the operating system, never by a credential file a copy of which is as good as +the original. + +## Considered Options + +### How the operator reaches the controller + +1. **A link to the controller's binary on `PATH`.** Refused by the hard rule, and not enough: the binary needs the + unit's account and environment. +2. **A wrapper the controller's module places on the control-node**, running the controller's binary as its + account with the unit's environment through `sudo`. Rejected. It is a second copy of the unit's environment to + keep in step (or a new process field for the node-engine to render), it reaches the controller from the + control-node only, and its proof is `sudo`, which every agent on the control-node holds until ADR 0266 is + switched on. +3. **A program on every node that asks the controller over the mesh.** Chosen. The controller already runs every + command line a verb asks for in a fresh process of its own binary, with its own environment (`runVerb`); one + more way of asking needs no copy of that environment anywhere. + +### Where the program comes from + +1. **A second artifact of the controller's module.** Rejected: that module is assigned to the control-node only, + so its files reach no other node. +2. **A module of its own, assigned to every node.** Chosen. It builds from its own directory, so a change to it + rebuilds it alone and a change to the controller does not rebuild it + ([ADR 0267](0267-an-artifact-is-rebuilt-only-when-its-build-source-changes-and-a-module-moves-only-along-build-dependencies-on-artifacts.md)). + +### How the person is proven + +1. **A credential file in the operator's home.** Rejected. Any agent running as the operator reads it, and a copy + works anywhere. +2. **`sudo`.** Rejected as the proof. It asks the account's password, or nothing where the rule says so, and + agents on the control-node hold the same `sudo` today. +3. **The account the program runs from, read by the node-engine from the kernel.** Chosen. The program connects to + a local socket the node-engine serves; the kernel tells the node-engine the connecting process's user id + (peer credentials). Nothing is presented, so nothing can be copied. The node-engine is on every node and runs + as root, so it is the one part of the mesh that can vouch for a local account. +4. **A second factor**: a one-time code or an approval on a verified channel + ([ADR 0234](0234-the-mesh-holds-a-conversation-with-its-operator.md), ADR 0259). Not now. It is the route for + a node whose agents must share the operator's account, and it waits for the approval flow (research 039). + +### Which account on which node is the terminal + +1. **The operator's account on any node.** Rejected. On every node but the control-node, and on the control-node + until ADR 0266 is switched on, agents run as that account. An agent would be the terminal. +2. **The operator's account on the control-node only.** Safe, and too narrow for later. +3. **The operator's account on a node where agents cannot be that account**: the control-node, and any node that + names an agent account of its own judged unable to become root without a person (ADR 0266 §1, §3, §4). + Chosen. + +## Decision + +### 1. `mesh-cli` is a module and the command it places + +**`mesh-cli` is a module of the catalogue, assigned to every node.** It builds one Go bundle from its own +directory, and an `archive` resource unpacks it into `/usr/local/bin`, so the program is +`/usr/local/bin/mesh-cli` on every node. It is the bundle's own file: no link, no second copy. The node-engine +moves it in beside what is there, refuses to write over a file it did not place, and takes out only what it +placed when the module goes. + +**`nox` is its alias**, `alias nox=mesh-cli`, contributed by the module to zsh's `normal` slot +([ADR 0204](0204-a-module-contributes-shell-code-to-the-login-shell-in-named-slots.md)). `/usr/local/bin` is +already on every account's `PATH`, so the module contributes no environment. The alias exists in interactive zsh +only: a script, `sudo`, a cron line and any document meant to work everywhere say `mesh-cli`. + +### 2. What the operator types + +`mesh-cli ` takes the controller's own command line, in the words the controller's commands and +their refusals already use: `mesh-cli settings set zsh '{"execute":"withhold"}' --node ` is +`settings set zsh … --node ` asked of the controller. It is run as the operator's own account, without +`sudo`: root is not the operator's account, and is answered as any other account (§4). + +`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. + +### 3. The node-engine vouches for the account + +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..cli`, as a request on + core NATS. Only that node's engine may publish under `mesh.control..`: 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. says with it whether the node's declaration names an agent account, and the engine's latest judgement of it + (ADR 0266 §3); +4. answers the program with what the controller answered, or says that the controller did not answer within its + call bound. + +The node-engine is trusted for its own node. Root on a node could claim any account there; root on a node that is +the terminal is already the operator's, and §4 makes no other node the terminal. + +### 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. + +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*. + +**Every other line is an ordinary call.** It goes through exactly what the `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: an agent account, root, an account that is not the operator's, or a node +whose agents may run as its operator. + +**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. + +### 5. What comes back + +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. + +### 6. What is shared lives in the SDK + +The socket's path, the program's request and answer, and the request on the bus with its subject are one package +of the SDK (`mesh-sdk`), imported by `mesh-cli`, the node-engine and the controller. None of them keeps its own +copy. The list of what is terminal-only is not shared: it stays in the controller, its one judge, and `mesh-cli` +shows the controller's words. + +### 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. +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). +3. **Later.** Streamed output and standard input; a second factor for a node whose agents must share the + operator's account. + +## Consequences + +- The operator has one command on every node, built, checked and moved like every other module, and the records' + "at the controller's terminal" names something that exists: `mesh-cli` (or `nox`) on the control-node. +- A terminal-only change typed on any other node is refused there with the reason, instead of being impossible to + type at all. +- No agent on the control-node gains anything it did not have; once ADR 0266 is switched on there, agents lose the + terminal entirely. +- **Same-account limits stay ADR 0266's boundary.** While an agent runs as the operator, it can do what the + operator's account can: put a `mesh-cli` of its own earlier on `PATH` (the zsh module puts the account's + `~/.local/bin` first), or redefine the alias outside the mesh's block. On a node where agents run as the + operator, the operator's account is not the terminal, so this reaches nothing terminal-only there. +- 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 listener every local account may reach. It accepts one line per connection, bounded in + size and in time, and runs nothing itself. +- The controller's own binary is still never on `PATH`. A record that says to type + `mesh-controller ` at the terminal means `mesh-cli ` on the control-node from this record on. + +## How it is checked + +- **The SDK:** the request and the answer survive a round trip, and the subject names the node. +- **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 any other account, or from another node without a judged agent account, runs with + `MESH_VERB=mesh-cli` and a terminal-only change is refused with the reason; 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 ` 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. + +## References + +- [Issue 343](../04-ISSUES/343-the-controllers-terminal-has-no-command/00-report.md): the gap. +- [Issue 339](../04-ISSUES/339-a-verb-can-place-a-directory-at-the-machines-own/00-report.md), + [issue 340](../04-ISSUES/340-an-agent-can-plant-a-hook-in-every-agent-session-on-the-mesh/00-report.md): what is + terminal-only, and how a verb is told from the terminal. +- [ADR 0181](0181-the-operator-account-is-a-node-fact-and-a-home-is-a-placement-root.md): the operator account. +- [ADR 0204](0204-a-module-contributes-shell-code-to-the-login-shell-in-named-slots.md): shell contributions. +- [ADR 0267](0267-an-artifact-is-rebuilt-only-when-its-build-source-changes-and-a-module-moves-only-along-build-dependencies-on-artifacts.md): + a module builds from its own source. +- [ADR 0234](0234-the-mesh-holds-a-conversation-with-its-operator.md): proof and assurance. +- ADR 0266 (hq pull request 225) and ADR 0259 (hq pull request 211): not yet merged, so named without links. diff --git a/04-ISSUES/343-the-controllers-terminal-has-no-command/00-report.md b/04-ISSUES/343-the-controllers-terminal-has-no-command/00-report.md new file mode 100644 index 00000000..34948b33 --- /dev/null +++ b/04-ISSUES/343-the-controllers-terminal-has-no-command/00-report.md @@ -0,0 +1,62 @@ +--- +status: located +opened: 2026-10-09 +located-in: [mesh-catalog (a new module, mesh-cli), mesh-host (a local socket the node-engine serves), mesh-controller (the answer on the bus), mesh-sdk (the shared request)] +fixed-by: +amended-design: +--- + +# 343. The controller's terminal has no command + +## Symptom + +On 2026-10-09 the operator, on the control-node in their own shell, typed a `mesh-controller` command line to make +a change the records say is made at the controller's terminal. The shell answered: + + zsh: command not found: mesh-controller + +The same day, read through the mesh's tools: the controller runs on the control-node as the unit +`mesh-controller.service`, which the mesh declared and wrote (the control-node's `node-service-manager.status`: active, running, +`FragmentPath` `/etc/systemd/system/mesh-controller.service`, `mesh_declared` true). + +## Why it is an issue + +**The records name a command that does not exist.** Issue 339, issue 340, ADR 0266 (hq pull request 225, not yet +merged) and the agent module's README say that certain changes are made "at the controller's terminal" alone, and +some give the line to type, such as `mesh-controller settings set claude-code ''` and +`mesh-controller node agent-account `. None of them says how the operator reaches that terminal, and +nothing in the mesh provides it: + +- The controller is a mesh process. The node-engine unpacks its bundle under the mesh's own directory and writes a + unit that runs it as the `mesh-controller` account, with an environment composed from the controller's module: + the files holding its stores' credentials, under a directory only that account may enter, and the ports of the + store and the bus as the mesh resolved them. No module puts a command on any `PATH`. +- Run by hand, the binary needs that account and that environment, rebuilt by hand under `sudo`. That is a + work-around, and the operator rejects it. +- In the controller's code the terminal is only a negative: a command line that did not come through a verb (no + `MESH_VERB`). Nothing says who the terminal is. + +**Impact on the operator.** Every terminal-only change waits: the agent module's managed settings, tool servers and +shared text (issue 340's steps), the places and accesses of issue 339, and a node's agent account (ADR 0266). The +refusals the controller gives through a verb send the operator to a terminal they cannot open. + +## Acceptance test (the operator's view) + +On the control-node, in my own shell and without `sudo`, I type `nox settings set zsh '{"execute":"withhold"}' +--node ` (or any other command line) and it runs against the live controller as the terminal. On any other +node the same terminal-only line is refused and says why. An agent session cannot use it to get round a +terminal-only refusal: as `agent` on the control-node it is refused, and on a node where agents run as the operator +that account is not the terminal. + +## The fix + +[ADR 0272](../../02-DECISIONS/0272-the-operator-reaches-the-controller-with-mesh-cli-on-every-node-and-a-node-is-the-terminal-only-where-no-agent-runs-as-its-operator.md) +(proposed): a module `mesh-cli` on every node places `/usr/local/bin/mesh-cli`, with the alias `nox` in +interactive zsh. It asks the node-engine over a local socket; the node-engine reads the account from the kernel and +asks the controller on its own node's subject; the controller runs the line as the terminal for the control-node's +operator account, and as an ordinary call for every other account and node. The diagnosis says what was read and +what each repository does. + +## How it will be checked + +The checks ADR 0272 lists, and the acceptance test above, run live on the control-node and on one other node. diff --git a/04-ISSUES/343-the-controllers-terminal-has-no-command/01-diagnosis.md b/04-ISSUES/343-the-controllers-terminal-has-no-command/01-diagnosis.md new file mode 100644 index 00000000..22e038ec --- /dev/null +++ b/04-ISSUES/343-the-controllers-terminal-has-no-command/01-diagnosis.md @@ -0,0 +1,75 @@ +# 343 — Diagnosis + +## 2026-10-09: what was read + +The record was searched first: `records_search` for "command not found mesh-controller" found nothing, and for +"controller's terminal" found issue 340 and research 039, which use the words and say nothing of how the terminal +is reached. + +### The controller + +- Its module declares one process, run as the `mesh-controller` account, `./mesh-controller serve`, with an + environment of credential files under its own directory (mode 0700, owned by that account) and the ports of the + store and the bus as `${seat:…}` references the controller resolves at composition. +- `throughAVerb` (`cmd/mesh-controller/modules.go`) is the whole of the terminal: a command line runs as the + terminal when `MESH_VERB` is not in its environment. `runVerb` (`cmd/mesh-controller/seatverbs.go`) sets it for + every line a verb runs, the named verbs and the generic `command` alike, and runs the line in a fresh process of + the controller's own binary with the serving process's environment. +- The generic `command` verb runs any command line today, except `settings set` and `settings clear` and a repair + without `--why`. ADR 0266 §7 makes it a list of reads; that is not built. + +### The node-engine + +- It writes a process's unit under `/etc/systemd/system`, and unpacks its bundle into + `/var/lib/mesh/daemons/`. A witnessed process's running build is moved aside when the next is placed, so + the path holds the build the unit runs, not always the build that is running (issue 289). +- It runs as root, and holds one bus account per node, granted to publish under `mesh.control..` and + nothing of another node's. It knows nothing of the node's operator account; the controller does (the node + record's `account`). +- An `archive` resource unpacks a bundle into a directory, moves its files in one by one where the directory was + not made by the mesh, refuses to write over a file it did not place, and on removal takes out only what it + placed. The node-engine's own module places its versions this way. +- No part of the mesh serves a local socket that reads a peer's credentials. + +### The bus + +- The controller subscribes `mesh.control.>` and may answer a request. The CONTROL stream names its subjects + (`mesh.control.*.report`, `mesh.control.enrol`), so a request on another subject under the prefix is not + persisted. + +## Ruled out + +- **A link to the binary on `PATH`.** The mesh creates no symbolic links, and the binary needs the unit's account + and environment. +- **A wrapper the controller's module places.** Designed first, on 2026-10-09: a file under `/usr/local/bin` + that runs the binary through `sudo` with the unit's environment, or a new process field from which the + node-engine writes one. Rejected by the operator: it is a work-around, it is a second copy of the unit's + environment, it reaches the control-node only, and its proof is `sudo`, which agents on the control-node hold. +- **The program as a second artifact of the controller's module.** That module is on the control-node only. + +## Located + +Four repositories, in the order they merge: + +1. **mesh-sdk:** the socket's path, the program's request and answer, the bus request and its subject. +2. **`mesh-host`:** the node-engine serves the socket, reads `SO_PEERCRED`, names the account, and asks the + controller on `mesh.control..cli`. +3. **mesh-controller:** the serving controller answers `mesh.control.*.cli`, judges the terminal (ADR 0272 §4), + and runs the line through `runVerb`'s path, without `MESH_VERB` for the terminal and with + `MESH_VERB=mesh-cli` otherwise, after the `command` verb's refusals. +4. **mesh-catalog:** the module `mesh-cli` (its program, its archive into `/usr/local/bin`, its zsh alias `nox`), + assigned to every node; and the agent module's README, whose `mesh-controller settings set …` becomes + `mesh-cli settings set …`. + +Each code change is a branch and a pull request, merged by the operator after ADR 0272 is accepted. The records +that tell the operator to type `mesh-controller ` at the terminal mean `mesh-cli ` on the +control-node once phase 1 is live; ADR 0266's `mesh-controller node agent-account` line is changed on its own +branch, which is not yet merged. + +## Later + +- Phase 2 of ADR 0272: a node other than the control-node becomes the terminal for its operator account once its + agents run under an account of their own (ADR 0266), judged. +- A node whose agents must share the operator's account (the operator's own machines) needs proof that it is the + operator: a second factor, or an approval on a verified channel (ADR 0234, ADR 0259, research 039). Not built + here.