The GDPR vs. EU AI Act Paradox: Why Standard LLM Observability Violates One Law to Comply with Another
Teams preparing for Art. 12 reuse their tracing stack. By default it keeps what data protection law wants kept least: the raw prompt.
user_idmasking opt-in
SDK v1.0
Day 180 is the six-month floor in Art. 19(1) and 26(6). At 1,000 conversations a day, a default setup then holds 180,000 plain-text transcripts: names, CPR numbers, diagnoses, whatever users typed.Fair note. LangSmith and Helicone offer masking, self-hosting and EU regions. Teams must configure them; the default logs raw text. Based on public documentation, October 2026.
§What each law asks
EU AI Act record
- Art. 12(1)
- Automatic event logs over the system’s lifetime
- Art. 19(1), 26(6)
- Keep them at least six months
- Art. 26(6)
- “unless provided otherwise in … Union law on the protection of personal data”
Art. 12(3) asks for input data only for remote biometric identification.
GDPR minimise
- Art. 5(1)(c)
- Only what is necessary
- Art. 5(1)(e)
- Identifiable no longer than necessary
- Art. 25(2)
- Necessary data only, by default
- Art. 32(1)(a)
- Pseudonymisation as a security measure
§The precise claim
Storing prompts is not unlawful in itself; with a lawful basis and short retention, many teams keep them for debugging. The conflict starts when the AI Act becomes the reason to keep them. And users volunteer Art. 9 health data (“I’m in hospital this week”), so free text cannot be bounded in advance. Only what you keep can.
A compliant log proves what happened. It does not need to hold what was said.
§Keyed HMAC: prove it, keep nothing said
Same text and key, same digest. So a disputed transcript is verified by recomputing, without the log ever holding it. The key matters: a plain SHA-256 of a CPR number is enumerable in seconds; without your key, the HMAC is not.Key custody. Keep the key in a KMS apart from the logs, rotate it, and record key_version.
// Captured on every request, kept for the store's retention period
tracer.log({
user_id: session.userId,
prompt: messages.at(-1).content,
completion: result.text,
tools: result.toolCalls,
});
import { createHmac } from "node:crypto";
const seal = (s: string) =>
createHmac("sha256", process.env.SPURVIK_HMAC_KEY!)
.update(s.normalize("NFC"))
.digest("hex"); // 64 hex characters
events.write({
schema: "spurvik.art12.event.v1",
system_id: "support-assistant@4.2.0",
input_digest: seal(prompt),
output_digest: seal(result.text),
router_action: decision.enforcedAction,
art50_notice_shown: true,
occurred_at: new Date().toISOString(),
});
One precision a DPO will ask for: a keyed-HMAC digest is pseudonymised data under GDPR Art. 4(5), not anonymous, while the key exists. Treat the store as personal data. What shrinks is what you hold, digests instead of transcripts. That is all “zero-PII” means here: no raw personal data persisted.Erasure. Destroying a key version makes its digests unlinkable, and unverifiable. Align key lifetimes with retention.
§What the record holds
spurvik.art12.event.v1 SDK v1.0
| Field | Why | Personal data? |
|---|---|---|
occurred_at | Ordering, period of use | No |
system_id, model_ref | Which version acted (Art. 72) | No |
input_digest, output_digest | Verify a disputed transcript | Pseudonymised, Art. 4(5) |
router_action | Gate decision, e.g. ask_human (Art. 14) | No |
reviewer_id | Who approved, where oversight applied | Employee data |
art50_notice_shown | Art. 50(1) notice displayed | No |
key_version, retention_until | Rotation; the six-month clock | No |
SDK v1.0 writes these sealed events today, into a store you choose in the EU. Hosted fra1 retention: planned
§Timing
Regulation (EU) 2026/1744 set that date. Choosing a record format now costs less than deleting six months of transcripts later.
§Questions for engineering
- Which systems write prompt text to logs, where, and for how long?
- Where “AI Act compliance” justifies retention, does that article need the text?
- Who holds the HMAC key, and how is it rotated?
- Can you verify a disputed transcript without storing it?