declaration: an image may be named by the digest of its own configuration

A manifest digest is assigned by a registry on push, so insisting on one meant a
registry had to exist before the thing that lets a mesh have a registry could
start — a dependency the pinning rule created by accident, not a pin. The mesh's
own control plane is built from source and lives in no public registry.

A bare sha256:... names an image the machine already holds, by the digest of its
own configuration: immutable and unforgeable in exactly the way the rule asks
for. Absent, it says so plainly rather than failing at a pull nothing serves.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
This commit is contained in:
2026-09-10 22:40:02 +02:00
parent 46cc06847d
commit 4af9483219
3 changed files with 59 additions and 3 deletions
+9
View File
@@ -1154,6 +1154,15 @@ func ensureImage(ctx context.Context, cri, image string, run Runner) error {
if _, err := run(ctx, cri, "image", "inspect", image); err == nil { if _, err := run(ctx, cri, "image", "inspect", image); err == nil {
return nil return nil
} }
// An image named by the digest of its own configuration is one this machine was supposed to
// already hold — built here, or handed over. There is no registry that answers for it, so
// pulling would fail somewhere that names a network problem instead of a missing image.
if strings.HasPrefix(image, "sha256:") {
return fmt.Errorf(
"image %s is not on this machine, and an image named by its own digest cannot be "+
"fetched: nothing serves it. Build it here, or load it, before applying this",
image)
}
if _, err := run(ctx, cri, "pull", image); err != nil { if _, err := run(ctx, cri, "pull", image); err != nil {
return fmt.Errorf("pulling image %s: %w", image, err) return fmt.Errorf("pulling image %s: %w", image, err)
} }
+24 -3
View File
@@ -874,20 +874,41 @@ func checkMode(where, mode string) []string {
return nil return nil
} }
// checkImage insists on a digest. // checkImage insists on content, not on a name.
// //
// A tag moves and a digest does not. The bundle's whole claim is that what it names is exact // A tag moves and a digest does not. The bundle's whole claim is that what it names is exact
// (novox/hq ADR 0006), and a bundle pinning `postgres:17` pins nothing — it names whatever // (novox/hq ADR 0006), and a bundle pinning `postgres:17` pins nothing — it names whatever
// that tag points at on the day the host happens to run. // that tag points at on the day the host happens to run.
//
// **Two forms say something exact, and only one of them needs a registry.** `name@sha256:…` is a
// manifest digest, which a registry assigns on push. A bare `sha256:…` is an image the machine
// already holds, addressed by the digest of its own configuration — equally immutable, equally
// unforgeable, and requiring nothing to have served it.
//
// That second form is what a first machine needs. The mesh's own control plane exists in no public
// registry and never will: it is built from source, and until this mesh has a registry of its own
// there is nowhere to push it to and therefore no manifest digest to name it by. Insisting on one
// would mean a registry has to exist before the thing that lets a mesh have a registry can start —
// which is not a pin, it is a dependency the rule accidentally created. A machine that built an
// image, or was handed one, can name it by what it is.
func checkImage(where, image string) []string { func checkImage(where, image string) []string {
if image == "" { if image == "" {
return []string{where + ": a container needs an image"} return []string{where + ": a container needs an image"}
} }
// An image this machine holds, named by the digest of its own configuration.
if strings.HasPrefix(image, "sha256:") {
if len(image) != len("sha256:")+64 {
return []string{fmt.Sprintf(
"%s: image id %q is not a sha256 digest", where, image)}
}
return nil
}
name, digest, found := strings.Cut(image, "@") name, digest, found := strings.Cut(image, "@")
if !found || name == "" { if !found || name == "" {
return []string{fmt.Sprintf( return []string{fmt.Sprintf(
"%s: image %q is not pinned. Write it as name@sha256:... — a tag moves, and a "+ "%s: image %q is not pinned. Write it as name@sha256:… — or as sha256:… for an image "+
"bundle that pinned a tag would not be pinned", where, image)} "this machine already holds. A tag moves, and a bundle that pinned a tag would "+
"not be pinned", where, image)}
} }
if !strings.HasPrefix(digest, "sha256:") || len(digest) != len("sha256:")+64 { if !strings.HasPrefix(digest, "sha256:") || len(digest) != len("sha256:")+64 {
return []string{fmt.Sprintf( return []string{fmt.Sprintf(
+26
View File
@@ -219,6 +219,32 @@ func TestAnImageMustBePinnedByDigest(t *testing.T) {
} }
} }
// An image the machine already holds, addressed by the digest of its own configuration.
//
// **The form a first machine needs.** A manifest digest is assigned by a registry on push, so
// insisting on one means a registry has to exist before the thing that lets a mesh have a registry
// can start. The mesh's own control plane is built from source and lives in no public registry; a
// machine that built it, or was handed it, names it by what it is — a content address, not a name,
// and immutable in exactly the way the rule asks for.
func TestAnImageTheMachineHoldsIsNamedByItsOwnDigest(t *testing.T) {
held := "sha256:" + strings.Repeat("b", 64)
if _, err := ParseTrusted([]byte(`{"declaration":1,"resources":[
{"id":"control","type":"container","name":"mesh-control","image":"` + held + `"}
]}`)); err != nil {
t.Errorf("an image named by its own digest was refused: %v", err)
}
// Still a digest, though. A truncated one names several images, and which one ran would be
// whichever the runtime happened to match first.
for _, bad := range []string{"sha256:abc", "sha256:", "sha256:" + strings.Repeat("b", 63)} {
if _, err := ParseTrusted([]byte(`{"declaration":1,"resources":[
{"id":"control","type":"container","name":"mesh-control","image":"` + bad + `"}
]}`)); err == nil {
t.Errorf("image id %q was accepted and is not a digest", bad)
}
}
}
func TestAFieldTheNewTypesDoNotUseIsRefused(t *testing.T) { func TestAFieldTheNewTypesDoNotUseIsRefused(t *testing.T) {
// The field-set check must cover the types added last, not only the three it was written // The field-set check must cover the types added last, not only the three it was written
// for. A package that carries a `content` is a control plane believing it asked for // for. A package that carries a `content` is a control plane believing it asked for