EU AI Act Article 12 and Article 26(6): audit log retention enforced in SQL

Storage-layer trigger-enforced retention — not config-file aspiration. Replay the proof in three curl calls.

The Regulation

Two provisions of Regulation (EU) 2024/1689 govern AI audit log retention. Article 12 is a provider-side design requirement: providers of high-risk AI systems must design those systems to automatically record events to the extent technically possible. Article 26(6) is the deployer-side retention duty. The relevant Article 26(6) text:

"Deployers shall keep the logs automatically generated by that high-risk AI system to the extent such logs are under their control, for a period appropriate to the intended purpose of the high-risk AI system, of at least six months, unless provided otherwise in applicable Union or national law…"

Six months is the Article 26(6) minimum. Sectoral rules may push it longer — financial-services anti-money-laundering retention runs five to ten years, healthcare runs longer, public-sector procurement archive runs ten. AIEGIS defaults to a 1,825-day (5-year) configurable retention period, which may align with applicable sectoral requirements. The deployer determines the appropriate period for their use case and applicable law.

Why Most "Compliance" Retention Is Theatre

The standard pattern when a vendor claims a retention period is one of three:

  1. Config-file aspiration. A YAML key retention_days: 1825 sits in a settings file. An ops engineer with sudo can flip it tomorrow. The page claim survives; the actual retention does not.
  2. Cron-managed pruning. A nightly job deletes records older than N days. Nothing prevents an authenticated insider from running a manual DELETE, then running the prune so the gap looks scheduled.
  3. Application-layer guard. The application checks before deleting. Anyone who can reach the database directly — a DBA, a backup-restore script, an SRE incident-response shell — bypasses the guard entirely.

All three pass an auditor walkthrough. None of them survive an adversary with database credentials.

What Storage-Layer Enforcement Looks Like

The aiegis Grid audit ledger lives in a SQLite table called grid_ledger. Two triggers, attached at CREATE TABLE time, reject any attempt to DELETE or UPDATE a row:

CREATE TRIGGER trg_grid_ledger_no_delete
BEFORE DELETE ON grid_ledger
BEGIN
  SELECT RAISE(ABORT, 'grid_ledger is append-only');
END;

CREATE TRIGGER trg_grid_ledger_no_update
BEFORE UPDATE ON grid_ledger
BEGIN
  SELECT RAISE(ABORT, 'grid_ledger is append-only');
END;

Every row also carries a prev_hash and an event_hash, so the ledger is structurally a hash chain. A successful tamper would have to bypass the trigger, recompute the chain, and re-sign every downstream row. The trigger blocks the first step.

Replay the Proof

Three curl calls. Any auditor can run them right now.

1. The verifier endpoint says the floor is satisfied

curl -s https://aiegis.ie/grid/ledger/retention

# returns:
# {"retention_floor_days":1825,
#  "append_only_enforced":true,
#  "triggers_present":["trg_grid_ledger_no_delete","trg_grid_ledger_no_update"],
#  "ledger_size":198,
#  "satisfies_floor":true,
#  ...}

2. The SQL triggers exist (auditor-callable)

The same endpoint returns the exact trigger names from sqlite_master. An auditor can cross-check against the table-creation DDL we publish; if the trigger were removed the verifier response would no longer list it.

3. Direct tampering returns SQLITE_CONSTRAINT_TRIGGER

An operator with shell access to the database can try:

sqlite3 /opt/agent_platform/agents.db \
  "UPDATE grid_ledger SET payload_json='TAMPER' WHERE seq=1;"

# returns:
# Error: stepping, grid_ledger is append-only (19)
# exit 1

SQLite error code 19 is SQLITE_CONSTRAINT. The same call with DELETE returns the same error from the sibling trigger. There is no application-layer flag to flip, no DBA-mode bypass, no "trust me" config flag. The storage engine refuses.

How This Maps To Article 26

Article 26§6 of the same Regulation requires deployers to keep automatically-generated logs for "at least six months." AIEGIS defaults to 1,825 days (5 years) — over 10x the Article 26(6) minimum, within most sectoral overlay requirements, and configurable per applicable law. Signed evidence manifests for any period are produced by GET /api/policy/evidence?org_id=…&from=…&to=…, signed against the public key published at /.well-known/aiegis-evidence-pubkey.pem. The full per-sub-paragraph Article 26 mapping is at /article-26-walkthrough; the machine-readable version is /audit/article26-mapping.json.

What This Does Not Solve

Storage-layer enforcement on a single SQLite file protects against in-process tampering. It does not by itself protect against:

The point is not that storage-layer triggers solve every threat. The point is that they raise the bar from "trust the config file" to "the database itself refuses." That gap is the difference between a paperwork-only retention claim and one that survives an adversarial review.

Why You Care

If your audit log can be rewritten by the operator, your audit log isn't evidence in any jurisdiction that takes the EU AI Act seriously. EU AI Act fines run up to 3% of global annual turnover (€15M). An auditor walking into your environment with sqlite3 and seeing the trigger reject their probe is the answer to "show me." A config file saying retention_days: 1825 is not.

Run the curl. Try the tamper. The behaviour is publicly verifiable on a production endpoint, today.