Image maintenance evidence¶
container.yaml declares each image's lifecycle state, intended admission, review date, support boundary, and required runtime profiles. production is an intended policy state. It does not by itself prove registry availability, native runtime coverage, or vulnerability acceptance. The checked-in image catalogue is generated from metadata alone, so those evidence columns are unknown until matching records are supplied.
Regenerate the catalogue with:
uv run --frozen python -m scripts.maintenance \
--json-output docs/image-catalogue.json \
--markdown-output docs/image-catalogue.md
To include evidence from a run, add --evidence-dir DIR containing unique <name>-build-result.json files and <name>-<arch>-runtime-evidence.json files. Runtime status can be passed, failed, or skipped; absent evidence is unknown, and skipped checks remain visible even under a passed profile. A build result must match metadata's image, version, source revision, index digest, and complete architecture digest map. buildSucceededAt is the build completion time, distinct from the source commit time.
Registry observations are optional. Supply --registry-observations FILE with a JSON object keyed by metadata image name. Each entry must contain image, digest, reference, and observedAt. The reference must be the immutable image@sha256:... corresponding to the recorded build index digest; observedAt needs an ISO timestamp with timezone. An observation reports what was checked at that time. It is not a guarantee that a mutable tag still points there.
The Grype comparison action accepts a built OCI archive, its source revision, the image name and architecture, a maintained baseline tag of the same image, declared admission, and an output directory. It verifies the archive's image manifest/configuration digest, architecture and OCI source revision. For the baseline it resolves the tag to a registry index, selects exactly one architecture manifest, verifies that manifest by digest, and checks its configuration architecture. Both scans use pinned immutable sources. A candidate archive manifest digest is not the tar file's SHA-256, and can differ from the architecture manifest digest after publication rewrites transport metadata. The report preserves the prepublication source identity separately from the final published index and architecture digests.
The action installs Grype v0.119.0 from release assets whose AMD64 and ARM64 SHA-256 values are pinned alongside the version. Renovate tracks each release tag with its archive checksum and groups both architectures for review; CI requires their release tags to match. See the provider decision and local reproduction guide. It writes raw Grype JSON, scanner/database metadata, verified source identities, a comparison report, and an Actions summary. scripts.vulnerability compares vulnerability ID and package type/name, reporting new, fixed and remaining findings. New High/Critical findings with a known fixed version block production admission unless an unexpired scoped exception applies. Isolated-test findings remain visible but do not grant production admission. A genuinely absent latest baseline is confirmed by an authenticated GitHub Packages 404; all candidate findings are then new and production gates still apply. Registry authorization and network errors fail closed. A wrong architecture, source digest, or Grype source identity also fails closed.
For local triage, follow the runnable scan reproduction and compare raw Grype findings with the policy report. A policy exception belongs in vulnerability-exceptions.json and requires the exact image, architecture, candidate manifest digest, vulnerability ID, package type/name, owner, reason, and ISO expiry date. Exceptions expire automatically; changing a candidate digest requires a new review. The policy file starts empty.
On release finalization, scripts.release_evidence creates an independently addressable GitHub Release at <imageName>/maintenance/<runId>-<runAttempt>. It creates the release as a draft at the maintenance build's source revision, uploads and verifies the complete evidence asset, and only then publishes the release. GitHub release immutability stays enabled. These maintenance releases are not selected as the repository's latest release.
The existing <imageName>/v<version> release continues to identify the original source version. Each retained-version rebuild gets its own maintenance release instead of trying to append an asset to an already published immutable version release. The evidence record preserves releaseTag and releaseOriginRevision for the source version, and separately records evidenceReleaseTag, evidenceReleaseRevision, and buildSourceRevision for this publication. The asset filename contains the published index digest and run identity. Consumers must match its image, index digest, architecture digests, source revision, and run identity to the publication they intend to use.
An interrupted draft/upload/publish sequence can be retried. Existing draft assets must match the expected evidence, and an already published maintenance release is accepted only after its identity and evidence are verified. A mismatch is an error; do not delete or replace an immutable release to make the retry pass.
Finalization verifies each published architecture manifest and records its configuration digest. When native runtime or scan evidence is supplied, that configuration digest must match the published one even if archive and registry manifest digests differ. Publication requires one passed native runtime record and one passed vulnerability report for every declared architecture, bound to the same archive configuration, source revision, and workflow run. Metadata-disabled nested runtime probes and the documented notify_push integration limit are explicit exemptions; other skipped probes do not admit publication. Finalization downloads those records and requires them in the durable release asset. GitHub Actions artifacts have shorter retention and are an operational handoff, not the durable release record.