57/P2: The node-engine writes a filled file on the provisioner client's request, and nothing else (issue 668) #111

Merged
mesh-admin merged 7 commits from pkg/57-p2-local-write-verb into feat/57-config-and-provisioning 2026-10-14 01:15:13 +00:00
Contributor

What changed

  • A second local socket, /run/nox-mesh-host/fill.sock (internal/filling), beside mesh-cli's. It reuses mesh-cli's kernel checks (SO_PEERCRED, same mount and user namespaces, own socket directory), now exported from internal/meshcli/peer.go. The request is one JSON line {path, content} (content only); the answer is {path, action, digest, refused, failed}.
  • Account: the declaration gains provisioner_account (the provisioner client's account, by name), accepted by the strict parser, refused when root or not an account name. The contract is raised to 10 (link.ProvisionerContract), following P1's contract 9 and the units_test.go pattern. While the declaration names no account, every write is refused. The account is judged before the request is read, and again under the apply's lock.
  • Path: it must be clean and absolute (/etc//a, .., ./, a trailing slash and relative paths are refused), and the current declaration must name it in filled_files. Where the path leads (homes, agent homes, refused places, links under a home) is judged by the apply's own guards, because the write goes through applyOne.
  • Write: apply.WriteFilled applies the declared file with the client's content in place of the declaration's. An owned file goes through applyFile, a seed through applyFile's create-once, and a managed file through applyInto or applyBlock. The write keeps the Keep backup, the declared owner and mode (a file that carried a secret stays 0600), and the apply's record. Then, in the apply's order, it applies what reads the file: restart-on, reload-on, a process's restart-on, and a running container's env-file or a file mounted into it. Each of those is recorded as an apply records it. The apply loop's record and its "moved" rule became the helpers recordApplied and moved, with no change of behaviour.
  • Serialised with the apply: the write takes the same two locks every apply takes (applying in the process and store.Lock beside the state), reads the kept, verified declaration under them, and saves the state as an apply does.
  • Logs carry the path, the user id, the action and the digest, never the content.

Why

ADR 0349 §4, design 57 P2. The provisioner's client needs the node-engine to write what it fills, without holding the right to write the machine's files itself.

How it is proven (tests that fail before and pass now)

  • An undeclared path is refused: TestAnUndeclaredPathIsRefused and TestAWriteOfAPathNotFilledIsRefused.
  • Another account is refused: TestAnotherAccountIsRefused, TestTheSocketRefusesAnotherAccountBeforeReadingItsRequest (real socket, real SO_PEERCRED), TestEveryWriteIsRefusedWhileTheDeclarationNamesNoAccount and TestAnAccountThatIsRootHereIsRefused.
  • An unclean path is refused: TestAnUncleanPathIsRefused.
  • A write under reload-on reloads its program once, and the same write again reloads nothing: TestAWriteUnderReloadOnReloadsItsProgramOnce. Also TestAWriteUnderRestartOnRestartsItsProgram and TestAWriteOfAFileAContainerReadsRecreatesIt.
  • Owned, seed and managed are each written as P1 maps them, with the same action, bytes and mode as an apply of the same content: TestAFilledFileIsWrittenAsItsOwnershipSays (6 cases). Also TestAFilledFileThatCarriedASecretIsNotWorldReadable.
  • A write and an apply serialise: TestAWriteAndAnApplyOfTheSameFileSerialise (real locks: the write waits while an apply holds applying, and while another process holds store.Lock) and TestAWriteWaitsForTheApply.
  • The reader takes the new field: TestTheDeclarationNamesTheProvisionersAccount, TestAProvisionersAccountThatIsNoAccountIsRefused, validate_test and the contract check in units_test.

Commands run

sh merge-check.sh exits 0. It runs gofmt -l (clean), go vet, and go test -race -count=1 ./... (all pass). It prints "NOT RUN AGAINST A BUS": this machine holds no nats-server, so the bus tests in internal/link (those reading MESH_TEST_NATS) did not run. None of them is touched here.

Deliberately left

  • P7: with this change, a full apply still writes a filled file from the declaration's content, so a reconcile would write over the client's content. P7 makes the apply stop doing that for a moved module. No controller sends filled_files or provisioner_account before P6, so nothing changes on a node until then.
  • P3 and a later package create the provisioner client's account. P6 (the controller) sends provisioner_account only to a host saying contract 10.
  • P5 (the client) needs the socket path and the wire shape. They live in internal/filling here; moving them to mesh-sdk is P5's call.
  • A filled path declared by two resources is refused, because the request cannot say whose keys or block it carries.

Reviewer's checklist

  1. Scope: the issue's Expected and design 57's P2 only. The socket, the account, the path rules and the write through the apply.
  2. Done-when tests exist and fail before this change. They do not compile on the base, and the behaviour is new. Existing tests are untouched, except units_test and validate_test, which this change extends as P1 did.
  3. Composition: nothing composes differently for a module that has not moved. The apply loop's refactor keeps its behaviour, and the full suite passes.
  4. No secret in a log or fixture: the content is never logged, and the tests assert it. No database is touched.
  5. Strict parser: provisioner_account is read before any controller writes it, and the contract is raised to 10.
  6. gofmt, go vet, go test -race ./... and merge-check.sh pass. The bus tests did not run (no nats-server).
  7. Words follow the glossary (node-engine, controller, declaration, provisioner's client). ADR and package names are explained where they first appear.
## What changed - **A second local socket**, `/run/nox-mesh-host/fill.sock` (`internal/filling`), beside mesh-cli's. It reuses mesh-cli's kernel checks (SO_PEERCRED, same mount and user namespaces, own socket directory), now exported from `internal/meshcli/peer.go`. The request is one JSON line `{path, content}` (content only); the answer is `{path, action, digest, refused, failed}`. - **Account**: the declaration gains `provisioner_account` (the provisioner client's account, by name), accepted by the strict parser, refused when root or not an account name. The contract is raised to 10 (`link.ProvisionerContract`), following P1's contract 9 and the units_test.go pattern. While the declaration names no account, every write is refused. The account is judged before the request is read, and again under the apply's lock. - **Path**: it must be clean and absolute (`/etc//a`, `..`, `./`, a trailing slash and relative paths are refused), and the current declaration must name it in `filled_files`. Where the path leads (homes, agent homes, refused places, links under a home) is judged by the apply's own guards, because the write goes through `applyOne`. - **Write**: `apply.WriteFilled` applies the declared file with the client's content in place of the declaration's. An owned file goes through applyFile, a seed through applyFile's create-once, and a managed file through applyInto or applyBlock. The write keeps the Keep backup, the declared owner and mode (a file that carried a secret stays 0600), and the apply's record. Then, in the apply's order, it applies what reads the file: restart-on, reload-on, a process's restart-on, and a running container's env-file or a file mounted into it. Each of those is recorded as an apply records it. The apply loop's record and its "moved" rule became the helpers `recordApplied` and `moved`, with no change of behaviour. - **Serialised with the apply**: the write takes the same two locks every apply takes (`applying` in the process and `store.Lock` beside the state), reads the kept, verified declaration under them, and saves the state as an apply does. - **Logs** carry the path, the user id, the action and the digest, never the content. ## Why ADR 0349 §4, design 57 P2. The provisioner's client needs the node-engine to write what it fills, without holding the right to write the machine's files itself. ## How it is proven (tests that fail before and pass now) - An undeclared path is refused: `TestAnUndeclaredPathIsRefused` and `TestAWriteOfAPathNotFilledIsRefused`. - Another account is refused: `TestAnotherAccountIsRefused`, `TestTheSocketRefusesAnotherAccountBeforeReadingItsRequest` (real socket, real SO_PEERCRED), `TestEveryWriteIsRefusedWhileTheDeclarationNamesNoAccount` and `TestAnAccountThatIsRootHereIsRefused`. - An unclean path is refused: `TestAnUncleanPathIsRefused`. - A write under reload-on reloads its program once, and the same write again reloads nothing: `TestAWriteUnderReloadOnReloadsItsProgramOnce`. Also `TestAWriteUnderRestartOnRestartsItsProgram` and `TestAWriteOfAFileAContainerReadsRecreatesIt`. - Owned, seed and managed are each written as P1 maps them, with the same action, bytes and mode as an apply of the same content: `TestAFilledFileIsWrittenAsItsOwnershipSays` (6 cases). Also `TestAFilledFileThatCarriedASecretIsNotWorldReadable`. - A write and an apply serialise: `TestAWriteAndAnApplyOfTheSameFileSerialise` (real locks: the write waits while an apply holds `applying`, and while another process holds `store.Lock`) and `TestAWriteWaitsForTheApply`. - The reader takes the new field: `TestTheDeclarationNamesTheProvisionersAccount`, `TestAProvisionersAccountThatIsNoAccountIsRefused`, `validate_test` and the contract check in `units_test`. ## Commands run `sh merge-check.sh` exits 0. It runs gofmt -l (clean), go vet, and `go test -race -count=1 ./...` (all pass). It prints "NOT RUN AGAINST A BUS": this machine holds no nats-server, so the bus tests in `internal/link` (those reading MESH_TEST_NATS) did not run. None of them is touched here. ## Deliberately left - **P7**: with this change, a full apply still writes a filled file from the declaration's content, so a reconcile would write over the client's content. P7 makes the apply stop doing that for a moved module. No controller sends `filled_files` or `provisioner_account` before P6, so nothing changes on a node until then. - **P3 and a later package** create the provisioner client's account. **P6** (the controller) sends `provisioner_account` only to a host saying contract 10. - **P5** (the client) needs the socket path and the wire shape. They live in `internal/filling` here; moving them to mesh-sdk is P5's call. - A filled path declared by two resources is refused, because the request cannot say whose keys or block it carries. ## Reviewer's checklist 1. [x] Scope: the issue's Expected and design 57's P2 only. The socket, the account, the path rules and the write through the apply. 2. [x] Done-when tests exist and fail before this change. They do not compile on the base, and the behaviour is new. Existing tests are untouched, except units_test and validate_test, which this change extends as P1 did. 3. [x] Composition: nothing composes differently for a module that has not moved. The apply loop's refactor keeps its behaviour, and the full suite passes. 4. [x] No secret in a log or fixture: the content is never logged, and the tests assert it. No database is touched. 5. [x] Strict parser: `provisioner_account` is read before any controller writes it, and the contract is raised to 10. 6. [x] gofmt, go vet, go test -race ./... and merge-check.sh pass. The bus tests did not run (no nats-server). 7. [x] Words follow the glossary (node-engine, controller, declaration, provisioner's client). ADR and package names are explained where they first appear.
mesh-admin added 4 commits 2026-10-14 00:37:57 +00:00
The local write of a filled file (ADR 0349 §4) accepts only the provisioner client's account, which the declaration names; the reader takes provisioner_account before any controller sends it (contract 10). The apply's record and its rule for what moved become helpers the write uses too. Issue 668.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The provisioner's client (ADR 0349 §4) fills a module's templates and needs the node-engine to write them: it must not hold the right to write the machine's files itself. A second local socket accepts a write only from the account the declaration names as the provisioner client's (SO_PEERCRED, same namespaces), only for a clean path the declaration names as filled, under the apply's own locks, and writes it through the apply as the declared file would be, firing restart-on, reload-on and container reads. Issue 668.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A filled file is the declared file with the client's content: applied through applyFile, applyInto or applyBlock by who owns it at runtime, with the apply's guards, Keep and record, then restart-on, reload-on and container reads applied as an apply does. Issue 668.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Serve the local write of a filled file on its own socket
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate head 499a687149be merged into trunk 6b54fed706f0 — pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes wi…
mesh/repo-check head 499a687149be merged into trunk 6b54fed706f0 — pass: its merge-check.sh passed
499a687149
A second unix socket beside mesh-cli's: the kernel's word on the asking account (SO_PEERCRED, same namespaces), that account judged against the declaration before its request is read, the path judged clean, absolute and filled, and the write done under the apply's lock. The journal has the path and digest, never the content. Issue 668.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Contributor

Delivery novox/mesh-host@2767278228bc — delivered since 2026-10-14T01:15:52Z

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
  • grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before mesh-host is judged on its first machine

Transitions

  • 2026-10-14 01:07 (new) → proposed (announced): novox/mesh-host#111's head announced
  • 2026-10-14 01:14 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit
  • 2026-10-14 01:14 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
  • 2026-10-14 01:15 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
  • 2026-10-14 01:15 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 — not a failed walk that carried a later commit for it, after which it waits for a walk of its own commit
  • 2026-10-14 01:15 delivering → delivered (done): its walk is done, carrying its own commit: 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-host@2767278228bc` — **delivered** since 2026-10-14T01:15:52Z **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 - grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before mesh-host is judged on its first machine **Transitions** - 2026-10-14 01:07 (new) → proposed (announced): novox/mesh-host#111's head announced - 2026-10-14 01:14 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit - 2026-10-14 01:14 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail - 2026-10-14 01:15 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move - 2026-10-14 01:15 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 — not a failed walk that carried a later commit for it, after which it waits for a walk of its own commit - 2026-10-14 01:15 delivering → delivered (done): its walk is done, carrying its own commit: 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

Merge check at 499a6871
Judged: head 499a687149be merged into trunk 6b54fed706f0; read beside it: mesh-catalog ebf7f3773dc7, mesh-controller 2d18f438c516, mesh-controller-main 2d18f438c516, mesh-tools 954e1a53eef9

  • 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
  • grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before mesh-host is judged on its first machine
  • read by no module's build: cmd/mesh-host/filling_test.go, cmd/mesh-host/units_test.go, internal/apply/filled_test.go, internal/declaration/provisioner_test.go, internal/filling/filling.go, internal/filling/filling_test.go, validate/validate_test.go

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

--- 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-14T00:35:24Z 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-14T00:35:24Z

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

notes:
  - cmd/mesh-host/filling_test.go, cmd/mesh-host/units_test.go, internal/apply/filled_test.go, internal/declaration/provisioner_test.go, internal/filling/filling.go, internal/filling/filling_test.go and validate/validate_test.go read by no module's build, and rebuild nothing (issue 280)
--- 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.853s
ok  	github.com/novox/mesh-host/examples	1.027s
ok  	github.com/novox/mesh-host/internal/accounts	1.437s
ok  	github.com/novox/mesh-host/internal/apply	14.369s
ok  	github.com/novox/mesh-host/internal/bootstrap	4.108s
ok  	github.com/novox/mesh-host/internal/bundle	1.012s
ok  	github.com/novox/mesh-host/internal/declaration	1.053s
ok  	github.com/novox/mesh-host/internal/filling	1.173s
ok  	github.com/novox/mesh-host/internal/firewall	1.694s
ok  	github.com/novox/mesh-host/internal/identity	1.134s
ok  	github.com/novox/mesh-host/internal/image	1.059s
ok  	github.com/novox/mesh-host/internal/inventory	1.081s
ok  	github.com/novox/mesh-host/internal/link	10.500s
ok  	github.com/novox/mesh-host/internal/liveness	2.132s
ok  	github.com/novox/mesh-host/internal/meshcli	1.585s
ok  	github.com/novox/mesh-host/internal/network	1.094s
ok  	github.com/novox/mesh-host/internal/outward	1.085s
ok  	github.com/novox/mesh-host/internal/profile	1.653s
ok  	github.com/novox/mesh-host/internal/reachable	1.085s
ok  	github.com/novox/mesh-host/internal/store	1.245s
ok  	github.com/novox/mesh-host/internal/system	1.053s
ok  	github.com/novox/mesh-host/internal/tunnel	1.055s
ok  	github.com/novox/mesh-host/internal/units	1.078s
ok  	github.com/novox/mesh-host/internal/upgrade	1.206s
ok  	github.com/novox/mesh-host/internal/witness	15.005s
ok  	github.com/novox/mesh-host/packaging	1.047s
?   	github.com/novox/mesh-host/rootsearch	[no test files]
ok  	github.com/novox/mesh-host/validate	1.011s
**Merge check** at `499a6871` Judged: head `499a687149be` merged into trunk `6b54fed706f0`; read beside it: mesh-catalog `ebf7f3773dc7`, mesh-controller `2d18f438c516`, mesh-controller-main `2d18f438c516`, mesh-tools `954e1a53eef9` - `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 - grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before mesh-host is judged on its first machine - read by no module's build: cmd/mesh-host/filling_test.go, cmd/mesh-host/units_test.go, internal/apply/filled_test.go, internal/declaration/provisioner_test.go, internal/filling/filling.go, internal/filling/filling_test.go, validate/validate_test.go Run by the build seat on ace as `build-1791938297026340805` (`builds --log build-1791938297026340805`). ``` --- 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-14T00:35:24Z 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-14T00:35:24Z 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 notes: - cmd/mesh-host/filling_test.go, cmd/mesh-host/units_test.go, internal/apply/filled_test.go, internal/declaration/provisioner_test.go, internal/filling/filling.go, internal/filling/filling_test.go and validate/validate_test.go read by no module's build, and rebuild nothing (issue 280) --- 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.853s ok github.com/novox/mesh-host/examples 1.027s ok github.com/novox/mesh-host/internal/accounts 1.437s ok github.com/novox/mesh-host/internal/apply 14.369s ok github.com/novox/mesh-host/internal/bootstrap 4.108s ok github.com/novox/mesh-host/internal/bundle 1.012s ok github.com/novox/mesh-host/internal/declaration 1.053s ok github.com/novox/mesh-host/internal/filling 1.173s ok github.com/novox/mesh-host/internal/firewall 1.694s ok github.com/novox/mesh-host/internal/identity 1.134s ok github.com/novox/mesh-host/internal/image 1.059s ok github.com/novox/mesh-host/internal/inventory 1.081s ok github.com/novox/mesh-host/internal/link 10.500s ok github.com/novox/mesh-host/internal/liveness 2.132s ok github.com/novox/mesh-host/internal/meshcli 1.585s ok github.com/novox/mesh-host/internal/network 1.094s ok github.com/novox/mesh-host/internal/outward 1.085s ok github.com/novox/mesh-host/internal/profile 1.653s ok github.com/novox/mesh-host/internal/reachable 1.085s ok github.com/novox/mesh-host/internal/store 1.245s ok github.com/novox/mesh-host/internal/system 1.053s ok github.com/novox/mesh-host/internal/tunnel 1.055s ok github.com/novox/mesh-host/internal/units 1.078s ok github.com/novox/mesh-host/internal/upgrade 1.206s ok github.com/novox/mesh-host/internal/witness 15.005s ok github.com/novox/mesh-host/packaging 1.047s ? github.com/novox/mesh-host/rootsearch [no test files] ok github.com/novox/mesh-host/validate 1.011s ```
Author
Contributor

Verdict: CHANGES at 499a687149

Ran in my own worktree of 499a687: gofmt -l . (clean), go vet ./... (clean), sh merge-check.sh (exit 0; it said NOT RUN AGAINST A BUS because this machine has no nats-server, so the bus tests in internal/link were skipped). What holds: SO_PEERCRED and the namespace check run before any byte of the request is read. The account is judged again under the lock. The lock order is applying and then store.Lock, the same as applyAndKeepHeld, so I found no deadlock. A client that is slow before its request is read holds no lock. The content is not logged. Contract 10 is raised the same way as P1 raised 9. A final-component link is replaced by the rename and never followed.

Must change:

  1. internal/apply/filled.go:69 — WriteFilled never sets in.held (the apply sets it at apply.go:720 from the library verdicts). A container that reads the filled file and mounts a library whose disk or mount is missing is recreated anyway. That bypasses the ADR 0328 §9 guard, so the container's writes land on the root filesystem. Compute the held roots, or refuse to recreate such a container, and add a test.
  2. internal/filling/filling.go:265-269 — the slot is taken before the peer is judged, and the socket is 0666. Any local account can keep all 4 slots busy: each refused connection drains for up to 1 s while it holds a slot. The provisioner's client then only ever gets busy. The busy refusals also drain for 1 s with no bound on how many run at once, so a connection flood costs one goroutine and one fd each. mesh-cli's socket judges the account before it takes a slot and bounds its waiting refusals (refusalsWaiting). Do the same here: judge the uid against a cheap check before taking a slot, and bound the drains.
  3. internal/filling/filling.go:257 — any accept error that is not a timeout (for example EMFILE under that flood) ends Serve, and with it the write verb, until the engine restarts. Back off and keep serving, as net/http does.

Should (can follow):
4. filling.go:220 — mesh-cli's Serve chmods the directory to 0755 after its ownership check (meshcli.go:219). This Serve does not, so it relies on mesh-cli's Serve having run.
5. filling.go:297 — the decoder does not call DisallowUnknownFields. Use it so the wire stays strict for P5.
6. No test shows that a silent client, while it holds the socket open, neither blocks an apply nor holds a slot past within. No test covers the two-resources refusal either.
7. Parent directories on the path are still checked by path name, not walked by descriptor (issue 556). This is inherited from applyFile, but the write can now happen whenever the client asks. Say so in issue 556.

Verdict: CHANGES at 499a687149beabb115c596e1d77eb1e30f6b2864 Ran in my own worktree of 499a687: `gofmt -l .` (clean), `go vet ./...` (clean), `sh merge-check.sh` (exit 0; it said NOT RUN AGAINST A BUS because this machine has no nats-server, so the bus tests in internal/link were skipped). What holds: SO_PEERCRED and the namespace check run before any byte of the request is read. The account is judged again under the lock. The lock order is `applying` and then `store.Lock`, the same as applyAndKeepHeld, so I found no deadlock. A client that is slow before its request is read holds no lock. The content is not logged. Contract 10 is raised the same way as P1 raised 9. A final-component link is replaced by the rename and never followed. Must change: 1. internal/apply/filled.go:69 — WriteFilled never sets `in.held` (the apply sets it at apply.go:720 from the library verdicts). A container that reads the filled file and mounts a library whose disk or mount is missing is recreated anyway. That bypasses the ADR 0328 §9 guard, so the container's writes land on the root filesystem. Compute the held roots, or refuse to recreate such a container, and add a test. 2. internal/filling/filling.go:265-269 — the slot is taken before the peer is judged, and the socket is 0666. Any local account can keep all 4 slots busy: each refused connection drains for up to 1 s while it holds a slot. The provisioner's client then only ever gets `busy`. The `busy` refusals also drain for 1 s with no bound on how many run at once, so a connection flood costs one goroutine and one fd each. mesh-cli's socket judges the account before it takes a slot and bounds its waiting refusals (`refusalsWaiting`). Do the same here: judge the uid against a cheap check before taking a slot, and bound the drains. 3. internal/filling/filling.go:257 — any accept error that is not a timeout (for example EMFILE under that flood) ends Serve, and with it the write verb, until the engine restarts. Back off and keep serving, as net/http does. Should (can follow): 4. filling.go:220 — mesh-cli's Serve chmods the directory to 0755 after its ownership check (meshcli.go:219). This Serve does not, so it relies on mesh-cli's Serve having run. 5. filling.go:297 — the decoder does not call DisallowUnknownFields. Use it so the wire stays strict for P5. 6. No test shows that a silent client, while it holds the socket open, neither blocks an apply nor holds a slot past `within`. No test covers the two-resources refusal either. 7. Parent directories on the path are still checked by path name, not walked by descriptor (issue 556). This is inherited from applyFile, but the write can now happen whenever the client asks. Say so in issue 556.
mesh-admin added 1 commit 2026-10-14 00:46:41 +00:00
Hold a library's containers through the write verb, as a full apply does
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate head 76dc93c421bf merged into trunk 6b54fed706f0 — pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes wi…
mesh/repo-check head 76dc93c421bf merged into trunk 6b54fed706f0 — pass: its merge-check.sh passed
76dc93c421
Review 11959 of mesh-host#111: a write through the verb recreated a container that reads the filled file over a library whose disk was missing (ADR 0328 §9). Also refuses a filled path two resources declare, with a test. Issue 668.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mesh-admin added 1 commit 2026-10-14 00:47:52 +00:00
Judge a caller before it holds the socket, and keep accepting after a failed accept
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate out of date: judged against trunk 6b54fed706f0, the trunk is now c18d5a8ff38d; judged again on a recheck or when it is merged
mesh/repo-check out of date: judged against trunk 6b54fed706f0, the trunk is now c18d5a8ff38d; judged again on a recheck or when it is merged
0c17c76493
Review 11959 of mesh-host#111: any account could hold the four slots for a second each and starve the provisioner's client, and one failed accept ended the socket until the engine restarted. A caller is judged from the kernel's word before it holds a slot and, refused, is answered and closed at once; busy refusals that wait are bounded as mesh-cli's are; a failed accept is said, backed off and retried. The request refuses unknown fields, and the socket's directory is given its mode. Issue 668.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Contributor

Merge check at 76dc93c4
Judged: head 76dc93c421bf merged into trunk 6b54fed706f0; read beside it: mesh-catalog ebf7f3773dc7, mesh-controller 2d18f438c516, mesh-controller-main 2d18f438c516, mesh-tools 954e1a53eef9

  • 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
  • grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before mesh-host is judged on its first machine
  • read by no module's build: cmd/mesh-host/filling_test.go, cmd/mesh-host/units_test.go, internal/apply/filled_library_test.go, internal/apply/filled_test.go, internal/declaration/provisioner_test.go, internal/filling/filling.go, internal/filling/filling_test.go, validate/validate_test.go

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

--- 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-14T00:35:24Z 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-14T00:35:24Z

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

notes:
  - cmd/mesh-host/filling_test.go, cmd/mesh-host/units_test.go, internal/apply/filled_library_test.go, internal/apply/filled_test.go, internal/declaration/provisioner_test.go, internal/filling/filling.go, internal/filling/filling_test.go and validate/validate_test.go read by no module's build, and rebuild nothing (issue 280)
--- 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.859s
ok  	github.com/novox/mesh-host/examples	1.026s
ok  	github.com/novox/mesh-host/internal/accounts	1.453s
ok  	github.com/novox/mesh-host/internal/apply	5.267s
ok  	github.com/novox/mesh-host/internal/bootstrap	4.406s
ok  	github.com/novox/mesh-host/internal/bundle	1.052s
ok  	github.com/novox/mesh-host/internal/declaration	1.096s
ok  	github.com/novox/mesh-host/internal/filling	1.173s
ok  	github.com/novox/mesh-host/internal/firewall	1.854s
ok  	github.com/novox/mesh-host/internal/identity	1.133s
ok  	github.com/novox/mesh-host/internal/image	1.049s
ok  	github.com/novox/mesh-host/internal/inventory	1.076s
ok  	github.com/novox/mesh-host/internal/link	10.508s
ok  	github.com/novox/mesh-host/internal/liveness	2.150s
ok  	github.com/novox/mesh-host/internal/meshcli	1.583s
ok  	github.com/novox/mesh-host/internal/network	1.076s
ok  	github.com/novox/mesh-host/internal/outward	1.060s
ok  	github.com/novox/mesh-host/internal/profile	1.655s
ok  	github.com/novox/mesh-host/internal/reachable	1.056s
ok  	github.com/novox/mesh-host/internal/store	1.241s
ok  	github.com/novox/mesh-host/internal/system	1.046s
ok  	github.com/novox/mesh-host/internal/tunnel	1.053s
ok  	github.com/novox/mesh-host/internal/units	1.042s
ok  	github.com/novox/mesh-host/internal/upgrade	1.180s
ok  	github.com/novox/mesh-host/internal/witness	4.851s
ok  	github.com/novox/mesh-host/packaging	1.047s
?   	github.com/novox/mesh-host/rootsearch	[no test files]
ok  	github.com/novox/mesh-host/validate	1.009s
**Merge check** at `76dc93c4` Judged: head `76dc93c421bf` merged into trunk `6b54fed706f0`; read beside it: mesh-catalog `ebf7f3773dc7`, mesh-controller `2d18f438c516`, mesh-controller-main `2d18f438c516`, mesh-tools `954e1a53eef9` - `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 - grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before mesh-host is judged on its first machine - read by no module's build: cmd/mesh-host/filling_test.go, cmd/mesh-host/units_test.go, internal/apply/filled_library_test.go, internal/apply/filled_test.go, internal/declaration/provisioner_test.go, internal/filling/filling.go, internal/filling/filling_test.go, validate/validate_test.go Run by the build seat on shanks as `build-1791938823488081642` (`builds --log build-1791938823488081642`). ``` --- 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-14T00:35:24Z 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-14T00:35:24Z 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 notes: - cmd/mesh-host/filling_test.go, cmd/mesh-host/units_test.go, internal/apply/filled_library_test.go, internal/apply/filled_test.go, internal/declaration/provisioner_test.go, internal/filling/filling.go, internal/filling/filling_test.go and validate/validate_test.go read by no module's build, and rebuild nothing (issue 280) --- 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.859s ok github.com/novox/mesh-host/examples 1.026s ok github.com/novox/mesh-host/internal/accounts 1.453s ok github.com/novox/mesh-host/internal/apply 5.267s ok github.com/novox/mesh-host/internal/bootstrap 4.406s ok github.com/novox/mesh-host/internal/bundle 1.052s ok github.com/novox/mesh-host/internal/declaration 1.096s ok github.com/novox/mesh-host/internal/filling 1.173s ok github.com/novox/mesh-host/internal/firewall 1.854s ok github.com/novox/mesh-host/internal/identity 1.133s ok github.com/novox/mesh-host/internal/image 1.049s ok github.com/novox/mesh-host/internal/inventory 1.076s ok github.com/novox/mesh-host/internal/link 10.508s ok github.com/novox/mesh-host/internal/liveness 2.150s ok github.com/novox/mesh-host/internal/meshcli 1.583s ok github.com/novox/mesh-host/internal/network 1.076s ok github.com/novox/mesh-host/internal/outward 1.060s ok github.com/novox/mesh-host/internal/profile 1.655s ok github.com/novox/mesh-host/internal/reachable 1.056s ok github.com/novox/mesh-host/internal/store 1.241s ok github.com/novox/mesh-host/internal/system 1.046s ok github.com/novox/mesh-host/internal/tunnel 1.053s ok github.com/novox/mesh-host/internal/units 1.042s ok github.com/novox/mesh-host/internal/upgrade 1.180s ok github.com/novox/mesh-host/internal/witness 4.851s ok github.com/novox/mesh-host/packaging 1.047s ? github.com/novox/mesh-host/rootsearch [no test files] ok github.com/novox/mesh-host/validate 1.009s ```
Author
Contributor

Merge check at 0c17c764
Judged: head 0c17c76493f7 merged into trunk 6b54fed706f0; read beside it: mesh-catalog ebf7f3773dc7, mesh-controller 2d18f438c516, mesh-controller-main 2d18f438c516, mesh-tools 954e1a53eef9

  • 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
  • grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before mesh-host is judged on its first machine
  • read by no module's build: cmd/mesh-host/filling_test.go, cmd/mesh-host/units_test.go, internal/apply/filled_library_test.go, internal/apply/filled_test.go, internal/declaration/provisioner_test.go, internal/filling/filling.go, internal/filling/filling_test.go, validate/validate_test.go

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

--- 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-14T00:35:24Z 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-14T00:35:24Z

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

notes:
  - cmd/mesh-host/filling_test.go, cmd/mesh-host/units_test.go, internal/apply/filled_library_test.go, internal/apply/filled_test.go, internal/declaration/provisioner_test.go, internal/filling/filling.go, internal/filling/filling_test.go and validate/validate_test.go read by no module's build, and rebuild nothing (issue 280)
--- its merge-check.sh (go toolchain)
ok  	github.com/novox/mesh-host/cmd/mesh-bootstrap	1.014s
ok  	github.com/novox/mesh-host/cmd/mesh-host	5.830s
ok  	github.com/novox/mesh-host/examples	1.026s
ok  	github.com/novox/mesh-host/internal/accounts	1.444s
ok  	github.com/novox/mesh-host/internal/apply	5.073s
ok  	github.com/novox/mesh-host/internal/bootstrap	3.864s
ok  	github.com/novox/mesh-host/internal/bundle	1.037s
ok  	github.com/novox/mesh-host/internal/declaration	1.077s
ok  	github.com/novox/mesh-host/internal/filling	1.520s
ok  	github.com/novox/mesh-host/internal/firewall	1.660s
ok  	github.com/novox/mesh-host/internal/identity	1.113s
ok  	github.com/novox/mesh-host/internal/image	1.039s
ok  	github.com/novox/mesh-host/internal/inventory	1.065s
ok  	github.com/novox/mesh-host/internal/link	10.492s
ok  	github.com/novox/mesh-host/internal/liveness	2.111s
ok  	github.com/novox/mesh-host/internal/meshcli	1.573s
ok  	github.com/novox/mesh-host/internal/network	1.054s
ok  	github.com/novox/mesh-host/internal/outward	1.046s
ok  	github.com/novox/mesh-host/internal/profile	1.642s
ok  	github.com/novox/mesh-host/internal/reachable	1.042s
ok  	github.com/novox/mesh-host/internal/store	1.223s
ok  	github.com/novox/mesh-host/internal/system	1.045s
ok  	github.com/novox/mesh-host/internal/tunnel	1.053s
ok  	github.com/novox/mesh-host/internal/units	1.037s
ok  	github.com/novox/mesh-host/internal/upgrade	1.162s
ok  	github.com/novox/mesh-host/internal/witness	4.692s
ok  	github.com/novox/mesh-host/packaging	1.037s
?   	github.com/novox/mesh-host/rootsearch	[no test files]
ok  	github.com/novox/mesh-host/validate	1.009s
**Merge check** at `0c17c764` Judged: head `0c17c76493f7` merged into trunk `6b54fed706f0`; read beside it: mesh-catalog `ebf7f3773dc7`, mesh-controller `2d18f438c516`, mesh-controller-main `2d18f438c516`, mesh-tools `954e1a53eef9` - `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 - grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before mesh-host is judged on its first machine - read by no module's build: cmd/mesh-host/filling_test.go, cmd/mesh-host/units_test.go, internal/apply/filled_library_test.go, internal/apply/filled_test.go, internal/declaration/provisioner_test.go, internal/filling/filling.go, internal/filling/filling_test.go, validate/validate_test.go Run by the build seat on ace as `build-1791938891609958811` (`builds --log build-1791938891609958811`). ``` --- 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-14T00:35:24Z 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-14T00:35:24Z 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 notes: - cmd/mesh-host/filling_test.go, cmd/mesh-host/units_test.go, internal/apply/filled_library_test.go, internal/apply/filled_test.go, internal/declaration/provisioner_test.go, internal/filling/filling.go, internal/filling/filling_test.go and validate/validate_test.go read by no module's build, and rebuild nothing (issue 280) --- its merge-check.sh (go toolchain) ok github.com/novox/mesh-host/cmd/mesh-bootstrap 1.014s ok github.com/novox/mesh-host/cmd/mesh-host 5.830s ok github.com/novox/mesh-host/examples 1.026s ok github.com/novox/mesh-host/internal/accounts 1.444s ok github.com/novox/mesh-host/internal/apply 5.073s ok github.com/novox/mesh-host/internal/bootstrap 3.864s ok github.com/novox/mesh-host/internal/bundle 1.037s ok github.com/novox/mesh-host/internal/declaration 1.077s ok github.com/novox/mesh-host/internal/filling 1.520s ok github.com/novox/mesh-host/internal/firewall 1.660s ok github.com/novox/mesh-host/internal/identity 1.113s ok github.com/novox/mesh-host/internal/image 1.039s ok github.com/novox/mesh-host/internal/inventory 1.065s ok github.com/novox/mesh-host/internal/link 10.492s ok github.com/novox/mesh-host/internal/liveness 2.111s ok github.com/novox/mesh-host/internal/meshcli 1.573s ok github.com/novox/mesh-host/internal/network 1.054s ok github.com/novox/mesh-host/internal/outward 1.046s ok github.com/novox/mesh-host/internal/profile 1.642s ok github.com/novox/mesh-host/internal/reachable 1.042s ok github.com/novox/mesh-host/internal/store 1.223s ok github.com/novox/mesh-host/internal/system 1.045s ok github.com/novox/mesh-host/internal/tunnel 1.053s ok github.com/novox/mesh-host/internal/units 1.037s ok github.com/novox/mesh-host/internal/upgrade 1.162s ok github.com/novox/mesh-host/internal/witness 4.692s ok github.com/novox/mesh-host/packaging 1.037s ? github.com/novox/mesh-host/rootsearch [no test files] ok github.com/novox/mesh-host/validate 1.009s ```
Author
Contributor

Verdict: CHANGES at 0c17c76493

Ran in my own worktree of 0c17c76: gofmt -l . (clean), go vet ./... (clean), sh merge-check.sh with a nats-server on PATH (exit 0; all 28 packages pass, the bus tests in internal/link ran against a throwaway nats-server).

The earlier review (comment 11959) is fixed. I reverted each fix alone and saw its test fail:

  • 1 (library hold): without in.held = heldLibraries(...), TestAWriteDoesNotRecreateAContainerHeldForItsLibrary fails. heldLibraries holds only on libraryWaitDisk, the same as the full apply, and is computed under the lock.
  • 2 (slot before judging): with the slot taken and the request drained before judgePeer, TestAFloodFromAnotherAccountDoesNotStopTheClient fails (the client gets "busy").
  • 3 (accept error): with Serve returning on a failed accept, TestTheSocketGoesOnAfterAnAcceptFails fails, but only by hanging until the test timeout (see 4 below).
  • 4 (directory mode), 5 (DisallowUnknownFields), 6 (two resources at one path; a silent client against within): each test fails with its fix reverted.

Security pass. The peer is judged before any byte is read, and a refused caller is answered and closed without being read. No lock is held while the engine waits on a client, because the request is decoded before Write takes the lock and the reply is written after the lock is released. Memory is bounded: an 8 MiB LimitReader per request, at most 4 at once, and only the client's account gets that far. Accept back-off starts at 50 ms, doubles up to 5 s and resets on success, so it cannot spin, and ErrClosed and ctx end it.

Must change:

  1. internal/filling/filling.go:352-370 — the journal rate limit can hide a refusal entirely. Within a minute of a refusal that was said, every further refusal from that user id is only counted. The count is said only when a later refusal from the same user id comes a minute or more after. If that account stops, its counted refusals are never said. The limit is also keyed by user id alone, so a different reason is hidden too. For example, an account first gets a plain "not the provisioner's account" line, and its namespace refusal (or its unreadable-peer refusal, which all share the ^uint32(0) key) inside that minute leaves no reason in the journal. Say the first refusal of each (user id, kind) pair. Also flush the pending counts on a timer, and when Serve ends. Add a test for each.

Should (can follow):
2. filling.go:91 and :299 — Accepts loads the signed declaration again for every connection and verifies it with ed25519. Under a flood, 16 of those run at once, and the client waits in the same queue. Caching the provisioner's user id, keyed on the declaration file's identity, would make the check before the slot cheap.
3. filling.go:299 — each accepted connection waits for judging in its own goroutine with no bound except RLIMIT_NOFILE. Past that, accepts fail and back off for up to 5 s, which also delays the client. Consider a bounded count of connections waiting to be judged, past which a caller is closed at once.
4. filling_test.go:220 — ask sets no deadline, so a regression such as reverting fix 3 hangs the package for the 10-minute test timeout instead of failing. Give it a deadline.
5. filling.go:208 — busyWaiting is a package-level channel, while slots and judging belong to each Serve call. Make it one Serve's too, for consistency and for tests that run in parallel.

Verdict: CHANGES at 0c17c76493f774cafd82394ab78a02f0f35db226 Ran in my own worktree of 0c17c76: `gofmt -l .` (clean), `go vet ./...` (clean), `sh merge-check.sh` with a nats-server on PATH (exit 0; all 28 packages pass, the bus tests in internal/link ran against a throwaway nats-server). The earlier review (comment 11959) is fixed. I reverted each fix alone and saw its test fail: - 1 (library hold): without `in.held = heldLibraries(...)`, TestAWriteDoesNotRecreateAContainerHeldForItsLibrary fails. heldLibraries holds only on libraryWaitDisk, the same as the full apply, and is computed under the lock. - 2 (slot before judging): with the slot taken and the request drained before judgePeer, TestAFloodFromAnotherAccountDoesNotStopTheClient fails (the client gets "busy"). - 3 (accept error): with Serve returning on a failed accept, TestTheSocketGoesOnAfterAnAcceptFails fails, but only by hanging until the test timeout (see 4 below). - 4 (directory mode), 5 (DisallowUnknownFields), 6 (two resources at one path; a silent client against `within`): each test fails with its fix reverted. Security pass. The peer is judged before any byte is read, and a refused caller is answered and closed without being read. No lock is held while the engine waits on a client, because the request is decoded before Write takes the lock and the reply is written after the lock is released. Memory is bounded: an 8 MiB LimitReader per request, at most 4 at once, and only the client's account gets that far. Accept back-off starts at 50 ms, doubles up to 5 s and resets on success, so it cannot spin, and ErrClosed and ctx end it. Must change: 1. internal/filling/filling.go:352-370 — the journal rate limit can hide a refusal entirely. Within a minute of a refusal that was said, every further refusal from that user id is only counted. The count is said only when a later refusal from the same user id comes a minute or more after. If that account stops, its counted refusals are never said. The limit is also keyed by user id alone, so a different reason is hidden too. For example, an account first gets a plain "not the provisioner's account" line, and its namespace refusal (or its unreadable-peer refusal, which all share the ^uint32(0) key) inside that minute leaves no reason in the journal. Say the first refusal of each (user id, kind) pair. Also flush the pending counts on a timer, and when Serve ends. Add a test for each. Should (can follow): 2. filling.go:91 and :299 — Accepts loads the signed declaration again for every connection and verifies it with ed25519. Under a flood, 16 of those run at once, and the client waits in the same queue. Caching the provisioner's user id, keyed on the declaration file's identity, would make the check before the slot cheap. 3. filling.go:299 — each accepted connection waits for `judging` in its own goroutine with no bound except RLIMIT_NOFILE. Past that, accepts fail and back off for up to 5 s, which also delays the client. Consider a bounded count of connections waiting to be judged, past which a caller is closed at once. 4. filling_test.go:220 — `ask` sets no deadline, so a regression such as reverting fix 3 hangs the package for the 10-minute test timeout instead of failing. Give it a deadline. 5. filling.go:208 — busyWaiting is a package-level channel, while slots and judging belong to each Serve call. Make it one Serve's too, for consistency and for tests that run in parallel.
mesh-admin added 1 commit 2026-10-14 01:06:50 +00:00
Say every refusal kind at once and never lose a count; bound the waiting and the busy per socket
mesh/merge-gate head 2767278228bc merged into trunk c18d5a8ff38d — pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes wi…
mesh/repo-check head 2767278228bc merged into trunk c18d5a8ff38d — pass: its merge-check.sh passed
mesh/delivery delivered
2767278228
Review 12004 of mesh-host#111: refusals counted after a said one reached the journal only if the same account was refused again a minute later, and a refusal of another kind hid behind it. The first of each (account, kind) is said at once, and what is counted is said on a timer and when the socket stops. Connections waiting to be judged are bounded below the open-file limit, the busy refusals belong to one Serve, and the tests' ask has a deadline. Issue 668.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Contributor

Verdict: APPROVE at 2767278228

Ran in my own worktree of 2767278: gofmt -l . (clean), go vet ./... (clean), sh merge-check.sh with a nats-server on PATH (exit 0; all 28 packages pass, and the bus tests in internal/link ran). I also ran go test -race -count=10 ./internal/filling/, which passed, so the new timing tests are not flaky here.

The must-fix from comment 12004 is fixed. I reverted each part alone and saw its test fail:

  • Keyed by user id only (kind ignored): TestRefusalsAreSaidOnceAndTheirCountIsNeverLost fails.
  • No flush on the timer: TestTheSocketSaysItsCountedRefusalsOnATimer fails.
  • No flush when Serve ends: TestTheSocketSaysItsCountedRefusalsWhenItStops fails. The flush is deferred before wg.Wait, so it runs after every connection goroutine has ended, and nothing counted later is lost.

The small fixes hold. ask now has a 5 s deadline: with the accept fix reverted, TestTheSocketGoesOnAfterAnAcceptFails fails in 5 s instead of hanging. The busy-refusal cap is a channel made in each Serve. Connections waiting to be judged are capped at 256, and a connection over the cap is closed unread and counted as overflow: with the bound removed, TestAConnectionOverTheWaitingBoundIsClosedUnread fails.

Nothing from comment 11959 regressed. The delta since 0c17c76 touches only internal/filling, and the tests for the library hold, the flood, the silent client, the directory mode, unknown fields and the two-resources refusal all pass.

Should (can follow):

  1. internal/filling/filling.go:286-296: the refusal ticker goroutine ends only on ctx.Done, but it is in wg. If Serve takes the ErrClosed branch (line 308) while ctx is still live, the deferred wg.Wait blocks forever and the error is never returned. Today that branch is unreachable, because only ctx closes the listener, but it would turn a loud failure into a silent hang. Stop the ticker on a channel that Serve closes when it returns.
  2. The caching of Accepts asked for in comment 12004 (point 2) is still open. It can follow.
Verdict: APPROVE at 2767278228bc3812004c16a3b55dd08fa5a7f791 Ran in my own worktree of 2767278: `gofmt -l .` (clean), `go vet ./...` (clean), `sh merge-check.sh` with a nats-server on PATH (exit 0; all 28 packages pass, and the bus tests in internal/link ran). I also ran `go test -race -count=10 ./internal/filling/`, which passed, so the new timing tests are not flaky here. The must-fix from comment 12004 is fixed. I reverted each part alone and saw its test fail: - Keyed by user id only (kind ignored): TestRefusalsAreSaidOnceAndTheirCountIsNeverLost fails. - No flush on the timer: TestTheSocketSaysItsCountedRefusalsOnATimer fails. - No flush when Serve ends: TestTheSocketSaysItsCountedRefusalsWhenItStops fails. The flush is deferred before `wg.Wait`, so it runs after every connection goroutine has ended, and nothing counted later is lost. The small fixes hold. `ask` now has a 5 s deadline: with the accept fix reverted, TestTheSocketGoesOnAfterAnAcceptFails fails in 5 s instead of hanging. The busy-refusal cap is a channel made in each Serve. Connections waiting to be judged are capped at 256, and a connection over the cap is closed unread and counted as `overflow`: with the bound removed, TestAConnectionOverTheWaitingBoundIsClosedUnread fails. Nothing from comment 11959 regressed. The delta since 0c17c76 touches only internal/filling, and the tests for the library hold, the flood, the silent client, the directory mode, unknown fields and the two-resources refusal all pass. Should (can follow): 1. internal/filling/filling.go:286-296: the refusal ticker goroutine ends only on ctx.Done, but it is in `wg`. If Serve takes the ErrClosed branch (line 308) while ctx is still live, the deferred `wg.Wait` blocks forever and the error is never returned. Today that branch is unreachable, because only ctx closes the listener, but it would turn a loud failure into a silent hang. Stop the ticker on a channel that Serve closes when it returns. 2. The caching of `Accepts` asked for in comment 12004 (point 2) is still open. It can follow.
Author
Contributor

Merge check at 27672782
Judged: head 2767278228bc merged into trunk c18d5a8ff38d; read beside it: mesh-catalog ebf7f3773dc7, mesh-controller df9e5728a925, mesh-controller-main df9e5728a925, mesh-tools 954e1a53eef9

  • 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
  • grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before mesh-host is judged on its first machine
  • read by no module's build: cmd/mesh-host/filling_test.go, cmd/mesh-host/units_test.go, internal/apply/filled_library_test.go, internal/apply/filled_test.go, internal/declaration/provisioner_test.go, internal/filling/filling.go, internal/filling/filling_test.go, validate/validate_test.go

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

--- 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-14T00:59:54Z 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-14T00:59:54Z

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

notes:
  - cmd/mesh-host/filling_test.go, cmd/mesh-host/units_test.go, internal/apply/filled_library_test.go, internal/apply/filled_test.go, internal/declaration/provisioner_test.go, internal/filling/filling.go, internal/filling/filling_test.go and validate/validate_test.go read by no module's build, and rebuild nothing (issue 280)
--- its merge-check.sh (go toolchain)
ok  	github.com/novox/mesh-host/cmd/mesh-bootstrap	1.031s
ok  	github.com/novox/mesh-host/cmd/mesh-host	10.531s
ok  	github.com/novox/mesh-host/examples	1.058s
ok  	github.com/novox/mesh-host/internal/accounts	3.362s
ok  	github.com/novox/mesh-host/internal/apply	45.162s
ok  	github.com/novox/mesh-host/internal/bootstrap	9.216s
ok  	github.com/novox/mesh-host/internal/bundle	1.085s
ok  	github.com/novox/mesh-host/internal/declaration	1.118s
ok  	github.com/novox/mesh-host/internal/filling	1.881s
ok  	github.com/novox/mesh-host/internal/firewall	2.743s
ok  	github.com/novox/mesh-host/internal/identity	2.097s
ok  	github.com/novox/mesh-host/internal/image	1.029s
ok  	github.com/novox/mesh-host/internal/inventory	1.089s
ok  	github.com/novox/mesh-host/internal/link	10.782s
ok  	github.com/novox/mesh-host/internal/liveness	3.464s
ok  	github.com/novox/mesh-host/internal/meshcli	1.605s
ok  	github.com/novox/mesh-host/internal/network	1.072s
ok  	github.com/novox/mesh-host/internal/outward	1.063s
ok  	github.com/novox/mesh-host/internal/profile	1.704s
ok  	github.com/novox/mesh-host/internal/reachable	1.016s
ok  	github.com/novox/mesh-host/internal/store	3.626s
ok  	github.com/novox/mesh-host/internal/system	1.031s
ok  	github.com/novox/mesh-host/internal/tunnel	1.033s
ok  	github.com/novox/mesh-host/internal/units	1.011s
ok  	github.com/novox/mesh-host/internal/upgrade	3.880s
ok  	github.com/novox/mesh-host/internal/witness	45.831s
ok  	github.com/novox/mesh-host/packaging	1.013s
?   	github.com/novox/mesh-host/rootsearch	[no test files]
ok  	github.com/novox/mesh-host/validate	1.012s
**Merge check** at `27672782` Judged: head `2767278228bc` merged into trunk `c18d5a8ff38d`; read beside it: mesh-catalog `ebf7f3773dc7`, mesh-controller `df9e5728a925`, mesh-controller-main `df9e5728a925`, mesh-tools `954e1a53eef9` - `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 - grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before mesh-host is judged on its first machine - read by no module's build: cmd/mesh-host/filling_test.go, cmd/mesh-host/units_test.go, internal/apply/filled_library_test.go, internal/apply/filled_test.go, internal/declaration/provisioner_test.go, internal/filling/filling.go, internal/filling/filling_test.go, validate/validate_test.go Run by the build seat on novox as `build-1791940034980212032` (`builds --log build-1791940034980212032`). ``` --- 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-14T00:59:54Z 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-14T00:59:54Z 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 notes: - cmd/mesh-host/filling_test.go, cmd/mesh-host/units_test.go, internal/apply/filled_library_test.go, internal/apply/filled_test.go, internal/declaration/provisioner_test.go, internal/filling/filling.go, internal/filling/filling_test.go and validate/validate_test.go read by no module's build, and rebuild nothing (issue 280) --- its merge-check.sh (go toolchain) ok github.com/novox/mesh-host/cmd/mesh-bootstrap 1.031s ok github.com/novox/mesh-host/cmd/mesh-host 10.531s ok github.com/novox/mesh-host/examples 1.058s ok github.com/novox/mesh-host/internal/accounts 3.362s ok github.com/novox/mesh-host/internal/apply 45.162s ok github.com/novox/mesh-host/internal/bootstrap 9.216s ok github.com/novox/mesh-host/internal/bundle 1.085s ok github.com/novox/mesh-host/internal/declaration 1.118s ok github.com/novox/mesh-host/internal/filling 1.881s ok github.com/novox/mesh-host/internal/firewall 2.743s ok github.com/novox/mesh-host/internal/identity 2.097s ok github.com/novox/mesh-host/internal/image 1.029s ok github.com/novox/mesh-host/internal/inventory 1.089s ok github.com/novox/mesh-host/internal/link 10.782s ok github.com/novox/mesh-host/internal/liveness 3.464s ok github.com/novox/mesh-host/internal/meshcli 1.605s ok github.com/novox/mesh-host/internal/network 1.072s ok github.com/novox/mesh-host/internal/outward 1.063s ok github.com/novox/mesh-host/internal/profile 1.704s ok github.com/novox/mesh-host/internal/reachable 1.016s ok github.com/novox/mesh-host/internal/store 3.626s ok github.com/novox/mesh-host/internal/system 1.031s ok github.com/novox/mesh-host/internal/tunnel 1.033s ok github.com/novox/mesh-host/internal/units 1.011s ok github.com/novox/mesh-host/internal/upgrade 3.880s ok github.com/novox/mesh-host/internal/witness 45.831s ok github.com/novox/mesh-host/packaging 1.013s ? github.com/novox/mesh-host/rootsearch [no test files] ok github.com/novox/mesh-host/validate 1.012s ```
mesh-admin merged commit 53fd326189 into feat/57-config-and-provisioning 2026-10-14 01:15:13 +00:00
mesh-admin deleted branch pkg/57-p2-local-write-verb 2026-10-14 01:15:13 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-host#111