The playbook, the README and the status skill knew five statuses; the cycle check knew a sixth, 'fixed', and not 'wontfix'. Eleven issues sat in the sixth for weeks with their fixes shipped, one step short of closed. They are resolved; the check refuses the word from now on and accepts the one the playbook allows.
105 lines
5.0 KiB
Markdown
105 lines
5.0 KiB
Markdown
---
|
|
status: resolved
|
|
opened: 2026-09-01
|
|
located-in: [mesh-control]
|
|
fixed-by: mesh-control be62f49; mesh-lab f85dbb0
|
|
amended-design:
|
|
---
|
|
|
|
# 029 — The artifact store cannot be delivered by the artifact store
|
|
|
|
## Symptom
|
|
|
|
A mesh that has just bootstrapped cannot install a registry. The module describing one resolves,
|
|
composes and pushes; the build never completes, because there is nowhere to put what it builds.
|
|
|
|
It has never been seen, because the lab always has a registry standing before the mesh asks for
|
|
one, and so does any mesh built on a machine that already had one.
|
|
|
|
## What it actually blocks
|
|
|
|
Not "a registry cannot be installed" — **a mesh cannot hold its own modules.**
|
|
|
|
The artifact store is one store for everything a module ships: images, and archives, which are
|
|
directories from a module's repository packed and pushed as content-addressed blobs. So it is the
|
|
mesh's module catalogue in artefact form, and the same role a separate object store plays in the
|
|
arrangement being replaced.
|
|
|
|
Until it exists, a module can be described and resolved but nothing it carries can be kept
|
|
anywhere. A mesh that has just bootstrapped can therefore run only what its bundle already holds.
|
|
|
|
## The cycle
|
|
|
|
Three facts, each correct on its own:
|
|
|
|
- **A registry module's image is mirrored in.** `kind: upstream` pulls the reference the module
|
|
names and pushes it under a name of the mesh's own, so what a machine fetches is pinned by a
|
|
digest this mesh assigned rather than by a tag somebody else can move.
|
|
- **The builder publishes to the artifact store**, learned from its `artifact-store` binding —
|
|
the same binding any consumer of any provision gets.
|
|
- **The builder refuses to run without one**: *"a built artifact nobody can fetch is not built."*
|
|
|
|
So installing the thing that provides `artifact-store` requires something that provides
|
|
`artifact-store`.
|
|
|
|
## Why the design already answers it
|
|
|
|
The substrate record asks of each candidate *can it grant itself the thing it provides?* The store
|
|
cannot create its own database; the broker cannot create its own virtual host; and the registry
|
|
**cannot grant itself a repository**. That is why the registry is substrate by role.
|
|
|
|
The same sentence answers this. A module that provides the artifact store cannot be delivered
|
|
through the artifact store, so its image is not the mesh's to mirror — it is named directly and
|
|
pulled from upstream exactly once, which is what the bundle already does for the three images a
|
|
first node starts from.
|
|
|
|
## The fix
|
|
|
|
**The registry module names its image, and is never built.** A container naming
|
|
`registry@sha256:…` needs no builder, no binding and no store. Every module after it mirrors
|
|
normally, into the registry now running.
|
|
|
|
Nothing new is required: naming an image directly is what most modules do.
|
|
|
|
## What it costs, and what it does not
|
|
|
|
The machine running the first registry needs to reach a public registry once, to pull that one
|
|
image by digest. That is already true of a first node, which fetches three images the same way
|
|
before a mesh exists.
|
|
|
|
It does not weaken pinning. A digest is exact wherever it came from; what mirroring adds is that
|
|
the mesh keeps its own copy and does not depend on a tag somebody else controls. For one image, on
|
|
one machine, once, the bundle already accepts that trade and says why.
|
|
|
|
## The constraint this puts on a registry module
|
|
|
|
**A module providing `artifact-store` may not build artifacts of its own** — not its image, and
|
|
not a user interface or a tool server shipped beside it. There is nowhere to put them until it is
|
|
running.
|
|
|
|
A registry that wants more than the upstream image is therefore two modules: one that provides the
|
|
store and only names an image, and an ordinary module beside it that builds whatever else and
|
|
mirrors it in the normal way. That is a real limit on how a registry module can be written, and it
|
|
should be said out loud rather than discovered.
|
|
|
|
## How it is checked
|
|
|
|
A manifest that both provides `artifact-store` and declares built artifacts is refused where it is
|
|
written, naming the cycle. Otherwise the fault surfaces as a build that never returns, on a mesh
|
|
too new to have anybody watching it.
|
|
|
|
## Fixed
|
|
|
|
**The registry module names its image and is added, not built.** The lab's artifact-store test now
|
|
walks the only path open to a real first mesh — the manifest goes in directly, no builder and no
|
|
store involved — and passes.
|
|
|
|
**And the cycle is refused where it is written.** A manifest that provides `artifact-store` and
|
|
also declares built artifacts is refused at parse, naming the cycle: building publishes to the
|
|
store, so it asks the mesh to put an artifact into the thing that artifact is needed to create.
|
|
The provision name became a constant for the rule to turn on.
|
|
|
|
What surfaced it in the lab is worth keeping: the test had always *built* the registry module and
|
|
passed, because the scenario's stand-in for the public registry was standing there to receive the
|
|
push. A prop that quietly covers for the thing under test is how a bootstrap hole stays invisible.
|