Make a release approval expire
An approval that never lapses turns into a permanent exception. If a security team signs off releases one week at a time, the sign-off should stop working when the week is up, without anyone remembering to revoke it.
Every CI/lock policy carries an expires field, and it is part of what the
policy owner signs. cilock verify refuses an expired policy before it looks
at any evidence. In this session the prompt asks Claude to record a build,
write a release policy that expires seven days out, show it admitting the
build, and then show what a lapsed approval does, without touching the system
clock.
TestifySec wrote the prompt and sent it into the session. Claude Code and
cilock ran unedited, and the recording was not changed afterwards.
What happens, step by step
-
Two keys.
keys/build.keysigns the build evidence andkeys/policy.keysigns the policy. Whoever holds only the build key cannot approve a release; in the recording Claude holds both, so that separation exists only once the policy key goes to the approver. -
Record the build.
cilock run --offline --step build -k keys/build.key -a environment,go-build \ -o build.att.json -- go build -o bin/greet ./cmd/greet -
Write the policy. Claude writes a small script,
make-policy.sh <expiry> <out>, so the same rules can be signed with any expiry. Thebuildstep must be signed by the build key, carry material, product, go-build and command-run records, and exit0.cilock policy validatewarns that the first exit-code rule would pass a record with no exit code (the rego modules deny nothing on an empty predicate); Claude rewrites it withobject.get(input, "exitcode", -1)so a missing field is denied. -
This week's approval admits the build. Signed with an expiry seven days out (
2026-10-23T07:18:57Z), the policy verifiesbin/greet:Verification succeeded, exit0. -
A lapsed approval refuses the same evidence. The same rules, signed by the same key with an expiry a day in the past:
policy signature verified attestor policyverify failed: failed to verify policy: policy expired on 2026-10-15 02:18:57 -0500 CDTExit
1. The signature is valid and the evidence is unchanged; only the date differs.
What the expiry is checked against
The expiry is compared with the verifier's own clock when cilock verify
runs (attestation/policy/policy.go compares time.Now() with expires,
allowing for a configured clock-skew tolerance). At the end of the session
Claude suggests adding --timestamp-servers to rule out a clock set back;
that is not right for this check. A trusted timestamp on the evidence does not
change the comparison, so the guarantee is only as good as
the clock on the machine that verifies: run the gate on infrastructure whose
time you trust, such as a hosted CI runner, rather than on a laptop whose
clock can be set back.
Renewing an approval means signing the policy again with a new expiry. Nothing about the build evidence has to change. See Policy verification for the other policy fields, and Release gates for a gate that adds Rego over the evidence.
Run it yourself
The recording used cilock built from main at commit 5b614726, with local
ed25519 keys and no platform, so the signatures carry no timestamp.
Reference generated from the product documentation. Match commands and support details to your installed release.