Jochen asked whether the order made sense. It did not -- it followed when things happened to be decided, which after consolidation is fictional anyway since record 5 alone folds decisions taken across a week. Concretely wrong before: the domain statement sat at 8, after five engineering rules; the constitution was scattered across 5, 12 and 17; the tiers landed at 15, 16, 21 and 22 with process records in between. Now it walks: what the mesh is (1-3), its tiers from the bottom up (4-8), what runs on them and how it gets there (9-10), how it is built (11-16), how it is checked (17-18), how we work (19-23). Two things made this safe rather than free. It is a permutation, not a compaction, so the renames go through temporary names -- otherwise two files want one slot and one is lost. And the reference rewrite is a single simultaneous pass, because almost every number moved into a slot another number was vacating; replacing one at a time would have cascaded and pointed things at the wrong record while still resolving. Verified: 284 [ADR NNNN](path) links across the repository, all with matching text and target. The ordering principle is now stated in 19 rather than left implicit -- the repository already said "the numbering is the flow" about its folders, and there was no reason for the records to be the exception.
89 lines
3.5 KiB
Markdown
89 lines
3.5 KiB
Markdown
---
|
|
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 0006](../../02-DECISIONS/0006-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 0006](../../02-DECISIONS/0006-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 0016](../../02-DECISIONS/0016-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.
|