Evidence Vault
The system of record for compliance evidence. Every policy decision, HITL approval, DLP finding, and CI/CD run is captured as an immutable, signed, hash-chained event — queryable across all frameworks from a single store. No cloud required.
Core principle: the vault has exactly one write path (append a new EvidenceEvent) and many read paths. There is no update or delete on an event. Current state — like whether a HITL request is still pending — is always computed by reading the append-only log.
Architecture
Evidence Vault v2 is an event-sourced ledger applied to compliance evidence. Tamper-evidence is an architectural property, not a policy checkbox.
- EvidenceEvent — one immutable fact (Cedar decision, HITL action, DLP scan, CI/CD run, …). Signed with HMAC-SHA256 and linked to the previous event via
prev_event_hash. - EvidenceChain — computed grouping (e.g. HITL lifecycle from
hitl_requested→hitl_resolved). Rebuildable from raw events if the index is lost. - ControlMapping — links each event to N compliance controls (CO-004, AIUC-B006, ISO-A.6.2.8, …). One indexed lookup powers every framework report.
- RetentionPolicy — computed at write time from attached controls. The longest retention requirement wins — never the shortest.
Where data lives
| File | Contents |
|---|---|
~/.iris/evidence/<agent_id>/events.jsonl | Legacy runtime log (policy evaluations, violations) |
~/.iris/evidence/<agent_id>/evidence_events.jsonl | v2 append-only signed event log (system of record) |
~/.iris/evidence/<agent_id>/control_mappings.jsonl | Event → control many-to-many index |
~/.iris/evidence/<agent_id>/assessments.jsonl | Impact assessments (never pruned) |
Override the root with --vault-dir on CLI commands or set a custom path in SDK integrations.
Event types
Each event type has a strict payload schema — unknown fields are rejected at write time.
| event_type | When it fires |
|---|---|
cedar_decision | Every Cedar policy evaluation (PERMIT / BLOCK / HITL) |
hitl_requested | Human review queue opened |
hitl_resolved | Review approved, rejected, or timed out |
dlp_finding | SSN, PHI, credit card, or prompt-injection detected |
drift_detected | Policy intent vs compiled Cedar drift |
agent_registered | Agent passport created or updated |
policy_compiled | Intent compiled to Cedar in CI or locally |
rotation_event | API key or secret rotation |
org_policy_changed | iris-security.yaml or org policy repo change |
cicd_run | Pipeline run recorded from CI/CD (see below) |
event_expired | Tombstone when retention elapses or erasure completes |
Payloads never contain raw prompt content — only hashed identifiers (user_id_hash, rule IDs, metadata). pii_redacted is always true.
CLI commands
Record CI/CD evidence
Write a signed cicd_run event from any pipeline. Runs even when the governance gate failed — a failed check is evidence.
--system github_actions \
--run-id "${{ github.run_id }}" \
--pipeline-url "https://github.com/org/repo/actions/runs/123" \
--triggered-by "developer@company.com" \
--outcome success
Supported --system values: github_actions, gitlab, jenkins, terraform, argocd. Returns signed event_id and sequence_number as JSON.
Audit reports
iris evidence report --agent loan-processor --format markdown
iris evidence report --agent loan-processor --since 2026-01-01
Includes passport snapshot, compliance status, violation summary, retention window, and integrity check — ready for regulatory review.
List and export
iris evidence list --agent loan-processor --violations-only
iris evidence query --decision deny
iris evidence query --agent loan-processor --regulation AIUC-1 --risk-min 70
iris evidence export --agent loan-processor --output audit.json
iris evidence export --agent loan-processor --output aiuc1.json --format aiuc1
iris evidence stats
evidence query is the signal command — filter by agent, decision, regulation, date range, and risk instead of scrolling the full vault. Export formats: json, csv, aiuc1, pdf, oscal. The aiuc1 format pulls from the same ControlMapping table as iris certify --format aiuc1-export — one code path, not a separate scan.
GDPR erasure
Erasure writes a tombstone event and scrubs user-identifying payload fields while preserving control mappings (so "we satisfied CO-004 on this date" remains provable).
Evidence lifecycle
- Capture — governed action writes exactly one signed
EvidenceEvent. The only stage where new facts enter the vault. - Index — async, non-blocking. Updates
ControlMappingand chain views. Index failure does not lose the event — callrebuild_from_events()to recover. - Query — read-only. Reports,
iris certify, AIUC-1 export, and auditor packages operate here. Unscoped queries above 500 events are rejected. - Retention check — daily job marks events past
eligible_for_deletion_at. Legal holds skip deletion and log aretention_extendedevent. - Disposition — expired events get an
event_expiredtombstone; payload is scrubbed but the hash chain and timeline remain gap-free.
Retention model
When an event satisfies multiple controls, retention takes max() across all attached requirements — never the shortest period.
| Control | Framework | Days |
|---|---|---|
CO-RR-001 | Colorado AI Act | 1095 (3 years) |
LL144-005 | NYC Local Law 144 | 730 (2 years) |
PIPL-006 | China PIPL | 1095 (3 years) |
CCPA-006 | CCPA/CPRA ADMT | 1095 (3 years) |
HIPAA-001 | HIPAA (general) | 2190 (6 years) |
FEDRAMP-CONMON | FedRAMP | 1095 minimum |
AIUC-1 | AIUC-1 | 365 minimum (annual recert cadence) |
| unmapped | — | 365 default (never 0) |
Free tier prunes legacy events.jsonl after 30 days. Pro unlocks unlimited retention for long-horizon controls like HIPAA and Colorado's 3-year requirement.
Integrity verification
Every event is HMAC-signed over event_id + sequence_number + payload_hash + prev_event_hash. Tampering with event N breaks the chain for every subsequent event.
# Integrity section shows hash chain status
# Programmatic check (Evidence Vault API):
GET /v1/agents/<agent_id>/integrity
# → {"valid": true, "events_checked": N}
# or {"valid": false, "first_broken_link": "<event_id>"}
Query responses include a signed integrity proof (query_signature) so callers can prove they received exactly the result set returned.
Query API
Hosted and Enterprise deployments expose a read-heavy REST API. There is exactly one write endpoint and no DELETE or PATCH on events.
| Method | Endpoint | Purpose |
|---|---|---|
POST | /v1/events | Append one event (server signs — client signatures ignored) |
GET | /v1/agents/:id/events | List events (scoped by date range, event type) |
GET | /v1/agents/:id/chains | HITL and rotation lifecycle chains |
GET | /v1/controls/:id/evidence | All events satisfying a control — one query, all frameworks |
GET | /v1/frameworks/:id/coverage | FULL / PARTIAL / NONE breakdown for iris certify |
GET | /v1/agents/:id/integrity | Walk hash chain, return validity |
GET | /v1/agents/:id/export | Auditor package on demand (pdf, json, oscal, aiuc1) |
Admin scope (security team): POST /v1/erasure-requests, POST /v1/legal-holds, DELETE /v1/legal-holds/:id. Even admins cannot delete events — only request erasure through the lifecycle.
CI/CD integrations
Every integration writes cicd_run events at meaningful pipeline moments. Pipelines never read from the vault during deploy — evidence recording never blocks deployments.
Copy-ready templates live in the repo under docs/gha/:
| Platform | Template | Key pattern |
|---|---|---|
| GitHub Actions | docs/gha/developer.yml | if: always() — records evidence even on failure |
| GitLab CI | docs/gha/gitlab-ci.yml | rules: when: always |
| Jenkins | docs/gha/Jenkinsfile | post { always { ... } } |
| Terraform | terraform/evidence_record.tf | null_resource on policy hash change |
| ArgoCD | docs/gha/argocd-evidence-hook.yaml | PostSync hook after actual deploy |
GitHub Actions example
if: always()
run: |
iris evidence record-cicd \
--system github_actions \
--run-id "${{ github.run_id }}" \
--pipeline-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
--triggered-by "${{ github.actor }}" \
--outcome "${{ job.status }}"
Compliance as Code, Evidence as Code
These are architectural properties, not separate features:
- Compliance as Code —
governance/agents/<name>/policy.cedaris the compliance policy: version-controlled, PR-reviewed, diffable. See Policy as Code. - Evidence as Code — every pipeline run writes a permanent, queryable
EvidenceEvent. If you can ask "what changed in this PR," you can ask "what evidence did this PR generate" with the same tooling.
Compliance as Code declares what should happen. Evidence as Code proves what did happen. The gap between them is what iris drift check and iris sentinel monitor continuously.
Pair with certification
iris certify --agent loan-processor --framework aiuc-1 --format aiuc1-export
iris evidence export --agent loan-processor --format aiuc1 --output auditor-package.json
Certification readiness scores plus Evidence Vault exports form the auditor package. All framework reports query the same ControlMapping index — generate evidence once, satisfy many frameworks.
Environment variables
| Variable | Description |
|---|---|
IRIS_ENV | Resolved environment stamped on every event (dev, staging, production, …) |
IRIS_AGENT_ID | Default agent for record-cicd when --agent is omitted |
IRIS_VAULT_SIGNING_KEY | HMAC signing key override (default: derived per agent for local dev) |