node hand-over: the terminal hands a directory used as found to the mesh, asked of the node's engine (hq issue 356) #187

Merged
mesh-admin merged 3 commits from fix/356-node-hand-over-at-the-terminal into main 2026-10-09 17:57:17 +00:00
Contributor

hq issue 356 (hq pull request named in the thread once open); the engine's side is mesh-host #74, the catalogue's check mesh-catalog #165. A module's condition told the operator to run the node-engine's hand-over at the machine, as root (issue 339), the mesh had no channel for it, and the engine on the machine's PATH was a build from before the subcommand (the engine's PR fixes that).

Reviewers' checklist

  • Validate every caller value. handOverLine (cmd/mesh-controller/nodes.go) takes exactly a node and a path: the node a name (not a flag, not empty) the inventory knows (NodeByName); the path absolute and filepath.Clean(path) == path — no .., no doubled or trailing separator — before anything is asked. The engine judges the same values again and is the one that acts (mesh-host #74). AskHandOver (internal/link/handover.go) refuses an answer that is neither a record nor a refusal, and an unreadable one.
  • Trust-changing acts terminal-only. node hand-over is a node subcommand that is not in nodeReads, so terminalOnly refuses it through every verb and ordinaryLine through mesh-cli outside the terminal — no new rule, and TestAHandOverIsRefusedThroughEveryVerb holds it four ways (terminalOnly, the generic command verb's composition, the ordinary mesh-cli line, the node verb's composition). The ask goes on mesh.node.<node>.ask.hand-over: the controller's mesh.node.> publish grant, and the node's grant gains exactly its own subject and the right to answer what it was asked (AllowResponses for KindNode; a person still has none). The golden composition (internal/broker/testdata/composed.conf) changed by exactly those two lines.
  • Every test fails with its check removed. TestAHandOverLineIsJudgedBeforeItIsAsked (ten refusals, the one acceptance), TestAHandOverNamesWhoAsked, TestAHandOverIsRefusedThroughEveryVerb, TestTheUsedAsFoundConditionNamesTheOperatorsLine (the module's health names nox node hand-over <node> <directory> on the control-node, never the engine's binary, never a path; the condition's words are plain under conditions.PlainWords) (cmd/mesh-controller); TestTheHandOverAskAndAnswerKeepTheirFieldNames (held by the engine's twin), TestAHandOverIsAskedOfTheMachineAndItsAnswerComesBack (over a bus: the record and the refusal come back whole, a machine with no engine listening is said as not answering, an empty answer is refused, no bus is refused), TestAMachineHearsItsOwnHandOverAskAndMayAnswerIt (internal/link); TestOnlyWhatCanBeAskedMayAnswer now holds that a node answers its one ask and a person answers nothing (internal/broker).
  • Fail closed. A refusal from the engine is the command's error (" refused: … nothing was handed over"); no engine answering is an error naming the node-engine; a timeout says the outcome is not known and where to look (the module's condition); nothing is recorded on the controller's side at all.

What changes

  • node hand-over <node> <directory>: judges the line, asks the node's engine with who asked (MESH_CALLER, as every verb's caller is recorded — " through mesh-cli on " — or "the controller's terminal"), prints the engine's answer and that push <node> applies it now.
  • broker.AskHandOverSubject, the node's subscribe grant and AllowResponses; link.AskHandOver with the field names the engine holds.
  • Words (ADR 0272, the operator's rule of 2026-10-09): the module's wait says the nox line with the machine; the condition's "needs" line is plain (the line and the directory are in the summary and the evidence); nox-mesh-host overlay take and enrol in the network command's and the lab's texts use the binary's real name (install-time acts, at the machine).

Checks. merge-check.sh passed locally on the merge result (this branch on top of main at 1c7c385, after #184 and #185 merged): gofmt, vet, go test -race ./..., 26 packages ok. Not merged.

Order. This first — it composes the node's grant; then mesh-host #74. The catalogue's and hq's PRs in any order. Live, after both: nox node hand-over <control-node> <the messenger's data directory> on the control-node, then its next apply clears the messenger's condition.

hq issue 356 (hq pull request named in the thread once open); the engine's side is mesh-host #74, the catalogue's check mesh-catalog #165. A module's condition told the operator to run the node-engine's `hand-over` at the machine, as root (issue 339), the mesh had no channel for it, and the engine on the machine's PATH was a build from before the subcommand (the engine's PR fixes that). **Reviewers' checklist** - **Validate every caller value.** `handOverLine` (`cmd/mesh-controller/nodes.go`) takes exactly a node and a path: the node a name (not a flag, not empty) the inventory knows (`NodeByName`); the path absolute and `filepath.Clean(path) == path` — no `..`, no doubled or trailing separator — before anything is asked. The engine judges the same values again and is the one that acts (mesh-host #74). `AskHandOver` (`internal/link/handover.go`) refuses an answer that is neither a record nor a refusal, and an unreadable one. - **Trust-changing acts terminal-only.** `node hand-over` is a `node` subcommand that is not in `nodeReads`, so `terminalOnly` refuses it through every verb and `ordinaryLine` through mesh-cli outside the terminal — no new rule, and `TestAHandOverIsRefusedThroughEveryVerb` holds it four ways (`terminalOnly`, the generic command verb's composition, the ordinary mesh-cli line, the `node` verb's composition). The ask goes on `mesh.node.<node>.ask.hand-over`: the controller's `mesh.node.>` publish grant, and the node's grant gains exactly its own subject and the right to answer what it was asked (`AllowResponses` for `KindNode`; a person still has none). The golden composition (`internal/broker/testdata/composed.conf`) changed by exactly those two lines. - **Every test fails with its check removed.** `TestAHandOverLineIsJudgedBeforeItIsAsked` (ten refusals, the one acceptance), `TestAHandOverNamesWhoAsked`, `TestAHandOverIsRefusedThroughEveryVerb`, `TestTheUsedAsFoundConditionNamesTheOperatorsLine` (the module's health names `nox node hand-over <node> <directory>` on the control-node, never the engine's binary, never a path; the condition's words are plain under `conditions.PlainWords`) (`cmd/mesh-controller`); `TestTheHandOverAskAndAnswerKeepTheirFieldNames` (held by the engine's twin), `TestAHandOverIsAskedOfTheMachineAndItsAnswerComesBack` (over a bus: the record and the refusal come back whole, a machine with no engine listening is said as not answering, an empty answer is refused, no bus is refused), `TestAMachineHearsItsOwnHandOverAskAndMayAnswerIt` (`internal/link`); `TestOnlyWhatCanBeAskedMayAnswer` now holds that a node answers its one ask and a person answers nothing (`internal/broker`). - **Fail closed.** A refusal from the engine is the command's error ("<node> refused: … nothing was handed over"); no engine answering is an error naming the node-engine; a timeout says the outcome is not known and where to look (the module's condition); nothing is recorded on the controller's side at all. **What changes** - `node hand-over <node> <directory>`: judges the line, asks the node's engine with who asked (`MESH_CALLER`, as every verb's caller is recorded — "<account> through mesh-cli on <node>" — or "the controller's terminal"), prints the engine's answer and that `push <node>` applies it now. - `broker.AskHandOverSubject`, the node's subscribe grant and `AllowResponses`; `link.AskHandOver` with the field names the engine holds. - Words (ADR 0272, the operator's rule of 2026-10-09): the module's wait says the `nox` line with the machine; the condition's "needs" line is plain (the line and the directory are in the summary and the evidence); `nox-mesh-host overlay take` and `enrol` in the network command's and the lab's texts use the binary's real name (install-time acts, at the machine). **Checks.** `merge-check.sh` passed locally on the merge result (this branch on top of main at 1c7c385, after #184 and #185 merged): gofmt, vet, `go test -race ./...`, 26 packages ok. Not merged. **Order.** This first — it composes the node's grant; then mesh-host #74. The catalogue's and hq's PRs in any order. Live, after both: `nox node hand-over <control-node> <the messenger's data directory>` on the control-node, then its next apply clears the messenger's condition.
Author
Contributor

Delivery novox/mesh-controller@081d9244d6dd — delivered since 2026-10-09T18:13:21Z

Delivery plan — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step

  • tier 0: mesh-controller
  • tier 1: build-agent
  • tier 2: route-proxy
  • ace: receives build-agent, route-proxy
  • g14: receives build-agent
  • novox: receives mesh-controller, build-agent, route-proxy
  • shanks: receives build-agent
  • build-agent keeps data (ADR 0233): what it holds is backed up before it moves
  • route-proxy provides route: its consumers are sent again after it
  • route-proxy keeps data (ADR 0233): what it holds is backed up before it moves

Machines

  • mesh-controller on novox: passed — healthy 3 times over 3m39s
  • mesh-controller on the rest running it: sent
  • build-agent on ace: passed — healthy 3 times over 3m14s
  • build-agent on the rest running it: sent
  • route-proxy on ace: passed — healthy 3 times over 3m30s
  • route-proxy on the rest running it: sent

Transitions

  • 2026-10-09 17:47 (new) → proposed (announced): novox/mesh-controller#187's head announced
  • 2026-10-09 17:53 proposed → checked (checked): the verdict names this commit
  • 2026-10-09 17:53 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
  • 2026-10-09 17:57 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
  • 2026-10-09 17:57 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path
  • 2026-10-09 18:13 delivering → delivered (done): its walk is done: every machine of its deploy plan passed, or was left as its policy says

The commit's note under refs/notes/mesh-plan keeps every transition: git log --notes=mesh-plan.

<!-- mesh-delivery:view --> **Delivery** `novox/mesh-controller@081d9244d6dd` — **delivered** since 2026-10-09T18:13:21Z **Delivery plan** — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step - tier 0: mesh-controller - tier 1: build-agent - tier 2: route-proxy - ace: receives build-agent, route-proxy - g14: receives build-agent - novox: receives mesh-controller, build-agent, route-proxy - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - route-proxy provides route: its consumers are sent again after it - route-proxy keeps data (ADR 0233): what it holds is backed up before it moves **Machines** - mesh-controller on novox: passed — healthy 3 times over 3m39s - mesh-controller on the rest running it: sent - build-agent on ace: passed — healthy 3 times over 3m14s - build-agent on the rest running it: sent - route-proxy on ace: passed — healthy 3 times over 3m30s - route-proxy on the rest running it: sent **Transitions** - 2026-10-09 17:47 (new) → proposed (announced): novox/mesh-controller#187's head announced - 2026-10-09 17:53 proposed → checked (checked): the verdict names this commit - 2026-10-09 17:53 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail - 2026-10-09 17:57 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move - 2026-10-09 17:57 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path - 2026-10-09 18:13 delivering → delivered (done): its walk is done: every machine of its deploy plan passed, or was left as its policy says The commit's note under `refs/notes/mesh-plan` keeps every transition: `git log --notes=mesh-plan`.
Author
Contributor

The record: hq #256 (issue 356). The engine's side is mesh-host #74 (merge after this one), the catalogue's check mesh-catalog #165.

The record: hq #256 (issue 356). The engine's side is mesh-host #74 (merge after this one), the catalogue's check mesh-catalog #165.
jschoubben added 1 commit 2026-10-09 16:38:12 +00:00
node hand-over: the terminal hands a directory used as found to the mesh, asked of the node's engine (hq issue 356)
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
9006c82393
A module's condition told the operator to run the node-engine's hand-over at the machine, as root (issue 339), and the mesh had no channel for it. Now node hand-over <node> <path> is the controller's terminal's — a node subcommand that is not a read, so every verb and mesh-cli outside the terminal refuse it — and asks that node's engine on mesh.node.<node>.ask.hand-over, a request only the controller may publish and only that node's engine may hear and answer (its grant gains the subject and the right to answer what it was asked). The line's node and path are judged before anything is asked; the engine's answer is printed, a refusal as a refusal. Every text addressed to the operator names the nox line (ADR 0272); the condition's words stay plain.
jschoubben force-pushed fix/356-node-hand-over-at-the-terminal from 7ba3274408 to 9006c82393 2026-10-09 16:38:12 +00:00 Compare
Author
Contributor

Merge check at 7ba32744

  • mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy)
  • mesh/repo-check: PASS — its merge-check.sh passed

Delivery plan — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step

  • tier 0: mesh-controller
  • tier 1: build-agent
  • tier 2: route-proxy
  • ace: receives build-agent, route-proxy
  • g14: receives build-agent
  • novox: receives mesh-controller, build-agent, route-proxy
  • shanks: receives build-agent
  • build-agent keeps data (ADR 0233): what it holds is backed up before it moves
  • route-proxy provides route: its consumers are sent again after it
  • route-proxy keeps data (ADR 0233): what it holds is backed up before it moves

Run by the build seat on ace as build-1791563824735966092 (builds --log build-1791563824735966092).

--- the gate: build-agent, mesh-controller, route-proxy
mesh-controller: ok, 54 tool(s); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8)
long-running resources without health: 1
files asking for a setting without saying whether it is trusted: 0
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-09T16:31:14Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-09T16:31:14Z

modules the change moves:
  - mesh-controller (changed)

machines:
  - a machine (f35): composes
  - holds mesh-dns-resolver (idt): composes
  - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
  - a machine (yvbpsm): composes

a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy
--- its merge-check.sh (go toolchain)
ok  	github.com/novox/mesh-controller/cmd/mesh-builder	1.038s
ok  	github.com/novox/mesh-controller/cmd/mesh-controller	190.264s
ok  	github.com/novox/mesh-controller/examples/objectstore-provisioner	1.012s
ok  	github.com/novox/mesh-controller/examples/postgres-provisioner	1.017s
ok  	github.com/novox/mesh-controller/examples/redis-provisioner	1.012s
ok  	github.com/novox/mesh-controller/examples/route-proxy	1.119s
ok  	github.com/novox/mesh-controller/internal/artifacts	1.123s
ok  	github.com/novox/mesh-controller/internal/broker	2.651s
ok  	github.com/novox/mesh-controller/internal/builder	1.948s
ok  	github.com/novox/mesh-controller/internal/catalogue	4.411s
ok  	github.com/novox/mesh-controller/internal/conditions	2.447s
ok  	github.com/novox/mesh-controller/internal/envfile	1.086s
ok  	github.com/novox/mesh-controller/internal/facts	1.047s
ok  	github.com/novox/mesh-controller/internal/identity	4.144s
ok  	github.com/novox/mesh-controller/internal/inventory	73.932s
ok  	github.com/novox/mesh-controller/internal/lease	17.689s
ok  	github.com/novox/mesh-controller/internal/licences	3.784s
ok  	github.com/novox/mesh-controller/internal/licences/adapters	1.043s
ok  	github.com/novox/mesh-controller/internal/link	51.633s
ok  	github.com/novox/mesh-controller/internal/lint	2.025s
ok  	github.com/novox/mesh-controller/internal/outward	1.111s
ok  	github.com/novox/mesh-controller/internal/overlay	1.060s
ok  	github.com/novox/mesh-controller/internal/secrets	1.474s
ok  	github.com/novox/mesh-controller/internal/store	3.367s
ok  	github.com/novox/mesh-controller/internal/testbus	1.253s
ok  	github.com/novox/mesh-controller/internal/token	1.016s
**Merge check** at `7ba32744` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy) - `mesh/repo-check`: **PASS** — its merge-check.sh passed **Delivery plan** — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step - tier 0: mesh-controller - tier 1: build-agent - tier 2: route-proxy - ace: receives build-agent, route-proxy - g14: receives build-agent - novox: receives mesh-controller, build-agent, route-proxy - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - route-proxy provides route: its consumers are sent again after it - route-proxy keeps data (ADR 0233): what it holds is backed up before it moves Run by the build seat on ace as `build-1791563824735966092` (`builds --log build-1791563824735966092`). ``` --- the gate: build-agent, mesh-controller, route-proxy mesh-controller: ok, 54 tool(s); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8) long-running resources without health: 1 files asking for a setting without saying whether it is trusted: 0 1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-09T16:31:14Z says they run composing every machine as the mesh is the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued composing every machine with the change the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose) judged by controller development build against the facts of 2026-10-09T16:31:14Z modules the change moves: - mesh-controller (changed) machines: - a machine (f35): composes - holds mesh-dns-resolver (idt): composes - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes - a machine (yvbpsm): composes a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy --- its merge-check.sh (go toolchain) ok github.com/novox/mesh-controller/cmd/mesh-builder 1.038s ok github.com/novox/mesh-controller/cmd/mesh-controller 190.264s ok github.com/novox/mesh-controller/examples/objectstore-provisioner 1.012s ok github.com/novox/mesh-controller/examples/postgres-provisioner 1.017s ok github.com/novox/mesh-controller/examples/redis-provisioner 1.012s ok github.com/novox/mesh-controller/examples/route-proxy 1.119s ok github.com/novox/mesh-controller/internal/artifacts 1.123s ok github.com/novox/mesh-controller/internal/broker 2.651s ok github.com/novox/mesh-controller/internal/builder 1.948s ok github.com/novox/mesh-controller/internal/catalogue 4.411s ok github.com/novox/mesh-controller/internal/conditions 2.447s ok github.com/novox/mesh-controller/internal/envfile 1.086s ok github.com/novox/mesh-controller/internal/facts 1.047s ok github.com/novox/mesh-controller/internal/identity 4.144s ok github.com/novox/mesh-controller/internal/inventory 73.932s ok github.com/novox/mesh-controller/internal/lease 17.689s ok github.com/novox/mesh-controller/internal/licences 3.784s ok github.com/novox/mesh-controller/internal/licences/adapters 1.043s ok github.com/novox/mesh-controller/internal/link 51.633s ok github.com/novox/mesh-controller/internal/lint 2.025s ok github.com/novox/mesh-controller/internal/outward 1.111s ok github.com/novox/mesh-controller/internal/overlay 1.060s ok github.com/novox/mesh-controller/internal/secrets 1.474s ok github.com/novox/mesh-controller/internal/store 3.367s ok github.com/novox/mesh-controller/internal/testbus 1.253s ok github.com/novox/mesh-controller/internal/token 1.016s ```
Author
Contributor

Merge check at 9006c823

  • mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy)
  • mesh/repo-check: PASS — its merge-check.sh passed

Delivery plan — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step

  • tier 0: mesh-controller
  • tier 1: build-agent
  • tier 2: route-proxy
  • ace: receives build-agent, route-proxy
  • g14: receives build-agent
  • novox: receives mesh-controller, build-agent, route-proxy
  • shanks: receives build-agent
  • build-agent keeps data (ADR 0233): what it holds is backed up before it moves
  • route-proxy provides route: its consumers are sent again after it
  • route-proxy keeps data (ADR 0233): what it holds is backed up before it moves

Run by the build seat on novox as build-1791563916409013292 (builds --log build-1791563916409013292).

--- the gate: build-agent, mesh-controller, route-proxy
mesh-controller: ok, 54 tool(s); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8)
long-running resources without health: 1
files asking for a setting without saying whether it is trusted: 0
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-09T16:31:14Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-09T16:31:14Z

modules the change moves:
  - mesh-controller (changed)

machines:
  - a machine (f35): composes
  - holds mesh-dns-resolver (idt): composes
  - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
  - a machine (yvbpsm): composes

a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy
--- its merge-check.sh (go toolchain)
ok  	github.com/novox/mesh-controller/cmd/mesh-builder	1.053s
ok  	github.com/novox/mesh-controller/cmd/mesh-controller	248.389s
ok  	github.com/novox/mesh-controller/examples/objectstore-provisioner	1.020s
ok  	github.com/novox/mesh-controller/examples/postgres-provisioner	1.034s
ok  	github.com/novox/mesh-controller/examples/redis-provisioner	1.021s
ok  	github.com/novox/mesh-controller/examples/route-proxy	1.137s
ok  	github.com/novox/mesh-controller/internal/artifacts	1.072s
ok  	github.com/novox/mesh-controller/internal/broker	6.343s
ok  	github.com/novox/mesh-controller/internal/builder	10.909s
ok  	github.com/novox/mesh-controller/internal/catalogue	5.019s
ok  	github.com/novox/mesh-controller/internal/conditions	2.892s
ok  	github.com/novox/mesh-controller/internal/envfile	1.030s
ok  	github.com/novox/mesh-controller/internal/facts	1.036s
ok  	github.com/novox/mesh-controller/internal/identity	10.058s
ok  	github.com/novox/mesh-controller/internal/inventory	99.239s
ok  	github.com/novox/mesh-controller/internal/lease	18.091s
ok  	github.com/novox/mesh-controller/internal/licences	9.597s
ok  	github.com/novox/mesh-controller/internal/licences/adapters	1.010s
ok  	github.com/novox/mesh-controller/internal/link	57.244s
ok  	github.com/novox/mesh-controller/internal/lint	2.169s
ok  	github.com/novox/mesh-controller/internal/outward	1.036s
ok  	github.com/novox/mesh-controller/internal/overlay	1.025s
ok  	github.com/novox/mesh-controller/internal/secrets	1.482s
ok  	github.com/novox/mesh-controller/internal/store	5.810s
ok  	github.com/novox/mesh-controller/internal/testbus	1.179s
ok  	github.com/novox/mesh-controller/internal/token	1.017s
**Merge check** at `9006c823` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy) - `mesh/repo-check`: **PASS** — its merge-check.sh passed **Delivery plan** — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step - tier 0: mesh-controller - tier 1: build-agent - tier 2: route-proxy - ace: receives build-agent, route-proxy - g14: receives build-agent - novox: receives mesh-controller, build-agent, route-proxy - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - route-proxy provides route: its consumers are sent again after it - route-proxy keeps data (ADR 0233): what it holds is backed up before it moves Run by the build seat on novox as `build-1791563916409013292` (`builds --log build-1791563916409013292`). ``` --- the gate: build-agent, mesh-controller, route-proxy mesh-controller: ok, 54 tool(s); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8) long-running resources without health: 1 files asking for a setting without saying whether it is trusted: 0 1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-09T16:31:14Z says they run composing every machine as the mesh is the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued composing every machine with the change the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose) judged by controller development build against the facts of 2026-10-09T16:31:14Z modules the change moves: - mesh-controller (changed) machines: - a machine (f35): composes - holds mesh-dns-resolver (idt): composes - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes - a machine (yvbpsm): composes a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy --- its merge-check.sh (go toolchain) ok github.com/novox/mesh-controller/cmd/mesh-builder 1.053s ok github.com/novox/mesh-controller/cmd/mesh-controller 248.389s ok github.com/novox/mesh-controller/examples/objectstore-provisioner 1.020s ok github.com/novox/mesh-controller/examples/postgres-provisioner 1.034s ok github.com/novox/mesh-controller/examples/redis-provisioner 1.021s ok github.com/novox/mesh-controller/examples/route-proxy 1.137s ok github.com/novox/mesh-controller/internal/artifacts 1.072s ok github.com/novox/mesh-controller/internal/broker 6.343s ok github.com/novox/mesh-controller/internal/builder 10.909s ok github.com/novox/mesh-controller/internal/catalogue 5.019s ok github.com/novox/mesh-controller/internal/conditions 2.892s ok github.com/novox/mesh-controller/internal/envfile 1.030s ok github.com/novox/mesh-controller/internal/facts 1.036s ok github.com/novox/mesh-controller/internal/identity 10.058s ok github.com/novox/mesh-controller/internal/inventory 99.239s ok github.com/novox/mesh-controller/internal/lease 18.091s ok github.com/novox/mesh-controller/internal/licences 9.597s ok github.com/novox/mesh-controller/internal/licences/adapters 1.010s ok github.com/novox/mesh-controller/internal/link 57.244s ok github.com/novox/mesh-controller/internal/lint 2.169s ok github.com/novox/mesh-controller/internal/outward 1.036s ok github.com/novox/mesh-controller/internal/overlay 1.025s ok github.com/novox/mesh-controller/internal/secrets 1.482s ok github.com/novox/mesh-controller/internal/store 5.810s ok github.com/novox/mesh-controller/internal/testbus 1.179s ok github.com/novox/mesh-controller/internal/token 1.017s ```
jschoubben added 2 commits 2026-10-09 17:46:43 +00:00
The subject proved nothing: the bus lets any principal allowed to answer reply to a message it received on the reply subject that message named, so a tool server — the operator's account, every agent — could deliver a hand-over to an engine. The controller now signs the ask with the mesh's key over a fixed context (node, path, who asked, a minute's expiry, a fresh nonce), as declarations are signed, and the engine verifies it. The writers table gains the row for mesh.node.*.ask.hand-over; the subject's comment no longer claims who the engine hears. The line's known-node check and the refusal branch are tested; every check was removed in turn and a test failed.
Merge remote-tracking branch 'origin/main' into fix/356-node-hand-over-at-the-terminal
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
081d9244d6
Author
Contributor

Review addressed — head 081d924 (b55a38c, merged with main at 7477346).

Forgeable ask (blocking). link.SignHandOver signs the ask with the mesh's identity key (open.Identity, the key declarations are signed with) over HandOverContext ("novox-mesh hand-over v1\n") + the ask's bytes, so the signature is never a declaration's. The ask is {node, path, by, expires (now+1m), nonce (16 random bytes, hex)}, sent as SignedHandOver{ask, signature}. The engine verifies before reading (mesh-host #74).
Writers table. New row "a hand-over asked of a machine", mesh.node.*.ask.hand-over, Writes: isController (and designRows; to-be 45 §1 gains the row in hq #256). The false comment on AskHandOverSubject now says the subject bounds who is granted the publish and the signature who is believed.
KindNode AllowResponses. Kept: the request/reply needs it, and it is bounded — the node is its machine's engine (root there already), it is delivered only its own subjects (declare, ask.report, ask.hand-over, its _DELIVER), max 1 / 1m, and the controller records nothing from the answer. Narrowing per subject is not something the server's permissions express.
Test gaps. handOverAsked is the line with its two acts given: TestAHandOverAsksOnlyAKnownNodeAndFailsOnARefusal (unknown node → nothing asked; a refused line → nothing asked; the engine's refusal → error, nothing printed; a record → printed with nox push; a failing ask → error). TestAHandOverIsSignedWithAContextANonceAndAnExpiry, TestAHandOverAskHasOnePublisher (node, node tools, module refused at composition; the controller allowed).
Mutations (each check removed, a test failed): sign context, nonce, expiry, no signer, signed body sent, neither-answer, known node, refusal-fails, line clean/abs/node, writers row, node subscribes, node answers — 14 of 14 killed.
merge-check.sh on the merge result 081d924: exit 0 (gofmt, vet, go test -race ./...).

Review addressed — head 081d924 (b55a38c, merged with main at 7477346). **Forgeable ask (blocking).** `link.SignHandOver` signs the ask with the mesh's identity key (`open.Identity`, the key declarations are signed with) over `HandOverContext` ("novox-mesh hand-over v1\n") + the ask's bytes, so the signature is never a declaration's. The ask is `{node, path, by, expires (now+1m), nonce (16 random bytes, hex)}`, sent as `SignedHandOver{ask, signature}`. The engine verifies before reading (mesh-host #74). **Writers table.** New row "a hand-over asked of a machine", `mesh.node.*.ask.hand-over`, `Writes: isController` (and `designRows`; to-be 45 §1 gains the row in hq #256). The false comment on `AskHandOverSubject` now says the subject bounds who is granted the publish and the signature who is believed. **KindNode AllowResponses.** Kept: the request/reply needs it, and it is bounded — the node is its machine's engine (root there already), it is delivered only its own subjects (declare, ask.report, ask.hand-over, its _DELIVER), max 1 / 1m, and the controller records nothing from the answer. Narrowing per subject is not something the server's permissions express. **Test gaps.** `handOverAsked` is the line with its two acts given: `TestAHandOverAsksOnlyAKnownNodeAndFailsOnARefusal` (unknown node → nothing asked; a refused line → nothing asked; the engine's refusal → error, nothing printed; a record → printed with `nox push`; a failing ask → error). `TestAHandOverIsSignedWithAContextANonceAndAnExpiry`, `TestAHandOverAskHasOnePublisher` (node, node tools, module refused at composition; the controller allowed). **Mutations** (each check removed, a test failed): sign context, nonce, expiry, no signer, signed body sent, neither-answer, known node, refusal-fails, line clean/abs/node, writers row, node subscribes, node answers — 14 of 14 killed. **merge-check.sh** on the merge result 081d924: exit 0 (gofmt, vet, `go test -race ./...`).
Author
Contributor

Merge check at 081d9244

  • mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy)
  • mesh/repo-check: PASS — its merge-check.sh passed

Delivery plan — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step

  • tier 0: mesh-controller
  • tier 1: build-agent
  • tier 2: route-proxy
  • ace: receives build-agent, route-proxy
  • g14: receives build-agent
  • novox: receives mesh-controller, build-agent, route-proxy
  • shanks: receives build-agent
  • build-agent keeps data (ADR 0233): what it holds is backed up before it moves
  • route-proxy provides route: its consumers are sent again after it
  • route-proxy keeps data (ADR 0233): what it holds is backed up before it moves

Run by the build seat on g14 as build-1791568027361874825 (builds --log build-1791568027361874825).

--- the gate: build-agent, mesh-controller, route-proxy
mesh-controller: ok, 54 tool(s); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8)
long-running resources without health: 1
files asking for a setting without saying whether it is trusted: 0
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-09T17:45:02Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-09T17:45:02Z

modules the change moves:
  - mesh-controller (changed)

machines:
  - a machine (f35): composes
  - holds mesh-dns-resolver (idt): composes
  - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
  - a machine (yvbpsm): composes

a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy
--- its merge-check.sh (go toolchain)
ok  	github.com/novox/mesh-controller/cmd/mesh-builder	1.029s
ok  	github.com/novox/mesh-controller/cmd/mesh-controller	208.791s
ok  	github.com/novox/mesh-controller/examples/objectstore-provisioner	1.022s
ok  	github.com/novox/mesh-controller/examples/postgres-provisioner	1.029s
ok  	github.com/novox/mesh-controller/examples/redis-provisioner	1.022s
ok  	github.com/novox/mesh-controller/examples/route-proxy	1.213s
ok  	github.com/novox/mesh-controller/internal/artifacts	1.048s
ok  	github.com/novox/mesh-controller/internal/broker	2.561s
ok  	github.com/novox/mesh-controller/internal/builder	1.747s
ok  	github.com/novox/mesh-controller/internal/catalogue	4.330s
ok  	github.com/novox/mesh-controller/internal/conditions	2.338s
ok  	github.com/novox/mesh-controller/internal/envfile	1.026s
ok  	github.com/novox/mesh-controller/internal/facts	1.030s
ok  	github.com/novox/mesh-controller/internal/identity	3.950s
ok  	github.com/novox/mesh-controller/internal/inventory	70.010s
ok  	github.com/novox/mesh-controller/internal/lease	17.685s
ok  	github.com/novox/mesh-controller/internal/licences	3.511s
ok  	github.com/novox/mesh-controller/internal/licences/adapters	1.008s
ok  	github.com/novox/mesh-controller/internal/link	50.732s
ok  	github.com/novox/mesh-controller/internal/lint	1.885s
ok  	github.com/novox/mesh-controller/internal/outward	1.030s
ok  	github.com/novox/mesh-controller/internal/overlay	1.023s
ok  	github.com/novox/mesh-controller/internal/secrets	1.376s
ok  	github.com/novox/mesh-controller/internal/store	3.126s
ok  	github.com/novox/mesh-controller/internal/testbus	1.172s
ok  	github.com/novox/mesh-controller/internal/token	1.011s
**Merge check** at `081d9244` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy) - `mesh/repo-check`: **PASS** — its merge-check.sh passed **Delivery plan** — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step - tier 0: mesh-controller - tier 1: build-agent - tier 2: route-proxy - ace: receives build-agent, route-proxy - g14: receives build-agent - novox: receives mesh-controller, build-agent, route-proxy - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - route-proxy provides route: its consumers are sent again after it - route-proxy keeps data (ADR 0233): what it holds is backed up before it moves Run by the build seat on g14 as `build-1791568027361874825` (`builds --log build-1791568027361874825`). ``` --- the gate: build-agent, mesh-controller, route-proxy mesh-controller: ok, 54 tool(s); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8) long-running resources without health: 1 files asking for a setting without saying whether it is trusted: 0 1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-09T17:45:02Z says they run composing every machine as the mesh is the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued composing every machine with the change the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose) judged by controller development build against the facts of 2026-10-09T17:45:02Z modules the change moves: - mesh-controller (changed) machines: - a machine (f35): composes - holds mesh-dns-resolver (idt): composes - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes - a machine (yvbpsm): composes a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy --- its merge-check.sh (go toolchain) ok github.com/novox/mesh-controller/cmd/mesh-builder 1.029s ok github.com/novox/mesh-controller/cmd/mesh-controller 208.791s ok github.com/novox/mesh-controller/examples/objectstore-provisioner 1.022s ok github.com/novox/mesh-controller/examples/postgres-provisioner 1.029s ok github.com/novox/mesh-controller/examples/redis-provisioner 1.022s ok github.com/novox/mesh-controller/examples/route-proxy 1.213s ok github.com/novox/mesh-controller/internal/artifacts 1.048s ok github.com/novox/mesh-controller/internal/broker 2.561s ok github.com/novox/mesh-controller/internal/builder 1.747s ok github.com/novox/mesh-controller/internal/catalogue 4.330s ok github.com/novox/mesh-controller/internal/conditions 2.338s ok github.com/novox/mesh-controller/internal/envfile 1.026s ok github.com/novox/mesh-controller/internal/facts 1.030s ok github.com/novox/mesh-controller/internal/identity 3.950s ok github.com/novox/mesh-controller/internal/inventory 70.010s ok github.com/novox/mesh-controller/internal/lease 17.685s ok github.com/novox/mesh-controller/internal/licences 3.511s ok github.com/novox/mesh-controller/internal/licences/adapters 1.008s ok github.com/novox/mesh-controller/internal/link 50.732s ok github.com/novox/mesh-controller/internal/lint 1.885s ok github.com/novox/mesh-controller/internal/outward 1.030s ok github.com/novox/mesh-controller/internal/overlay 1.023s ok github.com/novox/mesh-controller/internal/secrets 1.376s ok github.com/novox/mesh-controller/internal/store 3.126s ok github.com/novox/mesh-controller/internal/testbus 1.172s ok github.com/novox/mesh-controller/internal/token 1.011s ```
mesh-admin merged commit 19eefb66c6 into main 2026-10-09 17:57:17 +00:00
mesh-admin deleted branch fix/356-node-hand-over-at-the-terminal 2026-10-09 17:57:18 +00:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-controller#187