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:
| 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-<target>.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-<target>.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
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 entriesor 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: writefails step 4 — they cannot mint a certificate for thedocker.ymlworkflow identity.
If any verification fails, do not run the artifact; report it via SECURITY.md.