Compliance Bundles (EU AI Act Art 12)
Cryptographic snapshot of every receipt in a period — what an auditor verifies offline. Merkle-rooted, signed, framework-mapped, self-contained JSON.
Bundle vs Report. A bundle is a machine-readable JSON file an auditor verifies offline. A Compliance Report is the human-readable PDF derived from the same data. Most teams produce both. See The Five Aira Sub-Products for the full taxonomy.
What it is
A compliance bundle is a sealed snapshot of every action receipt in a date range, organized to satisfy a specific regulatory framework's audit log requirements. Each bundle:
- Lists every receipt in the period in deterministic order.
- Builds a SHA-256 Merkle root over the receipts' payload hashes (RFC 6962 domain separation).
- Signs the bundle metadata with the active Aira signing backend (Ed25519 today).
- Optionally attaches an RFC 3161 timestamp token from an independent timestamping authority.
- Embeds the JWKS URL so the recipient can verify the bundle offline against the published public key.
The exported document is self-contained: an auditor downloads one JSON file and can re-run every verification step with OpenSSL. They never have to call Aira again.
Supported frameworks
| Framework | Identifier | What it maps |
|---|---|---|
| EU AI Act, Article 12 | eu_ai_act_art12 | Period of use, input/output hashes, system identifier, instruction hash, natural persons in the loop, policy decision chain, tamper-evidence |
| ISO/IEC 42001:2023 | iso_42001 | A.6.2.6 documentation, A.6.2.7 logging, A.7.4 performance management, A.9.4 stakeholder accountability |
| AIUC-1 | aiuc_1 | Tamper-evident audit log, cryptographic proof of human approval, independently verifiable signatures |
| SOC 2 Common Criteria 7 | soc_2_cc7 | CC7.1 logging, CC7.2 detection, CC7.3 response |
| Raw bundle | raw | No framework mapping; every receipt verbatim |
If you need a framework that isn't on the list, use raw and apply your own mapping post-hoc — every receipt's signed payload is included.
Create a bundle
Dashboard
Dashboard → Compliance → Seal new bundle. Pick a framework, a date range, optional title, and (optionally) an agent filter. The bundle is sealed immediately and you land on the detail page where you can download the JSON.
API
curl -X POST https://api.airaproof.com/api/v1/compliance/bundles \
-H "Authorization: Bearer aira_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"framework": "eu_ai_act_art12",
"period_start": "2026-01-01T00:00:00Z",
"period_end": "2026-04-01T00:00:00Z",
"title": "Q1 2026 evidence packet",
"agent_filter": ["payments-agent", "support-agent"]
}'The response includes the bundle id, Merkle root, signature, framework summary, and signing key id.
Download the self-contained JSON
curl https://api.airaproof.com/api/v1/compliance/bundles/<bundle_uuid>/export \
-H "Authorization: Bearer aira_live_xxx" \
-o aira-compliance.jsonThe exported document inlines every receipt's signed_payload and signature, plus the Merkle root, the bundle signature, the public key, the JWKS URL, and a verification recipe. This is the artifact you ship to a regulator.
Verify the bundle offline
import json
import hashlib
import base64
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
LEAF_PREFIX = b"\x00"
NODE_PREFIX = b"\x01"
def merkle_root(payload_hashes: list[str]) -> str:
"""RFC 6962-style Merkle root over receipt payload hashes."""
if not payload_hashes:
return hashlib.sha256(b"").hexdigest()
level = [hashlib.sha256(LEAF_PREFIX + ph.encode()).digest() for ph in payload_hashes]
while len(level) > 1:
if len(level) % 2 == 1:
level.append(level[-1])
level = [
hashlib.sha256(NODE_PREFIX + level[i] + level[i + 1]).digest()
for i in range(0, len(level), 2)
]
return level[0].hex()
with open("aira-compliance.json") as f:
bundle = json.load(f)
# 1. Recompute the Merkle root
recomputed = merkle_root([r["payload_hash"] for r in bundle["receipts"]])
assert recomputed == bundle["merkle_root"], "Merkle root does not match — bundle tampered"
print("Merkle root verified:", recomputed)
# 2. Verify the bundle signature
pub_bytes = base64.b64decode(bundle["signing"]["public_key"])
pub_key = Ed25519PublicKey.from_public_bytes(pub_bytes)
bundle_metadata = {
"org_uuid": bundle["org_uuid"],
"framework": bundle["framework"],
"period_start": bundle["period_start"],
"period_end": bundle["period_end"],
"receipt_count": bundle["receipt_count"],
"merkle_root": bundle["merkle_root"],
"agent_filter": bundle["agent_filter"],
"sealed_at": bundle["sealed_at"],
"alg": bundle["signing"]["algorithm"],
}
canonical = json.dumps(bundle_metadata, sort_keys=True, separators=(",", ":"))
sig_b64 = bundle["signature"].removeprefix("ed25519:")
sig_bytes = base64.urlsafe_b64decode(sig_b64)
pub_key.verify(sig_bytes, canonical.encode())
print("Bundle signature verified")
# 3. Verify each receipt
for r in bundle["receipts"]:
receipt_canonical = json.dumps(r["signed_payload"], sort_keys=True, separators=(",", ":"))
expected_hash = "sha256:" + hashlib.sha256(receipt_canonical.encode()).hexdigest()
assert expected_hash == r["payload_hash"], f"Receipt {r['receipt_uuid']} payload mismatch"
sig = base64.urlsafe_b64decode(r["signature"].removeprefix("ed25519:"))
pub_key.verify(sig, receipt_canonical.encode())
print(f"All {bundle['receipt_count']} receipts verified")If every assertion passes, the bundle is genuine. If any fails, you have proof of tampering.
Inclusion proofs
If you only want to prove that a single receipt was included in a bundle without holding the whole list, request a Merkle inclusion proof:
curl https://api.airaproof.com/api/v1/compliance/bundles/<bundle_uuid>/inclusion-proof/<receipt_uuid> \
-H "Authorization: Bearer aira_live_xxx"{
"bundle_uuid": "...",
"receipt_uuid": "...",
"leaf_hash": "abc123...",
"index": 42,
"leaf_count": 1000,
"siblings": ["...", "...", "..."],
"merkle_root": "..."
}A standalone verifier walks the siblings from the leaf to the root in O(log n) and confirms that the receipt was sealed into the bundle. This is useful when an auditor only cares about one specific event.
What gets verified
The bundle proves four things, end to end:
- Each receipt's signature is valid — the agent action it describes really happened with the data it describes.
- The Merkle root is consistent with the receipt list — no receipt was added or removed after sealing.
- The bundle metadata signature is valid — the framework, period, and agent filter were not tampered with.
- The RFC 3161 timestamp token (if attached) — Aira saw the bundle at a specific moment, witnessed by an independent timestamping authority.
Aira does not need to be online for verification. The recipient does not need an Aira account. Every verification step uses standard primitives (SHA-256, Ed25519, RFC 6962, RFC 3161, RFC 8037 JWKS) that any auditor's existing tooling already understands.
Output filtering
Scan what your agents actually output — flag leaks, deny bad receipts, or redact matched spans before they're hashed into the signed payload.
Compliance Reports — EU AI Act Article 12 / 9 / 6
Same data as a bundle, rendered as a regulator-ready PDF — what your compliance officer hands the regulator.