Audit-Trail — Architektur und GoBD-Konformität
SpeamCore protokolliert relevante Änderungen über zwei Mechanismen — Record-Versionshistorie (pro Modell-Zeile) und AuditEvent-Logging (pro Sicherheits- oder Compliance-Aktion). Diese Seite erklärt das technische Konzept, warum es so gebaut ist und welche rechtlichen Anforderungen es erfüllt.
Warum Audit-Trail?
| Anforderung | Quelle | Was muss protokolliert sein? |
|---|---|---|
| GoBD | Steuerrecht | Belege, Buchungen, Steuersätze — unveränderbar, nachvollziehbar, 10 Jahre |
| DSGVO | EU-Recht | Personal-Daten-Zugriffe und -Änderungen, Lösch-Aktionen mit Begründung |
| Compliance | intern + Versicherung | sicherheits-relevante Aktionen (Berechtigungen, Lösch-Aktionen) |
| Forensik | bei Verdacht | wer hat wann was geändert / gesehen |
| Reklamation | Kundenstreit | wer hat den Beleg X erstellt / geändert |
Was wird protokolliert?
Record-Revision (TRACKED_MODEL_NAMES, ~100 Modelle):
- Employee, Location, Workorder, Defect, Service, System, Checklist + Tile-Items
- Product, ProductType, Supplier, Manufacturer, ProductSupplier
- EmployeeContract, EmployeeTimeTracking, Course + Sub-Modelle
- Accounting, AccountingMonth, BankAccount, Transaction
- Project, ProjectPhase, ProjectItem, ProjectMember
- NotificationChannel, NotificationLog, NotificationTemplate
- Speambox + Sub-Modelle, SpeamBoard + Sub-Modelle
- Volle Liste:
api.speamcore.com/src/config/trackedModels.ts
AuditEvent-Kategorien (verifiziert im Modell-Enum):
ReportDownload— Nutzer lädt einen Report herunter (z. B. WorkTimeEvaluation, TechnicalReport)ComplianceAck— Compliance-Pflicht-Bestätigung durch MitarbeiterLogin— Authentifizierungs-EventOther— sonstige sicherheits-relevante Aktion
Was die Audit-Architektur NICHT abdeckt (Stand-Audit):
- Generische CRUD-Aktionen sind über Record-Revision dokumentiert, nicht im AuditEvent — d. h. ein „normaler" Beleg-Update erscheint nicht in
/api/audit-events, sondern als neue Version im Datensatz selbst. - Lese-Aktionen (Suche, Filter, UI-Navigation) werden weder revisioniert noch als AuditEvent geloggt.
Architektur — wo lebt der Audit-Trail?
Backend
- Audit-Log-Tabelle pro Mandant (multi-tenant getrennt)
- Pro Mutation Hook der Audit-Eintrag schreibt
- Eigene Service-Schicht — direkter API-Zugriff nicht möglich
Frontend
/audits-Modul für Anzeige (read-only)/chat-auditseparat für KI-Chat-Audit- Pro Datensatz-Detail-Seite ggf. Tab Audit / Verlauf
Storage-Strategie
Aktueller Stand: AuditEvent-Tabelle ist eine reguläre PostgreSQL-Tabelle ohne automatisches Hot-/Cold-Tiering. Record-Versionen werden über das revision-Spalte-System inline pro Datensatz geführt.
Für Aufbewahrung über mehrere Jahre (GoBD): Mandant-spezifische Backup-Strategie und manuelles Archivieren der relevanten DB-Tabellen — kein eingebauter Cold-Storage-Wechsel im Standard.
Wichtige Eigenschaften
Unveränderbarkeit (Append-Only)
AuditEvent-Einträge sind append-only über die API — der Router exponiert nur view/create-Endpoints. Direkter SQL-Zugriff zur Manipulation läuft an den Pflichten der Datenbank-Operatoren (DBA-Disziplin, kein technischer Schutz im Code).
Bei DSGVO-Auskunfts-/Löschungs-Anfragen: Pseudonymisierung empfohlen (User-Name in requestedByName auf „[gelöscht]" setzen, IDs bleiben), nicht echte Löschung.
Atomarität (für Record-Revision)
Bei den revision-getrackten Modellen wird der History-Eintrag in derselben Sequelize-Transaktion wie die Mutation geschrieben (über das addHistory-Hook in sequelizeCentralLog.ts). Wenn die Hook-Exekution scheitert, wird die ganze Mutation zurückgerollt.
AuditEvent-Einträge werden meist explizit aus dem Service-Code erstellt — Atomarität liegt beim Aufrufer (z. B. Report-Download-Endpoint legt nach erfolgreichem File-Stream den AuditEvent an).
Kontext-reichhaltig
Pro AuditEvent-Eintrag werden mitgeschrieben (verifiziert in auditEvent.model.ts):
requestedBy— Keycloak-User-IDrequestedByName— sprechender Name zum ZeitpunktparentType+parentId— auf welchen Datensatz bezieht sich die AktiontargetLabel— z. B. Mitarbeiter-Name beim Compliance-AckdocumentId+fileName— bei Report-Downloadsip,userAgentdata— Free-Form-JSON (z. B. Berichts-Zeitraum, Sektionen)occurredAt— UTC-Timestamp
GoBD-spezifische Anforderungen
| Anforderung | Wie umgesetzt |
|---|---|
| Lückenlosigkeit | bei TRACKED_MODELS: revision-Hook in jeder Sequelize-Transaktion |
| Unveränderbarkeit | API-seitig append-only; DB-Disziplin auf Operator-Ebene |
| Ordnungsmäßigkeit | chronologisch über occurredAt und sequenceId |
| Nachvollziehbarkeit | Record-Revision: vorher/nachher per Snapshot pro Versionseintrag |
| Aufbewahrung 10 Jahre | mandant-spezifische Backup-Policy, kein automatisches Cold-Tiering |
| Maschinelle Auswertbarkeit | Listen-API mit Filter/Sort, JSON-Export pro Eintrag |
DSGVO-spezifische Anforderungen
| Anforderung | Wie umgesetzt |
|---|---|
| Auskunft | pro Mitarbeiter alle Audit-Einträge filterbar |
| Berichtigung | bei falschen Daten neue Mutation + Audit, alte Werte sichtbar im Vorher |
| Löschung | Pseudonymisierung statt physischer Löschung |
| Datenminimierung | nur sicherheits-relevante Aktionen, keine Read-Logs |
| Aufbewahrungs-Limits | nach gesetzlicher Aufbewahrungs-Frist konsequent löschen |
Chat-Audit — separat
KI-Chat-Aktionen haben einen separaten Audit-Trail (Chat-Audit-Modul):
Pro KI-Chat-Aktion entstehen also zwei Einträge:
- Im Chat-Audit: der Prompt mit Kontext
- Im normalen Audit-Log: die ausgeführte Mutation
Nachvollziehbarkeit: bei Streit-Fällen lässt sich rekonstruieren ob ein Sachbearbeiter eine Aktion bewusst angewiesen hat oder die KI sie irrtümlich machte.
Praktische Tipps für Admins
- Stichproben monatlich — auffällige Lösch-Aktionen über
/auditsprüfen - Halbjährlich AuditEvent-Tabellen-Größe prüfen, Backup-Strategie reviewen
- Bei Verdachts-Fall sofort Zeitfenster engschneiden, AuditEvents exportieren, Record-Versions am betroffenen Datensatz prüfen
- Niemals Audit-Daten manuell auf DB-Ebene ändern — Compliance-Verstoß
Verwandte Doku
- Audit-Trail nutzen (Admin) — operative Anleitung
- Berechtigungen-CASL — wer was darf
- GoBD im Glossar