Renew the engine placed by hand to the proved build, and take a hand-over from the controller's terminal (hq issue 356) #74

Merged
mesh-admin merged 4 commits from fix/356-hand-over-by-nox-and-the-engine-on-path-renewed into main 2026-10-09 18:07:21 +00:00
Contributor

hq issue 356 (hq pull request named in the thread once open). On 2026-10-09 at 18:01 the operator typed the node-engine's hand-over on the control-node, as a condition told them to, and the engine on PATH answered "unexpected argument": /usr/bin/nox-mesh-host is the engine a person copied at installation, every delivered engine runs from the versions directory, and nothing ever renewed the file on PATH. And the hand-over itself was a terminal act at the machine with no channel.

Reviewers' checklist

  • Validate every caller value. AnswerHandOver (internal/link/handover.go) bounds the ask (8 KiB), refuses one that is not a hand-over, refuses nobody asking, a name over one line or 200 bytes or holding NUL, and no directory — before the handler sees anything. handOverFound (cmd/mesh-host/found.go) then judges the path: absolute; filepath.Clean(path) == path (so no .., no doubled or trailing separator); one the node's state records as used as found; on disk a directory and not a link; EvalSymlinks(path) == path (reached through no link). Nothing is recorded unless all pass.
  • Trust-changing acts terminal-only. The engine hears the ask on mesh.node.<node>.ask.hand-over, which only the controller may publish (mesh.node.> is the controller's grant; the node's grant for the subject and the right to answer are in mesh-controller's PR). The controller runs node hand-over only as its terminal (a node subcommand that is not a read is refused through every verb and through mesh-cli outside the terminal). The engine runs nothing it is sent; its one act is the record the next apply reads, as hand-over typed at the machine already did.
  • Every test fails with its check removed. TestTheFallbackBecomesACopyOfTheProvedEngine (bytes, mode 0755, running file untouched; already a copy → same inode, no temp file), TestAnEngineRunningFromTheFallbackRenewsNothing (SameFile), TestAMissingFallbackIsPlaced, TestALinkAtTheFallbackIsRefusedAndLeft (link refused, target untouched, link kept), TestAnUnreadableRunningEngineIsSaid, TestTheFallbackPathIsTheLaunchers (internal/upgrade); TestTheHandOverAskAndAnswerKeepTheirFieldNames, TestAHandOverAskIsJudgedBeforeAnythingIsRecorded (seven refusals, each counted as never reaching the handler) (internal/link); TestHandOverRecordsOnlyAFoundDirectory (the subcommand and the controller's ask are one judgement; who asked is recorded), TestAHandOverIsRefusedForEveryPathThatIsNotTheFoundDirectory (nine refusals, nothing recorded, the exact path taken), TestAFoundDirectoryIsStatedUnhealthy (the statement names nox node hand-over <node> <directory> on the control-node, never the binary, never the path) (cmd/mesh-host).
  • Fail closed. A refusal answers "nothing was handed over" and writes nothing; an engine with no handler refuses; a request with no reply inbox is dropped unrecorded; the fallback renewal refuses a link, a non-regular file and an unreadable running engine, and a failure to renew is said in the journal rather than hidden.

What changes

  1. The engine on PATH is the running build. upgrade.RenewFallback (internal/upgrade/fallback.go): when a build is proved (the mesh took a report under it — the same evidence known-good is written on, proving.heard in cmd/mesh-host/witnessing.go), the engine copies itself over the launcher's fallback (MESH_HOST_BIN, default /usr/bin/nox-mesh-host, the same variable the launcher reads) when the bytes differ: a copy, renamed into place, mode 0755 — never a link (ADR 0012), never a write through one, nothing while the engine runs from the fallback itself. Chosen over an exec-shim: the launcher runs that file as the engine while nothing is delivered (a shim there would run itself), and which version runs is the launcher's policy. The launcher is unchanged.
  2. The hand-over's channel. link.HandOvers / HearHandOvers (internal/link/hearing_nats.go): a request subscription on mesh.node.<node>.ask.hand-over, answered once on the reply it carried with HandOverAnswer{said|refused}; Queue.HandOver is the engine's judgement (handOverFrom), wired in run.go beside the report ask. Field names held on both sides. The engine's own hand-over <dir> stays, for a machine no mesh can reach, through the same handOverFound.
  3. The words (ADR 0272, the operator's rule of 2026-10-09). The statement, the apply's log line and every comment name nox node hand-over <node> <directory> on the control-node; the path stays the resource's target (evidence), never in the reason the operator's channel says. The usage says the binary is a daemon, the operator's command is nox, and the subcommands are install-time and rescue, run as root at the machine; the README says the same and uses the binary's real name.

Checks. make check (fmt, vet, go test ./..., the launcher's, rollback's and roused tests, build) passed locally on this tree before the issue was renumbered 353→356 (comments and the README only since); go test of internal/upgrade, internal/link, cmd/mesh-host and go build ./... pass at e74ee82. Not merged; nothing pushed to a node.

Order. mesh-controller's PR first (it composes the node's grant for the subject and the right to answer; an engine delivered before it has its subscription refused and says so, and nox node hand-over then says nothing on the node answers), then this. mesh-catalog's and hq's PRs in any order. After both are live: nox node hand-over <control-node> <the messenger's data directory> on the control-node, then its next apply.

hq issue 356 (hq pull request named in the thread once open). On 2026-10-09 at 18:01 the operator typed the node-engine's `hand-over` on the control-node, as a condition told them to, and the engine on PATH answered "unexpected argument": `/usr/bin/nox-mesh-host` is the engine a person copied at installation, every delivered engine runs from the versions directory, and nothing ever renewed the file on PATH. And the hand-over itself was a terminal act at the machine with no channel. **Reviewers' checklist** - **Validate every caller value.** `AnswerHandOver` (`internal/link/handover.go`) bounds the ask (8 KiB), refuses one that is not a hand-over, refuses nobody asking, a name over one line or 200 bytes or holding NUL, and no directory — before the handler sees anything. `handOverFound` (`cmd/mesh-host/found.go`) then judges the path: absolute; `filepath.Clean(path) == path` (so no `..`, no doubled or trailing separator); one the node's state records as used as found; on disk a directory and not a link; `EvalSymlinks(path) == path` (reached through no link). Nothing is recorded unless all pass. - **Trust-changing acts terminal-only.** The engine hears the ask on `mesh.node.<node>.ask.hand-over`, which only the controller may publish (`mesh.node.>` is the controller's grant; the node's grant for the subject and the right to answer are in mesh-controller's PR). The controller runs `node hand-over` only as its terminal (a `node` subcommand that is not a read is refused through every verb and through mesh-cli outside the terminal). The engine runs nothing it is sent; its one act is the record the next apply reads, as `hand-over` typed at the machine already did. - **Every test fails with its check removed.** `TestTheFallbackBecomesACopyOfTheProvedEngine` (bytes, mode 0755, running file untouched; already a copy → same inode, no temp file), `TestAnEngineRunningFromTheFallbackRenewsNothing` (SameFile), `TestAMissingFallbackIsPlaced`, `TestALinkAtTheFallbackIsRefusedAndLeft` (link refused, target untouched, link kept), `TestAnUnreadableRunningEngineIsSaid`, `TestTheFallbackPathIsTheLaunchers` (`internal/upgrade`); `TestTheHandOverAskAndAnswerKeepTheirFieldNames`, `TestAHandOverAskIsJudgedBeforeAnythingIsRecorded` (seven refusals, each counted as never reaching the handler) (`internal/link`); `TestHandOverRecordsOnlyAFoundDirectory` (the subcommand and the controller's ask are one judgement; who asked is recorded), `TestAHandOverIsRefusedForEveryPathThatIsNotTheFoundDirectory` (nine refusals, nothing recorded, the exact path taken), `TestAFoundDirectoryIsStatedUnhealthy` (the statement names `nox node hand-over <node> <directory>` on the control-node, never the binary, never the path) (`cmd/mesh-host`). - **Fail closed.** A refusal answers "nothing was handed over" and writes nothing; an engine with no handler refuses; a request with no reply inbox is dropped unrecorded; the fallback renewal refuses a link, a non-regular file and an unreadable running engine, and a failure to renew is said in the journal rather than hidden. **What changes** 1. **The engine on PATH is the running build.** `upgrade.RenewFallback` (`internal/upgrade/fallback.go`): when a build is proved (the mesh took a report under it — the same evidence `known-good` is written on, `proving.heard` in `cmd/mesh-host/witnessing.go`), the engine copies itself over the launcher's fallback (`MESH_HOST_BIN`, default `/usr/bin/nox-mesh-host`, the same variable the launcher reads) when the bytes differ: a copy, renamed into place, mode 0755 — never a link (ADR 0012), never a write through one, nothing while the engine runs from the fallback itself. Chosen over an exec-shim: the launcher runs that file as the engine while nothing is delivered (a shim there would run itself), and which version runs is the launcher's policy. The launcher is unchanged. 2. **The hand-over's channel.** `link.HandOvers` / `HearHandOvers` (`internal/link/hearing_nats.go`): a request subscription on `mesh.node.<node>.ask.hand-over`, answered once on the reply it carried with `HandOverAnswer{said|refused}`; `Queue.HandOver` is the engine's judgement (`handOverFrom`), wired in `run.go` beside the report ask. Field names held on both sides. The engine's own `hand-over <dir>` stays, for a machine no mesh can reach, through the same `handOverFound`. 3. **The words (ADR 0272, the operator's rule of 2026-10-09).** The statement, the apply's log line and every comment name `nox node hand-over <node> <directory>` on the control-node; the path stays the resource's target (evidence), never in the reason the operator's channel says. The usage says the binary is a daemon, the operator's command is `nox`, and the subcommands are install-time and rescue, run as root at the machine; the README says the same and uses the binary's real name. **Checks.** `make check` (fmt, vet, `go test ./...`, the launcher's, rollback's and roused tests, build) passed locally on this tree before the issue was renumbered 353→356 (comments and the README only since); `go test` of `internal/upgrade`, `internal/link`, `cmd/mesh-host` and `go build ./...` pass at e74ee82. Not merged; nothing pushed to a node. **Order.** mesh-controller's PR first (it composes the node's grant for the subject and the right to answer; an engine delivered before it has its subscription refused and says so, and `nox node hand-over` then says nothing on the node answers), then this. mesh-catalog's and hq's PRs in any order. After both are live: `nox node hand-over <control-node> <the messenger's data directory>` on the control-node, then its next apply.
mesh-admin added 1 commit 2026-10-09 16:35:43 +00:00
Renew the engine placed by hand to the proved build, and take a hand-over from the controller's terminal (hq issue 356)
mesh/merge-gate pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
e74ee82a76
The file on PATH was the engine a person copied at installation, and every delivered engine ran from the versions directory: the operator typed hand-over and a build from before it answered "unexpected argument". A proved engine now copies itself over that file when the bytes differ (never a link). And the hand-over of a directory used as found (issue 339) gets its channel: the controller's terminal asks this node's engine on mesh.node.<node>.ask.hand-over; the engine judges every value — the ask's size and shape, who asked, the path as it states it, a directory and no link, through no link — and records it or refuses and records nothing. Every text the engine addresses to the operator names the nox line (ADR 0272); the engine's own subcommands are install-time and rescue, and its usage says so.
Author
Contributor

Delivery novox/mesh-host@8fc1d72cd546 — failed since 2026-10-09T18:07:48Z

Delivery plan — builds mesh-host → ace, g14, novox, shanks; no bus step

  • tier 0: mesh-host
  • ace: receives mesh-host
  • g14: receives mesh-host
  • novox: receives mesh-host
  • shanks: receives mesh-host

Transitions

  • 2026-10-09 18:04 (new) → proposed (announced): novox/mesh-host#74's head announced
  • 2026-10-09 18:06 proposed → checked (checked): the verdict names this commit
  • 2026-10-09 18:06 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
  • 2026-10-09 18:07 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 18:07 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:07 delivering → failed (failed): its walk failed: a gate on a first machine (what it carried put back), a build, a machine

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

<!-- mesh-delivery:view --> **Delivery** `novox/mesh-host@8fc1d72cd546` — **failed** since 2026-10-09T18:07:48Z **Delivery plan** — builds mesh-host → ace, g14, novox, shanks; no bus step - tier 0: mesh-host - ace: receives mesh-host - g14: receives mesh-host - novox: receives mesh-host - shanks: receives mesh-host **Transitions** - 2026-10-09 18:04 (new) → proposed (announced): novox/mesh-host#74's head announced - 2026-10-09 18:06 proposed → checked (checked): the verdict names this commit - 2026-10-09 18:06 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail - 2026-10-09 18:07 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 18:07 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:07 delivering → failed (failed): its walk failed: a gate on a first machine (what it carried put back), a build, a machine 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 — renumbered from 353, which another branch took first; the code says 356). The controller's side is mesh-controller #187 (merge first: the node's grant), the catalogue's check mesh-catalog #165.

The record: hq #256 (issue 356 — renumbered from 353, which another branch took first; the code says 356). The controller's side is mesh-controller #187 (merge first: the node's grant), the catalogue's check mesh-catalog #165.
Author
Contributor

Merge check at e74ee82a

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

Delivery plan — builds mesh-host → ace, g14, novox, shanks; no bus step

  • tier 0: mesh-host
  • ace: receives mesh-host
  • g14: receives mesh-host
  • novox: receives mesh-host
  • shanks: receives mesh-host

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

--- the gate: mesh-host
mesh-host: ok
long-running resources without health: 0
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 0 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

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 1 module(s) in 1 tier(s): mesh-host
--- its merge-check.sh (go toolchain)
go: downloading git.novox.be/novox/mesh-sdk/go v0.1.11-0.20261009143344-f047d0a4a970
ok  	github.com/novox/mesh-host/cmd/mesh-bootstrap	1.016s
ok  	github.com/novox/mesh-host/cmd/mesh-host	5.671s
ok  	github.com/novox/mesh-host/examples	1.017s
ok  	github.com/novox/mesh-host/internal/accounts	1.025s
ok  	github.com/novox/mesh-host/internal/apply	2.293s
ok  	github.com/novox/mesh-host/internal/bootstrap	1.320s
ok  	github.com/novox/mesh-host/internal/bundle	1.019s
ok  	github.com/novox/mesh-host/internal/declaration	1.055s
ok  	github.com/novox/mesh-host/internal/firewall	1.698s
ok  	github.com/novox/mesh-host/internal/identity	1.051s
ok  	github.com/novox/mesh-host/internal/image	1.020s
ok  	github.com/novox/mesh-host/internal/inventory	1.030s
ok  	github.com/novox/mesh-host/internal/link	10.336s
ok  	github.com/novox/mesh-host/internal/liveness	2.042s
ok  	github.com/novox/mesh-host/internal/meshcli	1.342s
ok  	github.com/novox/mesh-host/internal/network	1.037s
ok  	github.com/novox/mesh-host/internal/outward	1.029s
ok  	github.com/novox/mesh-host/internal/profile	1.536s
ok  	github.com/novox/mesh-host/internal/reachable	1.022s
ok  	github.com/novox/mesh-host/internal/store	1.084s
ok  	github.com/novox/mesh-host/internal/system	1.017s
ok  	github.com/novox/mesh-host/internal/tunnel	1.018s
ok  	github.com/novox/mesh-host/internal/units	1.011s
ok  	github.com/novox/mesh-host/internal/upgrade	1.040s
ok  	github.com/novox/mesh-host/internal/witness	1.475s
ok  	github.com/novox/mesh-host/packaging	1.011s
?   	github.com/novox/mesh-host/rootsearch	[no test files]
ok  	github.com/novox/mesh-host/validate	1.011s
**Merge check** at `e74ee82a` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-host) - `mesh/repo-check`: **PASS** — its merge-check.sh passed **Delivery plan** — builds mesh-host → ace, g14, novox, shanks; no bus step - tier 0: mesh-host - ace: receives mesh-host - g14: receives mesh-host - novox: receives mesh-host - shanks: receives mesh-host Run by the build seat on g14 as `build-1791563763968800353` (`builds --log build-1791563763968800353`). ``` --- the gate: mesh-host mesh-host: ok long-running resources without health: 0 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 0 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 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 1 module(s) in 1 tier(s): mesh-host --- its merge-check.sh (go toolchain) go: downloading git.novox.be/novox/mesh-sdk/go v0.1.11-0.20261009143344-f047d0a4a970 ok github.com/novox/mesh-host/cmd/mesh-bootstrap 1.016s ok github.com/novox/mesh-host/cmd/mesh-host 5.671s ok github.com/novox/mesh-host/examples 1.017s ok github.com/novox/mesh-host/internal/accounts 1.025s ok github.com/novox/mesh-host/internal/apply 2.293s ok github.com/novox/mesh-host/internal/bootstrap 1.320s ok github.com/novox/mesh-host/internal/bundle 1.019s ok github.com/novox/mesh-host/internal/declaration 1.055s ok github.com/novox/mesh-host/internal/firewall 1.698s ok github.com/novox/mesh-host/internal/identity 1.051s ok github.com/novox/mesh-host/internal/image 1.020s ok github.com/novox/mesh-host/internal/inventory 1.030s ok github.com/novox/mesh-host/internal/link 10.336s ok github.com/novox/mesh-host/internal/liveness 2.042s ok github.com/novox/mesh-host/internal/meshcli 1.342s ok github.com/novox/mesh-host/internal/network 1.037s ok github.com/novox/mesh-host/internal/outward 1.029s ok github.com/novox/mesh-host/internal/profile 1.536s ok github.com/novox/mesh-host/internal/reachable 1.022s ok github.com/novox/mesh-host/internal/store 1.084s ok github.com/novox/mesh-host/internal/system 1.017s ok github.com/novox/mesh-host/internal/tunnel 1.018s ok github.com/novox/mesh-host/internal/units 1.011s ok github.com/novox/mesh-host/internal/upgrade 1.040s ok github.com/novox/mesh-host/internal/witness 1.475s ok github.com/novox/mesh-host/packaging 1.011s ? github.com/novox/mesh-host/rootsearch [no test files] ok github.com/novox/mesh-host/validate 1.011s ```
jschoubben added 1 commit 2026-10-09 17:45:49 +00:00
Believe a hand-over only signed by the mesh, and give it through no link (hq issue 356, review)
mesh/merge-gate pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
fc6c5a92e6
Two holes the review proved. The ask's subject proved nothing — a responder can reach it through a reply — so the engine now verifies the mesh's signature over a fixed context with the key it verifies declarations with, before reading anything, and refuses an ask for another node, expired, good too long, without a nonce, or replayed. And the apply followed a link the hand-over had refused: a component swapped for a link after the record carried root's chmod and chown to its target. A handed-over directory must now still resolve to itself, and is changed only through one descriptor opened from / with no link followed (fchmod, fchown). Tests for the reply guard, the run loop's hook, the fallback renewal on proof and a non-regular fallback; 27 checks each removed in turn, each failing a test.
Author
Contributor

Review addressed — head fc6c5a9 (main has not moved since b0d03be, so the branch is the merge result).

Forgeable ask (blocking). AnswerHandOver(check, handle, body) now verifies SignedHandOver with m.Signer (the key handleBody verifies declarations with, carried into natsLink.signer) over HandOverContext + the ask's bytes before reading anything out of the ask; then refuses an ask for another node, expired, with no expiry, good for longer than 1m + 1m skew, without a nonce of ≥16 bytes, or whose nonce was taken (process-wide HandOverNonces, each kept until its ask expires — a reconnect is no replay; a restart can replay only within the minute, and recording the same hand-over again is idempotent). An engine with no key believes nothing. HandOverReply is the subscription's whole body: no reply subject → nothing judged, recorded or said.
Apply follows a link (blocking). A handed-over directory now goes through applyHandedOver: EvalSymlinks(path) == path at apply time, then giveHandedOver — one descriptor from openUnderAs("/", path, kindDir) (every component openat O_NOFOLLOW|O_DIRECTORY from /, judged by fstat), Fchmod/Fchown on it. Nothing is changed by path. TestAHandedOverDirectoryIsChangedThroughNoLink swaps the parent, the grandparent and the directory itself for links to a victim tree after the hand-over: refused, the victim keeps mode 0700, and the error names which check refused. TestGiveHandedOverFollowsNoLink holds the descriptor walk alone.
Test gaps. TestAHandOverIsAnsweredOnlyWhenItCarriesAReply (reply guard), TestALinkThatHearsHandOversIsGivenTheQueues (run.go hook, through serve), TestNatsAHandOverIsAskedOfTheNode (real server: answered on its own subject, another node's unheard, a publish with no reply handled by nobody — run locally against a throwaway nats-server, skipped without MESH_TEST_NATS as the other Nats tests), TestOnlyAnAccountUnderThisBuildProvesIt now asserts renewFallback ran (fallback = this test binary's bytes, said) and that nothing before the proof renewed it — and points MESH_HOST_BIN at a temp file, so a test run as root never writes /usr/bin. TestAFallbackThatIsNotARegularFileIsRefused (a directory at the fallback; a directory as the running engine). The redundant same-file case in RenewFallback is gone (same bytes covers it).
Mutations: 27 checks each removed in turn, 27 killed (signature, no key, context, node, expired, too long, nonce shape, replay, nonce prune, by empty/line, no path, size, reply guard, run.go hook, the NATS respond, apply re-check, apply descriptor, handed-over branch, fallback not-regular/self not-regular/link/same-bytes, renewFallback called, path clean/link/through-link).
Checks: make check and merge-check.sh exit 0 at fc6c5a9.

Review addressed — head fc6c5a9 (main has not moved since b0d03be, so the branch is the merge result). **Forgeable ask (blocking).** `AnswerHandOver(check, handle, body)` now verifies `SignedHandOver` with `m.Signer` (the key `handleBody` verifies declarations with, carried into `natsLink.signer`) over `HandOverContext` + the ask's bytes **before reading anything out of the ask**; then refuses an ask for another node, expired, with no expiry, good for longer than 1m + 1m skew, without a nonce of ≥16 bytes, or whose nonce was taken (process-wide `HandOverNonces`, each kept until its ask expires — a reconnect is no replay; a restart can replay only within the minute, and recording the same hand-over again is idempotent). An engine with no key believes nothing. `HandOverReply` is the subscription's whole body: no reply subject → nothing judged, recorded or said. **Apply follows a link (blocking).** A handed-over directory now goes through `applyHandedOver`: `EvalSymlinks(path) == path` at apply time, then `giveHandedOver` — one descriptor from `openUnderAs("/", path, kindDir)` (every component `openat` O_NOFOLLOW|O_DIRECTORY from /, judged by fstat), `Fchmod`/`Fchown` on it. Nothing is changed by path. `TestAHandedOverDirectoryIsChangedThroughNoLink` swaps the parent, the grandparent and the directory itself for links to a victim tree after the hand-over: refused, the victim keeps mode 0700, and the error names which check refused. `TestGiveHandedOverFollowsNoLink` holds the descriptor walk alone. **Test gaps.** `TestAHandOverIsAnsweredOnlyWhenItCarriesAReply` (reply guard), `TestALinkThatHearsHandOversIsGivenTheQueues` (run.go hook, through `serve`), `TestNatsAHandOverIsAskedOfTheNode` (real server: answered on its own subject, another node's unheard, a publish with no reply handled by nobody — run locally against a throwaway nats-server, skipped without MESH_TEST_NATS as the other Nats tests), `TestOnlyAnAccountUnderThisBuildProvesIt` now asserts `renewFallback` ran (fallback = this test binary's bytes, said) and that nothing before the proof renewed it — and points `MESH_HOST_BIN` at a temp file, so a test run as root never writes `/usr/bin`. `TestAFallbackThatIsNotARegularFileIsRefused` (a directory at the fallback; a directory as the running engine). The redundant same-file case in `RenewFallback` is gone (same bytes covers it). **Mutations:** 27 checks each removed in turn, 27 killed (signature, no key, context, node, expired, too long, nonce shape, replay, nonce prune, by empty/line, no path, size, reply guard, run.go hook, the NATS respond, apply re-check, apply descriptor, handed-over branch, fallback not-regular/self not-regular/link/same-bytes, renewFallback called, path clean/link/through-link). **Checks:** `make check` and `merge-check.sh` exit 0 at fc6c5a9.
Author
Contributor

Merge check at fc6c5a92

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

Delivery plan — builds mesh-host → ace, g14, novox, shanks; no bus step

  • tier 0: mesh-host
  • ace: receives mesh-host
  • g14: receives mesh-host
  • novox: receives mesh-host
  • shanks: receives mesh-host

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

--- the gate: mesh-host
mesh-host: ok
long-running resources without health: 0
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 0 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

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 1 module(s) in 1 tier(s): mesh-host
--- its merge-check.sh (go toolchain)
ok  	github.com/novox/mesh-host/cmd/mesh-bootstrap	1.013s
ok  	github.com/novox/mesh-host/cmd/mesh-host	5.471s
ok  	github.com/novox/mesh-host/examples	1.018s
ok  	github.com/novox/mesh-host/internal/accounts	1.023s
ok  	github.com/novox/mesh-host/internal/apply	4.555s
ok  	github.com/novox/mesh-host/internal/bootstrap	1.509s
ok  	github.com/novox/mesh-host/internal/bundle	1.018s
ok  	github.com/novox/mesh-host/internal/declaration	1.045s
ok  	github.com/novox/mesh-host/internal/firewall	1.663s
ok  	github.com/novox/mesh-host/internal/identity	1.083s
ok  	github.com/novox/mesh-host/internal/image	1.019s
ok  	github.com/novox/mesh-host/internal/inventory	1.020s
ok  	github.com/novox/mesh-host/internal/link	10.461s
ok  	github.com/novox/mesh-host/internal/liveness	2.098s
ok  	github.com/novox/mesh-host/internal/meshcli	1.378s
ok  	github.com/novox/mesh-host/internal/network	1.077s
ok  	github.com/novox/mesh-host/internal/outward	1.064s
ok  	github.com/novox/mesh-host/internal/profile	1.530s
ok  	github.com/novox/mesh-host/internal/reachable	1.063s
ok  	github.com/novox/mesh-host/internal/store	1.233s
ok  	github.com/novox/mesh-host/internal/system	1.066s
ok  	github.com/novox/mesh-host/internal/tunnel	1.070s
ok  	github.com/novox/mesh-host/internal/units	1.059s
ok  	github.com/novox/mesh-host/internal/upgrade	1.181s
ok  	github.com/novox/mesh-host/internal/witness	4.627s
ok  	github.com/novox/mesh-host/packaging	1.060s
?   	github.com/novox/mesh-host/rootsearch	[no test files]
ok  	github.com/novox/mesh-host/validate	1.017s
**Merge check** at `fc6c5a92` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-host) - `mesh/repo-check`: **PASS** — its merge-check.sh passed **Delivery plan** — builds mesh-host → ace, g14, novox, shanks; no bus step - tier 0: mesh-host - ace: receives mesh-host - g14: receives mesh-host - novox: receives mesh-host - shanks: receives mesh-host Run by the build seat on ace as `build-1791567965774413759` (`builds --log build-1791567965774413759`). ``` --- the gate: mesh-host mesh-host: ok long-running resources without health: 0 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 0 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 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 1 module(s) in 1 tier(s): mesh-host --- its merge-check.sh (go toolchain) ok github.com/novox/mesh-host/cmd/mesh-bootstrap 1.013s ok github.com/novox/mesh-host/cmd/mesh-host 5.471s ok github.com/novox/mesh-host/examples 1.018s ok github.com/novox/mesh-host/internal/accounts 1.023s ok github.com/novox/mesh-host/internal/apply 4.555s ok github.com/novox/mesh-host/internal/bootstrap 1.509s ok github.com/novox/mesh-host/internal/bundle 1.018s ok github.com/novox/mesh-host/internal/declaration 1.045s ok github.com/novox/mesh-host/internal/firewall 1.663s ok github.com/novox/mesh-host/internal/identity 1.083s ok github.com/novox/mesh-host/internal/image 1.019s ok github.com/novox/mesh-host/internal/inventory 1.020s ok github.com/novox/mesh-host/internal/link 10.461s ok github.com/novox/mesh-host/internal/liveness 2.098s ok github.com/novox/mesh-host/internal/meshcli 1.378s ok github.com/novox/mesh-host/internal/network 1.077s ok github.com/novox/mesh-host/internal/outward 1.064s ok github.com/novox/mesh-host/internal/profile 1.530s ok github.com/novox/mesh-host/internal/reachable 1.063s ok github.com/novox/mesh-host/internal/store 1.233s ok github.com/novox/mesh-host/internal/system 1.066s ok github.com/novox/mesh-host/internal/tunnel 1.070s ok github.com/novox/mesh-host/internal/units 1.059s ok github.com/novox/mesh-host/internal/upgrade 1.181s ok github.com/novox/mesh-host/internal/witness 4.627s ok github.com/novox/mesh-host/packaging 1.060s ? github.com/novox/mesh-host/rootsearch [no test files] ok github.com/novox/mesh-host/validate 1.017s ```
jschoubben added 1 commit 2026-10-09 17:58:29 +00:00
Give a handed-over directory through no link on every apply, not only the first (hq issue 356, review)
mesh/merge-gate pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check fail: its merge-check.sh failed: --- FAIL: TestTheSetuidSearchRunsInTheBackgroundAndIsKeptForItsInterval (0.00s)
mesh/delivery superseded: a newer head of the same pull request
9a807de659
After the first apply the record is no longer used as found, so the directory read as the mesh's own and later applies set its mode and owner by path: a parent swapped for a link after the hand-over took carried root's chmod and chown to its target. The hand-over is now checked before that shortcut, so a handed-over directory always goes through applyHandedOver. The test swaps the parent and the grandparent after an apply already gave the directory its mode; both cases fail with the old order.
Author
Contributor

Confirmation review addressed. Head 9a807de.

The hand-over is now checked before the ours shortcut in applyDirectory: if existed && HandedOver()[handOverKey(r.ID, r.Path)] { return applyHandedOver(...) }. A handed-over directory therefore goes through the re-check and the no-link descriptor on every apply, not only the first. Before this, after the first apply the record was no longer as found, so the directory read as the mesh's own and later applies used chmodDirPath and own by path.

TestAHandedOverDirectoryIsChangedThroughNoLink gains two cases: the parent, and the grandparent, swapped for a link after an apply already gave the directory its mode (the record is checked to be no longer as found first). With the old branch order both new cases fail ("a handed-over directory reached through a link was applied"); with the fix both pass.

make check and merge-check.sh exit 0 at 9a807de. Main has not moved since b0d03be, so the branch is the merge result.

The same by-path gap for ordinary mesh directories outside a home is older than this issue and is not fixed here. It is recorded as hq issue 358 (hq #258).

Confirmation review addressed. Head **9a807de**. The hand-over is now checked before the `ours` shortcut in `applyDirectory`: `if existed && HandedOver()[handOverKey(r.ID, r.Path)] { return applyHandedOver(...) }`. A handed-over directory therefore goes through the re-check and the no-link descriptor on every apply, not only the first. Before this, after the first apply the record was no longer as found, so the directory read as the mesh's own and later applies used `chmodDirPath` and `own` by path. `TestAHandedOverDirectoryIsChangedThroughNoLink` gains two cases: the parent, and the grandparent, swapped for a link *after an apply already gave the directory its mode* (the record is checked to be no longer as found first). With the old branch order both new cases fail ("a handed-over directory reached through a link was applied"); with the fix both pass. `make check` and `merge-check.sh` exit 0 at 9a807de. Main has not moved since b0d03be, so the branch is the merge result. The same by-path gap for ordinary mesh directories outside a home is older than this issue and is not fixed here. It is recorded as hq issue 358 (hq #258).
Author
Contributor

Merge check at 9a807de6

  • mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-host)
  • mesh/repo-check: FAIL — its merge-check.sh failed: --- FAIL: TestTheSetuidSearchRunsInTheBackgroundAndIsKeptForItsInterval (0.00s)

Delivery plan — builds mesh-host → ace, g14, novox, shanks; no bus step

  • tier 0: mesh-host
  • ace: receives mesh-host
  • g14: receives mesh-host
  • novox: receives mesh-host
  • shanks: receives mesh-host

Run by the build seat on shanks as build-1791568726990600635 (builds --log build-1791568726990600635).

--- the gate: mesh-host
mesh-host: ok
long-running resources without health: 0
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 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-09T17:55: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:55:02Z

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 1 module(s) in 1 tier(s): mesh-host
--- its merge-check.sh (go toolchain)
ok  	github.com/novox/mesh-host/cmd/mesh-bootstrap	1.016s
ok  	github.com/novox/mesh-host/cmd/mesh-host	5.504s
ok  	github.com/novox/mesh-host/examples	1.020s
--- FAIL: TestTheSetuidSearchRunsInTheBackgroundAndIsKeptForItsInterval (0.00s)
    ways_test.go:158: searched 1 times, want 2: once at first, once when the result was older than its interval
FAIL
FAIL	github.com/novox/mesh-host/internal/accounts	0.025s
ok  	github.com/novox/mesh-host/internal/apply	4.676s
ok  	github.com/novox/mesh-host/internal/bootstrap	1.602s
ok  	github.com/novox/mesh-host/internal/bundle	1.030s
ok  	github.com/novox/mesh-host/internal/declaration	1.066s
ok  	github.com/novox/mesh-host/internal/firewall	1.840s
ok  	github.com/novox/mesh-host/internal/identity	1.102s
ok  	github.com/novox/mesh-host/internal/image	1.041s
ok  	github.com/novox/mesh-host/internal/inventory	1.043s
ok  	github.com/novox/mesh-host/internal/link	10.471s
ok  	github.com/novox/mesh-host/internal/liveness	2.076s
ok  	github.com/novox/mesh-host/internal/meshcli	1.360s
ok  	github.com/novox/mesh-host/internal/network	1.062s
ok  	github.com/novox/mesh-host/internal/outward	1.048s
ok  	github.com/novox/mesh-host/internal/profile	1.520s
ok  	github.com/novox/mesh-host/internal/reachable	1.033s
ok  	github.com/novox/mesh-host/internal/store	1.212s
ok  	github.com/novox/mesh-host/internal/system	1.036s
ok  	github.com/novox/mesh-host/internal/tunnel	1.035s
ok  	github.com/novox/mesh-host/internal/units	1.030s
ok  	github.com/novox/mesh-host/internal/upgrade	1.160s
ok  	github.com/novox/mesh-host/internal/witness	4.771s
ok  	github.com/novox/mesh-host/packaging	1.032s
?   	github.com/novox/mesh-host/rootsearch	[no test files]
ok  	github.com/novox/mesh-host/validate	1.028s
FAIL
**Merge check** at `9a807de6` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-host) - `mesh/repo-check`: **FAIL** — its merge-check.sh failed: --- FAIL: TestTheSetuidSearchRunsInTheBackgroundAndIsKeptForItsInterval (0.00s) **Delivery plan** — builds mesh-host → ace, g14, novox, shanks; no bus step - tier 0: mesh-host - ace: receives mesh-host - g14: receives mesh-host - novox: receives mesh-host - shanks: receives mesh-host Run by the build seat on shanks as `build-1791568726990600635` (`builds --log build-1791568726990600635`). ``` --- the gate: mesh-host mesh-host: ok long-running resources without health: 0 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 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-09T17:55: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:55:02Z 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 1 module(s) in 1 tier(s): mesh-host --- its merge-check.sh (go toolchain) ok github.com/novox/mesh-host/cmd/mesh-bootstrap 1.016s ok github.com/novox/mesh-host/cmd/mesh-host 5.504s ok github.com/novox/mesh-host/examples 1.020s --- FAIL: TestTheSetuidSearchRunsInTheBackgroundAndIsKeptForItsInterval (0.00s) ways_test.go:158: searched 1 times, want 2: once at first, once when the result was older than its interval FAIL FAIL github.com/novox/mesh-host/internal/accounts 0.025s ok github.com/novox/mesh-host/internal/apply 4.676s ok github.com/novox/mesh-host/internal/bootstrap 1.602s ok github.com/novox/mesh-host/internal/bundle 1.030s ok github.com/novox/mesh-host/internal/declaration 1.066s ok github.com/novox/mesh-host/internal/firewall 1.840s ok github.com/novox/mesh-host/internal/identity 1.102s ok github.com/novox/mesh-host/internal/image 1.041s ok github.com/novox/mesh-host/internal/inventory 1.043s ok github.com/novox/mesh-host/internal/link 10.471s ok github.com/novox/mesh-host/internal/liveness 2.076s ok github.com/novox/mesh-host/internal/meshcli 1.360s ok github.com/novox/mesh-host/internal/network 1.062s ok github.com/novox/mesh-host/internal/outward 1.048s ok github.com/novox/mesh-host/internal/profile 1.520s ok github.com/novox/mesh-host/internal/reachable 1.033s ok github.com/novox/mesh-host/internal/store 1.212s ok github.com/novox/mesh-host/internal/system 1.036s ok github.com/novox/mesh-host/internal/tunnel 1.035s ok github.com/novox/mesh-host/internal/units 1.030s ok github.com/novox/mesh-host/internal/upgrade 1.160s ok github.com/novox/mesh-host/internal/witness 4.771s ok github.com/novox/mesh-host/packaging 1.032s ? github.com/novox/mesh-host/rootsearch [no test files] ok github.com/novox/mesh-host/validate 1.028s FAIL ```
jschoubben added 1 commit 2026-10-09 18:04:13 +00:00
Keep the setuid search's time on the caller's clock
mesh/merge-gate pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery failed: its walk failed: a gate on a first machine (what it carried put back), a build, a machine
8fc1d72cd5
SetuidCache compared the caller's now with a time read from the wall clock when the search finished. When the caller's clock ran behind the wall clock, a search was never due again: TestTheSetuidSearchRunsInTheBackgroundAndIsKeptForItsInterval, whose clock is a fixed date, has failed on main and every branch since 2026-10-09 18:00 UTC. A search's time is now the now it was started at; a result reads at most one search older than it is, which errs towards searching again.
Author
Contributor

9a807de was rejected by mesh/repo-check, and not because of this change. The failing test is internal/accounts TestTheSetuidSearchRunsInTheBackgroundAndIsKeptForItsInterval, which this branch never touched. The same test fails on main (b0d03be) too.

Cause: SetuidCache.Look compares the caller's now with c.at, but c.at was read from the wall clock (time.Now()) when the search finished. The test's clock is a fixed date (2026-10-08 19:00 UTC). Its look at +24h is due only while the wall clock is earlier than 18:00 UTC on 2026-10-09. From then on the test fails on every branch; it passed at 17:48 and failed at 18:00.

Effect on a machine: it is the same defect. A caller whose clock runs behind the wall clock never searches again.

Fix: a separate commit, head 8fc1d72. A search's time (at, failedAt) is now the now it was started at, so there is one clock, the caller's. A result reads at most one search older than it is, which errs towards searching again. The test fails without the fix and passes with it (5 runs).

merge-check.sh and make check exit 0 at 8fc1d72. Main has not moved.

**9a807de was rejected by mesh/repo-check**, and not because of this change. The failing test is `internal/accounts` `TestTheSetuidSearchRunsInTheBackgroundAndIsKeptForItsInterval`, which this branch never touched. The same test fails on main (b0d03be) too. **Cause:** `SetuidCache.Look` compares the caller's `now` with `c.at`, but `c.at` was read from the wall clock (`time.Now()`) when the search finished. The test's clock is a fixed date (2026-10-08 19:00 UTC). Its look at +24h is due only while the wall clock is earlier than 18:00 UTC on 2026-10-09. From then on the test fails on every branch; it passed at 17:48 and failed at 18:00. **Effect on a machine:** it is the same defect. A caller whose clock runs behind the wall clock never searches again. **Fix:** a separate commit, head **8fc1d72**. A search's time (`at`, `failedAt`) is now the `now` it was started at, so there is one clock, the caller's. A result reads at most one search older than it is, which errs towards searching again. The test fails without the fix and passes with it (5 runs). `merge-check.sh` and `make check` exit 0 at 8fc1d72. Main has not moved.
Author
Contributor

Merge check at 8fc1d72c

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

Delivery plan — builds mesh-host → ace, g14, novox, shanks; no bus step

  • tier 0: mesh-host
  • ace: receives mesh-host
  • g14: receives mesh-host
  • novox: receives mesh-host
  • shanks: receives mesh-host

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

--- the gate: mesh-host
mesh-host: ok
long-running resources without health: 0
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 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-09T17:59:32Z 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:59:32Z

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 1 module(s) in 1 tier(s): mesh-host
--- its merge-check.sh (go toolchain)
ok  	github.com/novox/mesh-host/cmd/mesh-bootstrap	1.012s
ok  	github.com/novox/mesh-host/cmd/mesh-host	5.633s
ok  	github.com/novox/mesh-host/examples	1.016s
ok  	github.com/novox/mesh-host/internal/accounts	1.020s
ok  	github.com/novox/mesh-host/internal/apply	2.457s
ok  	github.com/novox/mesh-host/internal/bootstrap	1.403s
ok  	github.com/novox/mesh-host/internal/bundle	1.031s
ok  	github.com/novox/mesh-host/internal/declaration	1.061s
ok  	github.com/novox/mesh-host/internal/firewall	1.789s
ok  	github.com/novox/mesh-host/internal/identity	1.065s
ok  	github.com/novox/mesh-host/internal/image	1.027s
ok  	github.com/novox/mesh-host/internal/inventory	1.032s
ok  	github.com/novox/mesh-host/internal/link	10.495s
ok  	github.com/novox/mesh-host/internal/liveness	2.064s
ok  	github.com/novox/mesh-host/internal/meshcli	1.352s
ok  	github.com/novox/mesh-host/internal/network	1.050s
ok  	github.com/novox/mesh-host/internal/outward	1.035s
ok  	github.com/novox/mesh-host/internal/profile	1.531s
ok  	github.com/novox/mesh-host/internal/reachable	1.029s
ok  	github.com/novox/mesh-host/internal/store	1.097s
ok  	github.com/novox/mesh-host/internal/system	1.012s
ok  	github.com/novox/mesh-host/internal/tunnel	1.015s
ok  	github.com/novox/mesh-host/internal/units	1.007s
ok  	github.com/novox/mesh-host/internal/upgrade	1.049s
ok  	github.com/novox/mesh-host/internal/witness	1.442s
ok  	github.com/novox/mesh-host/packaging	1.008s
?   	github.com/novox/mesh-host/rootsearch	[no test files]
ok  	github.com/novox/mesh-host/validate	1.007s
**Merge check** at `8fc1d72c` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: mesh-host) - `mesh/repo-check`: **PASS** — its merge-check.sh passed **Delivery plan** — builds mesh-host → ace, g14, novox, shanks; no bus step - tier 0: mesh-host - ace: receives mesh-host - g14: receives mesh-host - novox: receives mesh-host - shanks: receives mesh-host Run by the build seat on g14 as `build-1791569060643270655` (`builds --log build-1791569060643270655`). ``` --- the gate: mesh-host mesh-host: ok long-running resources without health: 0 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 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-09T17:59:32Z 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:59:32Z 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 1 module(s) in 1 tier(s): mesh-host --- its merge-check.sh (go toolchain) ok github.com/novox/mesh-host/cmd/mesh-bootstrap 1.012s ok github.com/novox/mesh-host/cmd/mesh-host 5.633s ok github.com/novox/mesh-host/examples 1.016s ok github.com/novox/mesh-host/internal/accounts 1.020s ok github.com/novox/mesh-host/internal/apply 2.457s ok github.com/novox/mesh-host/internal/bootstrap 1.403s ok github.com/novox/mesh-host/internal/bundle 1.031s ok github.com/novox/mesh-host/internal/declaration 1.061s ok github.com/novox/mesh-host/internal/firewall 1.789s ok github.com/novox/mesh-host/internal/identity 1.065s ok github.com/novox/mesh-host/internal/image 1.027s ok github.com/novox/mesh-host/internal/inventory 1.032s ok github.com/novox/mesh-host/internal/link 10.495s ok github.com/novox/mesh-host/internal/liveness 2.064s ok github.com/novox/mesh-host/internal/meshcli 1.352s ok github.com/novox/mesh-host/internal/network 1.050s ok github.com/novox/mesh-host/internal/outward 1.035s ok github.com/novox/mesh-host/internal/profile 1.531s ok github.com/novox/mesh-host/internal/reachable 1.029s ok github.com/novox/mesh-host/internal/store 1.097s ok github.com/novox/mesh-host/internal/system 1.012s ok github.com/novox/mesh-host/internal/tunnel 1.015s ok github.com/novox/mesh-host/internal/units 1.007s ok github.com/novox/mesh-host/internal/upgrade 1.049s ok github.com/novox/mesh-host/internal/witness 1.442s ok github.com/novox/mesh-host/packaging 1.008s ? github.com/novox/mesh-host/rootsearch [no test files] ok github.com/novox/mesh-host/validate 1.007s ```
mesh-admin merged commit 090b9082eb into main 2026-10-09 18:07:21 +00:00
mesh-admin deleted branch fix/356-hand-over-by-nox-and-the-engine-on-path-renewed 2026-10-09 18:07:22 +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-host#74