diff --git a/04-ISSUES/039-the-lab-registry-was-silently-pinning-unpinned-modules/00-report.md b/04-ISSUES/039-the-lab-registry-was-silently-pinning-unpinned-modules/00-report.md new file mode 100644 index 0000000..5c052b8 --- /dev/null +++ b/04-ISSUES/039-the-lab-registry-was-silently-pinning-unpinned-modules/00-report.md @@ -0,0 +1,60 @@ +--- +status: open +opened: 2026-09-10 +located-in: [] +fixed-by: +amended-design: +--- + +# 039 — The lab's registry was pinning what the catalogue left unpinned + +## Symptom + +Nine container images across seven modules name their image by **tag** — the shape +`//:latest` — rather than by digest. They are the operator's own application +images, the ones built from their own source. + +[ADR 0006](../../02-DECISIONS/0006-the-substrate-and-the-control-plane.md) requires a digest, and +the host refuses a tag by name: *"image %q is not pinned. Write it as name@sha256:… — a tag moves, +and a bundle that pinned a tag would not be pinned."* + +**Every bed passed anyway, for as long as the lab has existed.** The lab raised a registry of its +own, pushed every image into it, and rewrote every reference in every manifest to the digest **that +registry had just assigned**. So a manifest naming a tag arrived at a machine naming a digest. The +rewriting was doing the pinning. + +It surfaced only when the lab's registry was deleted — the modules now carry their tags all the way +to the machine, and fail there, which is the correct behaviour finally being reachable. + +## Why this matters + +**A rule enforced by scenery is not enforced.** The host's refusal is right and has never once +fired in a bed, because nothing unpinned could reach it. The check exists, the tests are green, and +the property they appear to defend was being supplied by the test harness — which is the same shape +as [003](../003-firewall-scope-is-read-by-no-code/00-report.md), one layer further out: there the +rule was read by no code, here it is read by code that never saw a violation. + +**And these are the worst images for it to be true of.** A tag republished on every build is the +one reference that genuinely moves. A machine reconciling against an unchanged declaration can +change what it runs, with nothing in the declaration or the mesh's records saying anything did — +which is precisely the failure pinning exists to prevent, aimed at the images that change most +often. + +**The general form is the part worth keeping.** Any invariant the lab happens to satisfy +incidentally is an invariant no bed tests. The harness was not merely serving images; it was +quietly supplying a property of the system under test, and nothing said so. + +## Open questions + +- What should a manifest name for an image the operator builds themselves? A digest changes on + every build, so a manifest carrying one is wrong the moment anybody commits — which is the + argument [`12-a-module-repository`](../../03-DESIGN/01-to-be/12-a-module-repository.md) already + makes for *two* documents, the repository's naming artifacts and the mesh's naming digests. Is + this simply the build pipeline's absence showing, rather than seven mistakes? +- Until that pipeline exists, what names these images — and is a tag with a loud warning better or + worse than a digest that is stale by construction? +- How is *"nothing reaches a machine unpinned"* checked anywhere other than on the machine that + refuses it? A check that only ever runs at the last possible moment, on the one path a harness + was rewriting, is a check nobody can see failing. +- Which other invariants is the lab supplying rather than testing? This one was found by deleting + the thing that supplied it. That is not a repeatable technique.