An archive may be an upstream release pinned by URL and sha256, mirrored by the build seat (hq issue 345, ADR 0316) #251

Closed
mesh-admin wants to merge 2 commits from fix/345-reminder-test into main
Contributor

What. The build seat's part of novox/hq ADR 0316 §2 and §6 (to-be 56 §7), for issue 345.

  • catalogue.Artifact gains url, sha256, version, and file or within, for an archive only. Checked: an archive with a URL names no from, is fetched over https, is pinned by a 64-hex sha256 and a version; file is a plain file name, within a plain directory, not both (upstreamFileProblems).
  • The build seat (internal/builder/upstream_file.go) fetches the file once and keeps it in its workspace under its sha256 (<workspace>/upstream/<sha256>). A download whose digest is not the pin's fails the build and names both digests, and nothing is kept. The file is laid out as the artifact says (a single file kept whole under file, or a tar archive in gzip, bzip2 or xz with the content of within) and packed with the same pack as every archive. A link, a device, an entry outside the archive or outside within refuses the build by name, since the node-engine unpacks only files and directories. The node-engine is unchanged: it receives the mesh's own gzip archive by digest, as for any archive.
  • cmd/mesh-builder/Dockerfile: xz added to the image (Betterbird's release is .tar.xz; Go's standard library reads gzip and bzip2 only).
  • InstallationProblems (ADR 0112 check): an upstream pin's url and version name the vendor and its release, the world's, as a registry is an image's. Without this, version 1.5.6.1 reads as a public address and the vendor's host as an installation.

Why. A Betterbird release is about 80 MB per version, too large to keep in a repository at every upgrade, and Owl's licence forbids copying it to third parties, so it may not be in a repository at all. The vendor serves only Owl's newest version at one address: keeping the fetched file by digest is what lets a later build of the same pin succeed.

How it was tested.

  • internal/builder/upstream_file_test.go, against an https test server: a pinned release is fetched, checked, packed without its own directory, with modes kept, published as the module's archive, and two packings give one digest; a pin fetched once builds again after the address serves something else, and the vendor is asked once; a download that is not the pinned file fails, names both sha256 digests, publishes nothing and keeps nothing; an xpi is kept whole under its name; a link, an entry outside the directory, an entry outside the archive and an empty directory are refused; an xz release is read (skipped where no xz).
  • internal/catalogue/upstream_file_test.go: the two shapes are accepted; a missing or malformed sha256, no version, http, both from and url, a pin without url, a non-archive, a path as file, within leaving the archive, and file with within are each refused for their reason; the vendor and version are not taken for an installation, and any other name in the artifact still is.
  • By hand, not committed: the real Betterbird 153.4.0esr-bb10 .tar.xz (81,620,684 bytes, sha256 38dd2489…129e2, as the AUR recipe pins it) packed through upstreamArchive: 55 files, 120,342,573 bytes gzipped, under the node-engine's 512 MB.
  • The node-engine needs no change: a throwaway test in mesh-host (not committed) unpacked the release at a path, then three archives inside it (owl/, distribution/, defaults/pref merged into an existing directory), then a new release over the first: each inner archive's files survived the outer upgrade.
  • sh merge-check.sh passes locally (gofmt, vet, go test -race ./...); store tests skipped (no MESH_TEST_POSTGRES). A controller built from this head checks every catalogue manifest with mesh-catalog #232 (module check modules/*/module.json: 126 manifests, 69 long-running resources without health, as kept).

Order. This first: mesh-catalog #232 (same branch name, the module that pins Betterbird and Owl) uses the new fields, which the running controller refuses as unknown. A controller rollout: merge only while mesh-controller.plans says no plan is mid-gate. The build seat (build-agent) picks the change up when its image is rebuilt from this repository.

Limits I chose. The kept files are never collected: one per pin, about 80 MB per Betterbird upgrade, in the build seat's workspace. version is said, not checked against the file. The first build of the Owl pin must happen while the vendor still serves 1.5.6.1 (Last-Modified 2026-09-16 on 2026-10-12); after that the kept copy serves.

Reviewers' checklist.

  • A digest mismatch fails before anything is unpacked or published, and names both digests.
  • Nothing a vendor serves reaches a node but through the artifact store, by digest.
  • Only files and directories are packed; nothing escapes the workspace.
  • An archive without url builds exactly as before.

Part of issue 345 (not merged; waits for an independent review and the required checks).

**What.** The build seat's part of novox/hq ADR 0316 §2 and §6 (to-be 56 §7), for issue 345. - `catalogue.Artifact` gains `url`, `sha256`, `version`, and `file` or `within`, for an `archive` only. Checked: an archive with a URL names no `from`, is fetched over https, is pinned by a 64-hex sha256 and a version; `file` is a plain file name, `within` a plain directory, not both (`upstreamFileProblems`). - The build seat (`internal/builder/upstream_file.go`) fetches the file **once** and keeps it in its workspace under its sha256 (`<workspace>/upstream/<sha256>`). A download whose digest is not the pin's **fails the build and names both digests**, and nothing is kept. The file is laid out as the artifact says (a single file kept whole under `file`, or a tar archive in gzip, bzip2 or xz with the content of `within`) and packed with the same `pack` as every archive. A link, a device, an entry outside the archive or outside `within` refuses the build by name, since the node-engine unpacks only files and directories. The node-engine is unchanged: it receives the mesh's own gzip archive by digest, as for any archive. - `cmd/mesh-builder/Dockerfile`: `xz` added to the image (Betterbird's release is `.tar.xz`; Go's standard library reads gzip and bzip2 only). - `InstallationProblems` (ADR 0112 check): an upstream pin's `url` and `version` name the vendor and its release, the world's, as a registry is an image's. Without this, `version` 1.5.6.1 reads as a public address and the vendor's host as an installation. **Why.** A Betterbird release is about 80 MB per version, too large to keep in a repository at every upgrade, and Owl's licence forbids copying it to third parties, so it may not be in a repository at all. The vendor serves only Owl's newest version at one address: keeping the fetched file by digest is what lets a later build of the same pin succeed. **How it was tested.** - `internal/builder/upstream_file_test.go`, against an https test server: a pinned release is fetched, checked, packed without its own directory, with modes kept, published as the module's archive, and two packings give one digest; a pin fetched once builds again after the address serves something else, and the vendor is asked once; a download that is not the pinned file fails, names both sha256 digests, publishes nothing and keeps nothing; an xpi is kept whole under its name; a link, an entry outside the directory, an entry outside the archive and an empty directory are refused; an xz release is read (skipped where no xz). - `internal/catalogue/upstream_file_test.go`: the two shapes are accepted; a missing or malformed sha256, no version, http, both `from` and `url`, a pin without url, a non-archive, a path as `file`, `within` leaving the archive, and `file` with `within` are each refused for their reason; the vendor and version are not taken for an installation, and any other name in the artifact still is. - By hand, not committed: the real Betterbird 153.4.0esr-bb10 `.tar.xz` (81,620,684 bytes, sha256 38dd2489…129e2, as the AUR recipe pins it) packed through `upstreamArchive`: 55 files, 120,342,573 bytes gzipped, under the node-engine's 512 MB. - The node-engine needs no change: a throwaway test in mesh-host (not committed) unpacked the release at a path, then three archives inside it (`owl/`, `distribution/`, `defaults/pref` merged into an existing directory), then a new release over the first: each inner archive's files survived the outer upgrade. - `sh merge-check.sh` passes locally (gofmt, vet, `go test -race ./...`); store tests skipped (no `MESH_TEST_POSTGRES`). A controller built from this head checks every catalogue manifest with mesh-catalog #232 (`module check modules/*/module.json`: 126 manifests, 69 long-running resources without health, as kept). **Order.** This first: mesh-catalog #232 (same branch name, the module that pins Betterbird and Owl) uses the new fields, which the running controller refuses as unknown. A controller rollout: merge only while `mesh-controller.plans` says no plan is mid-gate. The build seat (build-agent) picks the change up when its image is rebuilt from this repository. **Limits I chose.** The kept files are never collected: one per pin, about 80 MB per Betterbird upgrade, in the build seat's workspace. `version` is said, not checked against the file. The first build of the Owl pin must happen while the vendor still serves 1.5.6.1 (Last-Modified 2026-09-16 on 2026-10-12); after that the kept copy serves. **Reviewers' checklist.** - [ ] A digest mismatch fails before anything is unpacked or published, and names both digests. - [ ] Nothing a vendor serves reaches a node but through the artifact store, by digest. - [ ] Only files and directories are packed; nothing escapes the workspace. - [ ] An archive without `url` builds exactly as before. Part of issue 345 (not merged; waits for an independent review and the required checks).
mesh-admin added 1 commit 2026-10-12 10:58:12 +00:00
Let an archive be an upstream release pinned by URL and sha256, mirrored by the build seat (hq issue 345, ADR 0316)
mesh/merge-gate pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without …
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery-group group fix/345-reminder-test rejected: a member's own check failed
mesh/delivery superseded: a newer head of the same pull request
9d108feab8
A mail client's release is about 80 MB per version, too large to keep in a
repository at every upgrade, and an add-on whose licence forbids copying it
to third parties may not be in a repository at all. An archive artifact may
now name url, sha256 and version (and file or within) instead of from: the
build seat fetches it once, refuses it when its digest is not the pin's and
names both, keeps it in its workspace under the digest (a vendor that serves
only its newest version cannot break a later build of the same pin), and
packs it like any archive, so a node gets the mesh's own archive by digest
and never asks the vendor. The build seat's image carries xz, which Go's
standard library does not read. The installation check takes the vendor's
address and version of such a pin for the world's, as a registry is an
image's.
Author
Contributor

Delivery novox/mesh-controller@3647928668bc — stopped since 2026-10-12T11:53:26Z

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

  • tier 0: mesh-controller
  • tier 1: build-agent
  • ace: receives build-agent
  • g14: receives build-agent
  • novox: receives mesh-controller, build-agent
  • shanks: receives build-agent
  • build-agent keeps data (ADR 0233): what it holds is backed up before it moves
  • 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 build-agent is judged on its first machine

Transitions

  • 2026-10-12 11:25 (new) → proposed (announced): novox/mesh-controller#251's head announced
  • 2026-10-12 11:32 proposed → checked (checked): the verdict names this commit
  • 2026-10-12 11:32 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
  • 2026-10-12 11:41 ready → proposed (recheck): its group fix/345-reminder-test lost its catalogue member: mesh-catalog #232 was closed and moved to #240 (branch fix/345-betterbird-module), so #251 is alone in its group
  • 2026-10-12 11:47 proposed → checked (checked): the verdict names this commit
  • 2026-10-12 11:47 checked → rejected (refused): the gate failed or could not run, or the repository's own check failed
  • 2026-10-12 11:53 rejected → stopped (closed): the pull request closed unmerged

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

<!-- mesh-delivery:view --> **Delivery** `novox/mesh-controller@3647928668bc` — **stopped** since 2026-10-12T11:53:26Z **Delivery plan** — builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step - tier 0: mesh-controller - tier 1: build-agent - ace: receives build-agent - g14: receives build-agent - novox: receives mesh-controller, build-agent - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - 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 build-agent is judged on its first machine **Transitions** - 2026-10-12 11:25 (new) → proposed (announced): novox/mesh-controller#251's head announced - 2026-10-12 11:32 proposed → checked (checked): the verdict names this commit - 2026-10-12 11:32 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail - 2026-10-12 11:41 ready → proposed (recheck): its group fix/345-reminder-test lost its catalogue member: mesh-catalog #232 was closed and moved to #240 (branch fix/345-betterbird-module), so #251 is alone in its group - 2026-10-12 11:47 proposed → checked (checked): the verdict names this commit - 2026-10-12 11:47 checked → rejected (refused): the gate failed or could not run, or the repository's own check failed - 2026-10-12 11:53 rejected → stopped (closed): the pull request closed unmerged The commit's note under `refs/notes/mesh-plan` keeps every transition: `git log --notes=mesh-plan`.
Author
Contributor

Merge check at 9d108fea

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

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

  • tier 0: mesh-controller
  • tier 1: build-agent
  • ace: receives build-agent
  • g14: receives build-agent
  • novox: receives mesh-controller, build-agent
  • shanks: receives build-agent
  • build-agent keeps data (ADR 0233): what it holds is backed up before it moves
  • 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 build-agent is judged on its first machine

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

--- the gate: build-agent, mesh-controller
mesh-controller: ok, 57 tool(s); WARNING: merge-window ask(s) for a setting and do(es) not say whether it is trusted, so it counts as trusted: set at the terminal alone; say "trusted" false where nothing trusts it (novox/hq issue 339); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8)
long-running resources without health: 1
files asking for a setting without saying whether it is trusted: 1
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-12T11:06: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-12T11:06:02Z

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

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

a merge would rebuild 2 module(s) in 2 tier(s): build-agent, mesh-controller

notes:
  - internal/builder/upstream_file_test.go and internal/catalogue/upstream_file_test.go read by no module's build, and rebuild nothing (issue 280)
--- its merge-check.sh (go toolchain)
ok  	github.com/novox/mesh-controller/cmd/mesh-builder	1.037s
ok  	github.com/novox/mesh-controller/cmd/mesh-controller	237.475s
ok  	github.com/novox/mesh-controller/examples/objectstore-provisioner	1.013s
ok  	github.com/novox/mesh-controller/examples/postgres-provisioner	1.015s
ok  	github.com/novox/mesh-controller/examples/redis-provisioner	1.011s
ok  	github.com/novox/mesh-controller/examples/route-proxy	1.150s
ok  	github.com/novox/mesh-controller/internal/artifacts	1.044s
?   	github.com/novox/mesh-controller/internal/beside	[no test files]
ok  	github.com/novox/mesh-controller/internal/broker	3.918s
ok  	github.com/novox/mesh-controller/internal/builder	13.892s
ok  	github.com/novox/mesh-controller/internal/catalogue	6.928s
ok  	github.com/novox/mesh-controller/internal/conditions	2.446s
ok  	github.com/novox/mesh-controller/internal/envfile	1.026s
ok  	github.com/novox/mesh-controller/internal/facts	1.031s
ok  	github.com/novox/mesh-controller/internal/identity	4.209s
ok  	github.com/novox/mesh-controller/internal/inventory	83.995s
ok  	github.com/novox/mesh-controller/internal/lease	17.701s
ok  	github.com/novox/mesh-controller/internal/licences	3.840s
ok  	github.com/novox/mesh-controller/internal/licences/adapters	1.006s
ok  	github.com/novox/mesh-controller/internal/link	59.599s
ok  	github.com/novox/mesh-controller/internal/lint	2.183s
ok  	github.com/novox/mesh-controller/internal/outward	1.030s
ok  	github.com/novox/mesh-controller/internal/overlay	1.032s
ok  	github.com/novox/mesh-controller/internal/secrets	1.429s
ok  	github.com/novox/mesh-controller/internal/store	3.281s
ok  	github.com/novox/mesh-controller/internal/testbus	1.174s
ok  	github.com/novox/mesh-controller/internal/token	1.018s
store tests, each run against the store:
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeWalksTheOthersAgain (0.35s)
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeEndsTheMergesThatContainIt (0.38s)
  ./cmd/mesh-controller --- PASS: TestAStopWalksTheOthersAgainInTheOpenBatch (0.37s)
  ./cmd/mesh-controller --- PASS: TestAStopNamesWhatStaysOnTheMachinesAndWhatStillCarriesIt (0.35s)
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeLeavesItOutOfABatch (0.34s)
  ./cmd/mesh-controller --- PASS: TestSavingPlansAndMovingMergesIsOneAct (0.32s)
  ./cmd/mesh-controller --- PASS: TestNodeShowAndStatusReadTheLastSendFromTheStore (0.37s)
  ./cmd/mesh-controller --- PASS: TestTheConditionsVerbReadsTheLooksTheBucketNoLongerHolds (0.26s)
  ./internal/inventory --- PASS: TestTheLastSendSaysWhatTheMachineAnswered (0.22s)
  ./internal/inventory --- PASS: TestAReportIsTheLastSendsAnswerOnlyWhenItIsAboutThatSend (0.21s)
  ./internal/inventory --- PASS: TestAnIssuedValueIsSealedToEveryHolderOnceAndNotAgain (0.23s)
  ./internal/inventory --- PASS: TestAnIssuedValueIsSealedAgainAfterAKeyChangeOrAnAcceptance (0.21s)
  ./internal/inventory --- PASS: TestAConditionsLooksAreKeptBesideTheBucket (0.20s)
**Merge check** at `9d108fea` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller) - `mesh/repo-check`: **PASS** — its merge-check.sh passed **Delivery plan** — builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step - tier 0: mesh-controller - tier 1: build-agent - ace: receives build-agent - g14: receives build-agent - novox: receives mesh-controller, build-agent - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - 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 build-agent is judged on its first machine Run by the build seat on g14 as `build-1791802698391025962` (`builds --log build-1791802698391025962`). ``` --- the gate: build-agent, mesh-controller mesh-controller: ok, 57 tool(s); WARNING: merge-window ask(s) for a setting and do(es) not say whether it is trusted, so it counts as trusted: set at the terminal alone; say "trusted" false where nothing trusts it (novox/hq issue 339); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8) long-running resources without health: 1 files asking for a setting without saying whether it is trusted: 1 1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-12T11:06: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-12T11:06:02Z modules the change moves: - mesh-controller (changed) machines: - a machine (f35): composes - holds mesh-dns-resolver (idt): composes - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes - a machine (yvbpsm): composes a merge would rebuild 2 module(s) in 2 tier(s): build-agent, mesh-controller notes: - internal/builder/upstream_file_test.go and internal/catalogue/upstream_file_test.go read by no module's build, and rebuild nothing (issue 280) --- its merge-check.sh (go toolchain) ok github.com/novox/mesh-controller/cmd/mesh-builder 1.037s ok github.com/novox/mesh-controller/cmd/mesh-controller 237.475s ok github.com/novox/mesh-controller/examples/objectstore-provisioner 1.013s ok github.com/novox/mesh-controller/examples/postgres-provisioner 1.015s ok github.com/novox/mesh-controller/examples/redis-provisioner 1.011s ok github.com/novox/mesh-controller/examples/route-proxy 1.150s ok github.com/novox/mesh-controller/internal/artifacts 1.044s ? github.com/novox/mesh-controller/internal/beside [no test files] ok github.com/novox/mesh-controller/internal/broker 3.918s ok github.com/novox/mesh-controller/internal/builder 13.892s ok github.com/novox/mesh-controller/internal/catalogue 6.928s ok github.com/novox/mesh-controller/internal/conditions 2.446s ok github.com/novox/mesh-controller/internal/envfile 1.026s ok github.com/novox/mesh-controller/internal/facts 1.031s ok github.com/novox/mesh-controller/internal/identity 4.209s ok github.com/novox/mesh-controller/internal/inventory 83.995s ok github.com/novox/mesh-controller/internal/lease 17.701s ok github.com/novox/mesh-controller/internal/licences 3.840s ok github.com/novox/mesh-controller/internal/licences/adapters 1.006s ok github.com/novox/mesh-controller/internal/link 59.599s ok github.com/novox/mesh-controller/internal/lint 2.183s ok github.com/novox/mesh-controller/internal/outward 1.030s ok github.com/novox/mesh-controller/internal/overlay 1.032s ok github.com/novox/mesh-controller/internal/secrets 1.429s ok github.com/novox/mesh-controller/internal/store 3.281s ok github.com/novox/mesh-controller/internal/testbus 1.174s ok github.com/novox/mesh-controller/internal/token 1.018s store tests, each run against the store: ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeWalksTheOthersAgain (0.35s) ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeEndsTheMergesThatContainIt (0.38s) ./cmd/mesh-controller --- PASS: TestAStopWalksTheOthersAgainInTheOpenBatch (0.37s) ./cmd/mesh-controller --- PASS: TestAStopNamesWhatStaysOnTheMachinesAndWhatStillCarriesIt (0.35s) ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeLeavesItOutOfABatch (0.34s) ./cmd/mesh-controller --- PASS: TestSavingPlansAndMovingMergesIsOneAct (0.32s) ./cmd/mesh-controller --- PASS: TestNodeShowAndStatusReadTheLastSendFromTheStore (0.37s) ./cmd/mesh-controller --- PASS: TestTheConditionsVerbReadsTheLooksTheBucketNoLongerHolds (0.26s) ./internal/inventory --- PASS: TestTheLastSendSaysWhatTheMachineAnswered (0.22s) ./internal/inventory --- PASS: TestAReportIsTheLastSendsAnswerOnlyWhenItIsAboutThatSend (0.21s) ./internal/inventory --- PASS: TestAnIssuedValueIsSealedToEveryHolderOnceAndNotAgain (0.23s) ./internal/inventory --- PASS: TestAnIssuedValueIsSealedAgainAfterAKeyChangeOrAnAcceptance (0.21s) ./internal/inventory --- PASS: TestAConditionsLooksAreKeptBesideTheBucket (0.20s) ```
mesh-admin left a comment
Author
Contributor

Verdict: request changes at 9d108fe

Independent review for issue 345 / ADR 0316 section 2. Fresh clone at 9d108fe; sh merge-check.sh with a throwaway Postgres 17: gofmt, go vet, go test -race -count=1 ./... and every listed store test pass (exit 0, no NOT RACE-CHECKED / NOT STORE-CHECKED lines). mesh/merge-gate and mesh/repo-check are green at this head. Note: mesh/delivery-group is red ("group fix/345-reminder-test rejected: a member's own check failed", 11:02 UTC) - not required here, but the group must pass its composed check before any member merges.

What is right: the digest is computed while the body streams into a .part temp file and the file is renamed into the cache only when it matches, so no byte is used before the check; a mismatch names both digests and keeps nothing. The download is capped at 512 MiB (LimitReader max+1, refused when exceeded). TLS is the default verifying transport. A kept file is re-hashed on every build and refetched when it differs (it must be: the workspace is mounted as HOME by check containers, issue 462). Unpacking takes only regular files and directories; symlinks, hard links, devices, fifos are refused by name; absolute and .. paths are refused; modes are reduced to 0644/0755 so setuid/setgid/sticky never survive; regular-file bytes are capped at 512 MiB total. Nodes never see the URL: Built carries only the store reference and digest, so a machine fetches the mesh's own archive as before. A pin change rebuilds through module.json.

Blocking

  1. The installation lint now waves through any installation name or address in url and version. upstreamPin skips both fields wholesale, so I probed it (catalogue test, not committed): https://git.novox.internal/novox/x/release.tar.gz, https://10.1.2.3/r.tar.gz, https://203.0.113.9/r.tar.gz and https://mail.novox.be/r.tar.gz all give zero InstallationProblems, and so does a version of git.novox.internal 10.0.0.1. That is exactly the leak the lint exists to stop, and a URL on the mesh's own forge is the most likely mistake. Suggested fix: keep judging url like any value, and let the vendor's host through the existing names-on-purpose declaration with its reason (that is what it is for); or, at minimum, refuse an IP literal host and a host in the reserved/installation TLDs (internal, test, invalid, example, local, lan, home.arpa) and keep checking the path. For version, skip only the dotted-quad address check (four-number versions) and keep the hostname check. A test per case.

Should fix (here or as named follow-up issues)

  1. No limit on the number of entries. The 512 MiB budget counts only regular-file bytes, so a gzip of millions of zero-byte file or directory headers is tiny, passes the download cap, and fills the build node's inodes. Cap the entry count (e.g. 100 000) and count headers toward the byte budget.
  2. xz can hang the build until its context ends. cmd.Wait() runs before anything drains the pipe; when data follows the tar end-of-archive (more than the 64 KiB pipe buffer), xz blocks writing and Wait never returns. Probed: 4 MiB of zeros after the tar ends -> xz could not read it: signal: killed after my 10 s test deadline; in production that is the whole build timeout. Drain out (bounded) to io.Discard before Wait.
  3. A pax global header is refused. Tarballs made by git archive (forge-generated source releases) begin with pax_global_header (type g), which Go's tar reader returns as an entry: refused as "is a g", or as "outside ". Skip TypeXGlobalHeader.
  4. A parent directory entry of a nested within is refused. With within: a/b, a tar listing a/ before a/b/ fails ("a/ is outside a/b"). Skip directory entries that are ancestors of the prefix.
  5. Redirects are followed to plain http. Integrity still holds through the digest, but a pin that says https should be fetched over https throughout: a CheckRedirect refusing a non-https hop is three lines.
  6. The upstream cache in the workspace grows without bound (up to 512 MiB per pin, about 80 MB for the mail client, never removed). Not blocking at today's rate of pin changes, but it needs an issue: e.g. remove kept files not used by any build for N days, or keep only the pins the catalogue currently names.
  7. Memory: file mode reads the whole upstream file into memory and pack holds the packed archive in memory - up to about 1 GiB at the caps. Fine at 80 MB; worth a streaming copy (or a lower cap for file) later.

Items 1 is the reason for request changes; 2 and 3 are cheap and in the area this change opens, so I would take them in the same pull request. Re-review on the new head.

Verdict: request changes at 9d108fe Independent review for issue 345 / ADR 0316 section 2. Fresh clone at 9d108fe; `sh merge-check.sh` with a throwaway Postgres 17: gofmt, go vet, `go test -race -count=1 ./...` and every listed store test pass (exit 0, no NOT RACE-CHECKED / NOT STORE-CHECKED lines). mesh/merge-gate and mesh/repo-check are green at this head. Note: mesh/delivery-group is red ("group fix/345-reminder-test rejected: a member's own check failed", 11:02 UTC) - not required here, but the group must pass its composed check before any member merges. What is right: the digest is computed while the body streams into a `.part` temp file and the file is renamed into the cache only when it matches, so no byte is used before the check; a mismatch names both digests and keeps nothing. The download is capped at 512 MiB (LimitReader max+1, refused when exceeded). TLS is the default verifying transport. A kept file is re-hashed on every build and refetched when it differs (it must be: the workspace is mounted as HOME by check containers, issue 462). Unpacking takes only regular files and directories; symlinks, hard links, devices, fifos are refused by name; absolute and `..` paths are refused; modes are reduced to 0644/0755 so setuid/setgid/sticky never survive; regular-file bytes are capped at 512 MiB total. Nodes never see the URL: `Built` carries only the store reference and digest, so a machine fetches the mesh's own archive as before. A pin change rebuilds through module.json. ## Blocking 1. **The installation lint now waves through any installation name or address in `url` and `version`.** `upstreamPin` skips both fields wholesale, so I probed it (catalogue test, not committed): `https://git.novox.internal/novox/x/release.tar.gz`, `https://10.1.2.3/r.tar.gz`, `https://203.0.113.9/r.tar.gz` and `https://mail.novox.be/r.tar.gz` all give zero InstallationProblems, and so does a version of `git.novox.internal 10.0.0.1`. That is exactly the leak the lint exists to stop, and a URL on the mesh's own forge is the most likely mistake. Suggested fix: keep judging `url` like any value, and let the vendor's host through the existing `names-on-purpose` declaration with its reason (that is what it is for); or, at minimum, refuse an IP literal host and a host in the reserved/installation TLDs (`internal`, `test`, `invalid`, `example`, `local`, `lan`, `home.arpa`) and keep checking the path. For `version`, skip only the dotted-quad address check (four-number versions) and keep the hostname check. A test per case. ## Should fix (here or as named follow-up issues) 2. **No limit on the number of entries.** The 512 MiB budget counts only regular-file bytes, so a gzip of millions of zero-byte file or directory headers is tiny, passes the download cap, and fills the build node's inodes. Cap the entry count (e.g. 100 000) and count headers toward the byte budget. 3. **xz can hang the build until its context ends.** `cmd.Wait()` runs before anything drains the pipe; when data follows the tar end-of-archive (more than the 64 KiB pipe buffer), xz blocks writing and Wait never returns. Probed: 4 MiB of zeros after the tar ends -> `xz could not read it: signal: killed` after my 10 s test deadline; in production that is the whole build timeout. Drain `out` (bounded) to io.Discard before Wait. 4. **A pax global header is refused.** Tarballs made by `git archive` (forge-generated source releases) begin with `pax_global_header` (type `g`), which Go's tar reader returns as an entry: refused as "is a g", or as "outside <within>". Skip `TypeXGlobalHeader`. 5. **A parent directory entry of a nested `within` is refused.** With `within: a/b`, a tar listing `a/` before `a/b/` fails ("a/ is outside a/b"). Skip directory entries that are ancestors of the prefix. 6. **Redirects are followed to plain http.** Integrity still holds through the digest, but a pin that says https should be fetched over https throughout: a `CheckRedirect` refusing a non-https hop is three lines. 7. **The upstream cache in the workspace grows without bound** (up to 512 MiB per pin, about 80 MB for the mail client, never removed). Not blocking at today's rate of pin changes, but it needs an issue: e.g. remove kept files not used by any build for N days, or keep only the pins the catalogue currently names. 8. Memory: `file` mode reads the whole upstream file into memory and `pack` holds the packed archive in memory - up to about 1 GiB at the caps. Fine at 80 MB; worth a streaming copy (or a lower cap for `file`) later. Items 1 is the reason for request changes; 2 and 3 are cheap and in the area this change opens, so I would take them in the same pull request. Re-review on the new head.
jschoubben added 1 commit 2026-10-12 11:25:20 +00:00
Judge an upstream pin's host like any name, and harden the upstream unpack (review 35, hq issue 345)
mesh/delivery-group group fix/345-reminder-test rejected: a member's own check failed
mesh/merge-gate pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without …
mesh/repo-check fail: its merge-check.sh failed: --- FAIL: TestTheInstallersFirstUserListIsWhatTheControllerWouldCompose (0.64s)
mesh/delivery stopped: the pull request closed unmerged
3647928668
The url was skipped by the installation check, so an installation's host or
an address passed; it is judged whole now, the vendor's host declared in the
artifact's names-on-purpose, and an address or a reserved name as the host
is refused. Only the version's dotted-quad reading is skipped. The unpack
caps its entries, drains xz before waiting for it, reads a pax global
header and a nested within's parents, and a redirect off https is not
followed.
Author
Contributor

Merge check at 36479286

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

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

  • tier 0: mesh-controller
  • tier 1: build-agent
  • ace: receives build-agent
  • g14: receives build-agent
  • novox: receives mesh-controller, build-agent
  • shanks: receives build-agent
  • build-agent keeps data (ADR 0233): what it holds is backed up before it moves
  • 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 build-agent is judged on its first machine
  • read by no module's build: internal/builder/upstream_file_test.go, internal/catalogue/upstream_file_test.go

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

--- the gate: build-agent, mesh-controller
mesh-controller: ok, 57 tool(s); WARNING: merge-window ask(s) for a setting and do(es) not say whether it is trusted, so it counts as trusted: set at the terminal alone; say "trusted" false where nothing trusts it (novox/hq issue 339); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8)
long-running resources without health: 1
files asking for a setting without saying whether it is trusted: 1
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-12T11:22:09Z 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-12T11:22:09Z

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

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

a merge would rebuild 2 module(s) in 2 tier(s): build-agent, mesh-controller

notes:
  - internal/builder/upstream_file_test.go and internal/catalogue/upstream_file_test.go read by no module's build, and rebuild nothing (issue 280)
--- its merge-check.sh (go toolchain)
ok  	github.com/novox/mesh-controller/cmd/mesh-builder	1.043s
ok  	github.com/novox/mesh-controller/cmd/mesh-controller	263.492s
ok  	github.com/novox/mesh-controller/examples/objectstore-provisioner	1.027s
ok  	github.com/novox/mesh-controller/examples/postgres-provisioner	1.084s
ok  	github.com/novox/mesh-controller/examples/redis-provisioner	1.018s
ok  	github.com/novox/mesh-controller/examples/route-proxy	1.268s
ok  	github.com/novox/mesh-controller/internal/artifacts	1.056s
?   	github.com/novox/mesh-controller/internal/beside	[no test files]
ok  	github.com/novox/mesh-controller/internal/broker	5.059s
ok  	github.com/novox/mesh-controller/internal/builder	41.852s
ok  	github.com/novox/mesh-controller/internal/catalogue	9.690s
ok  	github.com/novox/mesh-controller/internal/conditions	3.118s
ok  	github.com/novox/mesh-controller/internal/envfile	1.025s
ok  	github.com/novox/mesh-controller/internal/facts	1.031s
ok  	github.com/novox/mesh-controller/internal/identity	6.089s
ok  	github.com/novox/mesh-controller/internal/inventory	109.307s
ok  	github.com/novox/mesh-controller/internal/lease	17.777s
ok  	github.com/novox/mesh-controller/internal/licences	5.508s
ok  	github.com/novox/mesh-controller/internal/licences/adapters	1.021s
ok  	github.com/novox/mesh-controller/internal/link	66.505s
ok  	github.com/novox/mesh-controller/internal/lint	2.915s
ok  	github.com/novox/mesh-controller/internal/outward	1.065s
ok  	github.com/novox/mesh-controller/internal/overlay	1.050s
ok  	github.com/novox/mesh-controller/internal/secrets	1.669s
ok  	github.com/novox/mesh-controller/internal/store	3.800s
ok  	github.com/novox/mesh-controller/internal/testbus	1.254s
ok  	github.com/novox/mesh-controller/internal/token	1.026s
store tests, each run against the store:
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeWalksTheOthersAgain (0.66s)
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeEndsTheMergesThatContainIt (0.61s)
  ./cmd/mesh-controller --- PASS: TestAStopWalksTheOthersAgainInTheOpenBatch (0.51s)
  ./cmd/mesh-controller --- PASS: TestAStopNamesWhatStaysOnTheMachinesAndWhatStillCarriesIt (0.40s)
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeLeavesItOutOfABatch (0.38s)
  ./cmd/mesh-controller --- PASS: TestSavingPlansAndMovingMergesIsOneAct (1.36s)
  ./cmd/mesh-controller --- PASS: TestNodeShowAndStatusReadTheLastSendFromTheStore (0.50s)
  ./cmd/mesh-controller --- PASS: TestTheConditionsVerbReadsTheLooksTheBucketNoLongerHolds (0.40s)
  ./internal/inventory --- PASS: TestTheLastSendSaysWhatTheMachineAnswered (0.30s)
  ./internal/inventory --- PASS: TestAReportIsTheLastSendsAnswerOnlyWhenItIsAboutThatSend (0.30s)
  ./internal/inventory --- PASS: TestAnIssuedValueIsSealedToEveryHolderOnceAndNotAgain (0.33s)
  ./internal/inventory --- PASS: TestAnIssuedValueIsSealedAgainAfterAKeyChangeOrAnAcceptance (0.41s)
  ./internal/inventory --- PASS: TestAConditionsLooksAreKeptBesideTheBucket (0.29s)
**Merge check** at `36479286` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller) - `mesh/repo-check`: **PASS** — its merge-check.sh passed **Delivery plan** — builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step - tier 0: mesh-controller - tier 1: build-agent - ace: receives build-agent - g14: receives build-agent - novox: receives mesh-controller, build-agent - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - 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 build-agent is judged on its first machine - read by no module's build: internal/builder/upstream_file_test.go, internal/catalogue/upstream_file_test.go Run by the build seat on g14 as `build-1791804336372064161` (`builds --log build-1791804336372064161`). ``` --- the gate: build-agent, mesh-controller mesh-controller: ok, 57 tool(s); WARNING: merge-window ask(s) for a setting and do(es) not say whether it is trusted, so it counts as trusted: set at the terminal alone; say "trusted" false where nothing trusts it (novox/hq issue 339); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8) long-running resources without health: 1 files asking for a setting without saying whether it is trusted: 1 1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-12T11:22:09Z 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-12T11:22:09Z modules the change moves: - mesh-controller (changed) machines: - a machine (f35): composes - holds mesh-dns-resolver (idt): composes - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes - a machine (yvbpsm): composes a merge would rebuild 2 module(s) in 2 tier(s): build-agent, mesh-controller notes: - internal/builder/upstream_file_test.go and internal/catalogue/upstream_file_test.go read by no module's build, and rebuild nothing (issue 280) --- its merge-check.sh (go toolchain) ok github.com/novox/mesh-controller/cmd/mesh-builder 1.043s ok github.com/novox/mesh-controller/cmd/mesh-controller 263.492s ok github.com/novox/mesh-controller/examples/objectstore-provisioner 1.027s ok github.com/novox/mesh-controller/examples/postgres-provisioner 1.084s ok github.com/novox/mesh-controller/examples/redis-provisioner 1.018s ok github.com/novox/mesh-controller/examples/route-proxy 1.268s ok github.com/novox/mesh-controller/internal/artifacts 1.056s ? github.com/novox/mesh-controller/internal/beside [no test files] ok github.com/novox/mesh-controller/internal/broker 5.059s ok github.com/novox/mesh-controller/internal/builder 41.852s ok github.com/novox/mesh-controller/internal/catalogue 9.690s ok github.com/novox/mesh-controller/internal/conditions 3.118s ok github.com/novox/mesh-controller/internal/envfile 1.025s ok github.com/novox/mesh-controller/internal/facts 1.031s ok github.com/novox/mesh-controller/internal/identity 6.089s ok github.com/novox/mesh-controller/internal/inventory 109.307s ok github.com/novox/mesh-controller/internal/lease 17.777s ok github.com/novox/mesh-controller/internal/licences 5.508s ok github.com/novox/mesh-controller/internal/licences/adapters 1.021s ok github.com/novox/mesh-controller/internal/link 66.505s ok github.com/novox/mesh-controller/internal/lint 2.915s ok github.com/novox/mesh-controller/internal/outward 1.065s ok github.com/novox/mesh-controller/internal/overlay 1.050s ok github.com/novox/mesh-controller/internal/secrets 1.669s ok github.com/novox/mesh-controller/internal/store 3.800s ok github.com/novox/mesh-controller/internal/testbus 1.254s ok github.com/novox/mesh-controller/internal/token 1.026s store tests, each run against the store: ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeWalksTheOthersAgain (0.66s) ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeEndsTheMergesThatContainIt (0.61s) ./cmd/mesh-controller --- PASS: TestAStopWalksTheOthersAgainInTheOpenBatch (0.51s) ./cmd/mesh-controller --- PASS: TestAStopNamesWhatStaysOnTheMachinesAndWhatStillCarriesIt (0.40s) ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeLeavesItOutOfABatch (0.38s) ./cmd/mesh-controller --- PASS: TestSavingPlansAndMovingMergesIsOneAct (1.36s) ./cmd/mesh-controller --- PASS: TestNodeShowAndStatusReadTheLastSendFromTheStore (0.50s) ./cmd/mesh-controller --- PASS: TestTheConditionsVerbReadsTheLooksTheBucketNoLongerHolds (0.40s) ./internal/inventory --- PASS: TestTheLastSendSaysWhatTheMachineAnswered (0.30s) ./internal/inventory --- PASS: TestAReportIsTheLastSendsAnswerOnlyWhenItIsAboutThatSend (0.30s) ./internal/inventory --- PASS: TestAnIssuedValueIsSealedToEveryHolderOnceAndNotAgain (0.33s) ./internal/inventory --- PASS: TestAnIssuedValueIsSealedAgainAfterAKeyChangeOrAnAcceptance (0.41s) ./internal/inventory --- PASS: TestAConditionsLooksAreKeptBesideTheBucket (0.29s) ```
mesh-admin left a comment
Author
Contributor

Verdict: approve at 3647928

Re-review against review 35. Fresh checkout at 3647928; sh merge-check.sh with a throwaway Postgres 17 passes (gofmt, go vet, go test -race on every package, every listed store test; exit 0, no NOT RACE-CHECKED / NOT STORE-CHECKED). On the forge, mesh/merge-gate passed at 11:32:51 UTC (builds build-agent and mesh-controller; every machine (ace, g14, novox, shanks) composes as without the change) and mesh/repo-check passed at 11:32:51 UTC, so the gate is not stuck.

Blocking item 1 (lint) is fixed. I reran my probe URLs through ParseManifest + InstallationProblems:

  • https://git.novox.internal/... refused ("a name under .internal"), even when the artifact's names-on-purpose lists that host;
  • https://10.1.2.3/..., https://203.0.113.9/..., https://[2001:db8::1]/... refused ("an address rather than a name");
  • https://forge/... refused ("a name with no domain");
  • https://mail.novox.be/... without names-on-purpose: "a names mail.novox.be at build.artifacts[0].url";
  • version git.novox.internal 10.0.0.1: the name is still caught; version 1.5.6.1 passes; a vendor host named on purpose passes.

The new tests fail on the old code, hunk by hunk. Reverting only installation.go fails TestAnUpstreamFilesVendorIsNamedOnPurposeAndAnInstallationsIsRefused; reverting only the host check in build.go fails TestAnUpstreamFileFromAnAddressOrAReservedNameIsRefused; removing only the xz drain fails TestAnUpstreamTarWithAGlobalHeaderNestedWithinAndTrailingDataIsRead (xz killed at the 10 s deadline); disabling only noDowngrade fails TestARedirectOffHttpsIsNotFollowed; disabling only the entry cap fails TestAnUpstreamArchiveWithTooManyEntriesIsRefused.

Items 2 to 6 are fixed. My earlier probes now pass: a pax global header is skipped (with and without within), a parent directory entry of within: a/b is accepted, and 4 MiB after the tar end in an xz file reads in 10 ms. Entries over 100,000 are refused, and a redirect off https is refused.

Not blocking:

  • The xz drain is io.Copy(io.Discard, stream) with no limit. It writes nothing to disk and the build's context bounds it, so a hostile pin costs CPU time only. An io.LimitReader would bound that too.
  • A per-artifact names-on-purpose entry can still name an installation's public domain with a reason. That is the same escape the module-level one already gives, and a reviewer sees it in the diff, so I accept it.
  • Item 7 still needs an issue: the upstream cache in the build workspace is never cleaned (up to 512 MiB per pin). Item 8 (memory, up to about 1 GiB at the caps) can share that issue.

mesh/delivery-group is still red ("group fix/345-reminder-test rejected: a member's own check failed", 11:27 UTC). It is not required for this pull request, but the group must pass its combined check before any member merges.

Verdict: approve at 3647928 Re-review against review 35. Fresh checkout at 3647928; `sh merge-check.sh` with a throwaway Postgres 17 passes (gofmt, go vet, `go test -race` on every package, every listed store test; exit 0, no NOT RACE-CHECKED / NOT STORE-CHECKED). On the forge, mesh/merge-gate passed at 11:32:51 UTC (builds build-agent and mesh-controller; every machine (ace, g14, novox, shanks) composes as without the change) and mesh/repo-check passed at 11:32:51 UTC, so the gate is not stuck. **Blocking item 1 (lint) is fixed.** I reran my probe URLs through ParseManifest + InstallationProblems: - `https://git.novox.internal/...` refused ("a name under .internal"), even when the artifact's names-on-purpose lists that host; - `https://10.1.2.3/...`, `https://203.0.113.9/...`, `https://[2001:db8::1]/...` refused ("an address rather than a name"); - `https://forge/...` refused ("a name with no domain"); - `https://mail.novox.be/...` without names-on-purpose: "a names mail.novox.be at build.artifacts[0].url"; - version `git.novox.internal 10.0.0.1`: the name is still caught; version `1.5.6.1` passes; a vendor host named on purpose passes. **The new tests fail on the old code, hunk by hunk.** Reverting only installation.go fails TestAnUpstreamFilesVendorIsNamedOnPurposeAndAnInstallationsIsRefused; reverting only the host check in build.go fails TestAnUpstreamFileFromAnAddressOrAReservedNameIsRefused; removing only the xz drain fails TestAnUpstreamTarWithAGlobalHeaderNestedWithinAndTrailingDataIsRead (xz killed at the 10 s deadline); disabling only noDowngrade fails TestARedirectOffHttpsIsNotFollowed; disabling only the entry cap fails TestAnUpstreamArchiveWithTooManyEntriesIsRefused. **Items 2 to 6 are fixed.** My earlier probes now pass: a pax global header is skipped (with and without `within`), a parent directory entry of `within: a/b` is accepted, and 4 MiB after the tar end in an xz file reads in 10 ms. Entries over 100,000 are refused, and a redirect off https is refused. Not blocking: - The xz drain is `io.Copy(io.Discard, stream)` with no limit. It writes nothing to disk and the build's context bounds it, so a hostile pin costs CPU time only. An `io.LimitReader` would bound that too. - A per-artifact names-on-purpose entry can still name an installation's public domain with a reason. That is the same escape the module-level one already gives, and a reviewer sees it in the diff, so I accept it. - Item 7 still needs an issue: the upstream cache in the build workspace is never cleaned (up to 512 MiB per pin). Item 8 (memory, up to about 1 GiB at the caps) can share that issue. mesh/delivery-group is still red ("group fix/345-reminder-test rejected: a member's own check failed", 11:27 UTC). It is not required for this pull request, but the group must pass its combined check before any member merges.
Author
Contributor

Merge check at 36479286

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

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

  • tier 0: mesh-controller
  • tier 1: build-agent
  • ace: receives build-agent
  • g14: receives build-agent
  • novox: receives mesh-controller, build-agent
  • shanks: receives build-agent
  • build-agent keeps data (ADR 0233): what it holds is backed up before it moves
  • 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 build-agent is judged on its first machine
  • read by no module's build: internal/builder/upstream_file_test.go, internal/catalogue/upstream_file_test.go

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

--- the gate: build-agent, mesh-controller
mesh-controller: ok, 57 tool(s); WARNING: merge-window ask(s) for a setting and do(es) not say whether it is trusted, so it counts as trusted: set at the terminal alone; say "trusted" false where nothing trusts it (novox/hq issue 339); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8)
long-running resources without health: 1
files asking for a setting without saying whether it is trusted: 1
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-12T11:40:33Z 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-12T11:40:33Z

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

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

a merge would rebuild 2 module(s) in 2 tier(s): build-agent, mesh-controller

notes:
  - internal/builder/upstream_file_test.go and internal/catalogue/upstream_file_test.go read by no module's build, and rebuild nothing (issue 280)
--- its merge-check.sh (go toolchain)
ok  	github.com/novox/mesh-controller/cmd/mesh-builder	1.044s
ok  	github.com/novox/mesh-controller/cmd/mesh-controller	235.880s
ok  	github.com/novox/mesh-controller/examples/objectstore-provisioner	1.021s
ok  	github.com/novox/mesh-controller/examples/postgres-provisioner	1.028s
ok  	github.com/novox/mesh-controller/examples/redis-provisioner	1.020s
ok  	github.com/novox/mesh-controller/examples/route-proxy	1.151s
ok  	github.com/novox/mesh-controller/internal/artifacts	1.081s
?   	github.com/novox/mesh-controller/internal/beside	[no test files]
--- FAIL: TestTheInstallersFirstUserListIsWhatTheControllerWouldCompose (0.64s)
    genesis_template_test.go:29: judged against /var/lib/build-agent/workspace/check/mesh-host as cloned beside this check
    genesis_template_test.go:48: the installer's user list lets the controller publish [mesh.seat.mesh-delivery.tool.groups], which it does not derive
FAIL
FAIL	github.com/novox/mesh-controller/internal/broker	3.217s
ok  	github.com/novox/mesh-controller/internal/builder	34.711s
ok  	github.com/novox/mesh-controller/internal/catalogue	7.967s
ok  	github.com/novox/mesh-controller/internal/conditions	2.671s
ok  	github.com/novox/mesh-controller/internal/envfile	1.077s
ok  	github.com/novox/mesh-controller/internal/facts	1.098s
ok  	github.com/novox/mesh-controller/internal/identity	4.791s
ok  	github.com/novox/mesh-controller/internal/inventory	92.786s
ok  	github.com/novox/mesh-controller/internal/lease	17.774s
ok  	github.com/novox/mesh-controller/internal/licences	4.375s
ok  	github.com/novox/mesh-controller/internal/licences/adapters	1.064s
ok  	github.com/novox/mesh-controller/internal/link	60.281s
ok  	github.com/novox/mesh-controller/internal/lint	2.335s
ok  	github.com/novox/mesh-controller/internal/outward	1.113s
ok  	github.com/novox/mesh-controller/internal/overlay	1.100s
ok  	github.com/novox/mesh-controller/internal/secrets	1.476s
ok  	github.com/novox/mesh-controller/internal/store	3.578s
ok  	github.com/novox/mesh-controller/internal/testbus	1.228s
ok  	github.com/novox/mesh-controller/internal/token	1.019s
FAIL
--- what failed, picked from the whole of its output (the whole is in the build's log)
--- FAIL: TestTheInstallersFirstUserListIsWhatTheControllerWouldCompose (0.64s)
    genesis_template_test.go:29: judged against /var/lib/build-agent/workspace/check/mesh-host as cloned beside this check
    genesis_template_test.go:48: the installer's user list lets the controller publish [mesh.seat.mesh-delivery.tool.groups], which it does not derive
FAIL	github.com/novox/mesh-controller/internal/broker	3.217s
**Merge check** at `36479286` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller) - `mesh/repo-check`: **FAIL** — its merge-check.sh failed: --- FAIL: TestTheInstallersFirstUserListIsWhatTheControllerWouldCompose (0.64s) **Delivery plan** — builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step - tier 0: mesh-controller - tier 1: build-agent - ace: receives build-agent - g14: receives build-agent - novox: receives mesh-controller, build-agent - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - 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 build-agent is judged on its first machine - read by no module's build: internal/builder/upstream_file_test.go, internal/catalogue/upstream_file_test.go Run by the build seat on ace as `build-1791805316313523621` (`builds --log build-1791805316313523621`). ``` --- the gate: build-agent, mesh-controller mesh-controller: ok, 57 tool(s); WARNING: merge-window ask(s) for a setting and do(es) not say whether it is trusted, so it counts as trusted: set at the terminal alone; say "trusted" false where nothing trusts it (novox/hq issue 339); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8) long-running resources without health: 1 files asking for a setting without saying whether it is trusted: 1 1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-12T11:40:33Z 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-12T11:40:33Z modules the change moves: - mesh-controller (changed) machines: - a machine (f35): composes - holds mesh-dns-resolver (idt): composes - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes - a machine (yvbpsm): composes a merge would rebuild 2 module(s) in 2 tier(s): build-agent, mesh-controller notes: - internal/builder/upstream_file_test.go and internal/catalogue/upstream_file_test.go read by no module's build, and rebuild nothing (issue 280) --- its merge-check.sh (go toolchain) ok github.com/novox/mesh-controller/cmd/mesh-builder 1.044s ok github.com/novox/mesh-controller/cmd/mesh-controller 235.880s ok github.com/novox/mesh-controller/examples/objectstore-provisioner 1.021s ok github.com/novox/mesh-controller/examples/postgres-provisioner 1.028s ok github.com/novox/mesh-controller/examples/redis-provisioner 1.020s ok github.com/novox/mesh-controller/examples/route-proxy 1.151s ok github.com/novox/mesh-controller/internal/artifacts 1.081s ? github.com/novox/mesh-controller/internal/beside [no test files] --- FAIL: TestTheInstallersFirstUserListIsWhatTheControllerWouldCompose (0.64s) genesis_template_test.go:29: judged against /var/lib/build-agent/workspace/check/mesh-host as cloned beside this check genesis_template_test.go:48: the installer's user list lets the controller publish [mesh.seat.mesh-delivery.tool.groups], which it does not derive FAIL FAIL github.com/novox/mesh-controller/internal/broker 3.217s ok github.com/novox/mesh-controller/internal/builder 34.711s ok github.com/novox/mesh-controller/internal/catalogue 7.967s ok github.com/novox/mesh-controller/internal/conditions 2.671s ok github.com/novox/mesh-controller/internal/envfile 1.077s ok github.com/novox/mesh-controller/internal/facts 1.098s ok github.com/novox/mesh-controller/internal/identity 4.791s ok github.com/novox/mesh-controller/internal/inventory 92.786s ok github.com/novox/mesh-controller/internal/lease 17.774s ok github.com/novox/mesh-controller/internal/licences 4.375s ok github.com/novox/mesh-controller/internal/licences/adapters 1.064s ok github.com/novox/mesh-controller/internal/link 60.281s ok github.com/novox/mesh-controller/internal/lint 2.335s ok github.com/novox/mesh-controller/internal/outward 1.113s ok github.com/novox/mesh-controller/internal/overlay 1.100s ok github.com/novox/mesh-controller/internal/secrets 1.476s ok github.com/novox/mesh-controller/internal/store 3.578s ok github.com/novox/mesh-controller/internal/testbus 1.228s ok github.com/novox/mesh-controller/internal/token 1.019s FAIL --- what failed, picked from the whole of its output (the whole is in the build's log) --- FAIL: TestTheInstallersFirstUserListIsWhatTheControllerWouldCompose (0.64s) genesis_template_test.go:29: judged against /var/lib/build-agent/workspace/check/mesh-host as cloned beside this check genesis_template_test.go:48: the installer's user list lets the controller publish [mesh.seat.mesh-delivery.tool.groups], which it does not derive FAIL github.com/novox/mesh-controller/internal/broker 3.217s ```
Author
Contributor

Moved to #255 (branch fix/345-builder-archive-pins): the same two commits rebased onto main 4b1dee6 without conflicts (range-diff = for both), so it leaves the group fix/345-reminder-test. Closing this one.

Moved to #255 (branch fix/345-builder-archive-pins): the same two commits rebased onto main 4b1dee6 without conflicts (range-diff `=` for both), so it leaves the group fix/345-reminder-test. Closing this one.
mesh-admin closed this pull request 2026-10-12 11:53:11 +00:00
This repo is archived. You cannot comment on pull requests.
No Reviewers
2 Participants
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-controller#251