Reference Pipeline

June 24, 2026 · View on GitHub

End-to-end walkthrough of the Occasio attestation pipeline. Read top-to-bottom; every step has a copy-paste artefact and an explanation of what it proves.

What this pipeline is for

When an AI coding agent (Claude Code, Cline, MCP-routed, etc.) opens a PR, the reviewer's question is "What did the agent actually do?" — every tool call, every blocked attempt, every secret redacted, under which policy, on what audit-chain commitment. This pipeline answers that question with a signed artifact that can be verified offline by any third party using only cosign and the published predicate type.

Three deliverables land on the PR:

  1. A Check Run with a human-readable summary of the run.
  2. A workflow artifact containing the signed predicate JSON and the Sigstore Bundle.
  3. A View Evidence link that opens the standalone viewer page with both files pre-loaded.

Try it locally in 30 seconds

occasio demo attest

This builds a synthetic audit chain, an unsigned attestation, runs the canonical-JSON round-trip check, and previews the Check Run summary. No Sigstore, no GitHub, no API key — just the pipeline working against in-memory data. Use this to validate the build end-to-end before deploying any of the rest.

Step 1 — your agent runs under Occasio

A Occasio session must produce events into ~/.occasio/pipeline-events.jsonl. The simplest way is to invoke Claude Code (or any supported agent) through the local proxy:

occasio claude --hardened

--hardened routes Read/Glob/Grep through the unified runtime so tool calls are intercepted locally, distillation applied, and secret scanning runs on every tool result. The ~/.occasio/session.json file gets a fresh run_id per session.

Step 2 — produce an attestation locally (sanity check)

After a session ends, run:

occasio attest --run-id "$(jq -r .run_id ~/.occasio/session.json)"

This writes attestation.json to the cwd. The predicate is unsigned (signature: null) — that part lands in the GitHub Action step below. Inspect the predicate; the file is human-readable JSON. The audit_chain.last_hash is the commitment: signing it later transitively commits to every event in the slice.

Step 3 — drop in the GitHub Action

Create .github/workflows/attest-on-pr.yml in your repo:

name: Attest AI-generated PR

on:
  pull_request:
    branches: [main]

permissions:
  id-token: write    # Sigstore keyless via GitHub OIDC
  checks:    write   # post the PR Check Run
  contents:  read

jobs:
  attest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 2     # so files-changed can diff HEAD^..HEAD

      # ── Your AI-agent step here ────────────────────────────────────
      # The agent must run under Occasio so its tool calls land in
      # ~/.occasio/pipeline-events.jsonl. Example with Claude Code:
      - run: npm i -g @occasiolabs/occasio @anthropic-ai/claude-code
      - run: occasio claude --hardened < .github/agent-prompt.txt
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

      # ── Sign + Check Run ───────────────────────────────────────────
      - uses: occasiolabs/occasio/integrations/attest-action@v1
        # All inputs optional — run_id auto-resolves from session.json,
        # paths default to ~/.occasio/*. Defaults are tuned for the
        # 95% case.

That's it. The composite Action handles:

  • occasio bundle --sign using the workflow's OIDC token (no key management)
  • Self-verifying the bundle with occasio verify --strict, then uploading the single run.occasio.json evidence bundle as a workflow artifact (90-day retention)
  • Creating the Check Run via the GitHub API

Step 4 — what reviewers see

The Check Run lands on the PR as:

✓ Occasio Attested · 47 calls · 2 blocked
  Claude Opus 4.7 · Policy strict-v2.1 (sha a126…3a)
  Chain ✓ verified · Signature ✓ Sigstore keyless
  [View evidence ↗]   [Artifact ↗]

Click View evidence to open the viewer with the artifact's two JSON files. The viewer runs two browser-side checks (DSSE-payload equivalence + audit-chain replay) and surfaces the Rekor transparency log link for cryptographic verification.

The viewer deliberately does not verify the Sigstore certificate chain in-browser — bundling Fulcio/Rekor trust roots in-browser is a serious build problem we have not solved cheaply, and we are honest about it on the page itself. Offline crypto-verification is one CLI call:

occasio verify --strict run.occasio.json

That command runs six checks in order, all of which must pass — schema, manifest integrity, chain-slice integrity (anchored to the attestation's first_hash/last_hash), policy binding, git-state cross-check, and the Sigstore signature. See VERIFY.md for the full list, and python-verifier.md for the independent Python re-verification (python docs/verify_bundle.py run.occasio.json --strict).

Step 5 — what auditors do

Auditors do not need access to the producer's machine. They download the workflow artifact, install Occasio (or cosign), and verify offline. The signed artifact is portable and self-contained: predicate JSON + Sigstore Bundle + (optionally) the chain file.

For a SOC2 audit period the workflow becomes:

  1. Pull all occasio-evidence-bundle artifacts from the period (GitHub API)
  2. For each: occasio verify --strict run.occasio.json and capture the exit code
  3. Aggregate the execution_summary data: how many runs, how many blocks, what rules, what files

The audit chain is hash-linked across all of an agent's runs on the same machine; verifying any one slice does not require touching the producer's full log.

Compatibility and what's stable

  • Predicate URI https://github.com/occasiolabs/occasio/spec/agent-attestation/v1 is canonical. It will not be moved or re-pointed.
  • Required fields in v1 do not change without bumping the URI to /v2.
  • New optional fields can land in v1.x (currently reserved: subject.git_commit, subject.files_changed — already in the schema, populated by the GitHub Action).
  • The Sigstore Bundle is the standard sigstore-bundle+json;version=0.2 shape — works with cosign, sigstore-js, sigstore-python, any future conformant tool.

What this does NOT yet do

  • Multi-commit attestations. Today the predicate binds to a single run_id. v1.1 will likely add subject.git_commits[] and a way to merge slices for a PR that includes N commits from M runs.
  • Policy provenance. policy.file_hash commits to the file bytes. We do not yet carry the origin of the policy (was it committed to a repo? signed by a security team?). v1.1 may add policy.attestation_url for nested signed claims.

Reference / further reading