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 fulcio-roots.pem/tsa-chain.pem files are published for cross-checking and for verifiers without embedded trust. When used as trust anchors they must come from a channel you trust independently of cilock.dev: roots fetched from the same origin as the binary cannot authenticate that origin's output — a compromised origin would simply publish roots matching its forged evidence. The recipe below instead anchors on the roots embedded in your trusted verifier.

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 -fsSL "https://cilock.dev/policy/release-policy.json" -o release-policy.json

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

Do not run the binary you just extracted as its own verifier — a compromised origin can ship one that simply reports success, so a self-run is at most a functional smoke check, never provenance. Verify with a cilock you already trust: a prior install, a build from rookery, or another channel. A release-built cilock embeds the platform trust roots, so with a trusted one the root flags drop away.

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. The downloaded files supply only the evidence; the trust anchors are the platform Fulcio + TSA roots embedded in your trusted verifier at release-build time (inspect them with cilock version):

TRUSTED_CILOCK=/path/to/a/cilock/you/already/trust # a prior install — never ./cilock itself
"$TRUSTED_CILOCK" verify ./cilock -p release-policy.json \
--attestations cilock-${VERSION}-${PLAT}.source-git.att.json,cilock-${VERSION}-${PLAT}.build.att.json \
--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-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 "$TRUSTED_CILOCK" verify ./cilock -p release-policy.json \
--attestations "${STEM}.source-git.att.json,${STEM}.build.att.json" \
--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 embedded in your trusted verifier.
  • 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>
must supply a public key, CA certificates, a verifier, or a cilock built with embedded policy trustyour verifier has no embedded trust (stock/source build — check cilock version)verify with a release-built cilock you already trust, or pass --policy-ca-roots from an independently trusted channel
failed to build chain / cert chain errorsa --policy-ca-roots bundle you supplied is wrong or truncatedthe bundle must contain both the Fulcio CA and the Root CA — and come from a channel trusted independently of the download
timestamp / policy expired-style errorsyour verifier lacks embedded TSA roots and no --policy-timestamp-servers was givenwithout TSA roots the expired signing certs can't be validated as-of signing time; use a release-built trusted verifier or supply an independently trusted chain
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