Verifying a Crux release

July 22, 2026 · View on GitHub

Every Crux release (tag v*) ships with a verifiable supply chain — threat ref T.5 in THREAT_MODEL.md:

ArtifactWhat 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-<target>.txtsigned sha256 list covering every file in the release package (licences, docs, content)
install.shcosign keyless signature (install.sh.sig + install.sh.pem) and coverage by RELEASE-MANIFEST-linux-amd64.txt
crux-daemon-<target>.cdx.jsonsigned CycloneDX 1.6 SBOM of the workspace, per target
multiple.intoto.jsonlSLSA provenance for native-target binaries, SBOMs, and manifests, generated by the SLSA L3 builder
Container image ghcr.io/cuecrux/crux-daemoncosign 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 and slsa-verifier. Set the tag you are verifying once:

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:

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-<target>, corecruxctl-<target>, crux-hook-<target>, 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:

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:

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:

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:

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

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.