Audit-Konventionen¶
Read-only Reference. Was die Operatorin im Audit-Log erwarten kann und was nicht — speziell für Bulk-Aktionen, Anonymous-Actor-Pfade und die JSONB-Search-Semantik.
1 Eine Audit-Zeile pro Bulk-Aktion (TODO-Block-7-4-03)¶
Operatoren-Bulk-Aktionen (Soft-Delete N Tenants, Bulk-Assign M
Module) schreiben eine Audit-Zeile mit
entity_id="bulk:<count>" und der vollständigen ID-Liste in
details.after. Das ist eine bewusste Compliance-Design-Entscheidung
(Block 7.4-Review): ein Reviewer sieht „Operator hat 17 Tenants in
einer Aktion soft-gelöscht" als ein Event statt als 17 entkoppelte
Events.
Konsequenz für die Suche: Wer im Audit-Log nach einer
spezifischen Tenant-/Modul-ID sucht (?entity_id=<uuid>), findet
das Bulk-Event nicht — die UUID liegt in details.after, nicht in
entity_id. Zwei Wege, den Bulk-Pfad trotzdem zu finden:
- Action-Filter:
?action=tenant.bulk_soft_deletedbzw.?action=tenant_module.bulk_assigned— listet alle Bulk-Events in der Range. - JSON-Search:
?json_search=<uuid-fragment>matcht case-insensitiv aufdetails::text. Bei < 10 000 Audit-Rows ausreichend (Sequential-Scan < 200 ms). GIN-Index aufdetails JSONBist als Folge-TODO geplant, sobald die Tabelle wächst.
Die Aktions-Codes:
| Bulk-Action | Geschrieben von |
|---|---|
tenant.bulk_soft_deleted |
POST /api/v1/platform/tenants/bulk-soft-delete |
tenant_module.bulk_assigned |
POST /api/v1/tenants/{id}/modules/bulk |
2 Anonymous-Actor-Pfad (Block 11)¶
Public-Widget-Endpoints schreiben Audit-Rows ohne TenantContext.
actor_role='anonymous', actor_user='widget', actor_keycloak_id
ist NULL. Die tenant_id-Spalte ist gesetzt (aus dem Origin-
validierten Chatbot-Lookup). Die Aktions-Codes:
| Anonymous-Action | Endpoint |
|---|---|
chatbot_feedback.created |
POST /api/v1/feedback |
actor_keycloak_id IS NULL ist also kein Bug — es ist die
Anonymous-Signatur. Forensisch nutzt die Korrelation tenant_id +
details.session_id (vom Widget mitgesendet, falls vorhanden).
3 CSV-Export-Felder (TODO-Block-7-4-02 erledigt)¶
Seit v1.3.0-D2 enthält der CSV-Export (GET /api/v1/platform/audit/
export.csv) auch die forensischen Felder actor_keycloak_id,
ip_address, session_id. Die Header-Reihenfolge ist:
id, occurred_at, actor_role, actor_user, actor_keycloak_id,
tenant_id, chatbot_id, action, entity_type, entity_id,
ip_address, session_id [, details]
Mit ?include_details=true wird die JSONB-Spalte als String-Repr ans
Ende angehängt — relevant für die Bulk-Aktions-Lesbarkeit, weil dort
die ID-Liste liegt (siehe §1).
4 Date-Range-Validierung (TODO-Block-7-4-07 erledigt)¶
Beide Audit-Endpoints (GET /api/v1/platform/audit, GET
.../export.csv) lehnen ?date_from > date_to mit
422 date_from_after_date_to ab statt einer leeren Liste. Vorher war
„kein Audit gefunden" das einzige Signal.
5 Was NICHT auditiert wird¶
- Read-Only-Endpoints (alle GET) schreiben keine Audit-Rows.
- Vendor-Logins landen in
vendor_access_log, nicht inplatform_audit_log— siehe Block-3-Konzept. - Health-Probes und Statics werden nicht geloggt.
6 Retention¶
platform_audit_log hat eine 2-Jahres-Retention via pg_cron-
Archival (Block 1 §6.4). Audit-Rows älter als 730 Tage werden
in eine Archiv-Tabelle umgehängt; der Operator-UI-Reader liest nur
die Live-Tabelle.