Wheelhouse docs

check-images.py#

Reads agent/catalog.json and answers three questions about the images in it: are the references well formed, do they resolve at the registry, and are any of them pinned to a digest. It can also do the pinning.

bash
scripts/check-images.py agent/catalog.json --offline        # shape only, no network
scripts/check-images.py agent/catalog.json                  # + reachability
scripts/check-images.py agent/catalog.json --require-digest # fail unless all are pinned
scripts/check-images.py agent/catalog.json --pin            # resolve tags, rewrite the file

Standard library only, no registry credentials, and no dependency on a container runtime.

Options#

OptionMeaning
catalogThe catalogue file. Required.
--offlineShape check only. Never touches the network.
--require-digestExit non-zero unless every image is pinned by digest.
--pinResolve each tag to a digest and rewrite the catalogue. Needs the network; refused with --offline.

Builtin entries and entries with no image are skipped everywhere.

Exit 0 when nothing is wrong, 1 otherwise.

Why pinning matters here#

An app install is sold as a change you review and can roll back. A floating :latest breaks that promise quietly: the diff you approved names a tag, and the bytes behind that tag change without a commit, a release or an audit entry. name:tag@sha256:… keeps the tag for humans and makes the bytes part of the reviewed change.

The shape check#

The reference grammar is the one the router's container runtime accepts, and the one the agent stages verbatim:

host/repo[:tag][@sha256:<64 hex>]
ComplaintMeans
not a fully-qualified referenceIt does not match the grammar at all.
host 'x' does not look like a registryNo dot and no port in the host part.
registry named twice in the referenceThe repository starts with a registry name — docker.io/docker.io/….
no tag and no digestNothing says which version.

--offline is the check CI can run on every push: no network, no credentials, so it cannot flake, and it catches the malformed references that used to reach the catalogue.

The reachability check#

Without --offline, each image is HEADed at its registry — anonymously, eight at a time. It fetches a pull token (docker.io, ghcr.io and lscr.io all speak the same token endpoint), then requests the manifest, accepting both Docker and OCI manifest and index types.

ok  docker.io/adguard/adguardhome:latest
BAD ghcr.io/example/thing:latest  HTTP 404

A digest, when present, is the authoritative reference; the tag is decoration.

The summary#

Every run ends with a count:

36 image(s); 0 pinned by digest, 36 floating.
Floating tags mean an install is not reproducible and not reviewable. Pin them with
--pin, then make --require-digest a gating check.
    adguard: docker.io/adguard/adguardhome:latest
    …

Floating tags are not an error by default. Pinning is a catalogue change somebody has to review, and failing every build until it lands would only teach people to ignore the output. --require-digest is how you make it a gate once it has landed.

--pin#

Resolves each unpinned image's tag to the digest the registry currently serves and rewrites the catalogue in place:

pin adguard  docker.io/adguard/adguardhome:latest -> docker.io/adguard/adguardhome:latest@sha256:…

rewrote agent/catalog.json: 36 image(s) pinned. Read the diff, then run `go test ./...` in agent/.

The tag is kept alongside the digest, so the entry is still readable by a person. An image whose digest cannot be resolved is reported and left alone.

It is a deliberate, online, human-run step that produces a diff somebody reads — deliberately not something CI does on its own.

Running it#

bash
# Every push, in CI:
python3 scripts/check-images.py agent/catalog.json --offline

# Occasionally, by hand:
python3 scripts/check-images.py agent/catalog.json

See also#

Checked against#

scripts/check-images.py (REF, parse, lint, head_manifest, images_of, main), agent/catalog.json, agent/apps.go (imageRef, validImageRef), docs/apps.md "Image pinning", README.md.

Updated 2026-09-02 tools catalogue images supply-chain