TRUST AND VERIFICATION
HOW TO VERIFY A VERITY IMAGE
This page is for the person who has to decide whether to trust an image pulled from
verity.supply. It describes what the publication gate actually checks, how
images are signed, how the SBOM and build provenance are produced, and gives runnable
commands to verify all three yourself. Every claim below comes from
docs/POLICY.md, docs/COMPLIANCE_AUDIT.md, and
docs/catalog.schema.json in this repository. Where this page and the raw
data disagree, trust the raw data at catalog.json.
PUBLICATION GATE
ZERO FIXABLE FINDINGS, NOT ZERO FINDINGS
The number on a catalog row and the pass/fail decision are two different things.
Grype scans every build candidate before it is pushed to a registry and records every known candidate vulnerability it finds, at any severity, on any track. Unfixed findings are not discarded: they remain in the scan report and in the published catalog entry. The gate itself only inspects one number, the count of findings that have a fix available. An image fails if that count is anything other than zero. A patched image may publish only when its final scan also shows zero fixable findings; wolfi images are held to the same zero-fixable bar. No image is admitted on the basis of improvement alone: a lower vulnerability count than the upstream base is evidence for diagnosis, not an exception to the gate.
That is why a catalog row can legitimately show a nonzero finding count and still have
passed the gate. A row reads 0 FINDINGS when the recorded scan contains no
findings of any kind. Otherwise it shows the worst recorded severity and total count,
such as HIGH 6; expanding the row reveals the full ALL FINDINGS
breakdown by severity. Every published image satisfies the same rule: zero reported
findings have an available fix. A nonzero count does not fail this publication gate; it
means vulnerabilities were detected but none were fixable at scan time, so none blocked
the release. The image is not free of known vulnerabilities.
Two scopes are worth separating too. The gate is evaluated once, against the Grype vulnerability database as it existed at scan time, immediately before publication. After publication, a separate monitoring workflow stores each platform SBOM once and compares its components against incremental OSV updates; if a fix later becomes available for something already published, that workflow opens or updates a tracking issue for the affected image version. It does not pull image layers, rescan the image, rebuild it, or alter the published catalog entry. The count in the catalog is therefore a statement about the scan performed at publish time, not a live, continuously reevaluated guarantee.
SIGNING
KEYLESS COSIGN SIGNATURES
Every image index is signed by digest. There is no long-lived private key to steal.
Signing uses cosign's keyless flow: the build workflow authenticates to Fulcio with
GitHub's short-lived OIDC identity and signs with a certificate that expires almost
immediately, rather than a stored private key. The workflow's only credential is the
ambient GITHUB_TOKEN; it requires no personal access token and no stored
signing secret. Verification checks the certificate's identity and issuer instead of a
public key.
Use Cosign 3.0.6 or newer. Older verifiers, including 2.6.1, fail against these signatures and attestations because of an OCI storage-format difference, not because the artifacts are missing.
SBOM
SPDX JSON, ONE PER PLATFORM
Every published digest carries a software bill of materials in a single format.
SPDX JSON is the only SBOM format the catalog produces; there is no CycloneDX
equivalent. Wolfi images use apko's native SPDX output. Patched images use Syft, run
against the final patched image rather than the pinned upstream. Trivy is used only to
produce the Copacetic patch report for patched images and never generates an SBOM. One
complete SBOM is attached to each platform's image digest as an OCI attestation, signed
the same way the image itself is signed, and can be verified or downloaded directly from
the registry. Melange-backed Wolfi images additionally retain package-level provenance
and a metadata subpackage containing the resolved go.mod and
go.sum used by the build.
BUILD PROVENANCE
SLSA PROVENANCE FOR EVERY DIGEST
Provenance ties a published digest back to the workflow run that built it.
Every pushed digest receives SLSA build provenance from
actions/attest-build-provenance plus a GitHub artifact attestation. Wolfi
builds use and upload the reviewed, checked-in apko lock as a build input. Melange-backed
builds are the exception: because their package signature is ephemeral, each run
produces a build-specific lock, which is uploaded together with the recipe, package
provenance, and resolved dependency metadata. Patched builds instead use the checked-in
source digest and record it in immutable build metadata. In every case, upstream or Wolfi
package updates require a pull request that changes the pin, recipe, or lock, so the
inputs behind a given provenance statement were reviewed before the build ran.
WORKED EXAMPLE
VERIFY AN IMAGE IN THREE COMMANDS
Substitute the digest from the catalog row you actually pulled.
Every row in the catalog exposes these same three commands, already filled in with that
row's registry reference. The example below uses caddy, one of the images in
the Wolfi track, pulled from verity.supply instead of a mutable tag:
docker pull verity.supply/caddy@sha256:DIGESTcosign verify --certificate-identity 'https://github.com/tektum/verity-images/.github/workflows/build.yaml@refs/heads/main' --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' verity.supply/caddy@sha256:DIGEST
cosign verify-attestation --type spdxjson --certificate-identity 'https://github.com/tektum/verity-images/.github/workflows/build.yaml@refs/heads/main' --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' verity.supply/caddy@sha256:DIGEST
gh attestation verify oci://verity.supply/caddy@sha256:DIGEST --repo tektum/verity-images
The signature command fails closed if the certificate identity or issuer does not match
exactly, so a signature produced by any workflow other than
build.yaml on refs/heads/main is rejected rather than silently
accepted. Pin the digest, not the tag: tags such as :latest or
:<version> are mutable discovery aids and can move.
SOURCE OF TRUTH
WHERE THE RAW DATA LIVES
The catalog UI is a rendering of these two files. Nothing on this site is authoritative on its own.
CATALOG.JSON HOLDS EVERY IMAGE, DIGEST, SCAN RESULT, AND VERIFICATION COMMAND.
CATALOG.SCHEMA.JSON DEFINES THE SHAPE EVERY ENTRY MUST MATCH, INCLUDING THE ZERO-FIXABLE POLICY, SBOM FORMAT, AND SIGNING IDENTITY.
OPEN CATALOG.JSON OPEN CATALOG.SCHEMA.JSON
The schema fixes several values as constants rather than free text:
schemaVersion is 2, policy.fixableVulnerabilitiesAllowed
is 0, policy.sbomFormat is SPDX-JSON,
policy.cosignMinimumVersion is 3.0.6, and every per-image
scan object must report exactly 0 fixable findings, whatever its
total finding count is. Each image entry also carries its own
verification.signature, verification.sbom, and
verification.provenance commands, generated directly from that image's
registry reference rather than typed by hand.