ADR-0154 : Index d'audit partitionné par event_time et vue aux noms de la référence
Contexte
La référence d'architecture (B13.2) demande une table d'index d'audit partitionnée par event_time, avec contraintes d'accès, rétention et archivage, et des colonnes nommées event_type, severity, identités, identifiants d'exécution, decision_reason, policy_revision, evidence_*, sanitized_metadata, result_status. eai.audit_events était une table simple (V010) dont les informations vivaient dans des colonnes jsonb chaînées (actor, resource, decision, context, evidence, attributes).
Décision
- V186 transforme
eai.audit_eventsen table partitionnée (RANGE surevent_time, une partition par mois UTC, une partition DEFAULT pour ne jamais refuser un événement). Les lignes existantes sont copiées à l'identique (même id, même hash, mêmes horodatages) : la chaîne reste vérifiable. - Une table partitionnée ne peut pas porter
UNIQUE (tenant_id, sequence)sans la clé de partition : les clés de chaîne vivent danseai.audit_event_keys(clé primaire(tenant_id, sequence),event_idunique), alimentée par un déclencheur AFTER INSERT dans la même transaction. Une séquence ou un identifiant en double échoue toujours. eai.audit_ensure_partitions(months_ahead, from_month)crée les partitions des mois suivants et y déplace, dans la même transaction, les lignes de ce mois trouvées dans DEFAULT. Le jobaudit_partitionsdu worker (plan audit) l'appelle chaque jour avec trois mois d'avance. La fonction n'est pas exécutable pareai_app.- Chaque partition reçoit la même RLS forcée que la table mère et
eai_appn'y a ni UPDATE, ni DELETE, ni TRUNCATE. - Les événements écrits avant V150 n'ont pas d'
event_time(leur hash a été calculé sans) : ils restent NULL, dans DEFAULT, pour ne pas casser la chaîne. - V187 ajoute la vue
eai.audit_index(security_invoker) qui expose chaque événement sous les noms de la référence, calculés depuis les champs chaînés : rien n'est recopié, rien ne peut diverger.evidence_statevautsealeddès qu'un sceau signé couvre la séquence (sealed_atest alors l'heure de ce sceau),chainedavant.
Conséquences
PostgreSQL n'est toujours pas le support WORM : la preuve reste l'export signé en stockage objet qualifié (ADR existants du lot G03). Les requêtes par tenant et séquence passent par l'index (tenant_id, sequence) de chaque partition. Le test de correspondance du modèle de données compte les tables partitionnées (relkind p) et ignore leurs partitions.
Preuves
services/control-plane/tests/test_audit_partitions_index.py, services/control-plane/tests/test_core_audit.py, services/control-plane/tests/test_evidence_gateway.py, services/control-plane/tests/test_core_rls_forced.py.