Skip to content

CI / CD integration

seal verify is designed for CI. It is read‑only, never prompts, never makes network calls, and exits with the same well‑defined codes on every platform.

The simplest gate is one line:

Terminal window
seal verify --quiet

Exit 0 permits the pipeline to continue under the configured policy. Exit 1 means a mismatch or unverified bundle under block. Exit 2 means a fatal configuration or system error, including a missing or invalid lockfile.

0 does not always mean everything matches: removed-only bundles produce Drift, and warn allows mismatches with Warning. Keep policy: "block" for a gate that fails on changed or newly discovered bundles.

Run verification after installing or preparing the bundle files, then make it a required successful step before any pipeline step that consumes them. Use the same files and lockfile for the check and subsequent use.

Seal checks the checkout at that moment. It does not intercept an agent’s loads, verify later writes, or protect a developer session that already read changed instructions. Continuous enforcement requires integration in the agent’s load path.

Review bundle changes together with seal.json, including edits to discovery and policy. Use required reviews and protected branches or equivalent controls to establish which snapshot is approved. Apply those controls to the CI workflow too.

If a pull request changes both a bundle and its hashes, seal verify can pass. The check establishes agreement with the supplied lockfile, not independent approval of that lockfile. Do not automatically re-pin in a verification job.

Seal also does not pin the CI actions, container images, or remote downloads used to install the verifier. Manage those dependencies through your normal CI supply-chain controls.

name: CI
on: [push, pull_request]
jobs:
seal:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install seal
run: |
SEAL_VERSION=v1.1.1
curl -L -o /usr/local/bin/seal \
"https://github.com/SamyGhannad/seal-cli/releases/download/${SEAL_VERSION}/seal-linux-amd64"
chmod +x /usr/local/bin/seal
- name: Verify Seal lockfile
run: seal verify --quiet

Pinning to a specific tag is recommended for reproducibility; check the releases page before bumping SEAL_VERSION.

If you prefer building from source on every run (no asset fetch):

- uses: actions/setup-go@v5
with:
go-version: 'stable'
- run: go install github.com/SamyGhannad/seal-cli/cmd/seal@latest
- run: seal verify --quiet

The source-build examples use @latest for brevity. For locked-down CI, replace it with a reviewed commit such as @949c84e96b4926616ba7ec81fccbf055965201c8.

seal-verify:
image: golang:latest
script:
- go install github.com/SamyGhannad/seal-cli/cmd/seal@latest
- seal verify --json > seal-report.json
artifacts:
when: always
paths:
- seal-report.json

--json writes the report and returns the same gating exit code as the other output modes. One invocation is enough; when: always preserves the report when verification blocks the job.

version: 2.1
jobs:
seal:
docker:
- image: cimg/go:stable
steps:
- checkout
- run:
name: Install seal
command: go install github.com/SamyGhannad/seal-cli/cmd/seal@latest
- run:
name: Verify Seal lockfile
command: seal verify --quiet
workflows:
build:
jobs:
- seal

A .git/hooks/pre-commit (or pre‑commit) hook catches drift before it leaves your machine:

.git/hooks/pre-commit
#!/usr/bin/env sh
set -e
seal verify --quiet

chmod +x .git/hooks/pre-commit and the next commit runs the check.

For a pre-commit framework config:

.pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: seal-verify
name: seal verify
entry: seal verify --quiet
language: system
pass_filenames: false

seal verify --json emits to stdout. Diagnostics still go to stderr, so piping is safe:

Terminal window
seal verify --json > seal-report.json 2> seal-report.log

The JSON output is stable:

{
"status": "Blocked",
"verified": ["./.claude/skills/code-review"],
"unverified": ["./.claude/skills/new-thing"],
"removed": [],
"mismatch": ["./.claude/skills/deploy-guard"]
}
  • status is one of "Verified", "Drift", "Warning", "Blocked".
  • The four arrays (verified, unverified, removed, mismatch) are always present.
  • Arrays are sorted lexicographically by UTF‑8 bytes (NFC).

For local inspection, a jq one-liner lists every bundle that needs attention:

Terminal window
seal verify --json | jq '.unverified + .mismatch + .removed | unique'

Do not use that pipeline as the CI gate without preserving Seal’s exit code: the shell can return jq’s success even when verification failed. Gate with a direct seal verify invocation.

seal verify is fast (one SHA‑256 per file under each sealed root) but if you check out very large bundle trees, CI caches can help:

  • Cache the Go install of seal between jobs.
  • Do not cache seal.json itself. It is part of the repository and must be the version under review.

seal verify prints a human summary by default, but it goes to stderr. CI systems that only capture stdout will see nothing. Either:

  • Use --quiet and rely on the exit code (recommended).
  • Use --json and parse the result in the script.
  • Redirect stderr to stdout: seal verify 2>&1.

Avoid running plain seal verify and relying on the human output in CI. The human format is meant for local terminals.

Symptom Likely cause Fix
seal: verify: seal.json not found in current directory Job is running outside the repo root, or seal.json is missing. cd to the repo root before running; ensure seal.json is committed.
seal: verify: invalid lockfile: … Lockfile is corrupted or out of spec. Read the message and correct the invalid configuration; review changes before re-pinning.
Exit 1 with status: "Blocked" A tracked bundle changed, or a new bundle appeared under discovery, without re‑pinning. Locally: git diff the bundle, then seal pin <path> if the change is intentional.
Exit 2 after merging The lockfile is invalid, such as a recorded key not statically produced by discovery, nested bundle keys, or overlapping discovery patterns. Read the diagnostic, choose the intended boundary model, then re‑pin or prune and commit seal.json.