Verify assesses. Your pipeline still decides.
Two working example pipelines call the local CLI after your build and tests, and stop the pipeline when the result is FAIL or NO_CONSTA under fail-closed configuration.
BUILD → TEST → VERIFY → DECISION → (deployment: a separate system, not Verify) # integrations/github-actions/verify.yml
name: verify
on: [push]
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm ci
working-directory: cli
- name: Run Verify against the build artifact
working-directory: cli
run: |
node bin/verify.mjs kernel-evaluate \
--bundle ../artifact/bundle.json \
--pins ../artifact/pins.json \
--json > result.json
- name: Fail closed on FAIL or NO_CONSTA
run: |
status=$(node -e "console.log(require('./cli/result.json').result.security_assessment_status)")
echo "status=$status"
if [ "$status" != "PASS" ]; then
echo "::error::Verify status is $status, stopping the pipeline."
exit 1
fi # integrations/generic-ci/verify.sh
set -euo pipefail
node cli/bin/verify.mjs kernel-evaluate \
--bundle "$ARTIFACT_BUNDLE" --pins "$ARTIFACT_PINS" --json > result.json
status=$(node -e "console.log(require('./result.json').result.security_assessment_status)")
if [ "$status" != "PASS" ]; then
echo "Verify status is $status — refusing to continue." >&2
exit 1
fi
echo "Verify: PASS. Continuing pipeline." No secrets are embedded in either example, and neither example pushes to a remote or performs a deployment.
How a pipeline gate actually decides.
How this works
- 01
Your build produces the record and its pins
Verify does not create these. Your pipeline states what it built and what it expects, and hands both over.
- 02
The gate evaluates and reports three things
Whether the record is interpretable, what the evidence shows, and — always — that no permission is granted.
- 03
The pipeline reads the result, not the exit code
A zero exit means the record parsed. The example scripts read security_assessment_status and record_valid before deciding anything.
- 04
It stops on FAIL and on NO_CONSTA alike
An unfavourable answer and an unanswerable one both halt the pipeline. Absence of evidence is not treated as evidence of safety.
Step by step
-
Copy integrations/generic-ci/verify.sh into your pipeline.
You will see a script that needs two paths: your record and your pins.
That means it plugs in after your tests, before whatever ships things.
-
Run it once by hand with a passing example.
You will see Verify: PASS, and the pipeline continues.
That means the gate is wired correctly.
-
Run it again pointing at the UNFAVOURABLE_RESULT example.
You will see it refuses and exits non-zero.
That means your pipeline will now stop on a bad answer instead of shipping it.