Verifiable provenance for AI-assisted commits. Generate receipts. Verify releases. Attest for auditors. 🧾
git commit→ receipt → policy evaluation → signed Verification Summary Attestation (VSA)
AI writes 30–50% of new code at most companies. Copilot, ChatGPT, Claude, Cursor — it all goes into git commit with no systematic record of what was human and what was machine.
EU AI Act transparency obligations are phasing in now — general-purpose AI transparency rules from August 2025, high-risk system requirements from August 2026. Code with undocumented AI involvement is a compliance gap.
Your auditors will ask: "Which commits involved AI tools? Can you show the audit trail?"
And then: "Did you verify every release against policy before shipping?"
| Who asks | What they need |
|---|---|
| SOC 2 / ISO 27001 auditor | Tamper-evident record of declared AI involvement per commit |
| EU AI Act compliance | Transparency evidence for commits with AI markers + verified release attestation |
| Insurance underwriter | Policy-evaluated verification of declared AI involvement per release |
| GRC / supply-chain security | SLSA-compatible Verification Summary Attestation (VSA) |
| Engineering leadership | "We track declared AI involvement and verify every release" — with cryptographic receipts |
AIIR answers both questions. Generate receipts for every commit. Verify releases against policy. Attest the result as a machine-readable VSA. One tool. Zero dependencies. Apache 2.0.
┌─ Receipt: g1-a3f8b2c1d4e5f6a7b8c9d0e1...
│ Commit: c4dec85630
│ Subject: feat: add new auth middleware
│ Author: Jane Dev <jane@example.com>
│ Files: 4 changed
│ AI: YES (copilot)
│ Hash: sha256:7f3a...
│ Time: 2026-03-06T09:48:59Z
└──────────────────────────────────────────
Every commit gets a content-addressed receipt — a JSON object that records what was committed, who wrote it, and whether AI was involved. Change one character? The hash breaks. That's integrity (tamper detection).
For authenticity (proving who generated the receipt), enable Sigstore keyless signing — see Sigstore signing below. Without signing, receipts prove internal consistency but not provenance — anyone who can run aiir on the same commit can produce an equivalent receipt.
AIIR operates at three levels, matching modern supply-chain security architecture:
git commit
↓
AIIR receipt (content-addressed, per-commit)
↓
Sigstore signing (OIDC identity, transparency log)
↓
Policy evaluation (org rules, AI-usage thresholds)
↓
Verification Summary Attestation (in-toto Statement v1)
↓
CI gate (PASS / FAIL — enforceable via branch protection)
This aligns directly with SLSA, Sigstore, and in-toto — the same stack used by npm, PyPI, Kubernetes, and the Linux kernel.
For developers: Add aiir to your CI and get a pass/fail check on every PR.
For security teams: Get policy-evaluated verification results as signed attestations.
For auditors: Query the JSONL ledger or VSA artifacts — every claim is cryptographically verifiable.
pip install aiir
cd your-repo
aiir --prettyThat's it. Your last commit now has a receipt in .aiir/receipts.jsonl — a
tamper-evident JSONL ledger that auto-indexes and deduplicates. Run it again:
same commit, zero duplicates. Zero dependencies. Python 3.9+.
Receipts are content-addressed — change one byte, the hash breaks:
aiir --verify .aiir/receipts.jsonl --explainSee Tamper Detection for a walkthrough of what happens when a receipt is modified.
Evaluate all receipts against policy and emit a Verification Summary Attestation (VSA):
aiir --verify-release --policy strict --emit-vsaAIIR acts as a verifier — it identifies itself (https://invariantsystems.io/verifiers/aiir), names the policy, evaluates every receipt, computes commit coverage, and emits a signed PASS/FAIL decision as an in-toto Statement v1. Downstream CI gates, auditors, and GRC platforms consume the VSA without re-evaluating receipts themselves.
Any MCP-aware AI assistant (Claude, Copilot, Cursor, Continue, Cline, Windsurf) can discover and use AIIR as a tool. Add to your MCP config:
{
"mcpServers": {
"aiir": {
"command": "aiir-mcp-server",
"args": ["--stdio"]
}
}
}Now your AI assistant generates receipts automatically after writing code. It says: "I just committed code for you. Let me generate an AIIR receipt."
# Receipt the last commit (auto-saves to .aiir/receipts.jsonl)
aiir --pretty
# Receipt a whole PR branch
aiir --range origin/main..HEAD --pretty
# Only AI-authored commits, save to directory (CI mode)
aiir --ai-only --output .receipts/
# Print JSON to stdout for piping (bypasses ledger)
aiir --json | jq .receipt_id
# JSON Lines output for streaming / piping
aiir --range HEAD~5..HEAD --jsonl | jq .receipt_id
# Custom ledger location
aiir --ledger .audit/
# Verify a receipt — with human-readable explanation
aiir --verify receipt.json --explain
# Wrap receipts in an in-toto Statement v1 envelope (SLSA-compatible)
aiir --range HEAD~3..HEAD --in-toto --output .receipts/
# Sign + wrap for full supply-chain attestation
aiir --sign --in-toto --output .receipts/
# Attach agent attestation metadata (Copilot, Cursor, Claude, etc.)
aiir --agent-tool copilot --agent-model gpt-4o --agent-context ide
# Initialize .aiir/ directory for a new project
aiir --init # scaffolds receipts.jsonl, index, config, .gitignore
aiir --init --policy strict # also creates policy.json
# Review receipts — human attestation that a commit was reviewed
aiir --review HEAD # defaults to "approved"
aiir --review abc123 --review-outcome rejected --review-comment "needs refactor"
# Commit trailers — append AIIR metadata to git commit messages
aiir --trailer # prints AIIR-Receipt, AIIR-Type, AIIR-AI, AIIR-Verified
# Policy engine — initialise and enforce org-wide rules
aiir --policy-init strict # creates .aiir/policy.json
aiir --check --policy strict # CI gate: fail if policy violated
aiir --check --max-ai-percent 50 # fail if >50% commits are AI-authored
# Verify an entire release against policy, emit a VSA
aiir --verify-release --receipts .aiir/receipts.jsonl --emit-vsa --policy strict
# Ledger utilities
aiir --stats # dashboard of ledger statistics
aiir --badge # shields.io badge Markdown
aiir --export backup.json # portable JSON bundle
# Privacy — omit file paths from receipts
aiir --redact-files --namespace acme-corp
# Native GitLab CI mode (MR comments, dotenv outputs)
aiir --gitlab-ci --output .receipts/
# GitLab SAST report for Security Dashboard
aiir --gitlab-ci --gl-sast-report# .github/workflows/aiir.yml — signed receipts (default)
name: AIIR
on:
push:
tags-ignore: ['**'] # Don't receipt tag pushes (receipts the whole history)
pull_request:
permissions:
id-token: write # Required for Sigstore keyless signing
contents: read
checks: write # P0: aiir/verify Check Run on every PR
pull-requests: write # P3: automatic receipt summary comment on PRs
jobs:
receipt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: invariant-systems-ai/aiir@v1
with:
output-dir: .receipts/Signing is on by default — each receipt gets a Sigstore bundle (Fulcio certificate + Rekor transparency log entry).
Artifacts uploaded automatically when output-dir is set.
Automatic PR integration: When
GITHUB_TOKENis available, the Action automatically:
- Creates an
aiir/verifyCheck Run — visible as a pass/fail status check on every PR (enforce via branch protection). Requireschecks: write.- Posts a receipt summary comment — idempotent (updates in place, no comment spam). Requires
pull-requests: write.
Unsigned (opt out of signing, no permissions needed)
name: AIIR
on:
push:
tags-ignore: ['**']
pull_request:
jobs:
receipt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: invariant-systems-ai/aiir@v1
with:
sign: falseNote: Without signing, receipts are tamper-evident (hash integrity) but not tamper-proof (anyone who can run
aiiron the same commit can recreate a matching receipt). For cryptographic non-repudiation, use the signed default above.
# .pre-commit-config.yaml
repos:
- repo: https://github.com/invariant-systems-ai/aiir
rev: v1.2.4
hooks:
- id: aiirRuns post-commit (after your commit is created, since it needs the commit SHA to generate a receipt). Customise with args:
- id: aiir
args: ["--ai-only", "--output", ".receipts"]CI/CD Catalog component (recommended — browse in Catalog):
# .gitlab-ci.yml — one line
include:
- component: gitlab.com/invariant-systems/aiir/receipt@1
inputs:
stage: testAll inputs are optional and typed:
| Input | Type | Default | Description |
|---|---|---|---|
stage |
string | test |
Pipeline stage |
version |
string | 1.2.4 |
AIIR version from PyPI |
ai-only |
boolean | false |
Only receipt AI-authored commits |
output-dir |
string | .aiir-receipts |
Artifact output directory |
artifact-expiry |
string | 90 days |
Artifact retention |
sign |
boolean | true |
Sigstore keyless signing (GitLab OIDC) |
gl-sast-report |
boolean | false |
Generate SAST report for Security Dashboard |
approval-threshold |
number | 0 |
AI% threshold for extra MR approvals (0 = off) |
extra-args |
string | "" |
Additional CLI flags |
Legacy include (still works — no Catalog required):
include:
- remote: 'https://raw.githubusercontent.com/invariant-systems-ai/aiir/v1.2.4/templates/gitlab-ci.yml'Self-hosted GitLab? Mirror the repo and use project: instead:
include:
- project: 'your-group/aiir'
ref: 'v1.2.4'
file: '/templates/gitlab-ci.yml'Customise via pipeline variables: AIIR_VERSION, AIIR_AI_ONLY, AIIR_EXTRA_ARGS, AIIR_ARTIFACT_EXPIRY. See templates/gitlab-ci.yml for the full template.
# Receipt the current repo (mount it in)
docker run --rm -v "$(pwd):/repo" -w /repo invariantsystems/aiir --pretty
# AI-only, save receipts
docker run --rm -v "$(pwd):/repo" -w /repo invariantsystems/aiir --ai-only --output .receipts/Works in any CI/CD system that supports container steps — Tekton, Buildkite, Drone, Woodpecker, etc.
Bitbucket Pipelines
# bitbucket-pipelines.yml
pipelines:
default:
- step:
name: AIIR Receipt
image: python:3.11
script:
- pip install aiir
- aiir --pretty --output .receipts/
artifacts:
- .receipts/**Full template with PR support: templates/bitbucket-pipelines.yml
Azure DevOps
# azure-pipelines.yml
steps:
- task: UsePythonVersion@0
inputs: { versionSpec: '3.11' }
- script: pip install aiir && aiir --pretty --output .receipts/
displayName: 'Generate AIIR receipt'
- publish: .receipts/
artifact: aiir-receiptsFull template with PR/CI detection: templates/azure-pipelines.yml
CircleCI
# .circleci/config.yml
jobs:
receipt:
docker:
- image: cimg/python:3.11
steps:
- checkout
- run: pip install aiir && aiir --pretty --output .receipts/
- store_artifacts:
path: .receiptsFull template: templates/circleci/config.yml
Jenkins
// Jenkinsfile
pipeline {
agent { docker { image 'python:3.11' } }
stages {
stage('AIIR Receipt') {
steps {
sh 'pip install aiir && aiir --pretty --output .receipts/'
archiveArtifacts artifacts: '.receipts/**'
}
}
}
}Full template: templates/jenkins/Jenkinsfile
AIIR detects three categories of signals in commit metadata:
Commits where a human or tool explicitly attributed AI involvement.
| Signal | Examples |
|---|---|
| Copilot | Co-authored-by: Copilot, Co-authored-by: GitHub Copilot |
| ChatGPT | Generated by ChatGPT, Co-authored-by: ChatGPT |
| Claude | Generated by Claude, Co-authored-by: Claude |
| Cursor | Generated by Cursor, Co-authored-by: Cursor |
| Amazon Q / CodeWhisperer | amazon q, codewhisperer, Co-authored-by: Amazon Q |
| Devin | Co-authored-by: Devin, devin[bot] |
| Gemini | gemini code assist, google gemini, gemini[bot] |
| GitLab Duo | gitlab duo, duo code suggestions, duo chat, duo enterprise, Co-authored-by: gitlab duo |
| Tabnine | tabnine in commit metadata |
| Aider | aider: prefix in commit messages |
| Generic markers | AI-generated, LLM-generated, machine-generated |
| Git trailers | Generated-by:, AI-assisted:, Tool: git trailers |
Commits made by CI bots and automated dependency tools. These are not gen-AI assistance — they indicate automated (non-human) authorship.
| Signal | Examples |
|---|---|
| Dependabot | dependabot[bot] as author |
| Renovate | renovate[bot] as author |
| Snyk | snyk-bot as author |
| CodeRabbit | coderabbit[bot] as author |
| GitHub Actions | github-actions[bot] as author |
| GitLab Bot | gitlab-bot as author |
| DeepSource | deepsource[bot] as author |
Note: Since v1.0.4, bot and AI signals are fully separated. A Dependabot commit gets
is_bot_authored: trueandauthorship_class: "bot", notis_ai_authored: true. Theauthorship_classfield provides a single structured value:"human","ai_assisted","bot", or"ai+bot".
Detection scope and limitations
AI detection uses heuristic_v2 — matching on commit metadata signals.
What it catches:
- Commits with
Co-authored-by: Copilotor similar trailers - Bot-authored commits (Dependabot, Renovate) — classified separately as
bot - Explicit AI markers (
AI-generated,Generated by ChatGPT, etc.)
What it doesn't catch:
- Copilot inline completions (no metadata trace by default)
- ChatGPT/Claude copy-paste without attribution
- Squash-merged AI branches with clean messages
- Amended commits that remove AI trailers
This is by design — AIIR receipts what's declared, not what's hidden. Receipts prove the integrity of what was recorded, not the completeness of AI involvement.
Ledger — .aiir/ directory
By default, aiir appends receipts to a local JSONL ledger:
.aiir/
├── receipts.jsonl # One receipt per line (append-only)
└── index.json # Auto-maintained lookup index
Why a ledger?
- One file to commit —
git add .aiir/is your entire audit trail - Auto-deduplicates — re-running
aiiron the same commit is a no-op - Git-friendly — append-only JSONL means clean diffs and easy
git blame - Queryable —
jq,grep, andwc -lall work naturally
index.json tracks every commit SHA, receipt count, and authorship breakdown:
{
"version": 1,
"receipt_count": 42,
"ai_commit_count": 7,
"bot_commit_count": 3,
"ai_percentage": 16.7,
"unique_authors": 5,
"first_receipt": "2026-03-01T10:00:00Z",
"latest_timestamp": "2026-03-06T09:48:59Z",
"commits": {
"c4dec85630...": { "receipt_id": "g1-a3f8...", "ai": true, "bot": false, "authorship_class": "ai_assisted", "author": "jane@example.com" },
"e7b1a9f203...": { "receipt_id": "g1-b2c1...", "ai": false, "bot": true, "authorship_class": "bot", "author": "dependabot[bot]" }
}
}Output modes:
| Flag | Behaviour |
|---|---|
| (none) | Append to .aiir/receipts.jsonl (default) |
--ledger .audit/ |
Append to custom ledger directory |
--json |
Print JSON to stdout — no ledger write* |
--jsonl |
Print JSON Lines to stdout — no ledger write* |
--output dir/ |
Write individual files to dir/ — no ledger write* |
--pretty |
Human-readable summary to stderr (combines with any mode) |
* Adding --ledger explicitly overrides and writes to both destinations.
Tip: Add .aiir/ to your repo. It becomes a permanent, auditable,
append-only record of every receipted commit.
Receipt format
Each receipt is a content-addressed JSON document:
{
"type": "aiir.commit_receipt",
"schema": "aiir/commit_receipt.v1",
"receipt_id": "g1-a3f8b2c1d4e5f6a7b8c9d0e1f2a3b4",
"content_hash": "sha256:7f3a...",
"timestamp": "2026-03-06T09:48:59Z",
"commit": {
"sha": "c4dec85630232666aba81b6588894a11d07e5d18",
"author": { "name": "Jane Dev", "email": "jane@example.com" },
"subject": "feat: add receipt generation to CI",
"diff_hash": "sha256:b2c1...",
"files_changed": 4
},
"ai_attestation": {
"is_ai_authored": true,
"signals_detected": ["message_match:co-authored-by: copilot"],
"signal_count": 1,
"is_bot_authored": false,
"bot_signals_detected": [],
"bot_signal_count": 0,
"authorship_class": "ai_assisted",
"detection_method": "heuristic_v2"
},
"extensions": {}
}Content-addressed = the receipt_id is derived from SHA-256 of the receipt's canonical JSON. Change any field → hash changes → receipt invalid.
is_ai_authored= true when AI coding tools are detected (Copilot, ChatGPT, Claude, etc.).is_bot_authored= true when automation/CI bots are detected (Dependabot, Renovate, etc.).authorship_class= structured category:"human","ai_assisted","bot", or"ai+bot". These are fully independent — a Dependabot commit getsbot, notai_assisted.
Note: Unsigned receipts are tamper-evident, not tamper-proof. Anyone who can re-run
aiiron the same commit can recreate a matching receipt. For cryptographic non-repudiation, enable Sigstore signing.
Receipt identity depends on repository provenance. The
provenance.repositoryfield (yourgit remote get-url origin) is part of the content hash. The same commit will produce a differentreceipt_idif the remote URL changes — for example after a fork, a repo rename, or adding an origin to a previously local-only repo. If you need stable receipt identity as a durable external reference, generate receipts after your remote is configured. Fields insideextensions(such asnamespace) are not part of the content hash, so they can change without invalidating a receipt.
GitHub Action — inputs & outputs
| Input | Description | Default |
|---|---|---|
ai-only |
Only receipt AI-authored commits | false |
commit-range |
Specific commit range (e.g., main..HEAD) |
Auto-detected from event |
output-dir |
Directory to write receipt JSON files | (prints to log) |
sign |
Sign receipts with Sigstore | true |
| Output | Description |
|---|---|
receipt_count |
Number of receipts generated |
ai_commit_count |
Number of AI-authored commits detected |
receipts_json |
Full JSON array of all receipts (set to "OVERFLOW" if >1 MB) |
receipts_overflow |
"true" when receipts_json exceeded 1 MB and was truncated |
⚠️ Security note onreceipts_json: Contains commit metadata which may include shell metacharacters. Never interpolate directly intorun:steps via${{ }}. Write to a file instead.
name: AI Audit Trail
on: pull_request
jobs:
receipt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: invariant-systems-ai/aiir@v1
id: receipt
with:
output-dir: .receipts/
- name: Comment on PR
if: steps.receipt.outputs.ai_commit_count > 0
uses: actions/github-script@v7
with:
script: |
const count = '${{ steps.receipt.outputs.ai_commit_count }}';
const total = '${{ steps.receipt.outputs.receipt_count }}';
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `🔐 **AIIR**: ${total} commits receipted, ${count} AI-authored.\n\nReceipts uploaded as build artifacts.`
});name: Require AI Receipts
on: pull_request
jobs:
receipt-gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: invariant-systems-ai/aiir@v1
id: receipt
- name: Enforce receipt policy
if: steps.receipt.outputs.ai_commit_count > 0
run: |
echo "✅ ${{ steps.receipt.outputs.ai_commit_count }} AI commits receipted"Sigstore signing
Optionally sign receipts with Sigstore keyless signing for cryptographic non-repudiation:
name: AIIR (Signed)
on:
push:
tags-ignore: ['**']
pull_request:
permissions:
id-token: write
contents: read
jobs:
receipt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: invariant-systems-ai/aiir@v1
with:
output-dir: .receipts/
sign: trueFork PRs: GitHub does not grant OIDC tokens to fork pull requests. If your project accepts external contributions, either use
sign: falsefor PRs or conditionally skip signing on forks. AIIR will detect the missing credential and fail with a clear error rather than hanging.
Each receipt gets an accompanying .sigstore bundle:
- Fulcio certificate — short-lived cert proving the signer's OIDC identity
- Rekor transparency log — tamper-evident public record
- Signature — cryptographic binding
Verify locally:
# Basic: checks signature is valid (any signer)
aiir --verify receipt.json --verify-signature
# Recommended: pin to a specific CI identity for non-repudiation
aiir --verify receipt.json --verify-signature \
--signer-identity "https://github.com/myorg/myrepo/.github/workflows/aiir.yml@refs/heads/main" \
--signer-issuer "https://token.actions.githubusercontent.com"
⚠️ Always use--signer-identityand--signer-issuerin production. Without identity pinning, verification accepts any valid Sigstore signature. That proves someone signed it, but not your CI signed it. Extensions fields are not part of the content hash and should not be treated as security-relevant.
Install signing support: pip install aiir[sign]
MCP server details
The AIIR MCP server exposes seven tools:
| Tool | Description |
|---|---|
aiir_receipt |
Generate receipts for commit(s). Accepts commit, range, ai_only, pretty. |
aiir_verify |
Verify a receipt file's integrity. Accepts file path. |
aiir_stats |
Ledger statistics: receipt count, AI percentage, unique authors. |
aiir_explain |
Human-readable explanation of verification results. |
aiir_policy_check |
Check ledger against org AI-usage policy thresholds. |
aiir_verify_release |
Release-scoped verification — evaluate receipts against policy, emit VSA. |
aiir_gitlab_summary |
GitLab-flavored Markdown summary for Duo Chat, MR comments, and CI. |
Install globally:
pip install aiirClaude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"aiir": {
"command": "aiir-mcp-server",
"args": ["--stdio"]
}
}
}VS Code / Copilot (.vscode/mcp.json):
{
"servers": {
"aiir": {
"command": "aiir-mcp-server",
"args": ["--stdio"]
}
}
}Cursor (.cursor/mcp.json):
{
"mcpServers": {
"aiir": {
"command": "aiir-mcp-server",
"args": ["--stdio"]
}
}
}Continue (.continue/mcpServers/aiir.yaml):
name: AIIR
version: 0.0.1
schema: v1
mcpServers:
- name: aiir
command: aiir-mcp-server
args:
- --stdioOr copy any JSON MCP config (e.g., from Claude Desktop) into .continue/mcpServers/mcp.json —
Continue auto-discovers it.
Cline (open MCP Servers panel → add to cline_mcp_settings.json):
{
"mcpServers": {
"aiir": {
"command": "aiir-mcp-server",
"args": ["--stdio"]
}
}
}Windsurf (~/.codeium/windsurf/mcp_config.json):
{
"mcpServers": {
"aiir": {
"command": "aiir-mcp-server",
"args": ["--stdio"]
}
}
}The server uses the same zero-dependency core as the CLI. No extra packages needed.
Release verification & VSA
Verify an entire release against policy and produce a machine-readable Verification Summary Attestation (VSA):
# Verify receipts against strict policy, emit VSA
aiir --verify-release --receipts .aiir/receipts.jsonl --policy strict --emit-vsa
# Custom subject for the VSA (e.g., an OCI image digest)
aiir --verify-release --receipts .aiir/receipts.jsonl \
--subject 'oci://ghcr.io/myorg/app@sha256:abc123...' --emit-vsa aiir-vsa.intoto.jsonlThe VSA is an in-toto Statement v1 with a https://slsa.dev/verification_summary/v1 predicate that records:
- Verifier identity — who ran the check
- Policy digest — SHA-256 of the policy that was evaluated
- Coverage metrics — commit coverage percentage, AI/bot/human breakdown
- Pass/fail result — per-receipt and aggregate evaluation
Policy presets: strict (hard-fail, signing required, max 50% AI), balanced (soft-fail, signing recommended), permissive (warn-only). Customise via .aiir/policy.json.
Policy engine
Enforce organisational AI-usage policies in CI:
# Initialise a policy file from a preset
aiir --policy-init strict # creates .aiir/policy.json
aiir --policy-init balanced
aiir --policy-init permissive
# Run policy checks against the ledger
aiir --check --policy strict
# Quick gate: fail if AI-authored percentage exceeds a threshold
aiir --check --max-ai-percent 50Three presets:
| Preset | Enforcement | Signing | Max AI % | Use case |
|---|---|---|---|---|
strict |
Hard-fail | Required | 50% | Regulated industries, SOC 2, EU AI Act |
balanced |
Soft-fail | Recommended | 80% | Most teams — catches issues without blocking |
permissive |
Warn-only | Optional | 100% | Early adoption, experimentation |
Per-receipt checks: signing status, provenance repository, authorship class, schema validity.
Aggregate checks: AI commit percentage cap.
Commit .aiir/policy.json to your repo so every contributor and CI run uses the same rules.
Agent attestation
Attach structured metadata identifying which AI tool generated a commit:
aiir --agent-tool copilot --agent-model gpt-4o --agent-context ide
aiir --agent-tool cursor --agent-model claude-sonnet-4-20250514 --agent-context ide
aiir --agent-tool claude-code --agent-context cliStored in extensions.agent_attestation (not part of the content hash — adding or changing agent metadata does not invalidate receipts):
{
"extensions": {
"agent_attestation": {
"tool_id": "copilot",
"model_class": "gpt-4o",
"run_context": "ide"
}
}
}Six allowlisted keys: tool_id, model_class, session_id, run_context, tool_version, confidence. All values are sanitised (string coercion, 200-char cap).
in-toto Statement wrapping
Wrap receipts in a standard in-toto Statement v1 envelope for compatibility with the supply-chain attestation ecosystem:
# Wrap a receipt in an in-toto envelope
aiir --in-toto --output .receipts/
# Combine with signing for full attestation
aiir --sign --in-toto --output .receipts/The predicate type is https://aiir.dev/commit_receipt/v1. The subject identifies the git commit by repository URL and SHA. This makes AIIR receipts native to:
- SLSA verifiers
- Sigstore policy-controller
- Kyverno / OPA/Gatekeeper admission policies
- Tekton Chains supply-chain security
Detection internals
See THREAT_MODEL.md for full STRIDE analysis, DREAD scoring, and attack trees.
Homoglyph detection uses the full Unicode TR39 confusable map — 669 single-codepoint → ASCII mappings across 69 scripts (Cyrillic, Greek, Armenian, Cherokee, Coptic, Lisu, Warang Citi, Mathematical, and others). Combined with NFKC normalization, this covers all single-character homoglyphs documented by the Unicode Consortium. Multi-character confusable sequences are not covered — see S-02 in the threat model.
Everything below is verifiable. No testimonials-behind-a-login — just public artifacts you can audit yourself.
| Proof | What it proves | Verify it |
|---|---|---|
| This repo receipts itself | Dogfood — AIIR generates its own receipts on every push to main |
for f in .receipts/*.json; do aiir --verify "$f"; done |
| 1600+ tests, 100% coverage | Every release passes Python 3.9–3.13 × Ubuntu/macOS/Windows | CI runs |
| 25 conformance test vectors | Third-party implementors can verify their hashing against ours | schemas/test_vectors.json |
| Public threat model | Full STRIDE/DREAD analysis — we show attackers what we defend against | THREAT_MODEL.md |
| SLSA provenance on every release | PyPI wheel has a verifiable build attestation | gh attestation verify aiir-*.whl --repo invariant-systems-ai/aiir |
| OpenSSF Scorecard | Automated security health assessment (branch protection, SAST, signing) | Scorecard |
| CycloneDX SBOM on every release | Machine-readable bill of materials attached to every GitHub Release | Latest release → aiir-sbom.cdx.json |
| Zero dependencies | Nothing to compromise — pip show aiir shows Requires: empty |
pip install aiir && pip show aiir |
| Browser verifier | Client-side receipt verification — no upload, no account, no server | invariantsystems.io/verify |
The dogfood CI workflow generates receipts
on every push to main and commits them back to .receipts/. Locally, the
pre-commit hook receipts every commit at
post-commit stage.
Extensive security controls. 1600+ tests. Zero dependencies.
See SECURITY.md, THREAT_MODEL.md, and Tamper Detection.
Receipts exist at three trust levels. Choose the right one for your use case:
| Tier | What you get | Use when |
|---|---|---|
Unsigned (sign: false) |
Tamper-evident — hash integrity detects modification, but anyone can recreate a matching receipt from the same commit | Local development, smoke testing, internal audit trails |
Signed (sign: true, default) |
Tamper-proof — Sigstore signature binds the entire receipt (including extensions) to an OIDC identity via a transparency log | CI/CD compliance, SOC 2 evidence, regulatory audit |
| Enveloped (in-toto Statement) | Signed + wrapped in a supply-chain attestation envelope with explicit subject and predicate binding | SLSA provenance, cross-system verification, EU AI Act evidence packages |
Rule of thumb: For anything an auditor will read, use Signed or Enveloped. Unsigned receipts are developer convenience — do not cite them as tamper-proof evidence. See SPEC.md §11.1 for the full normative definition.
AIIR publishes a formal specification and machine-readable schema for third-party implementors:
| Document | Purpose |
|---|---|
| SPEC.md | Normative specification — canonical JSON, content addressing, verification algorithm (RFC 2119) |
| schemas/commit_receipt.v1.schema.json | JSON Schema (draft 2020-12) for aiir/commit_receipt.v1 |
| schemas/verification_summary.v1.schema.json | JSON Schema for the Verification Summary Attestation (VSA) predicate |
| schemas/test_vectors.json | 25 conformance test vectors with precomputed hashes |
| THREAT_MODEL.md | STRIDE/DREAD threat model with comprehensive security controls |
| docs/tamper-detection.md | Walkthrough — what happens when a receipt is modified |
Built by Invariant Systems, Inc.
License: Apache-2.0 — See LICENSE
Trademarks: "AIIR", "AI Integrity Receipts", and "Invariant Systems" are trademarks of Invariant Systems, Inc. See TRADEMARK.md.
Signed releases: Every PyPI release is published using Trusted Publishers (OIDC) — no static API tokens. The GitHub Actions workflow authenticates to PyPI via short-lived OIDC tokens issued by GitHub's identity provider. This means:
- No secrets to leak — publishing credentials are ephemeral
- Verifiable provenance — each release is tied to a specific GitHub Actions run, commit SHA, and workflow file
- Tamper-resistant pipeline — the publish environment is protected by GitHub's deployment protection rules
The publish workflow runs: tag push → full test suite → build → OIDC publish → verify on PyPI.
Enterprise: The AIIR open-source library is and will remain free under Apache-2.0. Invariant Systems may offer additional commercial products and services (hosted verification, enterprise dashboards, SLA-backed APIs) under separate terms.