Skip to content

Verify a Candle release

Every Candle CLI release on https://github.com/candledottv/agentic/releases is built and signed by that repository’s release.yaml workflow. Nothing else signs releases; there is no long-lived key to steal. Three checks: the checksum, then two that check the signature and the exact release tag.

Terminal window
curl -fsSLO https://github.com/candledottv/agentic/releases/download/cli-v0.11.14/SHA256SUMS
curl -fsSLO https://github.com/candledottv/agentic/releases/download/cli-v0.11.14/candle-darwin-arm64
grep candle-darwin-arm64 SHA256SUMS | shasum -a 256 -c
Terminal window
gh attestation verify candle-darwin-arm64 --repo candledottv/agentic \
--cert-identity https://github.com/candledottv/agentic/.github/workflows/release.yaml@refs/tags/cli-v0.11.14

--cert-identity is the signing certificate’s subject: the workflow file and the release tag in one string. Pinning it exactly pins both, so an attestation the same workflow minted for a different tag does not verify. Change the tag to the release you downloaded. This is the check install.sh runs when it verifies with gh. Pass the flag on its own: gh treats its identity flags (--cert-identity, its regex form and the two signer flags) as mutually exclusive, and the signer-workflow flag alone would accept any tag’s attestation.

Terminal window
curl -fsSLO https://github.com/candledottv/agentic/releases/download/cli-v0.11.14/candle-darwin-arm64.sigstore.json
cosign verify-blob --new-bundle-format --bundle candle-darwin-arm64.sigstore.json \
--certificate-identity https://github.com/candledottv/agentic/.github/workflows/release.yaml@refs/tags/cli-v0.11.14 \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
candle-darwin-arm64

A signature proves the file was built by that workflow, from the commit the provenance names, for that tag. As in check 2, --certificate-identity pins the exact tag: change it to the release you downloaded. The regex form, --certificate-identity-regexp '^https://github.com/candledottv/agentic/\.github/workflows/release\.yaml@refs/tags/cli-v', is weaker: it accepts a signature minted for any cli-v tag, so an older signed binary would pass for a newer download. candle update runs this check in process, pinned to the exact version, before it replaces the binary; the installer runs it with the exact identity when cosign or gh is available, and always runs check 1.

--new-bundle-format says “expect a Sigstore bundle”, which is what Candle releases from 0.6.1 onward are signed as. Cosign also reads an older bundle format of its own, so leaving the flag off would quietly accept a shape that candle verify cannot read. It needs cosign 2.2 or newer. The one release signed the old way, cli-v0.6.0, verifies with cosign and gh but not with candle verify; run check 1 or check 2 on it instead.

No cosign or gh to install? If any working candle binary is already on the machine (an older release, one installed via Homebrew, or one built from source), it carries the same trusted root compiled in and can run the same check on its own, no network call and nothing else installed:

Terminal window
curl -fsSLO https://github.com/candledottv/agentic/releases/download/cli-v0.11.14/latest.json
candle verify candle-darwin-arm64 --bundle candle-darwin-arm64.sigstore.json

latest.json sits beside the other assets in every release, so downloading it alongside the bundle is what lets candle verify resolve the expected identity on its own; pass --identity instead if you would rather not fetch it (it is required when no latest.json is beside the bundle). --issuer overrides the expected OIDC issuer, https://token.actions.githubusercontent.com by default. This is the same call candle update makes in process before it replaces itself.

Each release also ships candle-<version>-<os>-<arch>.tar.gz for the four platforms, holding candle, candle-fido2 and LICENSE; Homebrew installs from these. They are listed in SHA256SUMS and carry build provenance, but they have no .sigstore.json of their own, so check 1 and check 2 apply to them and check 3 does not. Once unpacked, the candle and candle-fido2 inside are the same files as the signed binaries, and their SHA256SUMS lines match.

curl -fsSL https://candle.tv/install.sh | bash is the friendly line. The same script is a signed asset of every release, so you can fetch it pinned to a tag and check it before running it:

Terminal window
curl -fsSLO https://github.com/candledottv/agentic/releases/download/cli-v0.11.14/install.sh
curl -fsSLO https://github.com/candledottv/agentic/releases/download/cli-v0.11.14/install.sh.sigstore.json
cosign verify-blob --new-bundle-format --bundle install.sh.sigstore.json \
--certificate-identity https://github.com/candledottv/agentic/.github/workflows/release.yaml@refs/tags/cli-v0.11.14 \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
install.sh
bash install.sh

or from the source tree at the tag: curl -fsSL https://raw.githubusercontent.com/candledottv/agentic/cli-v0.11.14/install.sh. The script’s SHA-256 for each release is the install.sh line of that release’s SHA256SUMS.

The script refuses to install without a verifier (cosign, or a logged-in gh), and nothing is installed. On a host where you cannot or do not want to add one, verify on another machine and carry the files across, as in the next section. CANDLE_INSTALL_ALLOW_UNSIGNED=1 exists as an explicit opt-out: it installs on the checksum alone, which proves only that the file matches the list downloaded beside it, and prints the command to verify afterwards.

A bot box, a locked-down server, or anything else with no cosign, no gh and no candle yet. Do the signature check on a machine you trust, then move the verified files to the host and check only that they arrived intact.

On the trusted machine, which has cosign, a logged-in gh, or a working candle. Pick the host’s platform (linux-x64, linux-arm64, darwin-arm64 or darwin-x64), not this machine’s:

Terminal window
tag=cli-v0.11.14
asset=candle-linux-x64
base=https://github.com/candledottv/agentic/releases/download/$tag
curl -fsSLO $base/$asset
curl -fsSLO $base/$asset.sigstore.json
curl -fsSLO $base/SHA256SUMS
curl -fsSLO $base/SHA256SUMS.sigstore.json
curl -fsSLO $base/latest.json
candle verify $asset --bundle $asset.sigstore.json # or check 2 or check 3 above
candle verify SHA256SUMS --bundle SHA256SUMS.sigstore.json
grep " $asset\$" SHA256SUMS | shasum -a 256 -c

Both signatures must verify and the checksum must print OK before anything moves. Then copy the binary, SHA256SUMS and the two bundles to the host:

Terminal window
scp candle-linux-x64 candle-linux-x64.sigstore.json SHA256SUMS SHA256SUMS.sigstore.json bot-box:

On the host, check the binary against the line of the SHA256SUMS you verified, then install it:

Terminal window
grep ' candle-linux-x64$' SHA256SUMS | sha256sum -c # macOS: shasum -a 256 -c
mkdir -p ~/.local/bin && install -m 755 candle-linux-x64 ~/.local/bin/candle
candle --version

sha256sum -c must print candle-linux-x64: OK. Anything else means the copy is not the file you verified: delete it and copy again. Once candle is on the host it is its own verifier: candle update checks each new release’s signature in process before it replaces itself, and candle verify checks any other asset, with nothing else installed.

The host does not need candle-fido2 unless a security key will be plugged into it; a bot box never needs it. If it does, carry candle-fido2-<platform> the same way, verified with its own .sigstore.json and its SHA256SUMS line, and install it beside candle.

Every signature is recorded in Sigstore’s public log (Rekor). To list the entries for a release, take the SHA-256 of its SHA256SUMS from the release page and run rekor-cli search --sha <sha256>; a release has exactly one.