Zum Hauptinhalt springen

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?

AnforderungQuelleWas muss protokolliert sein?
GoBDSteuerrechtBelege, Buchungen, Steuersätze — unveränderbar, nachvollziehbar, 10 Jahre
DSGVOEU-RechtPersonal-Daten-Zugriffe und -Änderungen, Lösch-Aktionen mit Begründung
Complianceintern + Versicherungsicherheits-relevante Aktionen (Berechtigungen, Lösch-Aktionen)
Forensikbei Verdachtwer hat wann was geändert / gesehen
ReklamationKundenstreitwer 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 Mitarbeiter
  • Login — Authentifizierungs-Event
  • Other — 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-audit separat 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-ID
  • requestedByName — sprechender Name zum Zeitpunkt
  • parentType + parentId — auf welchen Datensatz bezieht sich die Aktion
  • targetLabel — z. B. Mitarbeiter-Name beim Compliance-Ack
  • documentId + fileName — bei Report-Downloads
  • ip, userAgent
  • data — Free-Form-JSON (z. B. Berichts-Zeitraum, Sektionen)
  • occurredAt — UTC-Timestamp

GoBD-spezifische Anforderungen

AnforderungWie umgesetzt
Lückenlosigkeitbei TRACKED_MODELS: revision-Hook in jeder Sequelize-Transaktion
UnveränderbarkeitAPI-seitig append-only; DB-Disziplin auf Operator-Ebene
Ordnungsmäßigkeitchronologisch über occurredAt und sequenceId
NachvollziehbarkeitRecord-Revision: vorher/nachher per Snapshot pro Versionseintrag
Aufbewahrung 10 Jahremandant-spezifische Backup-Policy, kein automatisches Cold-Tiering
Maschinelle AuswertbarkeitListen-API mit Filter/Sort, JSON-Export pro Eintrag

DSGVO-spezifische Anforderungen

AnforderungWie umgesetzt
Auskunftpro Mitarbeiter alle Audit-Einträge filterbar
Berichtigungbei falschen Daten neue Mutation + Audit, alte Werte sichtbar im Vorher
LöschungPseudonymisierung statt physischer Löschung
Datenminimierungnur sicherheits-relevante Aktionen, keine Read-Logs
Aufbewahrungs-Limitsnach 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 /audits prü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