Pin the example modules to images that exist

novox/hq 04-ISSUES/025. Every image reference in every example module
was sixty-four zeros — eighteen of them across five modules. Each
parsed, resolved, and composed into a declaration a host accepts, and
none could ever have started: the machine reaches `docker pull` and
stops. That is why those modules were written and not running, and no
check saw it because every check passed.

The host validates the shape of a reference and nothing more, which is
correct: verifying a digest exists means reaching a registry, and that
is the one thing a host must never have to do. So the last place that
could catch this is the wrong place to try.

The guard therefore sits where a declaration is composed, not where a
manifest is parsed. A file in a repository is allowed to await a pin —
the design already says the manifest in a repository names artifacts
while the manifest the mesh holds names digests, and the bundle works
exactly that way. What must never happen is a placeholder reaching a
machine, and composing is the last moment before one does.

Twelve third-party images resolved to real digests without pulling
anything, which is also the mechanism the open issue needs. Two
discoveries came free: mailu publishes to ghcr rather than Docker Hub,
so seven references named repositories that do not exist at all; and it
renamed roundcube to webmail, so that one would have failed even with
the right registry.

What stays a placeholder is the mesh's own provisioner images, which
genuinely have no digest until built and pushed — the bundle's problem,
legitimately unresolved here. The stand-in consumer now stands in with
a real image rather than an invented one.
This commit is contained in:
2026-09-01 15:13:33 +02:00
parent 2835f41a64
commit ee3cc1b6f4
8 changed files with 44 additions and 15 deletions
+1 -1
View File
@@ -26,7 +26,7 @@
"content": "GITEA__security__INTERNAL_TOKEN=${secret:internal-token}\nGITEA__database__DB_TYPE=postgres\nGITEA__database__HOST=${bound:postgres-database:at}:${bound:postgres-database:port}\nGITEA__database__NAME=gitea\nGITEA__database__USER=${bound:postgres-database:as}\nGITEA__database__PASSWD=${secret:postgres-database}\n"},
{"id": "server", "type": "container", "name": "gitea",
"image": "gitea@sha256:0000000000000000000000000000000000000000000000000000000000000000",
"image": "gitea/gitea@sha256:dfc61e347c8b582df918f4556401bf2cecdfbdb56c5282ae9488dd76fca3e41c",
"env": {"DB_TYPE": "postgres", "USER_UID": "1000", "USER_GID": "1000"},
"env-file": ["/var/lib/gitea/server.env"],
"ports": ["3000:3000", "2222:22"],
+1 -1
View File
@@ -30,7 +30,7 @@
{"id": "net", "type": "network", "name": "keycloak"},
{"id": "server", "type": "container", "name": "keycloak",
"image": "keycloak@sha256:0000000000000000000000000000000000000000000000000000000000000000",
"image": "quay.io/keycloak/keycloak@sha256:ecd43971114b0c764f8a3288dddab73f98cb473daccc4feaffe4dc14adeaf866",
"network": "keycloak",
"args": ["start-dev"],
"env": {"KC_DB": "postgres", "KC_HTTP_ENABLED": "true", "KC_HEALTH_ENABLED": "true"},
+9 -9
View File
@@ -31,24 +31,24 @@
{"id": "net", "type": "network", "name": "mailu"},
{"id": "resolver", "type": "container", "name": "mailu-resolver",
"image": "mailu-unbound@sha256:0000000000000000000000000000000000000000000000000000000000000000",
"image": "ghcr.io/mailu/unbound@sha256:142aaad82ad1b0d5b59a5f1303778dba61a3e0a540f5d969c48862bcc99f6f5d",
"network": "mailu",
"env-file": ["/var/lib/mailu/secret.env"]},
{"id": "redis", "type": "container", "name": "mailu-redis",
"image": "redis@sha256:0000000000000000000000000000000000000000000000000000000000000000",
"image": "redis@sha256:1db42ccef14898aa29bae778452d567534b59c107129cbc1163fb552de184d3c",
"network": "mailu",
"volumes": ["/services/mailu/data/redis:/data"]},
{"id": "admindb", "type": "container", "name": "mailu-admindb",
"image": "postgres@sha256:0000000000000000000000000000000000000000000000000000000000000000",
"image": "postgres@sha256:7456ef82e5f5bc43d997f4781bbd7c0d6389bff397564649a356e206ba473aee",
"network": "mailu",
"env": {"PGDATA": "/var/lib/postgresql/data/pgdata"},
"env-file": ["/var/lib/mailu/database.env"],
"volumes": ["/services/mailu/data/data/psql_admindb/pgdata:/var/lib/postgresql/data/pgdata"]},
{"id": "admin", "type": "container", "name": "mailu-admin",
"image": "mailu-admin@sha256:0000000000000000000000000000000000000000000000000000000000000000",
"image": "ghcr.io/mailu/admin@sha256:dcac20e9cbdad560faef9653b1b5ac0d9266f4098dc00f0e7f0d35f4e70ed8f1",
"network": "mailu",
"env-file": ["/var/lib/mailu/secret.env", "/var/lib/mailu/database.env", "/var/lib/mailu/admin.env"],
"volumes": [
@@ -57,7 +57,7 @@
]},
{"id": "imap", "type": "container", "name": "mailu-imap",
"image": "mailu-dovecot@sha256:0000000000000000000000000000000000000000000000000000000000000000",
"image": "ghcr.io/mailu/dovecot@sha256:46d18ba51032be8ebd6841aa49c1ef8762c729038c5fd86a081b5b884d478af9",
"network": "mailu",
"env-file": ["/var/lib/mailu/secret.env"],
"volumes": [
@@ -66,25 +66,25 @@
]},
{"id": "smtp", "type": "container", "name": "mailu-smtp",
"image": "mailu-postfix@sha256:0000000000000000000000000000000000000000000000000000000000000000",
"image": "ghcr.io/mailu/postfix@sha256:bbf882880f68849511710b35237a933f3fe80c4b28bf48ff20205dbd1f1433d7",
"network": "mailu",
"env-file": ["/var/lib/mailu/secret.env"],
"volumes": ["/services/mailu/data/mailqueue:/queue"]},
{"id": "antispam", "type": "container", "name": "mailu-antispam",
"image": "mailu-rspamd@sha256:0000000000000000000000000000000000000000000000000000000000000000",
"image": "ghcr.io/mailu/rspamd@sha256:e87ab93dd252cc69499caa5317dd10d445fd4291a7ecf6bca09793c7d475a0c8",
"network": "mailu",
"env-file": ["/var/lib/mailu/secret.env"],
"volumes": ["/services/mailu/data/filter:/var/lib/rspamd"]},
{"id": "webmail", "type": "container", "name": "mailu-webmail",
"image": "mailu-roundcube@sha256:0000000000000000000000000000000000000000000000000000000000000000",
"image": "ghcr.io/mailu/webmail@sha256:076b720fc766e58a97321cdb700e887c2008d6d323685fe59f323088333059dc",
"network": "mailu",
"env-file": ["/var/lib/mailu/secret.env"],
"volumes": ["/services/mailu/data/webmail:/data"]},
{"id": "front", "type": "container", "name": "mailu-front",
"image": "mailu-nginx@sha256:0000000000000000000000000000000000000000000000000000000000000000",
"image": "ghcr.io/mailu/nginx@sha256:09f28ab6d36367fcacc7994f7021f132ac845bdc05f04bf80906102d11aaa057",
"network": "mailu",
"env-file": ["/var/lib/mailu/secret.env"],
"ports": ["25:25", "465:465", "587:587", "993:993", "7080:80"],
+1 -1
View File
@@ -28,7 +28,7 @@
{"id": "net", "type": "network", "name": "minio"},
{"id": "server", "type": "container", "name": "minio",
"image": "minio@sha256:0000000000000000000000000000000000000000000000000000000000000000",
"image": "minio/minio@sha256:aefec8a86702aff0b0dcfdd9284bd7ab7c5631cbf9be63275799e6edcb30dfa2",
"network": "minio",
"args": ["server", "/data", "--console-address", ":9001"],
"env-file": ["/var/lib/minio/root.env"],
+1 -1
View File
@@ -28,7 +28,7 @@
{"id": "grants", "type": "directory", "path": "/var/lib/objectstore/grants", "mode": "0700"},
{"id": "store", "type": "container", "name": "mesh-store",
"image": "minio/minio@sha256:0000000000000000000000000000000000000000000000000000000000000000",
"image": "minio/minio@sha256:aefec8a86702aff0b0dcfdd9284bd7ab7c5631cbf9be63275799e6edcb30dfa2",
"args": ["server", "/data"],
"env": {"MINIO_ROOT_USER": "meshroot"},
"ports": ["9000:9000"],
+1 -1
View File
@@ -15,7 +15,7 @@
{"id": "config", "type": "directory", "path": "/etc/photos", "mode": "0750"},
{"id": "app", "type": "container", "name": "photos",
"image": "photos@sha256:0000000000000000000000000000000000000000000000000000000000000000",
"image": "alpine@sha256:c64c687cbea9300178b30c95835354e34c4e4febc4badfe27102879de0483b5e",
"env": {
"PHOTOS_STORE": "/etc/photos/store.json",
"PHOTOS_STORE_SECRET_FILE": "/etc/photos/store.secret"
+1 -1
View File
@@ -30,7 +30,7 @@
{"id": "net", "type": "network", "name": "postgres"},
{"id": "server", "type": "container", "name": "postgres",
"image": "postgres@sha256:0000000000000000000000000000000000000000000000000000000000000000",
"image": "postgres@sha256:7456ef82e5f5bc43d997f4781bbd7c0d6389bff397564649a356e206ba473aee",
"network": "postgres",
"env": {"POSTGRES_USER": "postgres", "POSTGRES_DB": "postgres"},
"env-file": ["/var/lib/postgres/superuser.env"],
+29
View File
@@ -342,6 +342,9 @@ func (r Resolution) Declaration(with Rendering) ([]map[string]any, error) {
if err := boundInto(copied, known, m.Module); err != nil {
return nil, err
}
if err := pinned(copied, m.Module); err != nil {
return nil, err
}
copied["id"] = m.Module + "." + fmt.Sprint(resource["id"])
// A service saying what it reflects names resources within its own module, so those
// are prefixed too or they would point at nothing.
@@ -638,3 +641,29 @@ func withMeshNames(resources []map[string]any, names map[string]string) []map[st
}
return out
}
// pinned refuses an image that is not really pinned, on its way to a machine.
//
// **Here and not at parse** (novox/hq 04-ISSUES/025). A manifest in a repository names artifacts
// and the manifest the mesh holds names digests — they are deliberately not the same document, so
// a file awaiting a pin is legitimate exactly as the bundle's is. What must never happen is a
// placeholder reaching a machine, and this is the last moment before one does.
//
// The host checks only the *shape* of a reference, and cannot do more: verifying a digest exists
// means reaching a registry, which is the one thing a host must never have to do. So sixty-four
// zeros satisfies every gate in the system and stops on the machine at `docker pull` — which is
// how eighteen of them shipped across five modules that parse, resolve and compose cleanly.
func pinned(resource map[string]any, module string) error {
image, ok := resource["image"].(string)
if !ok {
return nil
}
_, digest, found := strings.Cut(image, "@")
if !found || strings.Trim(strings.TrimPrefix(digest, "sha256:"), "0") != "" {
return nil
}
return fmt.Errorf(
"%s would send %v to a machine pinned to a placeholder digest, which is never a real "+
"image — it would be fetched and fail there. Resolve the tag to a digest first",
module, resource["id"])
}