Jochen: a normal application has 3-5 ADRs, maybe 10 for a large one, and we are at 65. Fair, and the cause is mine -- I recorded every FINDING as a decision rather than every fork in the road. Two merges, both cases where one decision had been split across many records because it was taken over several days rather than at once. 0019 absorbs ten records about how this repository works: what it is and that it is public, the folder flow, the two design layers, the issue front door, status in frontmatter, playbooks, the naming rule, the product name. Those were never ten decisions -- they were one, seen from ten angles as the repository took shape. 0016 absorbs the five about the lab: a node is a virtual machine, a router is scenery, a scenario declares the underlay, a scenario is a closed address space, and the two scenario classes. Same pattern -- one design, split by the order it was worked out in. The consolidated 0019 also raises the bar for what earns a record, since that is what produced 65: a record is warranted when there is a genuine fork -- a direction reversed, an alternative that will be proposed again, something contested. A finding is not a decision, and a bug is certainly not. Everything else belongs in the design document where the reasoning is actually read. The checker earned its place here. Deleting nine records left 13 dangling links across the repository and it named every one, including in AGENTS.md. Nothing was found by reading. Remaining clusters worth the same treatment: the host (8 records), delivery (5), modules (6), connectivity (4), substrate and control plane (4). That would be 52 down to roughly 30.
3.5 KiB
status, opened, located-in, fixed-by, amended-design
| status | opened | located-in | fixed-by | amended-design | ||
|---|---|---|---|---|---|---|
| open | 2026-08-28 |
|
009 — A digest-pinned image cannot be placed in the lab, so container cannot be tested there
Symptom
Two accepted decisions collide, and the collision makes one resource shape untestable.
-
ADR 0046 pins images by digest, and the host refuses an image reference that is not pinned:
resource "store": image "alpine:3.20" is not pinned. Write it as name@sha256:... -
The lab cannot place a digest-pinned image. A sealed scenario cannot reach a registry, so the lab exports an image from the workstation and loads it in the machine — and that loses the digest.
So a container resource is refused by the host if it names a tag, and unusable if it names a
digest. There is no declaration the lab can currently raise that exercises the shape.
What was measured
Not inferred. docker save alpine@sha256:d9e8… produces an archive with no repo tag, because
a repo digest exists only for an image a registry served. Loading it says:
Loaded image ID: sha256:63f227… (not "Loaded image: alpine:3.20")
and docker images then lists nothing — the image is there but dangling. A container declaring
that digest therefore falls through to the registry:
Unable to find image 'alpine@sha256:d9e8…' locally
dial tcp: lookup registry-1.docker.io: no such host
which is correct behaviour on a machine with no route out.
What is not affected
Everything else placed in the same sealed machine works, and was verified there:
| shape | |
|---|---|
package |
applied, idempotent |
service incl. boot: enabled |
applied, read back as enabled |
action |
ran, verified |
container |
blocked by this issue |
Why it matters more than one shape
The container shape is the substrate. Every step of raising a mesh past the container runtime is
a container (07-the-substrate.md), so the
bootstrap cannot be tested end-to-end until this is resolved — which is the thing the lab exists
for.
The shape of a resolution
A registry inside the scenario, on its public segment, that machines pull from. That is not a workaround: it is what the real mesh does — ADR 0048 names an OCI registry as substrate, and every node after the first pulls from the mesh's own. Testing against a registry is testing the real path rather than a stand-in for it.
It also removes the lab's export-and-push mechanism rather than fixing it, which is the better outcome: pushing image tarballs over the hypervisor was always a lab-only invention.
Not decided here, because it is design rather than repair: where the registry runs, whether it is scenery like the router (ADR 0016) or a placed artifact, and how images get into it.
Incidental, and already fixed
The lab's own check on the load was too weak: it matched "Loaded image", which is a prefix of
both Loaded image: and Loaded image ID:. So a load that produced an unusable dangling image
reported success, and the failure surfaced later as a container that would not start. It now
matches Loaded image: exactly and says what the runtime actually said.
That is this repository's own subject arriving in its own tooling: a check that passes on the wrong thing is worse than no check, because it moves the failure away from its cause.