Skip to main content

Verify a release offline (no platform needed)

Every CI/lock release publishes its proof alongside the binary. You can verify a downloaded cilock against the signed release policy with no TestifySec platform, tenant, or Archivista access — only the files on cilock.dev and the cilock binary itself. This is the path for air-gapped builds, zero-trust auditors, and anyone who doesn't have (or want) a platform login.

Already have a platform session? The one-liner on the Download page (cilock verify … --platform-url <host> --enable-archivista) pulls the evidence online and is simpler. This page is the offline path — it never talks to a platform.

What the release publishes for offline verify

For each release version the publisher uploads, under https://cilock.dev/dl/<version>/:

FileWhat it is
cilock-<version>-<os>-<arch>.tar.gzthe binary archive
cilock-<version>-<os>-<arch>.source-git.att.jsonthe source-git step's signed DSSE envelope
cilock-<version>-<os>-<arch>.build.att.jsonthe build step's signed DSSE envelope
fulcio-roots.pemthe platform Fulcio CA + Root CA the signing certs chain to
tsa-chain.pemthe RFC 3161 TSA cert chain
policy/release-policy.json (root key)the signed release policy

Both envelopes are needed — the policy declares a source-git step and a build step, so an offline verify must supply the evidence for both.

The machine-readable index of all of this is the per-version verification block in /dl/manifest.json:

{
"version": "v3.0.0",
"verification": {
"policy": "policy/release-policy.json",
"fulcioRoots": "v3.0.0/fulcio-roots.pem",
"tsaChain": "v3.0.0/tsa-chain.pem",
"attestations": [
{
"binary": "cilock-3.0.0-linux-amd64.tar.gz",
"os": "linux", "arch": "amd64",
"envelopes": [
{"step": "source-git", "file": "v3.0.0/cilock-3.0.0-linux-amd64.source-git.att.json", "sha256": "…"},
{"step": "build", "file": "v3.0.0/cilock-3.0.0-linux-amd64.build.att.json", "sha256": "…"}
]
}
]
}
}

Step 1 — download the binary + verification material

Pick your version and platform. For v3.0.0 on linux-amd64:

VERSION=3.0.0
PLAT=linux-amd64
BASE="https://cilock.dev/dl/v${VERSION}"

curl -fsSLO "${BASE}/cilock-${VERSION}-${PLAT}.tar.gz"
curl -fsSLO "${BASE}/cilock-${VERSION}-${PLAT}.source-git.att.json"
curl -fsSLO "${BASE}/cilock-${VERSION}-${PLAT}.build.att.json"
curl -fsSLO "${BASE}/fulcio-roots.pem"
curl -fsSLO "${BASE}/tsa-chain.pem"
curl -fsSL "https://cilock.dev/policy/release-policy.json" -o release-policy.json

tar xzf "cilock-${VERSION}-${PLAT}.tar.gz" cilock

You don't even need a pre-existing cilock to verify — the binary you just extracted can verify itself. (If you want an independent verifier, build one from rookery or grab another release.)

Step 2 — run the offline verify

The key flag is --platform-url "". It tells cilock to skip all platform access — no discovery doc, no Archivista lookup, no platform-derived timestamp verifier. Trust comes only from the files you downloaded:

cilock verify ./cilock -p release-policy.json \
--attestations cilock-${VERSION}-${PLAT}.source-git.att.json,cilock-${VERSION}-${PLAT}.build.att.json \
--policy-ca-roots fulcio-roots.pem \
--policy-timestamp-servers tsa-chain.pem \
--policy-emails [email protected] \
--policy-fulcio-oidc-issuer https://platform.testifysec.com/fulcio/oidc \
--platform-url ""

What each flag does:

FlagRole
-p / --policythe signed release policy the binary must satisfy
-a / --attestationsthe two per-step DSSE envelopes (comma-separated — one file per envelope)
--policy-ca-rootsfulcio-roots.pem — the CA the policy + attestation signing certs chain to. The bundle carries the Fulcio CA and the self-signed Root CA; cilock loads every cert and anchors on the self-signed Root.
--policy-timestamp-serverstsa-chain.pem — validates the RFC 3161 timestamps so the short-lived (~10 min) signing certs verify as of signing time, long after they expire
--policy-emailspins the keyless policy-signer identity so a policy signed by some other Fulcio cert can't be substituted
--policy-fulcio-oidc-issuerthe platform Fulcio OIDC issuer that minted the signer cert
--platform-url ""opt out of all platform access — this is what makes it offline

A passing run exits 0 and prints Verification succeeded. Branch on the exit code, never on grepped output:

STEM="cilock-${VERSION}-${PLAT}"
if cilock verify ./cilock -p release-policy.json \
--attestations "${STEM}.source-git.att.json,${STEM}.build.att.json" \
--policy-ca-roots fulcio-roots.pem \
--policy-timestamp-servers tsa-chain.pem \
--policy-emails [email protected] \
--policy-fulcio-oidc-issuer https://platform.testifysec.com/fulcio/oidc \
--platform-url ""; then
echo "release verified offline"
else
echo "VERIFY FAILED — do not run this binary" >&2
exit 1
fi

Prefer --offline if you like a named flag — it's an exact alias for --platform-url "".

What this proves

  • The binary's SHA-256 matches a subject in the build step's signed product attestation — you have the exact bytes the release pipeline produced.
  • Both steps' DSSE signatures chain to the TestifySec Platform Fulcio, anchored on the Platform Root CA you supplied.
  • The RFC 3161 timestamps place each signature inside its signing cert's validity window, so the proof holds years later even though Fulcio certs live ~10 minutes.
  • The signed release policy itself was signed by the pinned release-authority identity (--policy-emails), not some other cert.

Nothing in this chain depends on a TestifySec platform being reachable, or on you having an account. The proof is self-contained.

Troubleshooting

SymptomLikely causeFix
no passed collections presentthe binary doesn't match the envelopes (subject-digest mismatch) — usually mixed files from different releasesre-download the tarball and both .att.json files from the same <version>
failed to build chain / cert chain errorswrong or truncated fulcio-roots.pemre-download fulcio-roots.pem for this version; it must contain both the Fulcio CA and the Root CA
timestamp / policy expired-style errorsmissing or wrong tsa-chain.pemre-download tsa-chain.pem; without it the expired signing certs can't be validated as-of signing time
functionary mismatchthe policy expected a different signer identityconfirm --policy-emails / --policy-fulcio-oidc-issuer match the values published for this release (see the manifest / Download page)

See also