> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tracelane.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# EU AI Act Article 12 — control mapping

> Map EU AI Act Article 12 recordkeeping topics to Tracelane's shipped audit-ledger and offline-verifier capabilities for deployer evidence packs.

# EU AI Act Article 12 — control mapping

<Warning>
  **Tracelane is not certified, audited or assessed against the EU AI Act.** No
  conformity assessment has been carried out on this product, no notified body
  has been involved, and no CE marking exists. Article 12 binds the **provider
  and deployer of the high-risk AI system** — a logging vendor cannot discharge
  that obligation on your behalf, and this page does not claim to.

  What follows is a **control mapping**: what Tracelane ships, described in
  enough detail that you can decide for yourself what — if anything — is worth
  citing as evidence inside your own conformity assessment. That assessment, and
  the judgement about whether these controls are sufficient for your system,
  remain yours.
</Warning>

> **Applicability (high-risk / Annex III).** Article 12 recordkeeping
> obligations for high-risk AI systems apply on a still-provisional timeline
> (target 2027-12-02, pending final adoption) — the 2026-08-02 date is the
> general-purpose AI milestone, not the Article 12 high-risk enforcement date.
> Confirm the timeline that applies to your system with your own counsel; we
> are not in a position to advise on it.

Article 12 of the EU AI Act concerns automatically-generated logs that can be
independently verified. Tracelane's Enterprise plan ships a tamper-evident ledger,
the cryptographic proofs over it, and a verifier that a third party can run
offline without trusting us. Below is what each of those mechanisms actually
does, mapped to the topic of each Article 12 paragraph.

## Control mapping

### 12(2)(a) — automatic generation of logs over the lifetime of the system

| Shipped capability — what you may cite as evidence                                                                                                                                                                                                                                                                                                                                                              |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The gateway writes one audit-log row per gateway-proxied event — LLM provider calls and guardrail-layer verdicts — signed into a per-tenant SHA-256 hash chain at write time. Tool-call / A2A / MCP event kinds exist in the schema but are chained only when routed through the gateway; SDK/OTLP-captured spans are full-fidelity but not yet chained. See `crates/gateway/src/audit.rs::AuditChain::append`. |

### 12(2)(b) — log enables identification of situations the system may pose a risk

| Shipped capability — what you may cite as evidence                                                                                                                                                                                                                                                                |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Every row carries `event_type`, `actor`, full canonical `payload`, plus structured GenAI semconv attributes. Guardrail blocks and predictive-layer interventions are stored as first-class event types so a regulator-side query can identify "every time the system refused a customer request" in milliseconds. |

### 12(2)(c) — log retention for at least six months

| Shipped capability — what you may cite as evidence                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The Enterprise plan carries a **180-day minimum retention** commitment for ledger data. That commitment is contractual rather than programmatically enforced: no ledger table carries a TTL and no scheduled job deletes ledger rows, so ledger data is retained indefinitely on every plan today. Two properties carry the six-month requirement and both are checkable against a running system: (1) **no scheduled job deletes ledger data** — the retention sweep operates on span tables only and never on the ledger; (2) **the ledger tables carry no TTL**, so no row expires on its own. Contractual retention above the floor is supported. That floor is **operational and contractual rather than programmatically enforced** — retention is bounded by the absence of any automatic expiry, not by a runtime check, and a shorter contractual term would be a process failure, not one the software refuses. **Which store this describes:** the canonical ledger — chain rows and per-batch anchor records — lives in the Postgres control plane (`crates/gateway/src/db/ledger.rs`), the rows written in the same transaction that advances the chain head; export and verification read that store and never fall back to a copy. The ClickHouse `audit_log` table is a **derived read copy** for the dashboards, written after commit and rebuilt from the canonical store at boot — its mutation history describes the copy, not the ledger. **What we do not claim, because the limit matters more than the reassurance:** we do not assert that no ledger row has ever been removed by an operator with database access, nor that the software can enumerate every such operation beyond the database provider's own logs. What protects the ledger against loss of its store is an hourly archive of the canonical rows and anchor bundles to object storage, each file count-checked against the store before upload — a stated recovery point of **≤ 1 hour**. This row therefore describes a retention **mechanism**, not an invariant we cannot falsify. |

### 12(3)(a) — log conformity with established procedures

| Shipped capability — what you may cite as evidence                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Three reference verifiers (Rust / Python / TypeScript) live at `packages/verifier-{rust,python,typescript}/` and produce byte-identical `VerifyReport` JSON for the same input. The row format is therefore pinned by three independent implementations plus the exported wire format (`format: "v2.1"`, `crates/gateway/src/audit_export.rs`) rather than by prose, so a third party can re-derive the procedure from a running verifier instead of taking our description of it on trust. |

### 12(3)(b) — log access by the deploying operator

| Shipped capability — what you may cite as evidence                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /v1/audit/export?since=<iso>&until=<iso>` streams the workspace's raw ledger rows plus its anchor records as NDJSON (`crates/gateway/src/audit_export.rs`). Authentication is the ordinary `Authorization: Bearer <jwt \| tlane_…>` used everywhere else; the tenant is resolved from the validated claim and **never** from a query parameter, and the route is gated on the Enterprise plan entitlement — fail-closed, returning 503 if the control plane is unreachable. Omitting `limit` returns the complete ledger, seq-paginated and uncapped. |

### 12(3)(c) — log access by the market-surveillance authority

| Shipped capability — what you may cite as evidence                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| There is no separate regulator-facing access path, and deliberately so: the deployer exports the range itself via the endpoint above and hands the NDJSON to the authority, which verifies it **offline** with `tracelane-audit verify --file` plus the workspace's Ed25519 public key from `GET /v1/audit/pubkey`. Nothing in the verification step requires Tracelane to be online, cooperative, or trusted. The verifier binary is built by a release workflow configured for Cosign signing + attested build provenance + Trusted Publishing to crates.io; that workflow has not yet run on a tag, so today a verifying party builds it from source (Apache-2.0, reproducible) rather than downloading a signed artefact. |

### 12(3)(d) — automatic prevention of unauthorised access

| Shipped capability — what you may cite as evidence                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Per-row authorisation is enforced at the gateway: every ClickHouse query filters on the caller's tenant (a CI guard blocks any new query that omits the filter), and every export or verification read of the Postgres ledger is keyed on the caller's tenant. Ledger integrity in V1 is **per-tenant** — each workspace has its own sequence and its own Ed25519 keypair, so a workspace's chain is verifiable end to end independently of any other. Integrity guarantees that span across workspaces are on the roadmap. |

## Field mapping table (verifier output → Article 12 topic)

> **What is live.** The hash-chain integrity proof (`hash_chain_valid`) is
> **live** and independently verifiable offline. For Enterprise workspaces the
> Ed25519 Merkle-root signing (`signatures_valid`) and Sigstore Rekor v2 public
> anchoring (`rekor_anchors_resolved`) below are **live in production**
> (best-effort: if the public log is unreachable a batch stays
> signed-but-not-yet-anchored and still verifies via the chain + Ed25519). eIDAS
> QTSP timestamping remains on the roadmap.

| `VerifyReport` field                                 | What it proves                                                                                                                                                                                                | Article 12 topic   |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
| `rows_seen`                                          | log was generated automatically                                                                                                                                                                               | 12(2)(a)           |
| `hash_chain_valid`                                   | (live) the log has not been mutated since write                                                                                                                                                               | 12(3)(a), 12(3)(d) |
| `signatures_valid`                                   | every Merkle root is Ed25519-signed by the workspace's per-tenant key (live)                                                                                                                                  | 12(3)(a)           |
| `rekor_anchors_resolved`                             | anchored batches are independently verifiable against the public Sigstore Rekor v2 log, offline from the exported inclusion proof + checkpoint (live, best-effort)                                            | 12(3)(a), 12(3)(c) |
| `anchors_included`                                   | how many batches were anchored at all — read with `rekor_anchors_resolved`, which counts only those whose proof resolved. Anchoring is per-batch and best-effort, so neither field implies universal coverage | 12(3)(a)           |
| `errors[].seq` + `errors[].kind` + `errors[].detail` | the *specific* row that failed verification, identifying any tampering attempt                                                                                                                                | 12(3)(a), 12(3)(d) |

A field being green proves the cryptographic property named in the middle
column and nothing beyond it. Whether that property is sufficient evidence for
the obligation in the right-hand column is a judgement for you and your
assessor, not something the verifier can output.

## Statement of capabilities

Tracelane publishes a written statement at
[`/audit/conformance-statement`](/audit/conformance-statement)
describing the ledger's guarantees and their limits in one place, so you can
attach a single document to your own records.

## Running the verifier (independent-verification quick-start)

The verifier is Apache-2.0 and builds from source, which is the path available
today — no signed release artefact has been published yet.

```bash theme={"system"}
# 1. Build the verifier from source.
git clone https://github.com/tracelane/tracelane
cargo build --release -p tracelane-audit
#    -> target/release/tracelane-audit

# 2. Export the ledger range under audit (deployer-side, any Bearer
#    credential the workspace already uses).
curl -H "authorization: Bearer $TRACELANE_TOKEN" \
  'https://gateway.tracelane.dev/v1/audit/export?since=2026-05-01T00:00:00Z&until=2026-05-26T00:00:00Z' \
  -o ledger.ndjson

# 3. Verify it offline. Nothing here contacts Tracelane.
./target/release/tracelane-audit verify \
  --file ledger.ndjson \
  --tenant-pubkey I5rZ...workspace-ed25519-pubkey-base64
# Exit 0 -> PASS. Exit 1 -> FAIL (or INCOMPLETE). Exit 2 -> I/O.
```

Once the release workflow has run on a tag, the published binary can be
verified before use:

```bash theme={"system"}
cosign verify-blob \
  --bundle tracelane-audit-x86_64-unknown-linux-musl.cosign.bundle \
  --certificate-identity-regexp 'github.com/tracelane/tracelane/.github/workflows/release-audit-cli.yml' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  tracelane-audit-x86_64-unknown-linux-musl
```

**Without `--tenant-pubkey` the anchor layer does not run at all**, so a forged anchor would not be caught. The verifier therefore exits **non-zero with `INCOMPLETE`** when a ledger carries anchor records and no trusted key was supplied. Obtain the key out-of-band (dashboard Settings → Audit signing key, or `GET /v1/audit/pubkey`) — never from the export you are auditing.

For air-gapped verification, request an NDJSON export over secure
courier and run with `--file <path> --offline`. The chain + Merkle
checks run locally and are the live integrity proof. Rekor
inclusion-proof verification is live for anchored batches and runs
offline from the bundled inclusion proof + signed checkpoint (no live
Sigstore call needed); `--offline` skips the anchor layer entirely,
leaving the chain-integrity check.

## V1 launch deferrals

* **`tracelane-audit export --format pdf`** — a rendered evidence packet. The
  V1 `--format text` and `--format json` outputs are human-readable; they can
  be rendered to PDF with any external tool. V1.1 ships a built-in renderer.
* **Automated audit-data retention cleanup.** Retention above the floor is
  honoured today by the absence of any automatic expiry on ledger data; a
  scheduled cleanup that enforces the ceiling is planned. See the 12(2)(c)
  row above for exactly what is and is not claimed.

## Further reading

* [EU AI Act, full Article 12](https://artificialintelligenceact.eu/article/12/)
* [tracelane-audit CLI usage](/audit/verifier-cli)
* [Statement of capabilities](/audit/conformance-statement)
