Files
hq/04-ISSUES/121-builders-real-package-registry-grant-deadlocks-genesis/01-diagnosis.md
T
jochen a1d8b478ec Issue 121: retract step 4 — the forge's service is not built
Step 4 claimed the forge cannot exist as a container before the builder has built its image. The
forge's server is an upstream public image pinned by digest; only its runtime sidecar is built. The
store and the broker have the same shape, and ADR 0078 raises both at genesis as plumbing and adopts
them in place — so the forge can be raised the same way and serve git, packages and OCI before
anything is built.

What survives: a grant is minted by the provider's runtime sidecar, which is built, so the question
is whether raise-service, grant, build-sidecar simply works. Sequencing inside the mesh, not images.

The wrong version came from taking a record's bootstrap argument at face value instead of comparing it
to how the store and broker are raised — one command away in the manifests.
2026-09-26 15:36:19 +02:00

5.6 KiB
Raw Blame History

121 — Diagnosis

2026-09-26 — the seats work renamed the requirement; it did not reorder the bootstrap

The seats change landed the same day this was filed (ADRs 0109–0111 accepted, and the code on both main branches), and it touches exactly the two manifests this report names. That made it worth asking first whether the deadlock had been fixed in passing. It has not.

Step 1 — what the seats work changed

builder's requirement was renamed from package-registry to npm-package-registry, which is now a seat the forge holds; the forge claims npm-package-registry and git at mesh scope and serves both. The provision is the same relationship under a name the mesh defines rather than one a manifest invented.

Step 2 — the seat's holder cannot answer when there is no holder yet

The seat is consulted in exactly one branch of resolution: where several nodes provide the wanted provision and no pin says which. That is the ambiguity case, and it is not the bootstrap case. With nothing providing it at all, resolution takes the branch for no providers and refuses, naming the remedy:

nothing in this mesh provides "npm-package-registry", wanted by builder — assign gitea to a node

A refusal, not a deferral. So a seat delivering a provision does not make a requirement optional before the seat is filled, and nothing about the seats work moves the order.

Step 3 — ruled out: genesis has no escape hatch

Resolution has an Unchecked mode that "takes brokered requirements on trust instead of refusing when nothing answers them", which would be exactly the exemption a bootstrap needs. It is not one: it exists for the first of two passes — working out what each node offers — and its own comment states that a declaration is never built from an unchecked resolution. The second pass, the one that produces the declaration, refuses.

Step 4 — the other arm of the cycle is intact

The forge's manifest still carries a build section, its runtime image compiled from the toolchain's build and runtime bases like every other built module. builder is what runs that build. So both arms stand: the builder cannot be assigned before the forge provides the registry, and the forge cannot exist as a container before the builder has built its image.

2026-09-26, later — Step 4 is retracted: the forge's service is not built

Step 4 said the forge's runtime image is compiled by the builder, so the forge cannot exist as a container before the builder has built it. That is not what the forge's definition says, and it took one command to disprove.

The forge's server is an upstream public image pinned by digest. What the mesh builds is the forge's runtime sidecar — its own agent beside the service, a separate container. The store and the broker have exactly the same shape: an upstream server image, a built sidecar. And that shape is the point, because ADR 0078 raises the store and the broker at genesis as plumbing and adopts them in place as ordinary modules afterwards. Nothing stops the forge being raised the same way: its service can be serving git, packages and OCI before anything at all has been built.

So the second arm of the cycle, as this diagnosis stated it, does not exist.

What survives is narrower and is still worth answering. A grant — the credential the builder would use against the package registry — is minted and delivered by the provider's runtime sidecar, and that sidecar is built. So the question is whether a consumer can be granted a provision whose provider's sidecar is not yet running, and whether the ordering raise the service → grant → build the sidecar simply works. That is sequencing inside the mesh's own machinery, not a chicken-and-egg about images, and it may not be a deadlock at all.

The report's open questions should be read against this. In particular, a carried fallback binding may be answering a problem that adopt-in-place already solves.

Why the error is recorded rather than edited away: the wrong version was reached by taking a record's bootstrap argument at face value instead of checking it against how the store and the broker are actually raised — which is the one comparison the manifests make available in a single command.

What did change, and why it is the part worth recording

The three tests that pinned the carried-fallback design — the builder's own package-binding resource, its port taken from the node, its identity kept — were deleted by the controller's seats PR and replaced by seat-based tests that pass.

The report deliberately left those three failing so the gap would stay visible. It is no longer visible: the suite is green, and the only remaining trace is this issue. That is the same way the deadlock was invisible in the first place — a running mesh resolves the grant instantly, because the forge is already built and assigned. Twice now, the evidence has been removed by something correct.

What would actually answer it

  • A provision that resolves to a carried fallback when nothing provides it yet. Nothing in the model expresses "this, or a carried default until something can answer", for any provision. This would be the first, which is why it belongs in a record rather than in a manifest.
  • Genesis sequencing instead — build the forge before granting anything, with the builder never resolving the registry during that window.
  • The forge's runtime image not being the mesh's to build — an external image breaks the cycle by removing the dependency rather than by expressing it.

None is chosen here. The report's open questions stand as written.