Files
hq/04-ISSUES/009-a-digest-pinned-image-cannot-be-placed-in-the-lab/00-report.md
T
jschoubben e1febe8e0f Renumber the records 1 to 23
The consolidation left a sparse sequence -- 1, 4, 6, 7, 9, 10, 12, 15, 16, 18,
19, 25, 34, 35, 36, 37, 40, 42, 44, 45, 48, 49, 58 -- where the gaps were only
the archaeology of what used to be there.

Renumbered contiguously. Renames run in ascending order, so every target number
is already free and no two files ever collide.

The reference rewrite is one simultaneous pass rather than a sequence of
replacements. Numbers moved into slots other numbers were vacating -- the node
host went 37 to 16 while the lab went 16 to 9 -- so replacing one at a time
would have cascaded and silently pointed things at the wrong record.

Seven plain-text references survived the merges as prose rather than links,
naming records that no longer existed: the enrolment token, the link boundary,
what a declaration is, reachability, the repository structure. Each mapped to
the consolidated record that now holds it.

Verified rather than assumed: every [ADR NNNN](path) link now has matching text
and target, checked across the whole repository, and the checker passes.

Frontmatter `consolidates:` lists dropped -- they named records that are gone,
and each consolidated record already says in prose what it absorbed.
2026-08-28 23:28:34 +02:00

3.5 KiB

status, opened, located-in, fixed-by, amended-design
status opened located-in fixed-by amended-design
open 2026-08-28
mesh-lab
mesh-host

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 0021 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 0021 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 0009) 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.