Reconcile: adopt initialization's consolidated HQ as canonical, re-home this session's new work #24

Merged
jschoubben merged 177 commits from reconcile-init-into-main into main 2026-09-05 10:27:11 +00:00
Showing only changes of commit 2bbdc52440 - Show all commits
@@ -0,0 +1,97 @@
---
status: located
opened: 2026-09-01
located-in: [mesh-control, mesh-host]
fixed-by: partly — mesh-control ee3cc1b
amended-design:
---
# 025 — A module must pin a digest, and nothing produces one
## Symptom
Every image reference in every example module is **sixty-four zeros**:
```
gitea@sha256:0000000000000000000000000000000000000000000000000000000000000000
```
Eighteen of them, across five modules. Each one parses, resolves, and composes into a declaration
a host accepts. None of them could ever start: the machine would reach `docker pull` and stop.
This is why those modules are *written* and not *running*, and it was not visible from any check
because every check passes.
## Why nothing caught it
The host validates the **shape** of a reference and nothing else — that it is `name@sha256:` plus
sixty-four hexadecimal characters. Sixty-four zeros satisfies that exactly.
That check is not wrong. A host cannot verify a digest exists without reaching a registry, and
reaching a registry is precisely what the design refuses to make it do
([ADR 0006](../../02-DECISIONS/0006-the-substrate-and-the-control-plane.md)). The host is the last
place that could catch this and the wrong place to try.
## The actual gap
**A manifest must carry a digest, and nothing in the system produces one.**
- Images the mesh builds are fine: the bundle writes the digest down *after* building, which is
the whole reason the bundle exists in that shape.
- Images from anywhere else — a forge, a mail system, a database — have no path at all. Somebody
has to look up what `gitea:1.22` points at today and paste it in, and nothing re-checks it.
So the design is coherent about *pinning* and silent about *where a pin comes from*. A person
writing a module is asked for something they cannot reasonably produce by hand, and given a
placeholder shape that passes every gate.
## What a fix has to keep
- **The host still refuses a tag.** A digest is what makes a declaration exact, and that must not
soften. The fix belongs where a module is added or built, not on the machine.
- **A person writes a tag; the mesh holds a digest.** A manifest in a repository naming
`gitea:1.22` is readable and reviewable; the mesh resolving that against a registry once, and
recording the answer, is what makes it exact. The module table already records this shape for
source repositories — where it came from, the branch followed, the commit read — and an image is
the same question asked of a registry.
- **Re-resolving is a decision, not a side effect.** A tag that moves must not silently change what
a machine runs. Whatever resolves it records both, so *this pin is behind its tag* is a question
the mesh can answer rather than something discovered on a restart.
## Cheaply, now
An all-zero digest is a placeholder and never a real image. Refusing it costs three lines and
would have caught all eighteen the day they were written. It does not fix the gap; it stops the
gap being invisible.
## What this blocks
Every module that names a third-party image, which is every module that is not the mesh itself.
The forge and the mail system are otherwise ready to run.
## Half of it is done
**A placeholder can no longer reach a machine.** The refusal sits where a declaration is composed,
not where a manifest is parsed — a file awaiting a pin is legitimate, and the design already says
so for artifacts the mesh builds. Composing is the last moment before a machine sees it.
**The examples now pin images that exist.** Twelve third-party digests were resolved against their
registries without pulling anything, which is also the mechanism the rest of this issue needs:
`docker manifest inspect --verbose` answers *what does this tag point at* in about a second.
Two faults came free, and both had been invisible for the same reason as the digests: the mail
system's seven images named repositories that **do not exist** — it publishes to a different
registry entirely — and one of the seven had been renamed upstream. Nothing that only checks the
shape of a reference could ever have found either.
## What is still open
**Nothing turns a tag into a digest as part of the mesh's own work.** It was done by hand here,
which is exactly what this issue says a person should not be asked to do. The shape of the answer
is unchanged: a person writes a tag, the mesh resolves it once and records both, and *this pin is
behind its tag* becomes a question the mesh can answer.
**The mesh's own provisioner images still cannot be pinned in a repository**, because their digest
does not exist until they are built and pushed. That is the bundle's problem, and the bundle solves
it by writing the digest down after building. A module naming an image the mesh builds needs the
same treatment, and does not have it.