--- status: open opened: 2026-08-28 located-in: [mesh-lab, mesh-host] fixed-by: amended-design: --- # 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](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md)** 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`](../../03-DESIGN/01-to-be/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](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md) 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](../../02-DECISIONS/0009-the-lab.md)) 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.