--- title: Verifying Published Images sidebarTitle: Verify images description: How to verify signatures, provenance, SBOMs, and CVE triage for Sencho Docker images. --- Every Sencho **release** image is published to two registries with identical content: Docker Hub (`saelix/sencho`) and GitHub Container Registry (`ghcr.io/studio-saelix/sencho`). Release tags are signed and carry verifiable supply-chain artifacts on both registries. The `cosign verify` examples below use the Docker Hub path; substitute `ghcr.io/studio-saelix/sencho:` to verify the GHCR copy. Pre-merge preview tags (`pr-*`, `preview-*` on Docker Hub) are maintainer-published tester builds. They are **not** cosign-signed and have no SBOM or VEX attestations. Use them only for feature validation before merge; pin production to a release semver tag and verify signatures as below. ## Prerequisites Install the following tools once: ```bash # cosign (Sigstore) brew install cosign # macOS # or: https://github.com/sigstore/cosign/releases # syft (optional, for inspecting SBOM content) brew install syft # or: https://github.com/anchore/syft/releases # Trivy (optional, for local vulnerability scan with VEX) brew install trivy # or: https://github.com/aquasecurity/trivy/releases ``` ## Verify the image signature Every **release** tag is signed with cosign keyless signing via GitHub Actions OIDC. No private key is stored anywhere; the signing identity is bound to the GitHub Actions workflow that published the image. Preview tags (`pr-*`, `preview-*`) are not signed. ```bash cosign verify saelix/sencho: \ --certificate-identity-regexp "https://github.com/studio-saelix/sencho/.*" \ --certificate-oidc-issuer https://token.actions.githubusercontent.com ``` A successful verification prints the verified payload and exits 0. The Rekor transparency log entry is included in the output; you can also query it directly: ```bash cosign tree saelix/sencho: ``` ## Verify SLSA provenance BuildKit produces an SLSA v1 provenance attestation during every multi-arch build. Retrieve and verify it: ```bash cosign verify-attestation \ --type slsaprovenance \ --certificate-identity-regexp "https://github.com/studio-saelix/sencho/.*" \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ saelix/sencho: | jq -r '.payload' | base64 -d | jq ``` ## Retrieve the CycloneDX SBOM ```bash cosign verify-attestation \ --type cyclonedx \ --certificate-identity-regexp "https://github.com/studio-saelix/sencho/.*" \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ saelix/sencho: | jq -r '.payload' | base64 -d | jq ``` Alternatively, download `sbom.cdx.json` directly from the GitHub Release assets for the matching version. ## Retrieve the SPDX SBOM ```bash cosign verify-attestation \ --type spdxjson \ --certificate-identity-regexp "https://github.com/studio-saelix/sencho/.*" \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ saelix/sencho: | jq -r '.payload' | base64 -d | jq ``` `sbom.spdx.json` is also available as a GitHub Release asset. ## Retrieve the VEX document The OpenVEX document explains Sencho's triage decisions for any CVEs that appear in vendored dependencies but are not reachable via Sencho's usage of those dependencies: ```bash cosign verify-attestation \ --type openvex \ --certificate-identity-regexp "https://github.com/studio-saelix/sencho/.*" \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ saelix/sencho: | jq -r '.payload' | base64 -d | jq ``` `sencho.openvex.json` is also available as a GitHub Release asset and at `security/vex/sencho.openvex.json` in the source repository. ## Run a local vulnerability scan with VEX applied To reproduce the same scan that gates every release: ```bash # Download the VEX document curl -Lo sencho.openvex.json \ https://github.com/studio-saelix/sencho/releases/latest/download/sencho.openvex.json # Scan with VEX applied (exit 1 if any unresolved HIGH/CRITICAL CVE) trivy image \ --vex sencho.openvex.json \ --severity HIGH,CRITICAL \ --exit-code 1 \ saelix/sencho: ``` A clean result means no unresolved HIGH or CRITICAL CVEs. CVEs listed as `not_affected` in the VEX document are suppressed with their justification visible in the VEX file. Trivy 0.36.0 or later is required for `--vex` flag support. ## Available tags ### Release tags Each release publishes two moving tags plus one immutable tag, so you can choose how aggressively you want to track updates: | Tag | Example | Updates on | |---|---|---| | `latest` | `saelix/sencho:latest` | Every release | | `X.Y` | `saelix/sencho:0.42` | Every patch release in the `0.42.x` line | | `X.Y.Z` | `saelix/sencho:0.42.7` | Never (immutable) | For production, pin to `X.Y.Z` or `X.Y` and verify the signature on every pull. For staging or development, `latest` is fine. ### Integration tag (post-merge, pre-release) Every push to `main` publishes the current integration build so you can pull and smoke-test the exact code queued for the next release before it is ever tagged. This tag is GHCR-only, published to a separate package from the release image, and carries no signature, SBOM, or VEX attestation. | Tag | Example | Updates on | |---|---|---| | `dev` | `ghcr.io/studio-saelix/sencho-dev:dev` | Every push to `main` | | `dev-` | `ghcr.io/studio-saelix/sencho-dev:dev-a1b2c3d` | Never (immutable per build) | The `:dev` image is not for production. It is unsigned, is never published to Docker Hub, and does not carry the SBOM, provenance, or VEX attestations described above. ### Preview tags (pre-merge only) Maintainers publish these from open PRs for external validation. They are unsigned and not for production. | Tag | Example | Updates on | |---|---|---| | `pr-` | `saelix/sencho:pr-1526` | Each re-run of the preview workflow for that PR | | `preview-` | `saelix/sencho:preview-abc1234` | Never (immutable per build) |