Blog homeKYC for AI AgentsIntegration guideEU AI Act checklistCompare
← Back to blog
· By

EU AI Act Article 12: How to Implement Automatic Logging

Technical guide to implementing Article 12 automatic logging requirements for high-risk AI agents using Write-Once-Read-Many (WORM) database storage.

Preferred source on Google
EU AI Act Article 12
Table of Contents

TL;DR

  • Article 12 requires high-risk AI systems to automatically log every operation in a way that's traceable and tamper-evident.
  • A compliant log row needs: agent identity, event type, payload hash, model version/prompt hash, a cryptographic signature, and a hash-chain link to the prior row.
  • Standard application logs (stdout, unencrypted files) fail this standard — logging has to be WORM-enforced at the database layer.
  • Kakunin routes log writes through QStash async ingestion so logging never blocks the agent's own response latency.
  • Article 12 logs are the evidentiary substrate that Article 9 risk management and Article 14 human oversight both depend on.

What does a log entry need to satisfy EU AI Act Article 12?

An Article 12-compliant log row needs five things: the specific agent identity that generated the event, a payload hash of the input/output (not necessarily the raw content), the model version and prompt hash used, a cryptographic signature proving the row came from that agent and wasn't altered, and a hash-chain link to the previous row so a missing entry — not just a modified one — is detectable. Database-level WORM enforcement, not application-level discipline, is what makes this audit-defensible.

Introduction: The Article 12 Legal Requirement

Under Article 12 of the EU AI Act (Regulation EU 2024/1689), high-risk AI systems must technically enable automatic logging of events ('logs') throughout their lifecycle. The regulation specifies that logging capabilities must guarantee a level of traceability appropriate to the intended purpose of the system.

Specifically, Article 12(2) mandates that logs must enable:

  • Detection of operational anomalies and behavioral drift.
  • Post-incident investigation and root-cause reconstruction.
  • Continuous monitoring of system performance under Article 72.

For autonomous AI agents executing multi-step API calls and LLM reasoning loops, standard application logs (e.g., stdout or unencrypted log files) fail regulatory standards. This guide presents a production architecture for implementing Article 12 compliant automatic logging.

---

Technical Requirements for Article 12 Compliant Logs

To satisfy European supervisory audits, AI agent log storage must meet four criteria:

  • WORM Immutability: Write-Once-Read-Many storage where UPDATE and DELETE operations are cryptographically or at the database level forbidden.
  • Cryptographic Non-Repudiation: Every log entry must include a digital signature generated by the agent's private key (stored in AWS KMS).
  • Payload & Input Hash Traceability: Logs must record SHA-256 hashes of system prompts, context windows, model weight digests, and output payloads.
  • Time-Stamp Accuracy: ISO-8601 UTC timestamps synchronized via Network Time Protocol (NTP) to sub-millisecond precision.

---

Implementation Guide: WORM Audit Logging in TypeScript

Using the Kakunin SDK, ingesting an Article 12 compliant event takes five lines of code. Behind the scenes, Kakunin's PostgreSQL database enforces immutability via database-level triggers blocking UPDATE and DELETE operations on the audit_log table.

---

Exporting Logs for Regulators

When national competent authorities request compliance documentation, log packages can be exported in standardized JSON or PDF formats via our Printable Compliance Attestation Template.

For full framework mappings, consult our Regulatory Mapping Matrix.

Anatomy of a Compliant Log Entry

A single audit_log row needs to answer four questions on its own, without joining to any other table that might change: who or what triggered the event, what happened, when, and how the record can be proven unaltered. In Kakunin's schema, this means every row carries a tenant_id for isolation, an agent_id tied to the certificate issued for that agent, an event_type drawn from a fixed enum rather than a free-text string, and a payload_hash — a SHA-256 digest of the request or output body rather than the raw body itself where the raw content is sensitive or large.

Alongside those fields sits the traceability chain Article 12(2) actually cares about: the model identifier and version used for the inference step, a hash of the system prompt or policy configuration active at the time, and, where relevant, a reference to the input context window rather than a full copy of it. Storing full LLM context on every row is rarely necessary and often counterproductive from a data-minimization standpoint under GDPR Article 5(1)(c); a hash lets an auditor verify that a specific context was used without Kakunin retaining the content itself unless the tenant opts into full-payload retention.

The signature field is what turns a log row from a database entry into non-repudiable evidence. Kakunin signs the canonicalized row contents using the agent's private key material held in AWS KMS (RSA_2048), so the signature can be verified against the agent's X.509 certificate independently of Kakunin's own infrastructure. A regulator, or the tenant's own external auditor, does not need to trust Kakunin's database — they need to trust the KMS-backed key custody chain, which is a materially stronger position when a supervisory authority asks 'how do you know this log wasn't fabricated after the fact.'

Finally, every row carries a monotonically increasing sequence number scoped to the agent, plus the SHA-256 hash of the previous row in that sequence. This hash-chaining is what lets a verifier detect a missing row, not just a modified one — WORM triggers stop UPDATE and DELETE, but a hash chain is the only thing that proves nothing was ever silently skipped during ingestion.

Retention Periods and Storage Cost Planning

Article 12 does not itself specify a retention duration — it delegates that detail to sector-specific rules and to the provider's own technical documentation obligations under Article 11. For AI agents used by CASPs under MiCA, the practical anchor is MiCA's own record-keeping expectation, which aligns with the five-year retention window applied elsewhere in EU financial services record-keeping (comparable to MiFID II Article 16(6) and EMIR trade reporting norms). Financial AI operators outside crypto-asset services should map to whatever sectoral retention period already governs their transaction records, since supervisory authorities will expect AI decision logs to be retrievable for at least as long as the underlying financial records they relate to.

Five years of per-event logging for an autonomous agent making dozens of API calls per task adds up fast, and this is where a lot of teams underbudget. A conservative planning estimate: an agent handling 50 reasoning/tool-call events per completed task, at roughly 2KB per row once payload hashes (not raw payloads) are stored, generates about 100KB of audit_log data per task. At 10,000 tasks a day that is 1GB/day, or roughly 1.8TB over a five-year retention window, before index overhead. Storing raw prompts and outputs rather than hashes can push this ten to twenty times higher, which is the strongest practical argument for hash-referencing large payloads and keeping the originals in cheaper, separately-versioned object storage (Supabase Storage or S3 with its own WORM object lock) referenced by hash rather than duplicated inline.

Cost planning should also account for index bloat on the hash-chain and sequence columns, which are queried constantly during verification and rarely during normal operation — a partial or BRIN index strategy on the timestamp column keeps write throughput high without sacrificing the ability to do fast anomaly-detection scans across the full history.

Handling Log Volume at Scale

The single biggest mistake teams make when first implementing Article 12 logging is writing directly to the audit_log table synchronously, inline with the agent's own request path. An agent running a multi-step reasoning loop that blocks on a database round-trip after every tool call adds latency the agent's own SLA usually can't absorb, and it couples logging availability to task availability — if the logging write fails, does the agent's actual work fail too? Neither answer is good without a queue in between.

Batching Writes Without Breaking Immutability

Batching audit writes is safe as long as the hash chain is computed before the batch is queued, not after — the sequence number and previous-row hash must be assigned at event-generation time so that ingestion order and logical order can be reconciled even if a batch arrives out of order or a batch partially fails and is retried. A batch of ten events becomes ten independent rows on insert, each individually WORM-protected the moment its transaction commits; there is no compliance benefit to batching the events themselves, only to batching the network calls that deliver them.

Async Ingestion via QStash

Kakunin routes all audit log writes generated by agent activity — not the certificate issuance or revocation events, which are written synchronously because they gate access decisions — through QStash with retries set to 3. The agent's SDK call returns as soon as the event is durably queued, and the actual INSERT against Postgres happens in a worker that verifies the QStash signature before touching the database. This decouples an agent's response latency from audit-log write latency entirely, and because QStash retries on failure, a transient database blip does not silently drop a compliance-relevant event — it just delays it, typically well within the same second, and never anywhere close to the 60-second SLA Kakunin holds itself to for the separate concern of risk-score-triggered revocation.

The tradeoff worth naming explicitly: async ingestion means there is a small window, bounded by QStash's delivery latency, where an event has occurred but has not yet landed in the immutable log. For behavioral risk scoring this is fine, since the risk engine consumes events from the same queue in parallel rather than waiting on the audit_log write. For anything a human oversight workflow needs to see in real time, that same event stream — not the audit_log table — is the correct source, with audit_log serving as the durable, immutable record of what was seen rather than the live feed itself.

Common Implementation Mistakes

Four mistakes account for most of the Article 12 implementations that look compliant on paper but fail an actual audit.

The first is mutable logs disguised as immutable ones: a soft-delete flag, a status column that gets updated after the fact, or an application-layer check that merely discourages edits rather than a database trigger that forbids them. Article 12 compliance has to be enforced at the layer an application developer cannot route around, which is why Kakunin's WORM protection is a Postgres trigger blocking UPDATE and DELETE at the database level rather than an ORM-level guard — a service-role key with raw SQL access is exactly the kind of access an incident investigation will assume was available to a bad actor.

The second is a missing or broken hash chain. Teams often add cryptographic signing to individual rows and stop there, which proves a given row wasn't altered but does nothing to prove no row was deleted or inserted out of sequence. Without the previous-row-hash link, a sufficiently privileged attacker who can still reach the underlying storage (a snapshot restore, a replica with laxer permissions) could delete a row entirely and leave no trace. The chain is what makes deletion detectable even when direct row modification is already blocked.

The third is clock drift across distributed ingestion workers. If different QStash consumers or different regions timestamp events using local system clocks instead of a synchronized source, the ordering implied by timestamps can contradict the ordering implied by sequence numbers, which is exactly the kind of inconsistency a forensic reconstruction under Article 12(2)(b) will flag. NTP synchronization to sub-millisecond precision is necessary but not sufficient — the sequence number, not the timestamp, should be treated as the authoritative order for reconstruction, with the timestamp used for human-readable context.

The fourth is logging the decision but not the inputs that produced it. A row that records 'agent executed trade' with a timestamp and an outcome, but no reference to the market data, risk score, or system prompt in effect at that moment, satisfies the letter of 'log this event' while failing the actual purpose of Article 12 — enabling someone to understand why the system behaved as it did. Every state-changing event should carry enough referential context (via hash, not necessarily raw content) to answer 'why' as well as 'what.'

Article 12 in the Context of Articles 9 and 14

Article 12 logging is not an isolated paperwork requirement — it is the evidentiary substrate that Article 9's risk management system and Article 14's human oversight obligations both depend on to function rather than exist only on paper.

Supporting Article 9 Risk Management

Article 9 requires a continuous, iterative risk management process, which in practice means a system that can detect when an agent's behavior has drifted from its expected risk profile and respond before harm compounds. Kakunin's behavioral risk score, computed in real time on a 0.00–1.00 scale from the same event stream that feeds audit_log, is only as trustworthy as the log data underneath it — a risk model trained or scored against incomplete or tamperable logs will produce a risk assessment that cannot itself withstand audit. Auto-revocation at a risk score of 0.85 or above, propagated to the mTLS gateway via CRL/OCSP within a 60-second SLA, is the enforcement action; the audit_log entries generated before, during, and after that revocation are what let a supervisory authority verify the risk management system actually caught what it claims to have caught, rather than taking the vendor's word for it.

Enabling Article 14 Human Oversight

Article 14 requires that human overseers be able to correctly interpret a high-risk AI system's output and intervene when necessary, which is impossible if the only record of what the system did is the system's own real-time output. A compliant log gives a human overseer a durable, independently verifiable trail to review after an intervention — or after a decision not to intervene is later questioned — that does not depend on the agent's own self-reporting. In practice this means the same audit_log rows used for regulatory export should be queryable by a compliance team through a readable interface, not just as raw database rows; Kakunin exposes this through its dashboard and through the exportable compliance attestation format referenced earlier, so a human reviewer performing Article 14 oversight duties and an external auditor performing an Article 12 review are working from the same underlying evidence rather than two different systems of record that could drift apart. Teams building this integration themselves should consult the SDK's event schema reference at /docs/audit-log before mapping their own event types, since aligning custom event_type values to the same enum the risk engine consumes is what keeps the oversight and risk-management views consistent.

FAQ

Does Article 12 require raw prompts and outputs to be stored in the audit log, or is a hash sufficient?

The regulation requires traceability, not necessarily verbatim storage. Storing a SHA-256 hash of the prompt, context, and output alongside a reference to where the full content lives (in separately versioned storage) satisfies traceability while limiting how much sensitive data sits in the primary log table. Full-content logging is a defensible choice for higher-risk use cases, but hashing is generally the better default for data minimization under GDPR.

How long must AI agent logs be retained under the EU AI Act?

Article 12 itself doesn't set a fixed number; retention follows sector-specific record-keeping rules. For CASPs and financial AI operators, a five-year window aligned with MiFID II and MiCA record-keeping norms is the practical benchmark most teams should plan storage costs around, though your specific sectoral regulator's guidance should take precedence if it specifies something longer.

Can log ingestion be asynchronous, or does Article 12 require synchronous, blocking writes?

Asynchronous ingestion is fine as long as event ordering and hash-chain integrity are established at the point the event is generated, not at the point it's written to the database. Kakunin queues audit events through QStash with retries so agent response latency isn't coupled to log write latency, while sequence numbers assigned upfront preserve the ability to reconstruct exact order later.

What's the difference between WORM immutability and cryptographic signing, and do I need both?

WORM immutability (database triggers blocking UPDATE/DELETE) stops in-place tampering. Cryptographic signing proves a specific row was produced by a specific agent's key and hasn't been altered, independent of trusting the database operator. You need both: WORM alone doesn't survive a privileged actor with direct storage access, and signing alone doesn't stop someone from deleting a row entirely if the database permits deletes.

How do Article 12 logs relate to the human oversight requirements in Article 14?

Article 14 oversight is only meaningful if a human reviewer has an independent, tamper-evident record to check decisions against — otherwise oversight relies on the agent accurately reporting its own behavior. Article 12 logs are that record. Effective implementations expose the same underlying log data to both the human oversight dashboard and the regulatory export path, so the two never diverge.

Palash Bagchi
Published September 19, 2026

Founder of Kakunin, building cryptographic identity and compliance infrastructure for autonomous AI agents.

All articles →
Read more from the blog
Documentation →
API reference and guides