Renumbered from 117, which is taken on main by 'a module's own code is a container in one record and a process in another' — two reports claimed the same number and git would not have said so. Scrubbed the node's name and a real registry path; this repository is public.
4.8 KiB
status, opened, located-in, fixed-by, amended-design
| status | opened | located-in | fixed-by | amended-design | ||
|---|---|---|---|---|---|---|
| located | 2026-09-25 |
|
121 — builder requiring a real package-registry grant deadlocks a genesis bootstrap
What was observed
On the control-node, 2026-09-25, fixing builder's hand-faked package-registry binding (a
hardcoded JSON fragment standing in for a real grant, found live-blocking nothing but carrying no
correctness at all — see the migration log's account of the same night). The fix declared
requires: ["artifact-store", "package-registry"] on builder and let the mesh mint the grant
properly, mirroring how artifact-store was already required.
Running mesh-controller's own test suite against the catalogue as changed surfaced three tests
that pinned a different, deliberate design:
--- FAIL: TestTheBuildersCarriedPackageBindingTakesThePortFromTheNode
--- FAIL: TestTheBuildersCarriedBindingStartsWhereTheForgeServes
--- FAIL: TestTheBuildersPackageBindingKeepsItsIdentity
Read together with their own comment in foundation_manifests_test.go:
The builder carries a binding because at genesis nothing provides
package-registryto resolve one from; the day the forge is a module, the same consumer is told what the forge serves.
The tests expect builder to carry its own package-binding resource — a merge: json file with
protected: [provision, from, at, as], settable only on port via the ordinary settings layers —
matching defaults (port: 3000, scheme: http, npm-path: /api/packages/<owner>/npm/, as: mesh-builder, from: gitea) that agree with what gitea's own serves.package-registry declares.
Tonight's fix removed that resource entirely.
The dependency this protects against is real, not hypothetical. gitea's own module.json
carries a build section — its runtime image is compiled from a Dockerfile via mesh-tools's
build/runtime bases, the same as every other built module. builder is what runs that build. So:
buildernowrequires: package-registry, satisfiable only bygitea.giteacannot run — cannot exist as a container at all — untilbuilderhas built its image.
On the control-node tonight this is invisible: the mesh is already running, gitea is already built and
assigned, and the grant resolves immediately. A genesis bootstrap — a new mesh raised from nothing,
or that node fully re-raised for disaster recovery — hits the order the tests describe: builder is
needed to build gitea's image before gitea can be assigned, so builder cannot yet hold a real
package-registry grant, so (as tonight's fix has it) builder cannot start.
Why it matters beyond this instance
This is the same shape ADR 0075 already named for the mesh as a whole ("the bootstrap still has no package registry... that is the same pivot as everything else and it is not solved here") — but it now has a concrete second collision: the one module capable of building a package-registry provider's own image would refuse to run before that provider exists, precisely because it was made to depend on it correctly.
It is also a near miss worth writing down for its own reason: the fix that broke this was reviewed
against go build, the full test suite (which flagged it immediately — three tests, not zero), and
matched an explicitly-stated target design in 22-the-work-ahead.md written a week earlier. Nothing
about the fix was careless; the deadlock was invisible because the node it ran against had already
crossed the point where it would bite.
What is not decided here
- Whether
buildershould carry both — its own default/fallback binding for when no real grant can be resolved yet, and a realrequires: package-registryfor when one can — and if so, what decides which one is live at a given moment. Nothing in this mesh'srequires/providesresolution currently expresses "this, or a carried fallback if it cannot resolve" for any provision; this would be the first. - Whether the fallback belongs on
builderspecifically (matching what the removed tests already encoded) or is a shape the mesh should offer any module bootstrapping a provider it also depends on — the "builder builds the thing it needs to ask a favour of" pattern is not unique topackage-registryand could recur. - Whether genesis itself should sequence around this instead (build gitea before granting anything,
with
buildernever resolvingpackage-registryduring that specific window) rather than the manifest carrying two shapes of one credential.
Tonight's builder fix (mesh-catalog modules/builder/module.json) is left in place — correct for
the steady state, live and working on the control-node — with the three tests above left failing rather than
reverted or hacked to pass, so the gap stays visible rather than quietly patched over.