GitHub Actions

Run governance validation on every pull request. The SDK ships an asqav doctor subcommand that validates configuration and connectivity, and a programmatic export_bundle for packaging signed receipts into a self-contained, Merkle-rooted compliance bundle. Both work in CI without changes.

Prerequisites

Without the secret the doctor step reports FAIL ASQAV_API_KEY not set and fails the job, which is what you want.

Minimal workflow

yaml
# .github/workflows/governance.yml
name: governance

on:
  pull_request:
  push:
    branches: [main]

jobs:
  governance:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install asqav
        run: pip install "asqav[cli]"

      - name: Validate setup
        env:
          ASQAV_API_KEY: ${{ secrets.ASQAV_API_KEY }}
        run: asqav doctor

That is the whole CI gate. asqav doctor checks the API key is present, the backend is reachable, prints the SDK version, then returns non-zero if any check fails.

Workflow with compliance bundle export

If your test job signs actions, capture each SignedActionResponse to a JSONL file during the run, then export a bundle from that file as a workflow artifact. The bundle is what the auditor downloads.

A minimal pytest fixture that appends every receipt:

python
# conftest.py
import json
import pytest
from pathlib import Path

RECEIPTS_PATH = Path("ci-receipts.jsonl")

@pytest.fixture(autouse=True)
def capture_receipts(monkeypatch):
    # Append every agent.sign(...) response to ci-receipts.jsonl.
    import asqav

    real_sign = asqav.Agent.sign

    def wrapped(self, action_type, *args, **kwargs):
        sig = real_sign(self, action_type, *args, **kwargs)
        with RECEIPTS_PATH.open("a") as f:
            f.write(json.dumps({
                "signature_id": sig.signature_id,
                "action_id": sig.action_id,
                "agent_id": self.agent_id,
                "action_type": action_type,
                "algorithm": sig.algorithm,
                "timestamp": sig.timestamp,
                "verification_url": sig.verification_url,
                "chain_hash": sig.chain_hash,
            }) + "
")
        return sig

    monkeypatch.setattr(asqav.Agent, "sign", wrapped)
    yield

The matching workflow:

yaml
name: governance

on:
  pull_request:
  push:
    branches: [main]

jobs:
  governance:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install asqav and project deps
        run: |
          pip install "asqav[cli]"
          pip install -r requirements.txt

      - name: Validate setup
        env:
          ASQAV_API_KEY: ${{ secrets.ASQAV_API_KEY }}
        run: asqav doctor

      - name: Run tests (these sign actions via agent.sign)
        env:
          ASQAV_API_KEY: ${{ secrets.ASQAV_API_KEY }}
        run: pytest

      - name: Export compliance bundle
        run: |
          python <<'PY'
          import json
          from pathlib import Path
          from asqav.compliance import export_bundle

          path = Path("ci-receipts.jsonl")
          if not path.exists():
              print("no receipts captured this run, skipping bundle")
              raise SystemExit(0)

          receipts = [json.loads(line) for line in path.read_text().splitlines() if line]
          bundle = export_bundle(receipts, framework="eu_ai_act_art12")
          bundle.to_file("compliance-bundle.json")
          print(f"wrote {bundle.receipt_count} receipts, merkle_root={bundle.merkle_root}")
          PY

      - uses: actions/upload-artifact@v4
        if: hashFiles('compliance-bundle.json') != ''
        with:
          name: compliance-bundle
          path: compliance-bundle.json
          retention-days: 90

export_bundle accepts plain dicts, SignatureResponse, or SignedActionResponse, so the JSONL-of-dicts pattern works. The bundle carries framework metadata, every receipt, and a Merkle root over them, so an auditor can verify it offline without calling back to Asqav.

Frameworks: eu_ai_act_art12, eu_ai_act_art14, dora_ict, soc2.

Tips

Related