Files
mesh-catalog/modules/mesh-cli
jochen 2594a24b35
mesh/merge-gate pass: builds claude-code, mounts, route-adapter, new: modules/mesh-cli → ace, g14, novox, shanks; no bus step; every machine composes with …
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
mesh-cli: the operator's command on every node, nox in zsh (hq ADR 0272)
The records send the operator to "the controller's terminal", and on the
control-node nothing answered (hq issue 343). mesh-cli is a module of its
own, assigned to every node and built from its own directory: its program
is unpacked into /usr/local/bin by an archive resource (one file, no link,
no copy), and its zsh contribution makes nox its alias. It holds no
credential: it asks the node-engine on its machine over the SDK's go/cli
socket, and prints the controller's answer with its exit.

The agent module's refusals and README, and the route adapter's README, now
name `mesh-cli settings set …` on the control-node instead of a
mesh-controller command nobody could type; a settings file is passed inline,
since a path would be read where the controller runs.
2026-10-09 12:11:21 +02:00
..

mesh-cli

The operator's command on every node (novox/hq ADR 0272): mesh-cli <command line> asks the controller to run one of its command lines, from wherever the operator is. In interactive zsh it is also nox.

nox status
nox settings show zsh --node <node>
nox settings set zsh '{"execute":"withhold"}' --node <node>

What it places

What Where How
the program /usr/local/bin/mesh-cli the program bundle, unpacked by an archive resource: the node-engine moves the one file in, refuses to write over a file it did not place, and takes out only that file when the module goes
the alias nox the zsh module's mesh block in ~/.zshrc a shell contribution in the normal slot (ADR 0204)

No link and no second copy. /usr/local/bin is on every account's PATH already, so the module contributes no environment. The alias is interactive zsh only: a script, sudo, a cron line and any document meant to work everywhere say mesh-cli. In interactive zsh the alias hides any other nox (Arch's python-nox installs /usr/bin/nox); command nox still reaches it.

How it is answered

mesh-cli holds no credential and reads no file of the mesh. It writes one request to the node-engine's socket (the SDK's go/cli: the line, as words) and prints the one answer.

  1. The node-engine reads which account is asking from the kernel, never from the program, and asks the controller on its own node's subject.
  2. The controller judges (ADR 0272 §4):
    • the controller's terminal: the node's operator account, on the control-node. The line runs as the operator typed it there.
    • an ordinary call: the node's operator account on any other node, where agents may run as that account. It meets every refusal of the generic command verb, and a change made at the terminal alone is refused with the reason.
    • refused: any other account, root included. Run it as yourself, not under sudo.
  3. mesh-cli prints standard output and standard error as the command wrote them, says why a line was not the terminal or was refused, says when the output was cut to fit one bus message, and exits with the command's status.

A process that runs for a verb (it carries MESH_VERB) is refused before anything is asked: a verb is never the operator.

What it does not do yet

  • No standard input. A command that reads one gets nothing, and fails saying so.
  • No local files. A path in a line names a file where the controller runs, read by the controller's account. Give settings inline: nox settings set <module> "$(cat settings.json)" --node <node>.
  • No streaming. The answer arrives when the command ends, within the controller's bound on a call (five minutes). Interrupting mesh-cli stops the wait, not the command.
  • No other node is the terminal. Phase 2 of ADR 0272 adds a node whose agents run under an account of their own (ADR 0266), judged unable to become root.

Tests

go test ./...: the line is asked as given and the answer printed with its exit; a reason, a refusal and a cut are said and a refusal never exits 0; a verb's process is refused before asking; an engine that does not answer is said.