# Verifying a Crux release Every Crux release (tag `v*`) ships with a verifiable supply chain — threat ref **T.5** in [THREAT_MODEL.md](THREAT_MODEL.md): | Artifact | What proves it | |---|---| | Native release binaries (`corecruxd-*`, `crux-*`, `corecruxctl-*`, `crux-hook-*` for `linux-amd64` and both Darwin targets) | cosign keyless signature (`.sig` + `.pem` per file) + SLSA v1.0 provenance | | Cross Linux release binaries (the same four prefixes for `linux-musl-amd64` and `linux-arm64`) | cosign keyless signature; not yet included as SLSA subjects | | `RELEASE-MANIFEST-.txt` | signed sha256 list covering every file in the release package (licences, docs, content) | | `install.sh` | cosign keyless signature (`install.sh.sig` + `install.sh.pem`) and coverage by `RELEASE-MANIFEST-linux-amd64.txt` | | `crux-daemon-.cdx.json` | signed CycloneDX 1.6 SBOM of the workspace, per target | | `multiple.intoto.jsonl` | SLSA provenance for native-target binaries, SBOMs, and manifests, generated by the SLSA L3 builder | | Container image `ghcr.io/cuecrux/crux-daemon` | cosign keyless signature + CycloneDX SBOM attestation, both digest-addressed | All signatures are **keyless** (Sigstore): short-lived certificates bound to the GitHub Actions workflow identity, logged in the Rekor transparency log. There is no long-lived signing key that can leak — what you verify is "this exact artifact was built by the release workflow of `CueCrux/Crux` at this tag". Tooling: [`cosign`](https://docs.sigstore.dev/cosign/system_config/installation/) and [`slsa-verifier`](https://github.com/slsa-framework/slsa-verifier#installation). Set the tag you are verifying once: ```bash TAG=v0.5.0 # the release you downloaded REPO=CueCrux/Crux ``` ## 1. Verify a release binary Download the binary, its `.sig` and `.pem` from the GitHub Release, then: ```bash cosign verify-blob \ --certificate corecruxd-linux-amd64.pem \ --signature corecruxd-linux-amd64.sig \ --certificate-identity "https://github.com/${REPO}/.github/workflows/release.yml@refs/tags/${TAG}" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ corecruxd-linux-amd64 ``` Expected output: `Verified OK`. Repeat for `crux-`, `corecruxctl-`, `crux-hook-`, the SBOM and the manifest (same flags, different filenames). Before running the installer, verify `install.sh` with the same command and workflow identity, using `install.sh.pem` and `install.sh.sig`. ## 2. Verify the whole package via the signed manifest The licence/docs/content files are not individually signed; they are covered by the signed manifest: ```bash cosign verify-blob \ --certificate RELEASE-MANIFEST-linux-amd64.txt.pem \ --signature RELEASE-MANIFEST-linux-amd64.txt.sig \ --certificate-identity "https://github.com/${REPO}/.github/workflows/release.yml@refs/tags/${TAG}" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ RELEASE-MANIFEST-linux-amd64.txt sha256sum --check --ignore-missing RELEASE-MANIFEST-linux-amd64.txt ``` Manifest entries use the flat public release-asset filenames. Download the assets you want to verify into the same directory as the manifest; nested repository staging paths do not need to be recreated. The release workflow rejects duplicate basenames before publishing, so every checksum maps to one unambiguous public asset. The linux-amd64 manifest includes `install.sh`; the checksum command therefore provides a second, manifest-rooted check when both files are present. ## 3. Verify SLSA provenance `multiple.intoto.jsonl` (a release asset) attests that each native-target binary was built from this repository at this tag by the SLSA L3 trusted builder: ```bash slsa-verifier verify-artifact corecruxd-linux-amd64 \ --provenance-path multiple.intoto.jsonl \ --source-uri "github.com/${REPO}" \ --source-tag "${TAG}" ``` Expected output ends with `PASSED: SLSA verification passed`. The `linux-musl-amd64` and `linux-arm64` cross-build legs are cosign-signed but are not yet subjects in `multiple.intoto.jsonl`; do not claim a SLSA pass for those files. Their signatures and signed release manifests remain verifiable with steps 1 and 2. ## 4. Verify the container image Always verify by digest, never by floating tag: ```bash IMAGE=ghcr.io/cuecrux/crux-daemon:${TAG#v} DIGEST_REF=$(cosign triangulate --type digest "${IMAGE}" 2>/dev/null || docker buildx imagetools inspect "${IMAGE}" --format '{{json .Manifest.Digest}}' | xargs -I{} echo "ghcr.io/cuecrux/crux-daemon@{}") cosign verify \ --certificate-identity "https://github.com/${REPO}/.github/workflows/docker.yml@refs/tags/${TAG}" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ "${DIGEST_REF}" ``` And the SBOM attestation attached to the image: ```bash cosign verify-attestation \ --type cyclonedx \ --certificate-identity "https://github.com/${REPO}/.github/workflows/docker.yml@refs/tags/${TAG}" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ "${DIGEST_REF}" \ | jq -r '.payload' | base64 -d | jq '.predicate.metadata' ``` Note: images built from `main` merges (the `edge` tag) are signed with identity `…/docker.yml@refs/heads/main` instead of the tag ref. ## 5. Inspect the SBOM ```bash jq '.components | length' crux-daemon-linux-amd64.cdx.json # dependency count jq -r '.components[] | "\(.name) \(.version)"' crux-daemon-linux-amd64.cdx.json | sort ``` Feed it to any CycloneDX-aware scanner (`trivy sbom crux-daemon-linux-amd64.cdx.json`, Dependency-Track, etc.) to re-scan the exact dependency set that shipped. ## What failure looks like - A tampered binary fails step 1 with `Error: searching log query matched zero entries` or a hash mismatch. - A binary rebuilt outside CI fails step 3 (`FAILED: SLSA verification failed`) because no provenance exists for its hash. - An image re-pushed by an attacker with `packages: write` fails step 4 — they cannot mint a certificate for the `docker.yml` workflow identity. If any verification fails, do not run the artifact; report it via [SECURITY.md](../SECURITY.md).