Read an xz release in Go, so the build seat needs no xz tool (hq issue 345) #264

Merged
mesh-admin merged 3 commits from fix/345-xz-in-go into main 2026-10-12 17:35:57 +00:00
Contributor

Symptom. mesh-catalog #240's build (plan-1791809737454479380, build-1791810262889408363) failed: archive … betterbird-153.4.0esr-bb10.en-US.linux-x86_64.tar.xz: it is compressed with xz, and this build seat has no xz to read it.

Cause. #255 read xz by running the xz tool and added xz to cmd/mesh-builder/Dockerfile. The build seat does not run that image: its image is built from the catalogue's modules/build-agent/Dockerfile (with this repository as context), which installs docker-cli git alone. #255's rollout did rebuild build-agent, from that recipe, so the seat never had xz. My #255 report was wrong to say xz was in the seat's image.

Fix. The xz stream is read in Go with github.com/therootcompany/xz v1.0.1 (vendored; a port of xz-embedded, CC0), whose NewReader(r, dictMax) refuses a dictionary over 64 MiB (xz's largest preset, -9) before allocating it (review 65). What it unpacks to is bounded as before (maxUpstream 512 MiB, maxEntries 100,000). No tool is run, so the exec path, the drain and the wait go. The xz line in the unused cmd/mesh-builder/Dockerfile is taken back out.

Block limit (review 68). The reader keeps one delta or BCJ decoder per block and frees them only at the end of a stream, so a file of many tiny blocks grows the heap with its length (about 67 MiB live for a 6 MB xz --block-size=1 --delta file). Before decoding, xzWithinLimits (internal/builder/xz_framing.go) now walks the file's framing without decompressing: stream header, each block header, the LZMA2 chunk headers (each carries its compressed size, so a chunk is skipped unread), padding, check, index, footer, stream padding. It refuses a file of more than 1,024 blocks or more than 16 concatenated streams, and an index whose block count is not the count walked.

  • Why the framing and not the index alone: the reader compares the index with the blocks only after decoding them all, so a file whose index lies would still be decoded. The walk counts the blocks the reader will decode.
  • Why not refuse delta and BCJ outright: it needs the same walk to see each block header, and it would refuse sound releases filtered with BCJ.
  • Why 1,024: about 340 bytes of decoder per block, so at most about 0.35 MiB live. Betterbird's archive is 1 stream with 1 block; multi-threaded xz cuts blocks of 3 times the dictionary, so 512 MiB in blocks of 512 KiB still fits. 16 streams: a release is one stream, and the decoders are reused across streams, so that limit only bounds the walk's work.
  • The library stays unforked.

Tests.

  • TestAnXzReleaseIsReadWithoutAnyXzToolOnTheBuildSeat: a committed 252-byte .tar.xz fixture unpacked with PATH empty. Fails on main with the exact message above.
  • TestAnXzStreamAskingForTooLargeADictionaryIsRefused (review 65).
  • TestAnXzFileOfManyDeltaBlocksIsRefusedBeforeItIsDecoded: a 4.0 MB stream of one-byte delta blocks (about 202,000 blocks) whose index lists 1 block, built in Go by the test; refused with "more than 1024 xz blocks", and TotalAlloc grows less than 4 MiB. With the walk disabled it is decoded without error; the same file through the reader alone keeps 57 MiB live.
  • TestTheXzBlockLimitIsExact (1,024 blocks pass, 1,025 refused), TestAnXzFileOfTooManyStreamsIsRefused (17 streams), TestAnXzIndexThatDoesNotListItsBlocksIsRefused.
  • TestAnXzFileOfAFewDeltaBlocksAndTwoStreamsIsRead: the test's own builder makes streams that xz -t accepts and that unpack to the right bytes (delta blocks; two streams with padding), so the walk follows the same framing the reader does.
  • The real Betterbird archive is 1 stream with 1 block, within the limit; its result is unchanged (review 68 checked 55 files, packed sha256 74dd6b3b…bd7c).
  • go vet ./... clean; sh merge-check.sh with a throwaway Postgres 17 (MESH_TEST_POSTGRES) passes with exit 0: gofmt, go test -race on every package, every store test.

After delivery. build-agent rebuilds from the catalogue's recipe with this source; then re-run the Betterbird build of mesh-catalog #240's merge.

A controller and build-agent rollout: merge only while mesh-controller.plans says no plan is mid-gate. Part of issue 345. Not merged.

**Symptom.** mesh-catalog #240's build (plan-1791809737454479380, build-1791810262889408363) failed: `archive … betterbird-153.4.0esr-bb10.en-US.linux-x86_64.tar.xz: it is compressed with xz, and this build seat has no xz to read it`. **Cause.** #255 read xz by running the `xz` tool and added `xz` to `cmd/mesh-builder/Dockerfile`. The build seat does not run that image: its image is built from the catalogue's `modules/build-agent/Dockerfile` (with this repository as context), which installs `docker-cli git` alone. #255's rollout did rebuild build-agent, from that recipe, so the seat never had xz. My #255 report was wrong to say xz was in the seat's image. **Fix.** The xz stream is read in Go with `github.com/therootcompany/xz` v1.0.1 (vendored; a port of xz-embedded, CC0), whose `NewReader(r, dictMax)` refuses a dictionary over 64 MiB (xz's largest preset, `-9`) before allocating it (review 65). What it unpacks to is bounded as before (`maxUpstream` 512 MiB, `maxEntries` 100,000). No tool is run, so the exec path, the drain and the `wait` go. The xz line in the unused `cmd/mesh-builder/Dockerfile` is taken back out. **Block limit (review 68).** The reader keeps one delta or BCJ decoder per block and frees them only at the end of a stream, so a file of many tiny blocks grows the heap with its length (about 67 MiB live for a 6 MB `xz --block-size=1 --delta` file). Before decoding, `xzWithinLimits` (`internal/builder/xz_framing.go`) now walks the file's framing without decompressing: stream header, each block header, the LZMA2 chunk headers (each carries its compressed size, so a chunk is skipped unread), padding, check, index, footer, stream padding. It refuses a file of more than **1,024 blocks** or more than **16 concatenated streams**, and an index whose block count is not the count walked. - Why the framing and not the index alone: the reader compares the index with the blocks only after decoding them all, so a file whose index lies would still be decoded. The walk counts the blocks the reader will decode. - Why not refuse delta and BCJ outright: it needs the same walk to see each block header, and it would refuse sound releases filtered with BCJ. - Why 1,024: about 340 bytes of decoder per block, so at most about 0.35 MiB live. Betterbird's archive is 1 stream with 1 block; multi-threaded xz cuts blocks of 3 times the dictionary, so 512 MiB in blocks of 512 KiB still fits. 16 streams: a release is one stream, and the decoders are reused across streams, so that limit only bounds the walk's work. - The library stays unforked. **Tests.** - `TestAnXzReleaseIsReadWithoutAnyXzToolOnTheBuildSeat`: a committed 252-byte `.tar.xz` fixture unpacked with `PATH` empty. Fails on main with the exact message above. - `TestAnXzStreamAskingForTooLargeADictionaryIsRefused` (review 65). - `TestAnXzFileOfManyDeltaBlocksIsRefusedBeforeItIsDecoded`: a 4.0 MB stream of one-byte delta blocks (about 202,000 blocks) whose index lists 1 block, built in Go by the test; refused with "more than 1024 xz blocks", and TotalAlloc grows less than 4 MiB. With the walk disabled it is decoded without error; the same file through the reader alone keeps 57 MiB live. - `TestTheXzBlockLimitIsExact` (1,024 blocks pass, 1,025 refused), `TestAnXzFileOfTooManyStreamsIsRefused` (17 streams), `TestAnXzIndexThatDoesNotListItsBlocksIsRefused`. - `TestAnXzFileOfAFewDeltaBlocksAndTwoStreamsIsRead`: the test's own builder makes streams that `xz -t` accepts and that unpack to the right bytes (delta blocks; two streams with padding), so the walk follows the same framing the reader does. - The real Betterbird archive is 1 stream with 1 block, within the limit; its result is unchanged (review 68 checked 55 files, packed sha256 74dd6b3b…bd7c). - `go vet ./...` clean; `sh merge-check.sh` with a throwaway Postgres 17 (`MESH_TEST_POSTGRES`) passes with exit 0: gofmt, `go test -race` on every package, every store test. **After delivery.** build-agent rebuilds from the catalogue's recipe with this source; then re-run the Betterbird build of mesh-catalog #240's merge. A controller and build-agent rollout: merge only while `mesh-controller.plans` says no plan is mid-gate. Part of issue 345. Not merged.
mesh-admin added 1 commit 2026-10-12 13:11:02 +00:00
Read an xz release in Go, so the build seat needs no xz tool (hq issue 345)
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
10f76cafc4
Betterbird's pinned .tar.xz failed on the build seat with "this build seat has
no xz to read it": the seat's image is the catalogue's build-agent recipe,
which installs git and docker-cli alone, and xz had been added only to
cmd/mesh-builder/Dockerfile, which the seat does not build from. The stream
is now read with github.com/ulikunitz/xz (vendored), its dictionary capped
at xz's own largest preset, and what it unpacks to bounded as before; the
xz line in the unused Dockerfile is taken back out.
Author
Contributor

Delivery novox/mesh-controller@539a1c16cd4e — delivered since 2026-10-12T17:50:24Z

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

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

Machines

  • mesh-controller on novox: passed — healthy 3 times over 4m8s
  • mesh-controller on the rest running it: sent
  • build-agent on ace: passed — healthy 1 times over 1m45s
  • build-agent on the rest running it: sent
  • route-proxy on ace: passed — healthy 1 times over 1m25s
  • route-proxy on the rest running it: sent

Transitions

  • 2026-10-12 14:08 (new) → proposed (announced): novox/mesh-controller#264's head announced
  • 2026-10-12 14:15 proposed → checked (checked): the verdict names this commit
  • 2026-10-12 14:15 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
  • 2026-10-12 17:36 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
  • 2026-10-12 17:36 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-12 17:50 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-controller@539a1c16cd4e` — **delivered** since 2026-10-12T17:50:24Z **Delivery plan** — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step - tier 0: mesh-controller - tier 1: build-agent - tier 2: route-proxy - ace: receives build-agent, route-proxy - g14: receives build-agent - novox: receives mesh-controller, build-agent, route-proxy - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - route-proxy provides route: its consumers are sent again after it - route-proxy keeps data (ADR 0233): what it holds is backed up before it moves - 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, route-proxy is judged on its first machine **Machines** - mesh-controller on novox: passed — healthy 3 times over 4m8s - mesh-controller on the rest running it: sent - build-agent on ace: passed — healthy 1 times over 1m45s - build-agent on the rest running it: sent - route-proxy on ace: passed — healthy 1 times over 1m25s - route-proxy on the rest running it: sent **Transitions** - 2026-10-12 14:08 (new) → proposed (announced): novox/mesh-controller#264's head announced - 2026-10-12 14:15 proposed → checked (checked): the verdict names this commit - 2026-10-12 14:15 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail - 2026-10-12 17:36 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move - 2026-10-12 17:36 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-12 17:50 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 10f76caf

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

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

  • tier 0: mesh-controller
  • tier 1: build-agent
  • tier 2: route-proxy
  • ace: receives build-agent, route-proxy
  • g14: receives build-agent
  • novox: receives mesh-controller, build-agent, route-proxy
  • shanks: receives build-agent
  • build-agent keeps data (ADR 0233): what it holds is backed up before it moves
  • route-proxy provides route: its consumers are sent again after it
  • route-proxy keeps data (ADR 0233): what it holds is backed up before it moves
  • 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, route-proxy is judged on its first machine
  • read by no module's build: internal/builder/testdata/release.tar.xz, internal/builder/upstream_file_test.go, vendor/github.com/ulikunitz/xz/.gitignore, vendor/github.com/ulikunitz/xz/LICENSE, vendor/github.com/ulikunitz/xz/README.md, vendor/github.com/ulikunitz/xz/SECURITY.md, vendor/github.com/ulikunitz/xz/TODO.md, vendor/github.com/ulikunitz/xz/bits.go, vendor/github.com/ulikunitz/xz/crc.go, vendor/github.com/ulikunitz/xz/format.go, vendor/github.com/ulikunitz/xz/fox-check-none.xz, vendor/github.com/ulikunitz/xz/fox.xz, vendor/github.com/ulikunitz/xz/internal/hash/cyclic_poly.go, vendor/github.com/ulikunitz/xz/internal/hash/doc.go, vendor/github.com/ulikunitz/xz/internal/hash/rabin_karp.go, vendor/github.com/ulikunitz/xz/internal/hash/roller.go, vendor/github.com/ulikunitz/xz/internal/xlog/xlog.go, vendor/github.com/ulikunitz/xz/lzma/bintree.go, vendor/github.com/ulikunitz/xz/lzma/bitops.go, vendor/github.com/ulikunitz/xz/lzma/breader.go, vendor/github.com/ulikunitz/xz/lzma/buffer.go, vendor/github.com/ulikunitz/xz/lzma/bytewriter.go, vendor/github.com/ulikunitz/xz/lzma/decoder.go, vendor/github.com/ulikunitz/xz/lzma/decoderdict.go, vendor/github.com/ulikunitz/xz/lzma/directcodec.go, vendor/github.com/ulikunitz/xz/lzma/distcodec.go, vendor/github.com/ulikunitz/xz/lzma/encoder.go, vendor/github.com/ulikunitz/xz/lzma/encoderdict.go, vendor/github.com/ulikunitz/xz/lzma/fox.lzma, vendor/github.com/ulikunitz/xz/lzma/hashtable.go, vendor/github.com/ulikunitz/xz/lzma/header.go, vendor/github.com/ulikunitz/xz/lzma/header2.go, vendor/github.com/ulikunitz/xz/lzma/lengthcodec.go, vendor/github.com/ulikunitz/xz/lzma/literalcodec.go, vendor/github.com/ulikunitz/xz/lzma/matchalgorithm.go, vendor/github.com/ulikunitz/xz/lzma/operation.go, vendor/github.com/ulikunitz/xz/lzma/prob.go, vendor/github.com/ulikunitz/xz/lzma/properties.go, vendor/github.com/ulikunitz/xz/lzma/rangecodec.go, vendor/github.com/ulikunitz/xz/lzma/reader.go, vendor/github.com/ulikunitz/xz/lzma/reader2.go, vendor/github.com/ulikunitz/xz/lzma/state.go, vendor/github.com/ulikunitz/xz/lzma/treecodecs.go, vendor/github.com/ulikunitz/xz/lzma/writer.go, vendor/github.com/ulikunitz/xz/lzma/writer2.go, vendor/github.com/ulikunitz/xz/lzmafilter.go, vendor/github.com/ulikunitz/xz/make-docs, vendor/github.com/ulikunitz/xz/none-check.go, vendor/github.com/ulikunitz/xz/reader.go, vendor/github.com/ulikunitz/xz/writer.go

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

--- the gate: build-agent, mesh-controller, route-proxy
mesh-controller: ok, 58 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-12T13:06:42Z 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-12T13:06:42Z

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

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

a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy
--- its merge-check.sh (go toolchain)
ok  	github.com/novox/mesh-controller/cmd/mesh-builder	1.042s
ok  	github.com/novox/mesh-controller/cmd/mesh-controller	321.859s
ok  	github.com/novox/mesh-controller/examples/objectstore-provisioner	1.023s
ok  	github.com/novox/mesh-controller/examples/postgres-provisioner	1.027s
ok  	github.com/novox/mesh-controller/examples/redis-provisioner	1.026s
ok  	github.com/novox/mesh-controller/examples/route-proxy	1.145s
ok  	github.com/novox/mesh-controller/internal/artifacts	1.064s
?   	github.com/novox/mesh-controller/internal/beside	[no test files]
ok  	github.com/novox/mesh-controller/internal/broker	4.975s
ok  	github.com/novox/mesh-controller/internal/builder	46.768s
ok  	github.com/novox/mesh-controller/internal/catalogue	9.395s
ok  	github.com/novox/mesh-controller/internal/conditions	2.722s
ok  	github.com/novox/mesh-controller/internal/envfile	1.037s
ok  	github.com/novox/mesh-controller/internal/facts	1.029s
ok  	github.com/novox/mesh-controller/internal/identity	11.786s
ok  	github.com/novox/mesh-controller/internal/inventory	119.912s
ok  	github.com/novox/mesh-controller/internal/lease	19.217s
ok  	github.com/novox/mesh-controller/internal/licences	8.034s
ok  	github.com/novox/mesh-controller/internal/licences/adapters	1.017s
ok  	github.com/novox/mesh-controller/internal/link	72.863s
ok  	github.com/novox/mesh-controller/internal/lint	2.547s
ok  	github.com/novox/mesh-controller/internal/outward	1.056s
ok  	github.com/novox/mesh-controller/internal/overlay	1.033s
ok  	github.com/novox/mesh-controller/internal/secrets	1.542s
ok  	github.com/novox/mesh-controller/internal/store	4.159s
ok  	github.com/novox/mesh-controller/internal/testbus	1.225s
ok  	github.com/novox/mesh-controller/internal/token	1.029s
store tests, each run against the store:
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeWalksTheOthersAgain (0.54s)
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeEndsTheMergesThatContainIt (0.58s)
  ./cmd/mesh-controller --- PASS: TestAStopWalksTheOthersAgainInTheOpenBatch (0.73s)
  ./cmd/mesh-controller --- PASS: TestAStopNamesWhatStaysOnTheMachinesAndWhatStillCarriesIt (0.59s)
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeLeavesItOutOfABatch (0.63s)
  ./cmd/mesh-controller --- PASS: TestSavingPlansAndMovingMergesIsOneAct (0.53s)
  ./cmd/mesh-controller --- PASS: TestNodeShowAndStatusReadTheLastSendFromTheStore (0.68s)
  ./cmd/mesh-controller --- PASS: TestTheConditionsVerbReadsTheLooksTheBucketNoLongerHolds (0.36s)
  ./cmd/mesh-controller --- PASS: TestAWalkLetGoAndStoppedByVerbsIsSaidStartedAndEnded (0.61s)
  ./cmd/mesh-controller --- PASS: TestAWalkStoppedOrClosedByAPersonIsSaidEndedStopped (0.87s)
  ./cmd/mesh-controller --- PASS: TestAWalkRetriedByAPersonIsSaidStartedAgain (0.67s)
  ./cmd/mesh-controller --- PASS: TestABacklogReleasedByAPersonIsSaidStarted (0.87s)
  ./cmd/mesh-controller --- PASS: TestAWalkEndedByCancelOrClearIsSaidEnded (0.83s)
  ./cmd/mesh-controller --- PASS: TestAWalkRestartedByARebuildIsSaidStarted (0.67s)
  ./cmd/mesh-controller --- PASS: TestAWalkStoppedByHandAndRetriedEndsFailedWhenItFailsAgain (0.74s)
  ./internal/inventory --- PASS: TestTheLastSendSaysWhatTheMachineAnswered (0.29s)
  ./internal/inventory --- PASS: TestAReportIsTheLastSendsAnswerOnlyWhenItIsAboutThatSend (0.34s)
  ./internal/inventory --- PASS: TestAnIssuedValueIsSealedToEveryHolderOnceAndNotAgain (0.29s)
  ./internal/inventory --- PASS: TestAnIssuedValueIsSealedAgainAfterAKeyChangeOrAnAcceptance (0.29s)
  ./internal/inventory --- PASS: TestAConditionsLooksAreKeptBesideTheBucket (0.30s)
  ./internal/inventory --- PASS: TestAWalksStartIsKeptAndARefusedSaveLeavesItUnsaid (0.29s)
**Merge check** at `10f76caf` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy) - `mesh/repo-check`: **PASS** — its merge-check.sh passed **Delivery plan** — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step - tier 0: mesh-controller - tier 1: build-agent - tier 2: route-proxy - ace: receives build-agent, route-proxy - g14: receives build-agent - novox: receives mesh-controller, build-agent, route-proxy - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - route-proxy provides route: its consumers are sent again after it - route-proxy keeps data (ADR 0233): what it holds is backed up before it moves - 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, route-proxy is judged on its first machine - read by no module's build: internal/builder/testdata/release.tar.xz, internal/builder/upstream_file_test.go, vendor/github.com/ulikunitz/xz/.gitignore, vendor/github.com/ulikunitz/xz/LICENSE, vendor/github.com/ulikunitz/xz/README.md, vendor/github.com/ulikunitz/xz/SECURITY.md, vendor/github.com/ulikunitz/xz/TODO.md, vendor/github.com/ulikunitz/xz/bits.go, vendor/github.com/ulikunitz/xz/crc.go, vendor/github.com/ulikunitz/xz/format.go, vendor/github.com/ulikunitz/xz/fox-check-none.xz, vendor/github.com/ulikunitz/xz/fox.xz, vendor/github.com/ulikunitz/xz/internal/hash/cyclic_poly.go, vendor/github.com/ulikunitz/xz/internal/hash/doc.go, vendor/github.com/ulikunitz/xz/internal/hash/rabin_karp.go, vendor/github.com/ulikunitz/xz/internal/hash/roller.go, vendor/github.com/ulikunitz/xz/internal/xlog/xlog.go, vendor/github.com/ulikunitz/xz/lzma/bintree.go, vendor/github.com/ulikunitz/xz/lzma/bitops.go, vendor/github.com/ulikunitz/xz/lzma/breader.go, vendor/github.com/ulikunitz/xz/lzma/buffer.go, vendor/github.com/ulikunitz/xz/lzma/bytewriter.go, vendor/github.com/ulikunitz/xz/lzma/decoder.go, vendor/github.com/ulikunitz/xz/lzma/decoderdict.go, vendor/github.com/ulikunitz/xz/lzma/directcodec.go, vendor/github.com/ulikunitz/xz/lzma/distcodec.go, vendor/github.com/ulikunitz/xz/lzma/encoder.go, vendor/github.com/ulikunitz/xz/lzma/encoderdict.go, vendor/github.com/ulikunitz/xz/lzma/fox.lzma, vendor/github.com/ulikunitz/xz/lzma/hashtable.go, vendor/github.com/ulikunitz/xz/lzma/header.go, vendor/github.com/ulikunitz/xz/lzma/header2.go, vendor/github.com/ulikunitz/xz/lzma/lengthcodec.go, vendor/github.com/ulikunitz/xz/lzma/literalcodec.go, vendor/github.com/ulikunitz/xz/lzma/matchalgorithm.go, vendor/github.com/ulikunitz/xz/lzma/operation.go, vendor/github.com/ulikunitz/xz/lzma/prob.go, vendor/github.com/ulikunitz/xz/lzma/properties.go, vendor/github.com/ulikunitz/xz/lzma/rangecodec.go, vendor/github.com/ulikunitz/xz/lzma/reader.go, vendor/github.com/ulikunitz/xz/lzma/reader2.go, vendor/github.com/ulikunitz/xz/lzma/state.go, vendor/github.com/ulikunitz/xz/lzma/treecodecs.go, vendor/github.com/ulikunitz/xz/lzma/writer.go, vendor/github.com/ulikunitz/xz/lzma/writer2.go, vendor/github.com/ulikunitz/xz/lzmafilter.go, vendor/github.com/ulikunitz/xz/make-docs, vendor/github.com/ulikunitz/xz/none-check.go, vendor/github.com/ulikunitz/xz/reader.go, vendor/github.com/ulikunitz/xz/writer.go Run by the build seat on novox as `build-1791810667206442075` (`builds --log build-1791810667206442075`). ``` --- the gate: build-agent, mesh-controller, route-proxy mesh-controller: ok, 58 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-12T13:06:42Z 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-12T13:06:42Z modules the change moves: - mesh-controller (changed) machines: - a machine (f35): composes - holds mesh-dns-resolver (idt): composes - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes - a machine (yvbpsm): composes a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy --- its merge-check.sh (go toolchain) ok github.com/novox/mesh-controller/cmd/mesh-builder 1.042s ok github.com/novox/mesh-controller/cmd/mesh-controller 321.859s ok github.com/novox/mesh-controller/examples/objectstore-provisioner 1.023s ok github.com/novox/mesh-controller/examples/postgres-provisioner 1.027s ok github.com/novox/mesh-controller/examples/redis-provisioner 1.026s ok github.com/novox/mesh-controller/examples/route-proxy 1.145s ok github.com/novox/mesh-controller/internal/artifacts 1.064s ? github.com/novox/mesh-controller/internal/beside [no test files] ok github.com/novox/mesh-controller/internal/broker 4.975s ok github.com/novox/mesh-controller/internal/builder 46.768s ok github.com/novox/mesh-controller/internal/catalogue 9.395s ok github.com/novox/mesh-controller/internal/conditions 2.722s ok github.com/novox/mesh-controller/internal/envfile 1.037s ok github.com/novox/mesh-controller/internal/facts 1.029s ok github.com/novox/mesh-controller/internal/identity 11.786s ok github.com/novox/mesh-controller/internal/inventory 119.912s ok github.com/novox/mesh-controller/internal/lease 19.217s ok github.com/novox/mesh-controller/internal/licences 8.034s ok github.com/novox/mesh-controller/internal/licences/adapters 1.017s ok github.com/novox/mesh-controller/internal/link 72.863s ok github.com/novox/mesh-controller/internal/lint 2.547s ok github.com/novox/mesh-controller/internal/outward 1.056s ok github.com/novox/mesh-controller/internal/overlay 1.033s ok github.com/novox/mesh-controller/internal/secrets 1.542s ok github.com/novox/mesh-controller/internal/store 4.159s ok github.com/novox/mesh-controller/internal/testbus 1.225s ok github.com/novox/mesh-controller/internal/token 1.029s store tests, each run against the store: ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeWalksTheOthersAgain (0.54s) ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeEndsTheMergesThatContainIt (0.58s) ./cmd/mesh-controller --- PASS: TestAStopWalksTheOthersAgainInTheOpenBatch (0.73s) ./cmd/mesh-controller --- PASS: TestAStopNamesWhatStaysOnTheMachinesAndWhatStillCarriesIt (0.59s) ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeLeavesItOutOfABatch (0.63s) ./cmd/mesh-controller --- PASS: TestSavingPlansAndMovingMergesIsOneAct (0.53s) ./cmd/mesh-controller --- PASS: TestNodeShowAndStatusReadTheLastSendFromTheStore (0.68s) ./cmd/mesh-controller --- PASS: TestTheConditionsVerbReadsTheLooksTheBucketNoLongerHolds (0.36s) ./cmd/mesh-controller --- PASS: TestAWalkLetGoAndStoppedByVerbsIsSaidStartedAndEnded (0.61s) ./cmd/mesh-controller --- PASS: TestAWalkStoppedOrClosedByAPersonIsSaidEndedStopped (0.87s) ./cmd/mesh-controller --- PASS: TestAWalkRetriedByAPersonIsSaidStartedAgain (0.67s) ./cmd/mesh-controller --- PASS: TestABacklogReleasedByAPersonIsSaidStarted (0.87s) ./cmd/mesh-controller --- PASS: TestAWalkEndedByCancelOrClearIsSaidEnded (0.83s) ./cmd/mesh-controller --- PASS: TestAWalkRestartedByARebuildIsSaidStarted (0.67s) ./cmd/mesh-controller --- PASS: TestAWalkStoppedByHandAndRetriedEndsFailedWhenItFailsAgain (0.74s) ./internal/inventory --- PASS: TestTheLastSendSaysWhatTheMachineAnswered (0.29s) ./internal/inventory --- PASS: TestAReportIsTheLastSendsAnswerOnlyWhenItIsAboutThatSend (0.34s) ./internal/inventory --- PASS: TestAnIssuedValueIsSealedToEveryHolderOnceAndNotAgain (0.29s) ./internal/inventory --- PASS: TestAnIssuedValueIsSealedAgainAfterAKeyChangeOrAnAcceptance (0.29s) ./internal/inventory --- PASS: TestAConditionsLooksAreKeptBesideTheBucket (0.30s) ./internal/inventory --- PASS: TestAWalksStartIsKeptAndARefusedSaveLeavesItUnsaid (0.29s) ```
mesh-admin left a comment
Author
Contributor

Verdict: request changes at 10f76ca

Blocking

xz.ReaderConfig.DictCap is a floor, not a cap, so the 64 MiB limit does not bound memory. In the vendored lzmafilter.go (lzmaFilter.reader), the stream's own dictionary size replaces the configured one whenever it is larger: if dc > config.DictCap { config.DictCap = dc }. I proved it with a probe test (not committed). I made a real .tar.xz with xz --lzma2=dict=1MiB, patched the dictionary code in its block header, recomputed the header CRC32 and passed it through unpackUpstream:

  • dictionary code 36 (1 GiB): no error, and TotalAlloc grew by 1024 MiB;
  • dictionary code 40 (4 GiB - 1): no error, and TotalAlloc grew by 4096 MiB.

The stream is accepted and the dictionary is allocated in full, so the comment on xzDictCap ("a stream asking for more is refused") is not true. The digest pin limits who can supply such a file, but the code claims a limit it does not enforce, and a 4 GiB allocation on the build node is the failure the cap exists to prevent.

Two ways to fix it:

  • Read each block header's LZMA2 dictionary size before decoding and refuse a size above xzDictCap. A stream can hold several blocks, so either walk the index at the end of the stream, or wrap the reader so each block header is checked as it arrives.
  • Use a reader that takes a real maximum, for example github.com/therootcompany/xz (a port of xz-embedded, whose NewReader(r, dictMax) refuses a larger dictionary).

Either way, add a test that builds a stream asking for a dictionary above the cap (patched dictionary byte and recomputed CRC, as above) and expects a refusal that names the cap.

Checked and fine

  • Vendored copy: the go.sum lines match what go mod download fetched through the checksum database (h1:flR0y/x1hgM8EGV1AW3Xll6T413G0glV8UfBwR617V4=). GOFLAGS=-mod=mod go mod verify says "all modules verified". All 48 vendored files are byte-identical to the module cache copy of v0.5.17.
  • Licence: BSD-3-Clause, fine to vendor.
  • Known vulnerabilities: OSV lists none for v0.5.17. The two known advisories are fixed in this version: CVE-2021-29482 (fixed in 0.5.8) and CVE-2025-58058 (fixed in 0.5.15).
  • Size caps on the xz path: the decompressed stream still goes through untar, so the 512 MiB unpacked limit and the 100,000-entry limit still apply. Removing the exec, drain and wait code is right.
  • The new test fails on main: with main's upstream_file.go, TestAnXzReleaseIsReadWithoutAnyXzToolOnTheBuildSeat fails with "it is compressed with xz, and this build seat has no xz to read it".
  • Local run: I ran merge-check.sh with a throwaway Postgres 17. gofmt, go vet and go test -race on everything else passed. One unrelated test failed once: TestAMachineJoiningIsAPeerOfTheHubUntilItsTokenExpires with "no live token to issue for the tunnel key". It uses a 2-second token, so I think it is a timing flake under the full parallel race run. It passed 3 of 3 times run alone, and this pull request does not touch it.
  • Forge checks: mesh/merge-gate passed (builds build-agent, mesh-controller and route-proxy; every machine composes), and mesh/repo-check passed, both at 13:22 UTC.
Verdict: request changes at 10f76ca ## Blocking **`xz.ReaderConfig.DictCap` is a floor, not a cap, so the 64 MiB limit does not bound memory.** In the vendored `lzmafilter.go` (`lzmaFilter.reader`), the stream's own dictionary size replaces the configured one whenever it is larger: `if dc > config.DictCap { config.DictCap = dc }`. I proved it with a probe test (not committed). I made a real .tar.xz with `xz --lzma2=dict=1MiB`, patched the dictionary code in its block header, recomputed the header CRC32 and passed it through `unpackUpstream`: - dictionary code 36 (1 GiB): no error, and TotalAlloc grew by 1024 MiB; - dictionary code 40 (4 GiB - 1): no error, and TotalAlloc grew by 4096 MiB. The stream is accepted and the dictionary is allocated in full, so the comment on `xzDictCap` ("a stream asking for more is refused") is not true. The digest pin limits who can supply such a file, but the code claims a limit it does not enforce, and a 4 GiB allocation on the build node is the failure the cap exists to prevent. Two ways to fix it: - Read each block header's LZMA2 dictionary size before decoding and refuse a size above `xzDictCap`. A stream can hold several blocks, so either walk the index at the end of the stream, or wrap the reader so each block header is checked as it arrives. - Use a reader that takes a real maximum, for example `github.com/therootcompany/xz` (a port of xz-embedded, whose `NewReader(r, dictMax)` refuses a larger dictionary). Either way, add a test that builds a stream asking for a dictionary above the cap (patched dictionary byte and recomputed CRC, as above) and expects a refusal that names the cap. ## Checked and fine - **Vendored copy:** the go.sum lines match what `go mod download` fetched through the checksum database (`h1:flR0y/x1hgM8EGV1AW3Xll6T413G0glV8UfBwR617V4=`). `GOFLAGS=-mod=mod go mod verify` says "all modules verified". All 48 vendored files are byte-identical to the module cache copy of v0.5.17. - **Licence:** BSD-3-Clause, fine to vendor. - **Known vulnerabilities:** OSV lists none for v0.5.17. The two known advisories are fixed in this version: CVE-2021-29482 (fixed in 0.5.8) and CVE-2025-58058 (fixed in 0.5.15). - **Size caps on the xz path:** the decompressed stream still goes through `untar`, so the 512 MiB unpacked limit and the 100,000-entry limit still apply. Removing the exec, drain and wait code is right. - **The new test fails on main:** with main's `upstream_file.go`, `TestAnXzReleaseIsReadWithoutAnyXzToolOnTheBuildSeat` fails with "it is compressed with xz, and this build seat has no xz to read it". - **Local run:** I ran `merge-check.sh` with a throwaway Postgres 17. gofmt, go vet and `go test -race` on everything else passed. One unrelated test failed once: `TestAMachineJoiningIsAPeerOfTheHubUntilItsTokenExpires` with "no live token to issue for the tunnel key". It uses a 2-second token, so I think it is a timing flake under the full parallel race run. It passed 3 of 3 times run alone, and this pull request does not touch it. - **Forge checks:** mesh/merge-gate passed (builds build-agent, mesh-controller and route-proxy; every machine composes), and mesh/repo-check passed, both at 13:22 UTC.
jschoubben added 1 commit 2026-10-12 13:39:37 +00:00
Read xz with a real dictionary maximum (review 65, hq issue 345)
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
01fc9e1d73
ulikunitz/xz's ReaderConfig.DictCap is a minimum: a stream asking for a
1 GiB or 4 GiB dictionary was given it. github.com/therootcompany/xz, a port
of xz-embedded, takes a maximum and refuses a block whose LZMA2 dictionary is
larger before allocating it. 64 MiB, xz's largest preset.
Author
Contributor

Merge check at 01fc9e1d

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

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

  • tier 0: mesh-controller
  • tier 1: build-agent
  • tier 2: route-proxy
  • ace: receives build-agent, route-proxy
  • g14: receives build-agent
  • novox: receives mesh-controller, build-agent, route-proxy
  • shanks: receives build-agent
  • build-agent keeps data (ADR 0233): what it holds is backed up before it moves
  • route-proxy provides route: its consumers are sent again after it
  • route-proxy keeps data (ADR 0233): what it holds is backed up before it moves
  • 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, route-proxy is judged on its first machine
  • read by no module's build: internal/builder/testdata/release.tar.xz, internal/builder/upstream_file_test.go, vendor/github.com/therootcompany/xz/AUTHORS, vendor/github.com/therootcompany/xz/LICENSE, vendor/github.com/therootcompany/xz/README.md, vendor/github.com/therootcompany/xz/dec_bcj.go, vendor/github.com/therootcompany/xz/dec_delta.go, vendor/github.com/therootcompany/xz/dec_lzma2.go, vendor/github.com/therootcompany/xz/dec_stream.go, vendor/github.com/therootcompany/xz/dec_util.go, vendor/github.com/therootcompany/xz/dec_xz.go, vendor/github.com/therootcompany/xz/doc.go, vendor/github.com/therootcompany/xz/reader.go

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

--- the gate: build-agent, mesh-controller, route-proxy
mesh-controller: ok, 58 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-12T13:33:41Z 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-12T13:33:41Z

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

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

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

notes:
  - internal/builder/testdata/release.tar.xz, internal/builder/upstream_file_test.go, vendor/github.com/therootcompany/xz/AUTHORS, vendor/github.com/therootcompany/xz/LICENSE, vendor/github.com/therootcompany/xz/README.md, vendor/github.com/therootcompany/xz/dec_bcj.go, vendor/github.com/therootcompany/xz/dec_delta.go, vendor/github.com/therootcompany/xz/dec_lzma2.go, vendor/github.com/therootcompany/xz/dec_stream.go, vendor/github.com/therootcompany/xz/dec_util.go, vendor/github.com/therootcompany/xz/dec_xz.go, vendor/github.com/therootcompany/xz/doc.go and vendor/github.com/therootcompany/xz/reader.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	257.378s
ok  	github.com/novox/mesh-controller/examples/objectstore-provisioner	1.018s
ok  	github.com/novox/mesh-controller/examples/postgres-provisioner	1.026s
ok  	github.com/novox/mesh-controller/examples/redis-provisioner	1.017s
ok  	github.com/novox/mesh-controller/examples/route-proxy	1.160s
ok  	github.com/novox/mesh-controller/internal/artifacts	1.062s
?   	github.com/novox/mesh-controller/internal/beside	[no test files]
ok  	github.com/novox/mesh-controller/internal/broker	4.254s
ok  	github.com/novox/mesh-controller/internal/builder	36.469s
ok  	github.com/novox/mesh-controller/internal/catalogue	8.391s
ok  	github.com/novox/mesh-controller/internal/conditions	2.778s
ok  	github.com/novox/mesh-controller/internal/envfile	1.073s
ok  	github.com/novox/mesh-controller/internal/facts	1.085s
ok  	github.com/novox/mesh-controller/internal/identity	4.917s
ok  	github.com/novox/mesh-controller/internal/inventory	96.511s
ok  	github.com/novox/mesh-controller/internal/lease	17.784s
ok  	github.com/novox/mesh-controller/internal/licences	4.426s
ok  	github.com/novox/mesh-controller/internal/licences/adapters	1.067s
ok  	github.com/novox/mesh-controller/internal/link	60.898s
ok  	github.com/novox/mesh-controller/internal/lint	2.453s
ok  	github.com/novox/mesh-controller/internal/outward	1.127s
ok  	github.com/novox/mesh-controller/internal/overlay	1.099s
ok  	github.com/novox/mesh-controller/internal/secrets	1.501s
ok  	github.com/novox/mesh-controller/internal/store	3.631s
ok  	github.com/novox/mesh-controller/internal/testbus	1.271s
ok  	github.com/novox/mesh-controller/internal/token	1.023s
store tests, each run against the store:
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeWalksTheOthersAgain (0.46s)
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeEndsTheMergesThatContainIt (0.50s)
  ./cmd/mesh-controller --- PASS: TestAStopWalksTheOthersAgainInTheOpenBatch (0.49s)
  ./cmd/mesh-controller --- PASS: TestAStopNamesWhatStaysOnTheMachinesAndWhatStillCarriesIt (0.46s)
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeLeavesItOutOfABatch (0.43s)
  ./cmd/mesh-controller --- PASS: TestSavingPlansAndMovingMergesIsOneAct (0.41s)
  ./cmd/mesh-controller --- PASS: TestNodeShowAndStatusReadTheLastSendFromTheStore (0.49s)
  ./cmd/mesh-controller --- PASS: TestTheConditionsVerbReadsTheLooksTheBucketNoLongerHolds (0.32s)
  ./cmd/mesh-controller --- PASS: TestAWalkLetGoAndStoppedByVerbsIsSaidStartedAndEnded (0.51s)
  ./cmd/mesh-controller --- PASS: TestAWalkStoppedOrClosedByAPersonIsSaidEndedStopped (0.52s)
  ./cmd/mesh-controller --- PASS: TestAWalkRetriedByAPersonIsSaidStartedAgain (0.44s)
  ./cmd/mesh-controller --- PASS: TestABacklogReleasedByAPersonIsSaidStarted (0.46s)
  ./cmd/mesh-controller --- PASS: TestAWalkEndedByCancelOrClearIsSaidEnded (0.47s)
  ./cmd/mesh-controller --- PASS: TestAWalkRestartedByARebuildIsSaidStarted (0.45s)
  ./cmd/mesh-controller --- PASS: TestAWalkStoppedByHandAndRetriedEndsFailedWhenItFailsAgain (0.47s)
  ./internal/inventory --- PASS: TestTheLastSendSaysWhatTheMachineAnswered (0.26s)
  ./internal/inventory --- PASS: TestAReportIsTheLastSendsAnswerOnlyWhenItIsAboutThatSend (0.27s)
  ./internal/inventory --- PASS: TestAnIssuedValueIsSealedToEveryHolderOnceAndNotAgain (0.29s)
  ./internal/inventory --- PASS: TestAnIssuedValueIsSealedAgainAfterAKeyChangeOrAnAcceptance (0.27s)
  ./internal/inventory --- PASS: TestAConditionsLooksAreKeptBesideTheBucket (0.25s)
  ./internal/inventory --- PASS: TestAWalksStartIsKeptAndARefusedSaveLeavesItUnsaid (0.26s)
**Merge check** at `01fc9e1d` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy) - `mesh/repo-check`: **PASS** — its merge-check.sh passed **Delivery plan** — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step - tier 0: mesh-controller - tier 1: build-agent - tier 2: route-proxy - ace: receives build-agent, route-proxy - g14: receives build-agent - novox: receives mesh-controller, build-agent, route-proxy - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - route-proxy provides route: its consumers are sent again after it - route-proxy keeps data (ADR 0233): what it holds is backed up before it moves - 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, route-proxy is judged on its first machine - read by no module's build: internal/builder/testdata/release.tar.xz, internal/builder/upstream_file_test.go, vendor/github.com/therootcompany/xz/AUTHORS, vendor/github.com/therootcompany/xz/LICENSE, vendor/github.com/therootcompany/xz/README.md, vendor/github.com/therootcompany/xz/dec_bcj.go, vendor/github.com/therootcompany/xz/dec_delta.go, vendor/github.com/therootcompany/xz/dec_lzma2.go, vendor/github.com/therootcompany/xz/dec_stream.go, vendor/github.com/therootcompany/xz/dec_util.go, vendor/github.com/therootcompany/xz/dec_xz.go, vendor/github.com/therootcompany/xz/doc.go, vendor/github.com/therootcompany/xz/reader.go Run by the build seat on ace as `build-1791812389482187478` (`builds --log build-1791812389482187478`). ``` --- the gate: build-agent, mesh-controller, route-proxy mesh-controller: ok, 58 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-12T13:33:41Z 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-12T13:33:41Z modules the change moves: - mesh-controller (changed) machines: - a machine (f35): composes - holds mesh-dns-resolver (idt): composes - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes - a machine (yvbpsm): composes a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy notes: - internal/builder/testdata/release.tar.xz, internal/builder/upstream_file_test.go, vendor/github.com/therootcompany/xz/AUTHORS, vendor/github.com/therootcompany/xz/LICENSE, vendor/github.com/therootcompany/xz/README.md, vendor/github.com/therootcompany/xz/dec_bcj.go, vendor/github.com/therootcompany/xz/dec_delta.go, vendor/github.com/therootcompany/xz/dec_lzma2.go, vendor/github.com/therootcompany/xz/dec_stream.go, vendor/github.com/therootcompany/xz/dec_util.go, vendor/github.com/therootcompany/xz/dec_xz.go, vendor/github.com/therootcompany/xz/doc.go and vendor/github.com/therootcompany/xz/reader.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 257.378s ok github.com/novox/mesh-controller/examples/objectstore-provisioner 1.018s ok github.com/novox/mesh-controller/examples/postgres-provisioner 1.026s ok github.com/novox/mesh-controller/examples/redis-provisioner 1.017s ok github.com/novox/mesh-controller/examples/route-proxy 1.160s ok github.com/novox/mesh-controller/internal/artifacts 1.062s ? github.com/novox/mesh-controller/internal/beside [no test files] ok github.com/novox/mesh-controller/internal/broker 4.254s ok github.com/novox/mesh-controller/internal/builder 36.469s ok github.com/novox/mesh-controller/internal/catalogue 8.391s ok github.com/novox/mesh-controller/internal/conditions 2.778s ok github.com/novox/mesh-controller/internal/envfile 1.073s ok github.com/novox/mesh-controller/internal/facts 1.085s ok github.com/novox/mesh-controller/internal/identity 4.917s ok github.com/novox/mesh-controller/internal/inventory 96.511s ok github.com/novox/mesh-controller/internal/lease 17.784s ok github.com/novox/mesh-controller/internal/licences 4.426s ok github.com/novox/mesh-controller/internal/licences/adapters 1.067s ok github.com/novox/mesh-controller/internal/link 60.898s ok github.com/novox/mesh-controller/internal/lint 2.453s ok github.com/novox/mesh-controller/internal/outward 1.127s ok github.com/novox/mesh-controller/internal/overlay 1.099s ok github.com/novox/mesh-controller/internal/secrets 1.501s ok github.com/novox/mesh-controller/internal/store 3.631s ok github.com/novox/mesh-controller/internal/testbus 1.271s ok github.com/novox/mesh-controller/internal/token 1.023s store tests, each run against the store: ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeWalksTheOthersAgain (0.46s) ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeEndsTheMergesThatContainIt (0.50s) ./cmd/mesh-controller --- PASS: TestAStopWalksTheOthersAgainInTheOpenBatch (0.49s) ./cmd/mesh-controller --- PASS: TestAStopNamesWhatStaysOnTheMachinesAndWhatStillCarriesIt (0.46s) ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeLeavesItOutOfABatch (0.43s) ./cmd/mesh-controller --- PASS: TestSavingPlansAndMovingMergesIsOneAct (0.41s) ./cmd/mesh-controller --- PASS: TestNodeShowAndStatusReadTheLastSendFromTheStore (0.49s) ./cmd/mesh-controller --- PASS: TestTheConditionsVerbReadsTheLooksTheBucketNoLongerHolds (0.32s) ./cmd/mesh-controller --- PASS: TestAWalkLetGoAndStoppedByVerbsIsSaidStartedAndEnded (0.51s) ./cmd/mesh-controller --- PASS: TestAWalkStoppedOrClosedByAPersonIsSaidEndedStopped (0.52s) ./cmd/mesh-controller --- PASS: TestAWalkRetriedByAPersonIsSaidStartedAgain (0.44s) ./cmd/mesh-controller --- PASS: TestABacklogReleasedByAPersonIsSaidStarted (0.46s) ./cmd/mesh-controller --- PASS: TestAWalkEndedByCancelOrClearIsSaidEnded (0.47s) ./cmd/mesh-controller --- PASS: TestAWalkRestartedByARebuildIsSaidStarted (0.45s) ./cmd/mesh-controller --- PASS: TestAWalkStoppedByHandAndRetriedEndsFailedWhenItFailsAgain (0.47s) ./internal/inventory --- PASS: TestTheLastSendSaysWhatTheMachineAnswered (0.26s) ./internal/inventory --- PASS: TestAReportIsTheLastSendsAnswerOnlyWhenItIsAboutThatSend (0.27s) ./internal/inventory --- PASS: TestAnIssuedValueIsSealedToEveryHolderOnceAndNotAgain (0.29s) ./internal/inventory --- PASS: TestAnIssuedValueIsSealedAgainAfterAKeyChangeOrAnAcceptance (0.27s) ./internal/inventory --- PASS: TestAConditionsLooksAreKeptBesideTheBucket (0.25s) ./internal/inventory --- PASS: TestAWalksStartIsKeptAndARefusedSaveLeavesItUnsaid (0.26s) ```
mesh-admin left a comment
Author
Contributor

Verdict: request changes at 01fc9e1

The dictionary problem from review 65 is fixed. One allocation in the new library still grows with the input, and that is the reason for this verdict.

Blocking

The library keeps one filter decoder per block, so memory grows with the number of blocks. In the vendored dec_stream.go, each block header that names a delta or BCJ filter takes s.deltas[s.deltasUsed] / s.bcjs[s.bcjsUsed], or appends a new decoder, and then increments deltasUsed / bcjsUsed. Those counters are reset only in xzDecReset, which runs once per stream, not once per block. A single stream with many small blocks therefore appends one decoder per block. xz-embedded in C reuses one decoder; this is a bug in the port.

I measured it with a probe test (not committed). I made a 200 kB tar with real xz --block-size=1:

  • plain LZMA2, 5.3 MB file: about 2.4 MiB of heap still live after reading;
  • --delta=dist=1, 6.1 MB file: about 67 MiB live;
  • --x86 (BCJ), 6.1 MB file: about 28 MiB live.

That is roughly 11 bytes of heap per compressed byte for delta. At the 512 MiB download cap it comes to about 5.6 GiB. The 512 MiB unpacked limit and the 100,000-entry limit do not catch it, because the unpacked output is tiny. It is the same failure as the dictionary one (a pinned file exhausting the build node's memory), so I hold it to the same bar.

Two ways to fix it:

  • Patch the port so bcjsUsed and deltasUsed reset at each block header, or so a chain holds one decoder of each kind. Carry it as a replace to a fork on the mesh's forge (vendored, with go.sum to match) and report it upstream.
  • Or, before decoding, read the stream's index (at the end of the file, for every concatenated stream) and refuse more than N blocks. For example N = 10,000: the Betterbird release has 1, and multi-threaded xz makes blocks of 3 × the dictionary size.

Add a test with a many-block delta stream that checks live heap stays under a bound.

Checked and fine

  • Crafted dictionary probe, rerun: dictionary codes 32, 33, 36 and 40 (256 MiB, 384 MiB, 1 GiB, 4 GiB - 1) are refused with "xz: LZMA2 dictionary size exceeds max", and TotalAlloc grew by 0 MiB. The check runs in xzDecLZMA2Reset, before make, and the dictionary buffer is reused across blocks and streams up to the maximum.
  • The new test fails on the old code: TestAnXzStreamAskingForTooLargeADictionaryIsRefused, run on 10f76ca, fails (props 36 allocates 1,075,139,288 bytes, props 40 allocates 4,296,329,792 bytes, and neither is refused).
  • Vendored copy: go.sum matches go mod download through the checksum database (h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=). GOFLAGS=-mod=mod go mod verify says "all modules verified". All 11 vendored files are byte-identical to the module cache copy of v1.0.1.
  • Licence: CC0 1.0, fine.
  • Known vulnerabilities: OSV lists none for github.com/therootcompany/xz.
  • Other allocations are bounded: the block header goes into a fixed 1024-byte array, there are at most 4 filters, each check (CRC32, CRC64, SHA-256) is a fixed-size hash, the index is hashed rather than stored, and stream padding and multistream reuse the same decoder. The per-block filter decoders above are the only exception I found.
  • Real Betterbird archive: I fetched it from its pinned URL. Its sha256 is still 38dd2489...29e2, as pinned. It is 1 stream with 1 block. unpackUpstream with within: betterbird gives the same 55 files as tar -xJ, and the packed archive is byte-identical to the one 10f76ca's decoder produced (sha256 74dd6b3b...bd7c).
  • Local run: merge-check.sh with a throwaway Postgres 17 (gofmt, go vet, go test -race on every package, every store test) passes with exit 0.
  • Forge checks: mesh/merge-gate passed (builds build-agent, mesh-controller and route-proxy; every machine composes) and mesh/repo-check passed, both at 13:46 UTC.
Verdict: request changes at 01fc9e1 The dictionary problem from review 65 is fixed. One allocation in the new library still grows with the input, and that is the reason for this verdict. ## Blocking **The library keeps one filter decoder per block, so memory grows with the number of blocks.** In the vendored `dec_stream.go`, each block header that names a delta or BCJ filter takes `s.deltas[s.deltasUsed]` / `s.bcjs[s.bcjsUsed]`, or appends a new decoder, and then increments `deltasUsed` / `bcjsUsed`. Those counters are reset only in `xzDecReset`, which runs once per stream, not once per block. A single stream with many small blocks therefore appends one decoder per block. xz-embedded in C reuses one decoder; this is a bug in the port. I measured it with a probe test (not committed). I made a 200 kB tar with real `xz --block-size=1`: - plain LZMA2, 5.3 MB file: about 2.4 MiB of heap still live after reading; - `--delta=dist=1`, 6.1 MB file: about 67 MiB live; - `--x86` (BCJ), 6.1 MB file: about 28 MiB live. That is roughly 11 bytes of heap per compressed byte for delta. At the 512 MiB download cap it comes to about 5.6 GiB. The 512 MiB unpacked limit and the 100,000-entry limit do not catch it, because the unpacked output is tiny. It is the same failure as the dictionary one (a pinned file exhausting the build node's memory), so I hold it to the same bar. Two ways to fix it: - Patch the port so `bcjsUsed` and `deltasUsed` reset at each block header, or so a chain holds one decoder of each kind. Carry it as a `replace` to a fork on the mesh's forge (vendored, with go.sum to match) and report it upstream. - Or, before decoding, read the stream's index (at the end of the file, for every concatenated stream) and refuse more than N blocks. For example N = 10,000: the Betterbird release has 1, and multi-threaded xz makes blocks of 3 × the dictionary size. Add a test with a many-block delta stream that checks live heap stays under a bound. ## Checked and fine - **Crafted dictionary probe, rerun:** dictionary codes 32, 33, 36 and 40 (256 MiB, 384 MiB, 1 GiB, 4 GiB - 1) are refused with "xz: LZMA2 dictionary size exceeds max", and TotalAlloc grew by 0 MiB. The check runs in `xzDecLZMA2Reset`, before `make`, and the dictionary buffer is reused across blocks and streams up to the maximum. - **The new test fails on the old code:** `TestAnXzStreamAskingForTooLargeADictionaryIsRefused`, run on 10f76ca, fails (props 36 allocates 1,075,139,288 bytes, props 40 allocates 4,296,329,792 bytes, and neither is refused). - **Vendored copy:** go.sum matches `go mod download` through the checksum database (`h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=`). `GOFLAGS=-mod=mod go mod verify` says "all modules verified". All 11 vendored files are byte-identical to the module cache copy of v1.0.1. - **Licence:** CC0 1.0, fine. - **Known vulnerabilities:** OSV lists none for github.com/therootcompany/xz. - **Other allocations are bounded:** the block header goes into a fixed 1024-byte array, there are at most 4 filters, each check (CRC32, CRC64, SHA-256) is a fixed-size hash, the index is hashed rather than stored, and stream padding and multistream reuse the same decoder. The per-block filter decoders above are the only exception I found. - **Real Betterbird archive:** I fetched it from its pinned URL. Its sha256 is still 38dd2489...29e2, as pinned. It is 1 stream with 1 block. `unpackUpstream` with `within: betterbird` gives the same 55 files as `tar -xJ`, and the packed archive is byte-identical to the one 10f76ca's decoder produced (sha256 74dd6b3b...bd7c). - **Local run:** `merge-check.sh` with a throwaway Postgres 17 (gofmt, go vet, `go test -race` on every package, every store test) passes with exit 0. - **Forge checks:** mesh/merge-gate passed (builds build-agent, mesh-controller and route-proxy; every machine composes) and mesh/repo-check passed, both at 13:46 UTC.
jschoubben added 1 commit 2026-10-12 14:08:36 +00:00
Refuse an xz file of more than 1,024 blocks before decoding it (review 68, hq issue 345)
mesh/merge-gate head 539a1c16cd4e merged into trunk ba685b17f5e1 — pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus…
mesh/repo-check head 539a1c16cd4e merged into trunk ba685b17f5e1 — pass: its merge-check.sh passed
mesh/delivery delivered
539a1c16cd
The xz reader keeps one delta or BCJ decoder for every block of a stream and frees them only when the
stream ends, so a file of many tiny blocks grows the heap with its length: about 67 MiB live for a 6 MB
`xz --block-size=1 --delta` file, about 5.6 GiB at the 512 MiB download cap.

The file's framing is now walked first, without decompressing: stream header, each block header, the
LZMA2 chunk headers (each carries its compressed size, so a chunk is skipped unread), padding, check,
index and footer. A file of more than 1,024 blocks or 16 streams is refused, as is an index whose block
count is not the count walked. The index alone is not trusted because the reader compares it with the
blocks only after decoding them all, so a lying index would still be decoded. Refusing delta and BCJ
outright was not chosen: it needs the same walk to see each block header, and would refuse sound
releases filtered with BCJ.

1,024 blocks keep at most about 0.35 MiB of decoders. Betterbird's archive is 1 stream with 1 block;
multi-threaded xz cuts blocks of 3 times the dictionary, so 512 MiB in blocks of 512 KiB still fits.
The library stays unforked.
Author
Contributor

Merge check at 539a1c16

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

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

  • tier 0: mesh-controller
  • tier 1: build-agent
  • tier 2: route-proxy
  • ace: receives build-agent, route-proxy
  • g14: receives build-agent
  • novox: receives mesh-controller, build-agent, route-proxy
  • shanks: receives build-agent
  • build-agent keeps data (ADR 0233): what it holds is backed up before it moves
  • route-proxy provides route: its consumers are sent again after it
  • route-proxy keeps data (ADR 0233): what it holds is backed up before it moves
  • 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, route-proxy is judged on its first machine
  • read by no module's build: internal/builder/testdata/release.tar.xz, internal/builder/upstream_file_test.go, internal/builder/xz_framing_test.go, vendor/github.com/therootcompany/xz/AUTHORS, vendor/github.com/therootcompany/xz/LICENSE, vendor/github.com/therootcompany/xz/README.md, vendor/github.com/therootcompany/xz/dec_bcj.go, vendor/github.com/therootcompany/xz/dec_delta.go, vendor/github.com/therootcompany/xz/dec_lzma2.go, vendor/github.com/therootcompany/xz/dec_stream.go, vendor/github.com/therootcompany/xz/dec_util.go, vendor/github.com/therootcompany/xz/dec_xz.go, vendor/github.com/therootcompany/xz/doc.go, vendor/github.com/therootcompany/xz/reader.go

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

--- the gate: build-agent, mesh-controller, route-proxy
mesh-controller: ok, 58 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-12T14:02:23Z 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-12T14:02:23Z

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

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

a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy
--- its merge-check.sh (go toolchain)
ok  	github.com/novox/mesh-controller/cmd/mesh-builder	1.037s
ok  	github.com/novox/mesh-controller/cmd/mesh-controller	247.882s
ok  	github.com/novox/mesh-controller/examples/objectstore-provisioner	1.027s
ok  	github.com/novox/mesh-controller/examples/postgres-provisioner	1.036s
ok  	github.com/novox/mesh-controller/examples/redis-provisioner	1.024s
ok  	github.com/novox/mesh-controller/examples/route-proxy	1.249s
ok  	github.com/novox/mesh-controller/internal/artifacts	1.105s
?   	github.com/novox/mesh-controller/internal/beside	[no test files]
ok  	github.com/novox/mesh-controller/internal/broker	4.291s
ok  	github.com/novox/mesh-controller/internal/builder	34.548s
ok  	github.com/novox/mesh-controller/internal/catalogue	8.309s
ok  	github.com/novox/mesh-controller/internal/conditions	2.882s
ok  	github.com/novox/mesh-controller/internal/envfile	1.127s
ok  	github.com/novox/mesh-controller/internal/facts	1.104s
ok  	github.com/novox/mesh-controller/internal/identity	4.979s
ok  	github.com/novox/mesh-controller/internal/inventory	92.721s
ok  	github.com/novox/mesh-controller/internal/lease	17.800s
ok  	github.com/novox/mesh-controller/internal/licences	4.568s
ok  	github.com/novox/mesh-controller/internal/licences/adapters	1.090s
ok  	github.com/novox/mesh-controller/internal/link	60.240s
ok  	github.com/novox/mesh-controller/internal/lint	2.529s
ok  	github.com/novox/mesh-controller/internal/outward	1.131s
ok  	github.com/novox/mesh-controller/internal/overlay	1.118s
ok  	github.com/novox/mesh-controller/internal/secrets	1.544s
ok  	github.com/novox/mesh-controller/internal/store	3.703s
ok  	github.com/novox/mesh-controller/internal/testbus	1.303s
ok  	github.com/novox/mesh-controller/internal/token	1.027s
store tests, each run against the store:
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeWalksTheOthersAgain (0.42s)
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeEndsTheMergesThatContainIt (0.45s)
  ./cmd/mesh-controller --- PASS: TestAStopWalksTheOthersAgainInTheOpenBatch (0.43s)
  ./cmd/mesh-controller --- PASS: TestAStopNamesWhatStaysOnTheMachinesAndWhatStillCarriesIt (0.41s)
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeLeavesItOutOfABatch (0.41s)
  ./cmd/mesh-controller --- PASS: TestSavingPlansAndMovingMergesIsOneAct (0.37s)
  ./cmd/mesh-controller --- PASS: TestNodeShowAndStatusReadTheLastSendFromTheStore (0.44s)
  ./cmd/mesh-controller --- PASS: TestTheConditionsVerbReadsTheLooksTheBucketNoLongerHolds (0.32s)
  ./cmd/mesh-controller --- PASS: TestAWalkLetGoAndStoppedByVerbsIsSaidStartedAndEnded (0.45s)
  ./cmd/mesh-controller --- PASS: TestAWalkStoppedOrClosedByAPersonIsSaidEndedStopped (0.44s)
  ./cmd/mesh-controller --- PASS: TestAWalkRetriedByAPersonIsSaidStartedAgain (0.37s)
  ./cmd/mesh-controller --- PASS: TestABacklogReleasedByAPersonIsSaidStarted (0.44s)
  ./cmd/mesh-controller --- PASS: TestAWalkEndedByCancelOrClearIsSaidEnded (0.50s)
  ./cmd/mesh-controller --- PASS: TestAWalkRestartedByARebuildIsSaidStarted (0.45s)
  ./cmd/mesh-controller --- PASS: TestAWalkStoppedByHandAndRetriedEndsFailedWhenItFailsAgain (0.58s)
  ./internal/inventory --- PASS: TestTheLastSendSaysWhatTheMachineAnswered (0.26s)
  ./internal/inventory --- PASS: TestAReportIsTheLastSendsAnswerOnlyWhenItIsAboutThatSend (0.27s)
  ./internal/inventory --- PASS: TestAnIssuedValueIsSealedToEveryHolderOnceAndNotAgain (0.27s)
  ./internal/inventory --- PASS: TestAnIssuedValueIsSealedAgainAfterAKeyChangeOrAnAcceptance (0.27s)
  ./internal/inventory --- PASS: TestAConditionsLooksAreKeptBesideTheBucket (0.26s)
  ./internal/inventory --- PASS: TestAWalksStartIsKeptAndARefusedSaveLeavesItUnsaid (0.25s)
**Merge check** at `539a1c16` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy) - `mesh/repo-check`: **PASS** — its merge-check.sh passed **Delivery plan** — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step - tier 0: mesh-controller - tier 1: build-agent - tier 2: route-proxy - ace: receives build-agent, route-proxy - g14: receives build-agent - novox: receives mesh-controller, build-agent, route-proxy - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - route-proxy provides route: its consumers are sent again after it - route-proxy keeps data (ADR 0233): what it holds is backed up before it moves - 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, route-proxy is judged on its first machine - read by no module's build: internal/builder/testdata/release.tar.xz, internal/builder/upstream_file_test.go, internal/builder/xz_framing_test.go, vendor/github.com/therootcompany/xz/AUTHORS, vendor/github.com/therootcompany/xz/LICENSE, vendor/github.com/therootcompany/xz/README.md, vendor/github.com/therootcompany/xz/dec_bcj.go, vendor/github.com/therootcompany/xz/dec_delta.go, vendor/github.com/therootcompany/xz/dec_lzma2.go, vendor/github.com/therootcompany/xz/dec_stream.go, vendor/github.com/therootcompany/xz/dec_util.go, vendor/github.com/therootcompany/xz/dec_xz.go, vendor/github.com/therootcompany/xz/doc.go, vendor/github.com/therootcompany/xz/reader.go Run by the build seat on ace as `build-1791814136071167014` (`builds --log build-1791814136071167014`). ``` --- the gate: build-agent, mesh-controller, route-proxy mesh-controller: ok, 58 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-12T14:02:23Z 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-12T14:02:23Z modules the change moves: - mesh-controller (changed) machines: - a machine (f35): composes - holds mesh-dns-resolver (idt): composes - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes - a machine (yvbpsm): composes a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy --- its merge-check.sh (go toolchain) ok github.com/novox/mesh-controller/cmd/mesh-builder 1.037s ok github.com/novox/mesh-controller/cmd/mesh-controller 247.882s ok github.com/novox/mesh-controller/examples/objectstore-provisioner 1.027s ok github.com/novox/mesh-controller/examples/postgres-provisioner 1.036s ok github.com/novox/mesh-controller/examples/redis-provisioner 1.024s ok github.com/novox/mesh-controller/examples/route-proxy 1.249s ok github.com/novox/mesh-controller/internal/artifacts 1.105s ? github.com/novox/mesh-controller/internal/beside [no test files] ok github.com/novox/mesh-controller/internal/broker 4.291s ok github.com/novox/mesh-controller/internal/builder 34.548s ok github.com/novox/mesh-controller/internal/catalogue 8.309s ok github.com/novox/mesh-controller/internal/conditions 2.882s ok github.com/novox/mesh-controller/internal/envfile 1.127s ok github.com/novox/mesh-controller/internal/facts 1.104s ok github.com/novox/mesh-controller/internal/identity 4.979s ok github.com/novox/mesh-controller/internal/inventory 92.721s ok github.com/novox/mesh-controller/internal/lease 17.800s ok github.com/novox/mesh-controller/internal/licences 4.568s ok github.com/novox/mesh-controller/internal/licences/adapters 1.090s ok github.com/novox/mesh-controller/internal/link 60.240s ok github.com/novox/mesh-controller/internal/lint 2.529s ok github.com/novox/mesh-controller/internal/outward 1.131s ok github.com/novox/mesh-controller/internal/overlay 1.118s ok github.com/novox/mesh-controller/internal/secrets 1.544s ok github.com/novox/mesh-controller/internal/store 3.703s ok github.com/novox/mesh-controller/internal/testbus 1.303s ok github.com/novox/mesh-controller/internal/token 1.027s store tests, each run against the store: ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeWalksTheOthersAgain (0.42s) ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeEndsTheMergesThatContainIt (0.45s) ./cmd/mesh-controller --- PASS: TestAStopWalksTheOthersAgainInTheOpenBatch (0.43s) ./cmd/mesh-controller --- PASS: TestAStopNamesWhatStaysOnTheMachinesAndWhatStillCarriesIt (0.41s) ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeLeavesItOutOfABatch (0.41s) ./cmd/mesh-controller --- PASS: TestSavingPlansAndMovingMergesIsOneAct (0.37s) ./cmd/mesh-controller --- PASS: TestNodeShowAndStatusReadTheLastSendFromTheStore (0.44s) ./cmd/mesh-controller --- PASS: TestTheConditionsVerbReadsTheLooksTheBucketNoLongerHolds (0.32s) ./cmd/mesh-controller --- PASS: TestAWalkLetGoAndStoppedByVerbsIsSaidStartedAndEnded (0.45s) ./cmd/mesh-controller --- PASS: TestAWalkStoppedOrClosedByAPersonIsSaidEndedStopped (0.44s) ./cmd/mesh-controller --- PASS: TestAWalkRetriedByAPersonIsSaidStartedAgain (0.37s) ./cmd/mesh-controller --- PASS: TestABacklogReleasedByAPersonIsSaidStarted (0.44s) ./cmd/mesh-controller --- PASS: TestAWalkEndedByCancelOrClearIsSaidEnded (0.50s) ./cmd/mesh-controller --- PASS: TestAWalkRestartedByARebuildIsSaidStarted (0.45s) ./cmd/mesh-controller --- PASS: TestAWalkStoppedByHandAndRetriedEndsFailedWhenItFailsAgain (0.58s) ./internal/inventory --- PASS: TestTheLastSendSaysWhatTheMachineAnswered (0.26s) ./internal/inventory --- PASS: TestAReportIsTheLastSendsAnswerOnlyWhenItIsAboutThatSend (0.27s) ./internal/inventory --- PASS: TestAnIssuedValueIsSealedToEveryHolderOnceAndNotAgain (0.27s) ./internal/inventory --- PASS: TestAnIssuedValueIsSealedAgainAfterAKeyChangeOrAnAcceptance (0.27s) ./internal/inventory --- PASS: TestAConditionsLooksAreKeptBesideTheBucket (0.26s) ./internal/inventory --- PASS: TestAWalksStartIsKeptAndARefusedSaveLeavesItUnsaid (0.25s) ```
mesh-admin left a comment
Author
Contributor

Verdict: approve at 539a1c16cd

Re-review against review 68. The per-block decoder growth is now refused before anything is decoded, and the walk that refuses it is itself bounded.

My probes. I reran my files through xzWithinLimits:

  • xz --block-size=1 files (6.1 MB delta, 6.1 MB BCJ, 5.3 MB plain) are each refused with "more than 1024 xz blocks" in about 1 ms, with about 90 KiB allocated.
  • unpackUpstream on the delta file refuses it the same way.
  • 1,024 one-byte blocks pass and 1,025 are refused; 16 concatenated streams pass and 17 are refused.

The walk cannot allocate or loop without bound:

  • Allocations are fixed. Its only allocations are full(n) with n at most 1,023 (the block header), 12, 4, 2 or 3 bytes; chunks are skipped with Discard, which copies nothing.
  • Every loop consumes input. Each pass reads at least one byte: an LZMA2 chunk declares at most 65,536 bytes and at least 1, a variable-length number is at most 9 bytes, and the index loop runs at most 2 × 1,024 times. The walk is therefore linear in the file, which is at most 512 MiB.
  • Truncation is safe. A truncated file, or a skip past EOF, ends in io.ErrUnexpectedEOF.
  • Fuzzing found nothing. I ran xzFraming on fuzzed input for 90 s (532,905 runs, seeded with real multi-block, multi-stream and delta files) with no panic and no hang.

The walk follows the reader's framing:

  • Block headers: size is (b + 1) × 4 and the CRC32 is checked.
  • LZMA2 chunks: control 0x00 ends the block; 0x01 and 0x02 are stored chunks of a 16-bit size + 1; 0x80 and above are LZMA chunks with 4 size bytes, plus the properties byte from 0xC0 up (state reset with new properties, and dictionary reset); 0x03 to 0x7F are refused. The reader (xz-embedded) consumes exactly the declared compressed bytes of each chunk and ends a block only at 0x00, so the walk is at the same byte offset as the reader at every block boundary. That means no file that passes the walk gives the reader more blocks than were counted.
  • Padding, check, index, streams: block padding must be zeros, the check size is 4 << ((id - 1) / 3), and the index count must equal the walked count. Stream padding must be zeros in multiples of 4, and the next non-zero byte must start a valid stream header.
  • What I tried against it: trailing junk after a valid stream, an index that lies, and a 17th stream are all refused. I found no file the walk passes with fewer blocks than it holds.

The new tests fail without the walk. With only the xzWithinLimits call removed, three tests fail:

  • TestAnXzFileOfManyDeltaBlocksIsRefusedBeforeItIsDecoded (the 4 MB one-byte-block file is not refused);
  • TestAnXzFileOfTooManyStreamsIsRefused;
  • TestAnXzIndexThatDoesNotListItsBlocksIsRefused. This one passes untar without the walk, because untar stops at the tar end and never reads the index, so the walk is also the only index check this path has.

Real Betterbird archive. Its sha256 is still 38dd24890a66b4cf9a45bc24e15192e00c26b929e1bb8df7974c16a4539129e2. The walk passes it in 84 ms (1 stream, 1 block). The unpacked files match tar -xJ (55 files), and the packed archive is byte-identical to the one from 01fc9e1 and 10f76ca (sha256 74dd6b3be3a8ff65a38fe483a410920b5715079f17455a208a2e076ff124bd7c).

Why the gate builds route-proxy. route-proxy's manifest in the catalogue compiles examples/route-proxy from novox/mesh-controller. This pull request changes go.mod, go.sum and vendor/modules.txt, which are build source for every Go program compiled from the repository. go list -deps ./examples/route-proxy shows it does not import internal/builder or the xz package. The rebuild is expected and harmless: it was already in the gate at 10f76ca, the first head that touched go.mod, and the program's code does not change.

Checks. My local merge-check.sh with a throwaway Postgres 17 (gofmt, go vet, go test -race on every package, every store test) exited 0. On the forge, mesh/merge-gate passed (builds build-agent, mesh-controller and route-proxy for ace, g14, novox and shanks) and mesh/repo-check passed, both at 14:15 UTC.

Not blocking.

  • The walk reads the whole file once more before decoding, which takes about 84 ms for 78 MB.
  • The limit belongs in the library: worth reporting upstream that bcjsUsed and deltasUsed are not reset per block.
Verdict: approve at 539a1c16cd4e11feb49511e1b8b859c03d478076 Re-review against review 68. The per-block decoder growth is now refused before anything is decoded, and the walk that refuses it is itself bounded. **My probes.** I reran my files through `xzWithinLimits`: - `xz --block-size=1` files (6.1 MB delta, 6.1 MB BCJ, 5.3 MB plain) are each refused with "more than 1024 xz blocks" in about 1 ms, with about 90 KiB allocated. - `unpackUpstream` on the delta file refuses it the same way. - 1,024 one-byte blocks pass and 1,025 are refused; 16 concatenated streams pass and 17 are refused. **The walk cannot allocate or loop without bound:** - **Allocations are fixed.** Its only allocations are `full(n)` with n at most 1,023 (the block header), 12, 4, 2 or 3 bytes; chunks are skipped with `Discard`, which copies nothing. - **Every loop consumes input.** Each pass reads at least one byte: an LZMA2 chunk declares at most 65,536 bytes and at least 1, a variable-length number is at most 9 bytes, and the index loop runs at most 2 × 1,024 times. The walk is therefore linear in the file, which is at most 512 MiB. - **Truncation is safe.** A truncated file, or a skip past EOF, ends in `io.ErrUnexpectedEOF`. - **Fuzzing found nothing.** I ran `xzFraming` on fuzzed input for 90 s (532,905 runs, seeded with real multi-block, multi-stream and delta files) with no panic and no hang. **The walk follows the reader's framing:** - **Block headers:** size is (b + 1) × 4 and the CRC32 is checked. - **LZMA2 chunks:** control 0x00 ends the block; 0x01 and 0x02 are stored chunks of a 16-bit size + 1; 0x80 and above are LZMA chunks with 4 size bytes, plus the properties byte from 0xC0 up (state reset with new properties, and dictionary reset); 0x03 to 0x7F are refused. The reader (xz-embedded) consumes exactly the declared compressed bytes of each chunk and ends a block only at 0x00, so the walk is at the same byte offset as the reader at every block boundary. That means no file that passes the walk gives the reader more blocks than were counted. - **Padding, check, index, streams:** block padding must be zeros, the check size is 4 << ((id - 1) / 3), and the index count must equal the walked count. Stream padding must be zeros in multiples of 4, and the next non-zero byte must start a valid stream header. - **What I tried against it:** trailing junk after a valid stream, an index that lies, and a 17th stream are all refused. I found no file the walk passes with fewer blocks than it holds. **The new tests fail without the walk.** With only the `xzWithinLimits` call removed, three tests fail: - `TestAnXzFileOfManyDeltaBlocksIsRefusedBeforeItIsDecoded` (the 4 MB one-byte-block file is not refused); - `TestAnXzFileOfTooManyStreamsIsRefused`; - `TestAnXzIndexThatDoesNotListItsBlocksIsRefused`. This one passes untar without the walk, because untar stops at the tar end and never reads the index, so the walk is also the only index check this path has. **Real Betterbird archive.** Its sha256 is still 38dd24890a66b4cf9a45bc24e15192e00c26b929e1bb8df7974c16a4539129e2. The walk passes it in 84 ms (1 stream, 1 block). The unpacked files match `tar -xJ` (55 files), and the packed archive is byte-identical to the one from 01fc9e1 and 10f76ca (sha256 74dd6b3be3a8ff65a38fe483a410920b5715079f17455a208a2e076ff124bd7c). **Why the gate builds route-proxy.** route-proxy's manifest in the catalogue compiles `examples/route-proxy` from novox/mesh-controller. This pull request changes go.mod, go.sum and vendor/modules.txt, which are build source for every Go program compiled from the repository. `go list -deps ./examples/route-proxy` shows it does not import internal/builder or the xz package. The rebuild is expected and harmless: it was already in the gate at 10f76ca, the first head that touched go.mod, and the program's code does not change. **Checks.** My local `merge-check.sh` with a throwaway Postgres 17 (gofmt, go vet, `go test -race` on every package, every store test) exited 0. On the forge, mesh/merge-gate passed (builds build-agent, mesh-controller and route-proxy for ace, g14, novox and shanks) and mesh/repo-check passed, both at 14:15 UTC. **Not blocking.** - The walk reads the whole file once more before decoding, which takes about 84 ms for 78 MB. - The limit belongs in the library: worth reporting upstream that `bcjsUsed` and `deltasUsed` are not reset per block.
Author
Contributor

Merge check at 539a1c16
Judged: head 539a1c16cd4e merged into trunk ba685b17f5e1; read beside it: mesh-catalog 23ea0f431faf, mesh-controller-main ba685b17f5e1, mesh-host a1f9856b3957, mesh-tools 954e1a53eef9

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

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

  • tier 0: mesh-controller
  • tier 1: build-agent
  • tier 2: route-proxy
  • ace: receives build-agent, route-proxy
  • g14: receives build-agent
  • novox: receives mesh-controller, build-agent, route-proxy
  • shanks: receives build-agent
  • build-agent keeps data (ADR 0233): what it holds is backed up before it moves
  • route-proxy provides route: its consumers are sent again after it
  • route-proxy keeps data (ADR 0233): what it holds is backed up before it moves
  • 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, route-proxy is judged on its first machine
  • read by no module's build: internal/builder/testdata/release.tar.xz, internal/builder/upstream_file_test.go, internal/builder/xz_framing_test.go, vendor/github.com/therootcompany/xz/AUTHORS, vendor/github.com/therootcompany/xz/LICENSE, vendor/github.com/therootcompany/xz/README.md, vendor/github.com/therootcompany/xz/dec_bcj.go, vendor/github.com/therootcompany/xz/dec_delta.go, vendor/github.com/therootcompany/xz/dec_lzma2.go, vendor/github.com/therootcompany/xz/dec_stream.go, vendor/github.com/therootcompany/xz/dec_util.go, vendor/github.com/therootcompany/xz/dec_xz.go, vendor/github.com/therootcompany/xz/doc.go, vendor/github.com/therootcompany/xz/reader.go

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

--- the gate: build-agent, mesh-controller, route-proxy
mesh-controller: ok, 58 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-12T17:18: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-12T17:18:24Z

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

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

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

notes:
  - internal/builder/testdata/release.tar.xz, internal/builder/upstream_file_test.go, internal/builder/xz_framing_test.go, vendor/github.com/therootcompany/xz/AUTHORS, vendor/github.com/therootcompany/xz/LICENSE, vendor/github.com/therootcompany/xz/README.md, vendor/github.com/therootcompany/xz/dec_bcj.go, vendor/github.com/therootcompany/xz/dec_delta.go, vendor/github.com/therootcompany/xz/dec_lzma2.go, vendor/github.com/therootcompany/xz/dec_stream.go, vendor/github.com/therootcompany/xz/dec_util.go, vendor/github.com/therootcompany/xz/dec_xz.go, vendor/github.com/therootcompany/xz/doc.go and vendor/github.com/therootcompany/xz/reader.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.038s
ok  	github.com/novox/mesh-controller/cmd/mesh-controller	255.834s
ok  	github.com/novox/mesh-controller/examples/objectstore-provisioner	1.012s
ok  	github.com/novox/mesh-controller/examples/postgres-provisioner	1.018s
ok  	github.com/novox/mesh-controller/examples/redis-provisioner	1.015s
ok  	github.com/novox/mesh-controller/examples/route-proxy	1.175s
ok  	github.com/novox/mesh-controller/internal/artifacts	1.043s
?   	github.com/novox/mesh-controller/internal/beside	[no test files]
ok  	github.com/novox/mesh-controller/internal/broker	3.951s
ok  	github.com/novox/mesh-controller/internal/builder	31.596s
ok  	github.com/novox/mesh-controller/internal/catalogue	7.092s
ok  	github.com/novox/mesh-controller/internal/conditions	2.420s
ok  	github.com/novox/mesh-controller/internal/envfile	1.027s
ok  	github.com/novox/mesh-controller/internal/facts	1.030s
ok  	github.com/novox/mesh-controller/internal/identity	4.104s
ok  	github.com/novox/mesh-controller/internal/inventory	85.020s
ok  	github.com/novox/mesh-controller/internal/lease	17.706s
ok  	github.com/novox/mesh-controller/internal/licences	3.757s
ok  	github.com/novox/mesh-controller/internal/licences/adapters	1.008s
ok  	github.com/novox/mesh-controller/internal/link	60.881s
ok  	github.com/novox/mesh-controller/internal/lint	2.169s
ok  	github.com/novox/mesh-controller/internal/outward	1.036s
ok  	github.com/novox/mesh-controller/internal/overlay	1.027s
ok  	github.com/novox/mesh-controller/internal/secrets	1.423s
ok  	github.com/novox/mesh-controller/internal/store	3.216s
ok  	github.com/novox/mesh-controller/internal/testbus	1.174s
ok  	github.com/novox/mesh-controller/internal/token	1.020s
store tests, each run against the store:
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeWalksTheOthersAgain (0.35s)
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeEndsTheMergesThatContainIt (0.37s)
  ./cmd/mesh-controller --- PASS: TestAStopWalksTheOthersAgainInTheOpenBatch (1.74s)
  ./cmd/mesh-controller --- PASS: TestAStopNamesWhatStaysOnTheMachinesAndWhatStillCarriesIt (0.65s)
  ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeLeavesItOutOfABatch (0.32s)
  ./cmd/mesh-controller --- PASS: TestSavingPlansAndMovingMergesIsOneAct (0.35s)
  ./cmd/mesh-controller --- PASS: TestAPlanFollowedFromTheStoreAnswersWhenItMoves (4.36s)
  ./cmd/mesh-controller --- PASS: TestNodeShowAndStatusReadTheLastSendFromTheStore (0.36s)
  ./cmd/mesh-controller --- PASS: TestTheConditionsVerbReadsTheLooksTheBucketNoLongerHolds (0.26s)
  ./cmd/mesh-controller --- PASS: TestAWalkLetGoAndStoppedByVerbsIsSaidStartedAndEnded (0.40s)
  ./cmd/mesh-controller --- PASS: TestAWalkStoppedOrClosedByAPersonIsSaidEndedStopped (0.40s)
  ./cmd/mesh-controller --- PASS: TestAWalkRetriedByAPersonIsSaidStartedAgain (0.37s)
  ./cmd/mesh-controller --- PASS: TestABacklogReleasedByAPersonIsSaidStarted (0.38s)
  ./cmd/mesh-controller --- PASS: TestAWalkEndedByCancelOrClearIsSaidEnded (0.48s)
  ./cmd/mesh-controller --- PASS: TestAWalkRestartedByARebuildIsSaidStarted (0.38s)
  ./cmd/mesh-controller --- PASS: TestAWalkStoppedByHandAndRetriedEndsFailedWhenItFailsAgain (0.42s)
  ./cmd/mesh-controller --- PASS: TestReplay237 (0.36s)
  ./cmd/mesh-controller --- PASS: TestAnUnassignThatLeavesItsNodeUnresolvedSaysSoOnceInItsAnswer (0.32s)
  ./cmd/mesh-controller --- PASS: TestAnUnassignThatLeavesItsNodeResolvedSaysNothingOfIt (0.35s)
  ./cmd/mesh-controller --- PASS: TestAnAssignThatLeavesItsNodeUnresolvedSaysOnlyThePlansRefusal (0.35s)
  ./cmd/mesh-controller --- PASS: TestADirectoryOwnersKeyIsRefusedThroughAVerb (0.39s)
  ./cmd/mesh-controller --- PASS: TestTheSendMakesNoValueForAnOutsideSecretNobodyGave (0.40s)
  ./cmd/mesh-controller --- PASS: TestAQuestionComposesWithoutAnOutsideSecretNobodyGave (0.39s)
  ./internal/inventory --- PASS: TestTheLastSendSaysWhatTheMachineAnswered (0.24s)
  ./internal/inventory --- PASS: TestAReportIsTheLastSendsAnswerOnlyWhenItIsAboutThatSend (0.23s)
  ./internal/inventory --- PASS: TestAnIssuedValueIsSealedToEveryHolderOnceAndNotAgain (0.23s)
  ./internal/inventory --- PASS: TestAnIssuedValueIsSealedAgainAfterAKeyChangeOrAnAcceptance (0.23s)
  ./internal/inventory --- PASS: TestAConditionsLooksAreKeptBesideTheBucket (0.24s)
  ./internal/inventory --- PASS: TestAPlanIdHoldingAQuoteIsReadAsAPlainId (0.22s)
  ./internal/inventory --- PASS: TestAWalksStartIsKeptAndARefusedSaveLeavesItUnsaid (0.22s)
**Merge check** at `539a1c16` Judged: head `539a1c16cd4e` merged into trunk `ba685b17f5e1`; read beside it: mesh-catalog `23ea0f431faf`, mesh-controller-main `ba685b17f5e1`, mesh-host `a1f9856b3957`, mesh-tools `954e1a53eef9` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy) - `mesh/repo-check`: **PASS** — its merge-check.sh passed **Delivery plan** — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step - tier 0: mesh-controller - tier 1: build-agent - tier 2: route-proxy - ace: receives build-agent, route-proxy - g14: receives build-agent - novox: receives mesh-controller, build-agent, route-proxy - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - route-proxy provides route: its consumers are sent again after it - route-proxy keeps data (ADR 0233): what it holds is backed up before it moves - 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, route-proxy is judged on its first machine - read by no module's build: internal/builder/testdata/release.tar.xz, internal/builder/upstream_file_test.go, internal/builder/xz_framing_test.go, vendor/github.com/therootcompany/xz/AUTHORS, vendor/github.com/therootcompany/xz/LICENSE, vendor/github.com/therootcompany/xz/README.md, vendor/github.com/therootcompany/xz/dec_bcj.go, vendor/github.com/therootcompany/xz/dec_delta.go, vendor/github.com/therootcompany/xz/dec_lzma2.go, vendor/github.com/therootcompany/xz/dec_stream.go, vendor/github.com/therootcompany/xz/dec_util.go, vendor/github.com/therootcompany/xz/dec_xz.go, vendor/github.com/therootcompany/xz/doc.go, vendor/github.com/therootcompany/xz/reader.go Run by the build seat on g14 as `build-1791825663577192956` (`builds --log build-1791825663577192956`). ``` --- the gate: build-agent, mesh-controller, route-proxy mesh-controller: ok, 58 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-12T17:18: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-12T17:18:24Z modules the change moves: - mesh-controller (changed) machines: - a machine (f35): composes - holds mesh-dns-resolver (idt): composes - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes - a machine (yvbpsm): composes a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy notes: - internal/builder/testdata/release.tar.xz, internal/builder/upstream_file_test.go, internal/builder/xz_framing_test.go, vendor/github.com/therootcompany/xz/AUTHORS, vendor/github.com/therootcompany/xz/LICENSE, vendor/github.com/therootcompany/xz/README.md, vendor/github.com/therootcompany/xz/dec_bcj.go, vendor/github.com/therootcompany/xz/dec_delta.go, vendor/github.com/therootcompany/xz/dec_lzma2.go, vendor/github.com/therootcompany/xz/dec_stream.go, vendor/github.com/therootcompany/xz/dec_util.go, vendor/github.com/therootcompany/xz/dec_xz.go, vendor/github.com/therootcompany/xz/doc.go and vendor/github.com/therootcompany/xz/reader.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.038s ok github.com/novox/mesh-controller/cmd/mesh-controller 255.834s ok github.com/novox/mesh-controller/examples/objectstore-provisioner 1.012s ok github.com/novox/mesh-controller/examples/postgres-provisioner 1.018s ok github.com/novox/mesh-controller/examples/redis-provisioner 1.015s ok github.com/novox/mesh-controller/examples/route-proxy 1.175s ok github.com/novox/mesh-controller/internal/artifacts 1.043s ? github.com/novox/mesh-controller/internal/beside [no test files] ok github.com/novox/mesh-controller/internal/broker 3.951s ok github.com/novox/mesh-controller/internal/builder 31.596s ok github.com/novox/mesh-controller/internal/catalogue 7.092s ok github.com/novox/mesh-controller/internal/conditions 2.420s ok github.com/novox/mesh-controller/internal/envfile 1.027s ok github.com/novox/mesh-controller/internal/facts 1.030s ok github.com/novox/mesh-controller/internal/identity 4.104s ok github.com/novox/mesh-controller/internal/inventory 85.020s ok github.com/novox/mesh-controller/internal/lease 17.706s ok github.com/novox/mesh-controller/internal/licences 3.757s ok github.com/novox/mesh-controller/internal/licences/adapters 1.008s ok github.com/novox/mesh-controller/internal/link 60.881s ok github.com/novox/mesh-controller/internal/lint 2.169s ok github.com/novox/mesh-controller/internal/outward 1.036s ok github.com/novox/mesh-controller/internal/overlay 1.027s ok github.com/novox/mesh-controller/internal/secrets 1.423s ok github.com/novox/mesh-controller/internal/store 3.216s ok github.com/novox/mesh-controller/internal/testbus 1.174s ok github.com/novox/mesh-controller/internal/token 1.020s store tests, each run against the store: ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeWalksTheOthersAgain (0.35s) ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeEndsTheMergesThatContainIt (0.37s) ./cmd/mesh-controller --- PASS: TestAStopWalksTheOthersAgainInTheOpenBatch (1.74s) ./cmd/mesh-controller --- PASS: TestAStopNamesWhatStaysOnTheMachinesAndWhatStillCarriesIt (0.65s) ./cmd/mesh-controller --- PASS: TestAStopWithoutAMergeLeavesItOutOfABatch (0.32s) ./cmd/mesh-controller --- PASS: TestSavingPlansAndMovingMergesIsOneAct (0.35s) ./cmd/mesh-controller --- PASS: TestAPlanFollowedFromTheStoreAnswersWhenItMoves (4.36s) ./cmd/mesh-controller --- PASS: TestNodeShowAndStatusReadTheLastSendFromTheStore (0.36s) ./cmd/mesh-controller --- PASS: TestTheConditionsVerbReadsTheLooksTheBucketNoLongerHolds (0.26s) ./cmd/mesh-controller --- PASS: TestAWalkLetGoAndStoppedByVerbsIsSaidStartedAndEnded (0.40s) ./cmd/mesh-controller --- PASS: TestAWalkStoppedOrClosedByAPersonIsSaidEndedStopped (0.40s) ./cmd/mesh-controller --- PASS: TestAWalkRetriedByAPersonIsSaidStartedAgain (0.37s) ./cmd/mesh-controller --- PASS: TestABacklogReleasedByAPersonIsSaidStarted (0.38s) ./cmd/mesh-controller --- PASS: TestAWalkEndedByCancelOrClearIsSaidEnded (0.48s) ./cmd/mesh-controller --- PASS: TestAWalkRestartedByARebuildIsSaidStarted (0.38s) ./cmd/mesh-controller --- PASS: TestAWalkStoppedByHandAndRetriedEndsFailedWhenItFailsAgain (0.42s) ./cmd/mesh-controller --- PASS: TestReplay237 (0.36s) ./cmd/mesh-controller --- PASS: TestAnUnassignThatLeavesItsNodeUnresolvedSaysSoOnceInItsAnswer (0.32s) ./cmd/mesh-controller --- PASS: TestAnUnassignThatLeavesItsNodeResolvedSaysNothingOfIt (0.35s) ./cmd/mesh-controller --- PASS: TestAnAssignThatLeavesItsNodeUnresolvedSaysOnlyThePlansRefusal (0.35s) ./cmd/mesh-controller --- PASS: TestADirectoryOwnersKeyIsRefusedThroughAVerb (0.39s) ./cmd/mesh-controller --- PASS: TestTheSendMakesNoValueForAnOutsideSecretNobodyGave (0.40s) ./cmd/mesh-controller --- PASS: TestAQuestionComposesWithoutAnOutsideSecretNobodyGave (0.39s) ./internal/inventory --- PASS: TestTheLastSendSaysWhatTheMachineAnswered (0.24s) ./internal/inventory --- PASS: TestAReportIsTheLastSendsAnswerOnlyWhenItIsAboutThatSend (0.23s) ./internal/inventory --- PASS: TestAnIssuedValueIsSealedToEveryHolderOnceAndNotAgain (0.23s) ./internal/inventory --- PASS: TestAnIssuedValueIsSealedAgainAfterAKeyChangeOrAnAcceptance (0.23s) ./internal/inventory --- PASS: TestAConditionsLooksAreKeptBesideTheBucket (0.24s) ./internal/inventory --- PASS: TestAPlanIdHoldingAQuoteIsReadAsAPlainId (0.22s) ./internal/inventory --- PASS: TestAWalksStartIsKeptAndARefusedSaveLeavesItUnsaid (0.22s) ```
mesh-admin merged commit d8f36a8fb7 into main 2026-10-12 17:35:57 +00:00
mesh-admin deleted branch fix/345-xz-in-go 2026-10-12 17:35:57 +00:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-controller#264