mesh-cli requires the console's endpoint and writes its address

nox now reaches the mesh through this machine's console, so the module
requires mcp-endpoint and writes the bound address to /etc/mesh-cli/console
rather than trusting a fixed port. No invokes: mesh-cli has no bus account,
and one nothing dials would only be minted.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
2026-11-02 10:42:03 +01:00
parent 5cdf8295f3
commit a3e9f2cc0f
5 changed files with 104 additions and 45 deletions
+46 -38
View File
@@ -1,12 +1,16 @@
# 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`.
The operator's command on every node: `mesh-cli <command line>`, in interactive zsh also `nox`. Each line calls the
seat verb or the module tool that owns its job, through this machine's console, and prints the owner's answer.
```
nox status
nox settings show zsh --node <node>
nox settings set zsh '{"execute":"withhold"}' --node <node>
nox conditions --severity urgent
nox deliveries
nox assign <node> <module> --why <text>
nox issue <number>
nox call <node>/node-service-manager.status '{"unit":"plex"}'
nox help
```
## What it places
@@ -14,57 +18,61 @@ nox settings set zsh '{"execute":"withhold"}' --node <node>
| 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 console's address | `/etc/mesh-cli/console` | a file resource from `files/console`: `http://127.0.0.1:${bound:mcp-endpoint:port}/mcp`, from the `mcp-endpoint` the module requires |
| 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.
No link and no second copy. `/usr/local/bin` is on every account's `PATH` already. 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.
`mesh-cli` holds no credential and has no bus account of its own (so its manifest declares no `invokes`: an account
nothing dials would only be minted and kept). It calls `mesh_call` on this machine's console, the mesh MCP server the
module node-tools serves on loopback (novox/hq ADR 0152: whoever is on the machine owns the mesh there), and the console
puts the call on the bus, as every agent session on the machine does. The console's address is read from
`/etc/mesh-cli/console`, or `MESH_CONSOLE` when set, or node-tools' declared port when the file is not there yet.
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. A line still running after ten seconds is followed by its call until it ends. `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. When no answer comes in
time it says the line may have run and names its call; "nothing ran" is said only of a line no controller
took.
- **Who judges.** The seat that owns the job: a change of placement is a proposal the operator approves
(`mesh-placement.propose`), a config value is set with its why (`mesh-config.config`), a silence or a hand act is
recorded as one. Until novox/hq issue 1126 the controller ran these lines and judged the account (ADR 0272); with
the controller unassigned, that judgement went with it.
- **The map.** Every line the controller ran, and the seat verb or tool that does its job now, or none, is
`commands.go`'s table (and hq's `.work/1126/nox-map.md` while issue 1126 runs). A line no seat or module owns says
that it was the controller's, that the controller is unassigned, and what to use instead, and exits 1 having called
nothing. A line nox cannot read says how it is written and exits 2.
- **Any address.** `nox call <address> [json]` calls any seat verb or tool; `nox search <words>` and
`nox describe <address>` find one and its arguments.
- **What is printed.** The owner's answer as the console gave it (JSON), on standard output with exit 0; an answer the
console marks an error (a refusal, nothing serving, no answer within its bound) on standard error with exit 1. A
console that does not answer is said as nothing having been called; no answer in time is said as a call that may
have run.
- **`nox status`** is the delivery view's (`mesh-delivery-view.view_status`). When the view does not answer, nox says
so first, shows the open conditions and the deliveries under their own addresses, and exits 1: they are never
passed off as the status.
- **`nox enrol open --key <file>`** runs here and asks nothing of the mesh (ADR 0587 §8).
A process that runs for a verb (it carries `MESH_VERB`) is refused before anything is asked: a verb is never the
A process that runs for a verb (it carries `MESH_VERB`) is refused before anything is called: 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.
- **The `MESH_VERB` refusal is a courtesy.** A verb's process is refused before asking, but the gate is the
controller: it judges the account the node-engine vouches for, whatever the program says.
- **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.
- **No formatting beyond `nox nodes`.** Answers are the owners' JSON.
- **Lines with no owner.** Accepting a secret from a person, making or setting the operator key, opening a sealed
secret locally, registering a module from another repository and forgetting a module have no seat verb yet; nox
names what to use meanwhile.
## Tests
`go test ./...`: the module places exactly one file (one archive resource, one Go bundle compiled to the binary
alone); the line is asked as given and the answer printed with its exit; a reason, a refusal, a cut and an outcome not known
are said and a refusal never exits 0; a verb's process is refused before asking; an engine that does not answer
is said.
`go test ./...`: every owned line calls its owner's address with the arguments it reads, against a stand-in console
answering MCP `tools/call` as node-tools does; a line with no owner or not read calls nothing and says why; an error
answer, a silent console and a verb's process are said; the status's fallback is labelled and exits 1; the console's
address is found; `enrol open` opens a sealed token locally; the module places the program and the console's address
and nothing else.
## Files it does not know
Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416).
- `/usr/local/bin`: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2).
- `/etc/mesh-cli`: the program's; it holds the console's address alone.