CI/CD

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.

Referencerunnable examples, not a hosted service
01 The flow
BUILD  →  TEST  →  VERIFY  →  DECISION  →  (deployment: a separate system, not Verify)
02 GitHub Actions
# 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
03 Generic shell pipeline
# 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.

04 The mechanism

How a pipeline gate actually decides.

How this works

  1. 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.

  2. 02

    The gate evaluates and reports three things

    Whether the record is interpretable, what the evidence shows, and — always — that no permission is granted.

  3. 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.

  4. 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

  1. 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.

  2. 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.

  3. 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.