Zum Inhalt springen

Compliance & Audit — Hash-Chain-Audit-Log, DSGVO-Rechte, GoBD-Verfahrensdokumentation

Live in Produktion

Feinkonzept · §3.11 Compliance & Audit — Hash-Chain, DSGVO-Rechte, GoBD-Verfahrensdoku

Abschnitt betitelt „Feinkonzept · §3.11 Compliance & Audit — Hash-Chain, DSGVO-Rechte, GoBD-Verfahrensdoku“

Einordnung. Dieses Feinkonzept setzt FUNKTIONSUMFANG §3.11 um — und ist aus drei Gründen das architektonisch wichtigste Feinkonzept der Kern-Suite: Erstens ist der Audit-Log die einzige technische Antwort auf „Wer hat wann was geändert?“, die bei einer GoBD-Prüfung oder einem arbeitsgerichtlichen Streit zählt. Zweitens kollidiert DSGVO Art. 17 (Löschung) hart mit §147 AO (10-Jahres-Aufbewahrung) — wir müssen beide bedienen, ohne gegen eines zu verstoßen, und zwar auf Zeilenebene. Drittens hat die Alt-App hier ein Loch gelassen (LESSONS-LEARNED §10): Audit-Events wurden geschrieben, aber konnten nachträglich bearbeitet werden; Verfahrensdokumentation war ein handgepflegtes Word-Dokument, das niemand mit dem tatsächlichen Systemstand abglich. Wir bauen hier die unveränderliche Wahrheit des Systems: Hash-Chain mit ed25519-Signaturen je Monatsschluss, Jahres-Notariats-Anker, automatisch generierte Verfahrensdokumentation, ein DSGVO-Antragsflow mit klar getrennten Löschung-/Maskierung-/Export-Pfaden.


feature_id: kern/11-compliance-audit
title: Compliance & Audit — Hash-Chain-Audit-Log, DSGVO-Rechte, GoBD-Verfahrensdokumentation
funktionsumfang_ref: §3.11
roadmap_horizont: MVP # Unverhandelbar — GoBD/DSGVO sind Day-1-Pflichten
plattformen:
mobile: nur-Ansicht # Mitarbeiter sieht eigene Audit-Spur + DSGVO-Flow
web: vollständig # Admin: Integrity-Check, Verfahrensdoku, DSGVO-Anträge
desktop: aus
owner_rolle: Admin # Datenschutz-Verantwortlicher / Geschäftsleitung
modul_gate_flag: module.kern.compliance-audit # kann NICHT abgeschaltet werden
compliance_flags:
gobd: true # Kern dieses Features
arbzg: false
vob: false
dsgvo: true # Art. 15, 17, 20, 30, 32 — alle hier
bfsg: true # DSGVO-Self-Service-Flow ist ESS
betrvg: true # §87(1)6 — Audit-Log ist Verhaltenskontroll-geeignet
stvg: false
weitere:
- "§147 AO (10-Jahres-Aufbewahrung)"
- "§257 HGB (Handelsbücher-Aufbewahrung)"
- "GoBD 2019 (Grundsätze ordnungsmäßiger Buchführung, elektronische Form)"
- "DSGVO Art. 15, 17, 20, 30, 32 (Auskunft, Löschung, Portabilität, Verzeichnis, Sicherheit)"
- "BDSG §26 (Beschäftigtendatenschutz)"
- "BetrVG §87 Abs. 1 Nr. 6 (Leistungs-/Verhaltenskontrolle)"
- "eIDAS Verordnung EU 910/2014 (qualifizierte Zeitstempel)"
referenzkunde:
status: TBD
name: "zu klären — Zielprofil: Handwerksbetrieb mit Datenschutzbeauftragtem (ab 20 MA) + aktivem Betriebsrat"
quelle: "Datenschutz-Audit-Interview + BR-Roundtable"
estimate_eng_tage: 42 # Hash-Chain (8), Integrity-Check (4), DSGVO-Flows (10),
# Verfahrensdoku-Generator (8), BR-Workflow (6), Retention-Engine (6)
abhängigkeiten:
- kern/01-zeiterfassung # Schreibt hierher
- kern/08-dienstplanung # Schreibt hierher
- kern/10-datev-integration # Monatsschluss-Lock hängt hiermit
- kern/09-auth-self-service # DSGVO-Self-Service-Einstieg

Marktrealität (DE-Handwerk). Eine GoBD-Prüfung trifft in der Regel mittlere Handwerksbetriebe alle 5–7 Jahre (Außenprüfung des Finanzamts), Datenschutz-Prüfungen der Landesdatenschutzbehörde nach Beschwerde oder Stichprobe. Im Krisenfall — ausscheidender Mitarbeiter fordert seine Daten per Art. 15 DSGVO, Staatsanwaltschaft beschlagnahmt Server, Steuerprüfer will Lohn-Journal 2023 sehen — ist der Audit-Log das einzige Dokument, das zählt. Heute passiert in typischen Betrieben Folgendes: Stammdaten werden in Excel gepflegt, DATEV-Export wird per USB-Stick zur Kanzlei gebracht, E-Mails werden gelöscht. Ein Datenschutz-Auskunftsantrag wird „so gut wie möglich“ aus Papier-Akten und Ordnern beantwortet — das ist weder vollständig noch rechtssicher. Die Alt-App hatte zwar einen audit_log-Tabelle, aber mit updated_at-Feld (änderbar), ohne Hash-Chain, ohne externe Verankerung. Ein technisch versierter Admin konnte Audit-Einträge editieren und die Spur verwischen. Für eine echte Betriebsprüfung: wertlos.

Rechtlicher Rahmen.

  • GoBD 2019 (BMF-Schreiben): fordert „unveränderbare, vollständige, richtige, zeitgerechte“ Aufzeichnungen in elektronischer Form. Verfahrensdokumentation muss vorhanden und aktuell sein.
  • §147 AO: 10 Jahre Aufbewahrung für Buchungsbelege (Lohn-/Rechnungen), 6 Jahre für Geschäftsbriefe.
  • §257 HGB: deckungsgleiche Aufbewahrung für Handelsbücher.
  • DSGVO Art. 5 Abs. 1 f („Integrität und Vertraulichkeit“) + Art. 32 („angemessene technische Maßnahmen“): fordert kryptographische Integritätssicherung als Stand der Technik.
  • Art. 15 DSGVO: Auskunftsrecht — Kopie aller personenbezogenen Daten binnen 30 Tagen (einmal verlängerbar).
  • Art. 17 DSGVO: Löschung — aber mit Rückausnahme Abs. 3 b) für gesetzliche Aufbewahrungspflichten (GoBD/§147 AO schlägt Art. 17 in Lohn-Daten).
  • Art. 20 DSGVO: Portabilität — strukturiertes, gängiges, maschinenlesbares Format.
  • Art. 30 DSGVO: Verzeichnis der Verarbeitungstätigkeiten (VVT).
  • BDSG §26: Beschäftigten-Datenschutz — Zweckbindung, keine Rasterfahndung im Audit-Log.
  • BetrVG §87 Abs. 1 Nr. 6: Audit-Log kann theoretisch zur Leistungs-/Verhaltenskontrolle genutzt werden (Anzahl Pausen-Buchungen pro MA, Stempel-Abweichungen). Das macht ihn mitbestimmungspflichtig. Wir lösen das, indem der Audit-Log für Manager-Rollen nur im Kontext einer konkreten Ressource sichtbar ist, nicht als aggregierte Verhaltensanalyse — und Aggregat-Views nur nach BR-Freigabe aktivierbar sind.

Schmerzpunkt der Alt-App. LESSONS-LEARNED §10 beschreibt den Minimal-Audit-Log der Alt-App: Tabelle mit id, user_id, action, entity_id, timestamp, diff_json. Updates waren möglich, kein Hash-Bezug zum Vorgänger, kein externer Anker. Bei einer Mustermandanten-Simulation im Review: Ein Junior-Admin löschte per Raw-SQL drei Einträge, niemand merkte es. Für GoBD eine rote Flagge. Ein Art. 15-Auskunftsantrag mussten über 8 Tabellen händisch zusammengeklickt werden, dauerte 4 Stunden. Wir machen das grundlegend anders: Append-only Hash-Chain mit ed25519-Signaturen je Monatsschluss-Anker, zentrale DSGVO-Exporteinheit, generierte Verfahrensdokumentation.

Erwarteter Outcome (SMART).

  • Spezifisch: Unveränderlicher Audit-Log, Integrity-Check-Button, DSGVO-Anträge als Self-Service, Verfahrensdoku als Live-PDF.
  • Messbar: Mustern-Auskunftsantrag < 15 min Bearbeitung (vs. 4 h Alt-App), Integrity-Check-Scan 10 Mio. Events < 30 s, Mitbestimmungs-Beleg-Zeit < 1 Woche.
  • Achievable: 42 Engineering-Tage inkl. BR-Freigabe-Workflow.
  • Relevant: Kern-Compliance — ohne das ist Werkszeit kein seriöses B2B-Produkt.
  • Terminiert: MVP-Release nicht verhandelbar. DOR §1.3.

Rolle Aktion Scope Plattform
Mitarbeiter Eigene Audit-Spur ansehen, DSGVO-Anträge stellen (Art. 15/17/20), eigenen Export herunterladen own 📱🌐
Manager Audit-Spur einer konkreten Ressource (z. B. Zeitbuchung) kontextbezogen ansehen team 🌐
Admin Integrity-Check starten, Verfahrensdoku herunterladen, DSGVO-Anträge bearbeiten, Retention-Policies konfigurieren all 🌐
Betriebsrat Aggregations-Views freigeben/widerrufen, eigene BR-Sicht auf Audit (nur bei freigegebenen Aggregaten) all (BR-Scope) 🌐
Datenschutzbeauftragter (DPO) VVT pflegen, DSFA dokumentieren, Datenpannen-Meldung auslösen all 🌐

Persona-Skizzen:

  • Frau Bengescu (Datenschutzbeauftragte extern, bedient 12 Handwerksbetriebe) — verlangt standardisiertes VVT im Dreispalten-Format, braucht DSGVO-Antrags-Dashboard mit SLA-Counter (30 Tage Art. 12 Abs. 3).
  • Herr Gärtner (BR-Vorsitzender Gebr. Schmidt, Installateur 58) — misstraut „IT, die alles weiß“. Braucht klaren Beleg, welche Daten Manager sehen und welche aggregierten Views er freigegeben hat.
  • Hannes (Mitarbeiter) — will einmal pro Jahr „Meine Daten herunterladen“ als JSON und seinen eigenen Stempel-Verlauf der letzten 6 Monate sehen.
  • Markus (Admin) — will den Integrity-Check vor einer Steuerprüfung starten und das PDF „Verfahrensdokumentation Werkszeit, Stand 20.04.2026“ drucken können.
  • Herr Prüfer (Finanzamt-Betriebsprüfer, 62) — erwartet IDEA-Export der Zeitbuchungen + Verfahrensdoku + Hash-Chain-Verifikations-Log.

US-01 [MVP] Als Admin möchte ich mit einem Klick prüfen, ob der Audit-Log
unversehrt ist, um vor der Steuerprüfung Gewissheit zu haben.
US-02 [MVP] Als Mitarbeiter möchte ich meine Datenauskunft (Art. 15) als
ZIP mit JSON + PDF + Anhängen herunterladen können,
ohne E-Mail an den Admin.
US-03 [MVP] Als Admin möchte ich sehen, welche DSGVO-Anträge offen sind,
deren SLA (30 Tage) läuft, um sie priorisiert abzuarbeiten.
US-04 [MVP] Als Admin möchte ich nach Ausscheiden eines Mitarbeiters
seine personenbezogenen Daten pseudonymisieren,
ohne Lohn-/Rechnungs-Belege zu verlieren (GoBD + Art. 17).
US-05 [MVP] Als Admin möchte ich die Verfahrensdokumentation (GoBD)
als PDF mit Stand-Datum automatisch aus dem Systemzustand
generieren lassen.
US-06 [V1] Als Betriebsrat möchte ich Aggregations-Views freigeben
(z. B. "Monatliche Durchschnitts-Überstunden je MA"), die
ohne BR-Freigabe nicht sichtbar sind.
US-07 [V1] Als Datenschutzbeauftragte möchte ich das Verzeichnis der
Verarbeitungstätigkeiten (VVT) aus Werkszeit als Excel/PDF
exportieren können, um es in mein Mandanten-Register zu
übertragen.
US-08 [V1.5] Als Admin möchte ich einen Jahres-Notariats-Anker erstellen,
der den Hash-Chain-Zustand zum Jahresende bei einem
qualifizierten Vertrauensdiensteanbieter verankert.
US-09 [V2] Als Admin möchte ich Datenpannen-Meldungen (Art. 33 DSGVO)
direkt aus Werkszeit an die Landesdatenschutzbehörde senden.

  • F-M-01 — Eigener Audit-Trail: Mitarbeiter sieht im „Meine Daten“-Bereich chronologische Liste seiner Events (Stempel, Urlaubsantrag, Schichttausch, Rechte-Änderung). Nur own-Scope. Paginiert (50/Seite), unendlich rückwärts.
  • F-M-02 — DSGVO-Antrags-Wizard: Drei Buttons: „Auskunft (Art. 15)“, „Export (Art. 20)”, „Löschung (Art. 17)“. Wizard führt durch: Bestätigung per Passkey, Hinweistext zu Aufbewahrungspflichten, Erstellung des Antrags, Status-Ticket.
  • F-M-03 — Antrags-Status: Liste eigener DSGVO-Anträge mit Status-Pills (Offen / In Bearbeitung / Abgeschlossen) + SLA-Counter.
  • F-M-04 — Download-Benachrichtigung: Push-Notification bei fertigem Auskunft-Paket, direkter Download-Link in der App (presigned URL, 7 Tage gültig).
  • F-W-01 — Audit-Log-Browser: Admin-View mit Filtern (Tenant implizit, Zeitraum, Akteur, Aktion, Ressource, Hash-Chain-Block). Spalten: Zeit UTC, Akteur, Aktion, Ressourcen-Typ, Ressourcen-ID, Diff-Summary, Hash-Prefix. Paginiert mit Cursor (nicht Offset), da die Tabelle schnell wächst.
  • F-W-02 — Integrity-Check: Prominente Aktion „Chain prüfen“. Startet Background-Job (§9), zeigt Fortschritt, liefert Report (grün = keine Abweichung; rot = Chain-Bruch an Block X mit genauem Zeitstempel + Eintrag-ID). Report als PDF + signiertes JSON downloadbar.
  • F-W-03 — DSGVO-Anträge-Dashboard: Kanban mit Spalten Offen / In Bearbeitung / Abgeschlossen. Pro Karte: Antragsteller, Typ (Art. 15/17/20), SLA-Countdown (rot bei <5 Tagen), Zuständiger.
  • F-W-04 — Art. 15/20-Export-Generator: Admin klickt „Export generieren“, System sammelt alle personenbezogenen Daten aus allen Tabellen (Zentral-View v_person_data_by_user), packt in ZIP (personendaten.json, nachweise.pdf, anhaenge/). Optional: Lohn-Daten nur zusammengefasst (nicht die LODAS-Datei, weil sie Daten anderer enthält).
  • F-W-05 — Art. 17-Pseudonymisierung: Admin wählt Mitarbeiter zur Pseudonymisierung nach Ausscheiden + 10 J. Aufbewahrung vorüber. System ersetzt Name/E-Mail/Adresse/SV-Nr. durch Hash (mit Salt pro Tenant), belässt Stunden/Urlaub/Lohn-Bezüge. Audit-Event dsgvo.pseudonymisiert.
  • F-W-06 — Verfahrensdoku-PDF: Button „Verfahrensdokumentation erzeugen“. Generiert 15-20-seitiges PDF mit Systemparametern (Jahrgang DATEV, Retention-Policies, Hash-Algorithmus, Verantwortliche Personen aus Admin-Stamm), Datum, SHA-256 des PDFs im Audit. Textbausteine aus gobd-templates/, dynamische Felder aus Tenant-Config.
  • F-W-07 — Retention-Policy-Editor: Admin sieht Tabelle „Tabelle → Aufbewahrungs-Regel → Rechtsgrundlage → Löschung/Maskierung“. Standard-Defaults geladen; Überschreibung mit Begründung. Änderungen werden Audit-gelogged, greifen nur ab Genehmigung durch DPO-Rolle (4-Augen).
  • F-W-08 — BR-Freigabe-Workflow für Aggregations-Views: Menü „Leistungs-Reports“ sperrt per Default. Admin fordert BR-Freigabe per Button an, BR-Rolle sieht in eigener Inbox, genehmigt/verwirft mit Begründung. Freigabe speichert BR-Zustimmungs-Dokument (PDF-Upload) + Audit-Event betrvg.aggregation.freigegeben.
  • F-W-09 — VVT-Export: Verzeichnis der Verarbeitungstätigkeiten als Excel (VVT_werkszeit_YYYYMMDD.xlsx) mit 10 Feldern pro Zeile gem. Art. 30 Abs. 1 (Verantwortlicher, Zweck, Betroffenenkategorien, Datenkategorien, Empfänger, Drittland, Löschfrist, TOM, Rechtsgrundlage, Einwilligungs-Ref). Jede Verarbeitung ein Eintrag, gelesen aus vvt_eintraege-Tabelle.
  • F-W-10 — Datenpannen-Workflow (V2): Button „Vorfall melden“ → Formular mit Meldekategorie, Betroffene, Zeitpunkt, Maßnahmen → Vorbereitet für Upload an Landesdatenschutzbehörde.
  • F-X-01 — Einheitlicher DSGVO-Status: Mitarbeiter (Mobile) und Admin (Web) sehen denselben Antrag mit synchronem Status.
  • F-X-02 — Audit-Event-Kanonisierung: Jedes mutierende Endpoint in Werkszeit schreibt audit_events-Eintrag mit fester Struktur (§7). Kein Eintrag ohne Event.
  • F-A-01 — ed25519-Schlüsselpaar-Management: Pro Tenant wird beim Anlegen ein ed25519-Schlüsselpaar in AWS KMS erzeugt (HSM-gesichert). Private Key verlässt KMS nie; Sign-Aufrufe laufen innerhalb KMS. Rotation jährlich per Policy, alter Public Key bleibt archiviert zur Verifikation.
  • F-A-02 — Notariats-Anker-Setup (V1.5): Admin konfiguriert qualifizierten Vertrauensdiensteanbieter (z. B. D-Trust, Swisscom Trust) für RFC 3161-Zeitstempel; jährlich zum 31.12. wird der Merkle-Root aller Monats-Anker extern verankert.
  • F-A-03 — Retention-Policy-Defaults: Tabellarisch voreingestellt nach Rechtsnorm (Lohn-Tabellen = §147 AO 10 J.; Projekt-Rechnungen = §257 HGB 10 J.; Zeitbuchungen = §16 Abs. 2 MiLoG / §41a EStG 2 J.; Bautagebücher = VOB/B §14 5 J.; Schulungs-Nachweise = DGUV V1 §4 10 J. + Vorhalte-Zeit).
  • F-A-04 — DPO-Rolle: Separate Rolle mit ausschließlichem Zugriff auf DSGVO-Dashboard, VVT, Datenpannen, Integrity-Reports — ohne operativen Schreibzugriff auf Zeit-/Lohn-Daten (Rollentrennung).
Anforderung-ID MVP V1 V1.5 V2
F-M-01 Audit-Trail
F-M-02 DSGVO-Wizard
F-W-01 Audit-Browser
F-W-02 Integrity-Check
F-W-03 DSGVO-Dashboard
F-W-04 Art. 15/20 Export
F-W-05 Art. 17 Pseudonym.
F-W-06 Verfahrensdoku-PDF
F-W-07 Retention-Editor
F-W-08 BR-Freigabe
F-W-09 VVT-Export
F-A-02 Notariats-Anker
F-W-10 Datenpannen-Workflow

HTML-Hero-Mockup: 11-compliance-audit.html.

┌────────────────────────────────┐
│ ← Meine Daten ⚙️ │
├────────────────────────────────┤
│ 🛡 DSGVO · GoBD · BetrVG │
│ │
│ Letzte 30 Tage │
│ ───────────── │
│ 📍 Mo 20.04. 07:02 │
│ Stempel Start · BV Müllerstr│
│ hash 4a8f…c9b2 │
│ │
│ ✍️ Fr 17.04. 14:22 │
│ Dienstplan KW17 freigegeben │
│ durch Th. Schmidt │
│ │
│ 🔑 Mi 15.04. 08:15 │
│ Passkey "iPhone Hannes" │
│ registriert │
│ │
│ 🌴 Di 14.04. 09:30 │
│ Urlaubsantrag KW22 erstellt │
│ │
│ ───────────── │
│ [Meine Daten (Art. 15/20)] │
│ [Löschung beantragen (Art. 17)]│
│ │
├────────────────────────────────┤
│ [Zeit] [Plan] [Doku] [Mehr] │
└────────────────────────────────┘
┌────────────────────────────────┐
│ ← Auskunft beantragen │
├────────────────────────────────┤
│ Was erhalten Sie? │
│ │
│ [x] Stammdaten │
│ [x] Zeitbuchungen (6 Monate) │
│ [x] Urlaubsanträge (seit 2024) │
│ [x] Schichttausch-Historie │
│ [x] Lohn-Summen pro Monat │
│ [ ] Detaillierte Lohn-Belege │
│ (enthält Daten Dritter — │
│ nicht möglich) │
│ [x] Audit-Trail eigener │
│ Aktionen │
│ │
│ Fertigstellung: bis 19.05.2026 │
│ (30 Tage, Art. 12 Abs. 3) │
│ │
│ Sie erhalten ein ZIP mit: │
│ • personendaten.json │
│ • auskunft.pdf (signiert) │
│ • anhaenge/ (PDFs, Fotos) │
│ │
│ [Weiter] (Schritt 2/3) │
└────────────────────────────────┘
┌────────────────────────────────────────────────────────────────────────────────┐
│ Werkszeit · Audit-Log Markus S. · Admin [Profil ▾] │
├────────────────────────────────────────────────────────────────────────────────┤
│ 🛡 GoBD Hash-Chain · DSGVO · §147 AO · Stand Block 48.172 (Mo 20.04. 08:40) │
│ │
│ Filter: Zeitraum [01.04.–20.04.2026 ▼] Akteur [Alle ▼] Aktion [Alle ▼] │
│ │
│ ┌──────────────────────────────────────────────────────────────────────────┐ │
│ │ Zeit UTC │ Akteur │ Aktion │ Res. │ Hash │ │
│ │ 20.04 06:42Z │ Hannes K. │ zeit.stempel.start │ zb… │ 4a8f… │ │
│ │ 20.04 06:40Z │ Mehmet Y. │ zeit.stempel.start │ zb… │ 3d2e… │ │
│ │ 19.04 16:08Z │ System │ datev.monatslauf.erst. │ ml… │ 7c91… │ │
│ │ 19.04 14:22Z │ H. Gärtner BR │ dienstplan.br.freigabe │ dp… │ 2b5a… │ │
│ │ 19.04 12:01Z │ Sabine M. │ urlaub.genehmigt │ ua… │ 9f64… │ │
│ │ 19.04 08:14Z │ Frau Müller │ datev.export.lodas │ ml… │ 8b1c… │ │
│ └──────────────────────────────────────────────────────────────────────────┘ │
│ │
│ [Chain prüfen (Integrity-Check)] [VVT-Export] [Verfahrensdoku PDF] │
└────────────────────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────────────────────┐
│ Integrity-Check · Tenant shk-gebruder-schmidt [⤓ PDF] [⤓ JSON] │
├────────────────────────────────────────────────────────────────────────────────┤
│ Start: 20.04.2026 09:14:22 UTC │
│ Ende: 20.04.2026 09:14:48 UTC │
│ Dauer: 26 s │
│ Events gesamt: 48.172 │
│ Monats-Anker: 47 (seit 01.06.2022) │
│ │
│ 📊 Ergebnis │
│ ✓ Hash-Chain vollständig (48.172 / 48.172) │
│ ✓ Alle 47 Monats-Anker gültig (ed25519 gegen Public Key 2026-01) │
│ ✓ Kein DB-UPDATE auf audit_events festgestellt │
│ ✓ Kein DB-DELETE auf audit_events festgestellt │
│ ✓ S3 Object Lock aktiv für alle Monats-Anker-Objekte │
│ │
│ Ergebnis-Signatur (ed25519): f3a8…9b12 (signiert mit Tenant-Private-Key) │
│ PDF-Hash: c4e2…70df (im Audit: compliance.integrity.ok) │
│ │
│ Empfehlung: Report quartalsweise archivieren (S3 bucket 'integrity-reports'). │
└────────────────────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────────────────────┐
│ Aufbewahrungs-Richtlinien │
├────────────────────────────────────────────────────────────────────────────────┤
│ ┌──────────────────────────────────────────────────────────────────────────┐ │
│ │ Tabelle │ Frist │ Rechtsgr. │ Bei Ablauf │ DPO-OK │ │
│ │ zeitbuchungen │ 2 J. │ §41a EStG │ Pseudonymisie. │ ✓ │ │
│ │ lohnjournal │ 10 J. │ §147 AO │ Pseudonymisie. │ ✓ │ │
│ │ rechnungen │ 10 J. │ §147 AO │ nichts │ ✓ │ │
│ │ bautagebuch │ 5 J. │ VOB/B §14 │ Löschung │ ✓ │ │
│ │ schulungsnachweise │ 10 J. │ DGUV V1 §4 │ Pseudonymisie. │ ✓ │ │
│ │ audit_events │ 10 J.+ │ §147 AO │ NIE Löschung │ ⛔ │ │
│ │ dsgvo_antraege │ 3 J. │ §79 BDSG │ Löschung │ ✓ │ │
│ │ passkey_credentials │ bei Ausscheiden │ Art. 17 │ Löschung │ ✓ │ │
│ └──────────────────────────────────────────────────────────────────────────┘ │
│ [+ Tabelle hinzufügen] [Defaults wiederherstellen] [Speichern (DPO-Review)] │
└────────────────────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────────────────────┐
│ Pseudonymisierung ausgeschiedener Mitarbeiter [Abbrechen] [OK] │
├────────────────────────────────────────────────────────────────────────────────┤
│ Mitarbeiter: Klaus Bernhart (ausgeschieden 31.03.2015) │
│ Ausscheiden: 31.03.2015 │
│ Aufbew.-Frist: 10 J. §147 AO → abgelaufen 01.04.2025 │
│ │
│ 🔴 Folgende Felder werden ersetzt: │
│ - Name → SHA256-Hash "a1b2c3d4…" (Salt pro Tenant) │
│ - E-Mail → gelöscht │
│ - Anschrift → gelöscht │
│ - SV-Nr. → gelöscht │
│ - Geburtsdatum → gelöscht │
│ │
│ 🟢 Folgende Felder bleiben erhalten (statistische Zwecke): │
│ - Stunden-Summen pro Monat │
│ - Lohn-Summen pro Monat │
│ - Projekt-Zuordnungen │
│ │
│ Begründung (Pflicht): │
│ ┌──────────────────────────────────────────────────────────────────────────┐ │
│ │ Aufbewahrungsfrist §147 AO nach Ausscheiden 31.03.2015 abgelaufen; │ │
│ │ Anspruchsverjährung §195 BGB geprüft; keine laufenden Verfahren. │ │
│ └──────────────────────────────────────────────────────────────────────────┘ │
│ │
│ [Pseudonymisieren] (wird als dsgvo.pseudonymisiert geloggt) │
└────────────────────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────────────────────┐
│ BetrVG §87(1)6 · Freigabe Aggregations-View │
├────────────────────────────────────────────────────────────────────────────────┤
│ Antrag vom: Markus (Admin) am 18.04.2026 │
│ View: "Durchschnittliche Überstunden je MA pro Monat" │
│ Zweck: Planungs-Grundlage Budget 2026-H2 │
│ Betroffene: 78 Monteure │
│ Zugriff: Bauleitung + Geschäftsleitung, read-only │
│ │
│ BR-Stellungnahme: │
│ ┌──────────────────────────────────────────────────────────────────────────┐ │
│ │ Freigegeben unter folgenden Bedingungen (BetrVG §87 Abs. 1 Nr. 6): │ │
│ │ - Nur Monats-Durchschnitte, keine Tages-Werte. │ │
│ │ - Mindest-Gruppengröße 5 MA, sonst Aggregation verweigert. │ │
│ │ - Automatische Löschung nach 12 Monaten. │ │
│ │ - Widerruf jederzeit per schriftlicher Mitteilung möglich. │ │
│ └──────────────────────────────────────────────────────────────────────────┘ │
│ │
│ BR-Beschluss-Dokument: [⤓ BR_Beschluss_2026-04-15.pdf] │
│ Gültig bis: 15.04.2027 │
│ │
│ [Freigeben] [Verwerfen (mit Begründung)] [Zurückstellen] │
└────────────────────────────────────────────────────────────────────────────────┘

Symbol-Konvention. 🛡 Compliance-Banner, ✍️ Manuell-Event, 🔑 Auth-Event, 📍 Geo/Stempel, 🌴 Urlaub, nicht löschbar.


apps/api/src/db/schema/audit.ts
// Zentrale, tenant-übergreifende Spine-Tabelle — aber RLS schneidet auf tenant_id
export const auditEventsTable = pgTable('audit_events', {
id: uuid('id').primaryKey().defaultRandom(), // UUID v7
tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id),
blockNumber: bigint('block_number', { mode: 'bigint' }).notNull(), // monoton steigend je Tenant
occurredAt: timestamp('occurred_at', { withTimezone: true }).notNull(),
actorUserId: uuid('actor_user_id').references(() => usersTable.id), // nullable für System-Events
actorKind: varchar('actor_kind', { length: 16 }).notNull(), // 'user' | 'system' | 'api-client'
action: varchar('action', { length: 64 }).notNull(), // 'zeit.stempel.start', 'datev.export.lodas'
resourceType: varchar('resource_type', { length: 32 }).notNull(), // 'zeitbuchung','dienstplan', ...
resourceId: uuid('resource_id'),
diff: jsonb('diff'), // Before/After, kanonisiert
ipHash: bytea('ip_hash'), // SHA-256 auf salted IP
userAgent: text('user_agent'),
// --- Hash-Chain ---
hashPrev: bytea('hash_prev').notNull(), // SHA-256 des Vorgängers
hashSelf: bytea('hash_self').notNull(), // SHA-256 dieser Zeile (kanonisiert)
}, (t) => ({
tenantBlockUq: uniqueIndex('audit_ev_tenant_block_uq').on(t.tenantId, t.blockNumber),
occurredIdx: index('audit_ev_occurred_idx').on(t.tenantId, t.occurredAt.desc()),
actorIdx: index('audit_ev_actor_idx').on(t.tenantId, t.actorUserId, t.occurredAt.desc()),
resourceIdx: index('audit_ev_resource_idx').on(t.tenantId, t.resourceType, t.resourceId),
}));
// Monats-Anker: am 1. jedes Monats um 00:10 UTC läuft Cronjob, signiert Chain-Stand
export const auditAnchorsTable = pgTable('audit_anchors', {
id: uuid('id').primaryKey().defaultRandom(),
tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id),
periodYear: integer('period_year').notNull(),
periodMonth: integer('period_month').notNull(),
fromBlock: bigint('from_block', { mode: 'bigint' }).notNull(),
toBlock: bigint('to_block', { mode: 'bigint' }).notNull(),
merkleRoot: bytea('merkle_root').notNull(), // Merkle-Tree über alle hash_self
ed25519Signature: bytea('ed25519_signature').notNull(), // KMS-Sign
keyVersion: varchar('key_version', { length: 16 }).notNull(), // 'v2026-01'
rfc3161Token: bytea('rfc3161_token'), // Optional Notariats-Zeitstempel
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
}, (t) => ({
tenantPeriodUq: uniqueIndex('audit_anchor_tenant_period_uq').on(t.tenantId, t.periodYear, t.periodMonth),
}));
// DSGVO-Anträge
export const dsgvoAntraegeTable = pgTable('dsgvo_antraege', {
id: uuid('id').primaryKey().defaultRandom(),
tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id),
antragstellerId: uuid('antragsteller_id').notNull().references(() => usersTable.id),
artikel: varchar('artikel', { length: 8 }).notNull(), // '15','17','20'
status: varchar('status', { length: 24 }).notNull(), // 'offen','in_bearbeitung','abgeschlossen','abgelehnt'
slaDeadline: timestamp('sla_deadline', { withTimezone: true }).notNull(),
assigneeId: uuid('assignee_id').references(() => usersTable.id),
exportUrl: text('export_url'), // presigned S3
ablehnungsGrund: text('ablehnungs_grund'),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
resolvedAt: timestamp('resolved_at', { withTimezone: true }),
hashPrev: bytea('hash_prev'),
hashSelf: bytea('hash_self'),
});
// Retention-Policies
export const retentionPoliciesTable = pgTable('retention_policies', {
id: uuid('id').primaryKey().defaultRandom(),
tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id),
tableName: varchar('table_name', { length: 64 }).notNull(),
retentionYears: integer('retention_years').notNull(),
onExpire: varchar('on_expire', { length: 24 }).notNull(), // 'loeschung','pseudonymisierung','nichts'
rechtsgrundlage: text('rechtsgrundlage').notNull(),
approvedByDpo: uuid('approved_by_dpo').references(() => usersTable.id),
approvedAt: timestamp('approved_at', { withTimezone: true }),
}, (t) => ({
tenantTableUq: uniqueIndex('retention_tenant_table_uq').on(t.tenantId, t.tableName),
}));
// VVT
export const vvtEintraegeTable = pgTable('vvt_eintraege', {
id: uuid('id').primaryKey().defaultRandom(),
tenantId: uuid('tenant_id').notNull().references(() => tenantsTable.id),
bezeichnung: varchar('bezeichnung', { length: 120 }).notNull(),
verantwortlicher: varchar('verantwortlicher', { length: 120 }).notNull(),
zweck: text('zweck').notNull(),
betroffeneKategorien: text('betroffene_kategorien').notNull(),
datenkategorien: text('datenkategorien').notNull(),
empfaenger: text('empfaenger'),
drittlandUebermittlung: text('drittland_uebermittlung'),
loeschfristen: text('loeschfristen').notNull(),
tomMasnahmen: text('tom_massnahmen').notNull(),
rechtsgrundlage: varchar('rechtsgrundlage', { length: 64 }).notNull(),
einwilligungsRef: text('einwilligungs_ref'),
});

RLS-Policy-Sketch.

ALTER TABLE audit_events ENABLE ROW LEVEL SECURITY;
CREATE POLICY audit_events_tenant_iso ON audit_events
USING (tenant_id = current_setting('app.tenant_id')::uuid);
CREATE POLICY audit_events_own_or_admin ON audit_events FOR SELECT
USING (
tenant_id = current_setting('app.tenant_id')::uuid
AND (
current_setting('app.roles') LIKE '%admin%'
OR current_setting('app.roles') LIKE '%dpo%'
OR (current_setting('app.scope') = 'own' AND actor_user_id = current_setting('app.user_id')::uuid)
OR (current_setting('app.scope') = 'team' AND resource_type = current_setting('app.resource_context'))
)
);
-- KRITISCH: keine UPDATE/DELETE-Policies — Tabelle ist DB-technisch append-only
REVOKE UPDATE, DELETE ON audit_events FROM PUBLIC;
-- Nur der Superuser (Schema-Migrationen) darf, normale Apps niemals.

ER-Bezug. audit_events wird von jedem mutierenden Write-Endpoint geschrieben — zeitbuchungen, urlaubsantraege, dienstplaene, datev_monatslaufe, users, passkey_credentials. Das Schreiben passiert im selben Transaction-Block wie die fachliche Mutation (atomar), so dass Konsistenz garantiert ist.


Pfad-Konvention: /v1/kern/compliance/… und /v1/kern/dsgvo/….

Methode Pfad Auth-Scope Rate-Limit Idempotenz Beschreibung
GET /v1/kern/compliance/audit audit:read Standard Audit-Log-Liste (Cursor-Pagination)
GET /v1/kern/compliance/audit/{id} audit:read Standard Event-Detail mit Hash
POST /v1/kern/compliance/audit/integrity-check audit:admin Privileged Idempotency-Key Startet Chain-Verifikation
GET /v1/kern/compliance/audit/integrity-check/{id} audit:admin Standard Report-Status + Download-URLs
POST /v1/kern/compliance/anchor/{year}/{month} system System Idempotency-Key Monats-Anker erzeugen (Cronjob)
GET /v1/kern/compliance/verfahrensdoku audit:admin Standard PDF generieren und Download
GET /v1/kern/compliance/vvt dpo Standard VVT als Excel/JSON
PUT /v1/kern/compliance/vvt/{id} dpo Standard Idempotency-Key VVT-Eintrag bearbeiten
GET /v1/kern/compliance/retention audit:admin Standard Retention-Policies-Liste
PUT /v1/kern/compliance/retention/{id} audit:admin + DPO-Co-Sign Privileged Idempotency-Key Policy ändern
POST /v1/kern/compliance/br/aggregation-request audit:admin Standard Idempotency-Key BR-Freigabe anfragen
POST /v1/kern/compliance/br/aggregation-request/{id}/freigeben betriebsrat Privileged Idempotency-Key BR-Entscheidung
POST /v1/kern/dsgvo/antrag own Privileged Idempotency-Key Art. 15/17/20 einreichen
GET /v1/kern/dsgvo/antrag/{id} own/admin/dpo Standard Status
POST /v1/kern/dsgvo/antrag/{id}/export-generieren admin/dpo Privileged Idempotency-Key Art. 15/20 Paket
POST /v1/kern/dsgvo/antrag/{id}/pseudonymisieren admin/dpo Privileged Idempotency-Key Art. 17 Flow

OpenAPI-Schema-Skizze (gekürzt):

paths:
/v1/kern/compliance/audit/integrity-check:
post:
operationId: startIntegrityCheck
x-werkszeit-scope: audit:admin
x-werkszeit-rate-limit: privileged
responses:
'202':
content:
application/json:
schema:
type: object
properties:
jobId: { type: string, format: uuid }
status: { type: string, enum: [queued] }
estimateSeconds: { type: integer }

Webhook-Events.

  • compliance.integrity.ok / compliance.integrity.broken
  • compliance.anchor.created
  • dsgvo.antrag.erstellt / dsgvo.antrag.abgeschlossen
  • betrvg.aggregation.freigegeben / betrvg.aggregation.widerrufen

Profil Begründung Mechanik
Online-only für Admin-Funktionen (Integrity-Check, DSGVO-Bearbeitung, Verfahrensdoku) Kryptographische Operationen laufen in KMS; Integrity-Check braucht vollständigen DB-Zugriff. UI-Block bei connectivity == none.
Read-only offline für Mobile-Audit-Trail Mitarbeiter möchte seine letzten Events auch im Keller sehen können. SQLite-Cache letzte 90 Tage, keine Mutation offline.
Online-only für DSGVO-Antragseinreichung Identitäts-Bestätigung per Passkey zwingend online. Button disabled bei keiner Verbindung, Hinweistext „Bitte online stellen“.

Konflikt-Strategie. Entfällt für Audit — Events sind append-only, keine Konflikte. Für DSGVO-Anträge: Duplikat-Detection via (antragsteller_id, artikel, status IN ('offen','in_bearbeitung'))-Constraint — zweiter Antrag gleichen Typs geht nicht, UI zeigt bestehenden Status.


  • Append-only: audit_events ohne UPDATE/DELETE-Berechtigungen für App-User. PostgreSQL REVOKE auf Tabellenebene + keine updated_at-Spalte + RLS ohne Write-Policy außer INSERT.
  • Hash-Chain: Jeder Eintrag hat hash_prev = hash_self(vorheriger_block_gleichen_tenants). Kanonisierung über JCS (JSON Canonicalization Scheme, RFC 8785) vor SHA-256.
  • Monats-Anker: Cronjob am 1. jedes Monats 00:10 UTC erzeugt Merkle-Tree über alle hash_self des Vormonats, signiert Root mit ed25519 (Key in AWS KMS). Signatur + Key-Version + Block-Range in audit_anchors abgelegt.
  • Notariats-Zeitstempel (V1.5): Jährlich zum 31.12. wird der Merkle-Root aller 12 Monats-Anker extern qualifiziert zeitgestempelt (RFC 3161 bei D-Trust oder Swisscom Trust). Token in rfc3161_token abgelegt.
  • Verfahrensdokumentation: Generator liest Tenant-Config, Retention-Policies, Schlüssel-Versionen, Personen-Verantwortliche; baut Markdown → PDF (Stand-Datum im Footer, SHA-256 des PDF wird Audit-Event compliance.verfahrensdoku.erzeugt).
  • Aufbewahrung 10 Jahre: audit_events + audit_anchors + signierte PDFs in S3 (werkszeit-audit-eu-central-1) mit Object Lock Compliance Mode, Retention 10 Jahre.
  • Integrity-Check: Verifiziert für alle Events: hash_self == SHA256(canonicalize(row_without_hashes) + hash_prev). Verifiziert für alle Anker: ed25519_verify(public_key, merkle_root, signature). Bei Abweichung: compliance.integrity.broken-Event (ironischerweise ein Audit-Eintrag) + Admin-Alert.

— nicht zutreffend; ArbZG-Compliance wird in §3.1/§3.8 umgesetzt, nicht hier.

— nicht zutreffend.

  • Art. 5 Datenminimierung: Audit-Log speichert kein IP im Klartext, nur SHA-256(salt||ip) (Salt pro Tenant, rotierend). Keine User-Agents länger als 512 Zeichen.
  • Art. 6 Rechtsgrundlage: Art. 6 Abs. 1 c (Rechtspflicht) + Art. 6 Abs. 1 f (berechtigtes Interesse an Nachvollziehbarkeit) — dokumentiert in VVT-Eintrag „Audit-Log“.
  • Art. 9: keine besonderen Kategorien im Audit. Krank-Diagnose wird als Abwesenheits-Typ geloggt, nicht als ICD-Code.
  • Art. 15 Auskunft: Zentral-Service sammelt aus audit_events (gefiltert auf actor_user_id = X ODER resource_id IN (X owns)), aus Stammdaten, aus Zeit/Urlaub/Lohn. Export als ZIP (<15 min SLA, 30 Tage Pflicht-SLA Art. 12 Abs. 3).
  • Art. 17 Löschung mit GoBD-Kollision: Zwei-Pfad-Strategie:
    • Pfad A (Tabellen ohne Aufbewahrungspflicht, z. B. passkey_credentials): physische Löschung.
    • Pfad B (GoBD-pflichtige Tabellen, z. B. zeitbuchungen): Pseudonymisierung nach Ablauf (Name → SHA-256(salt||name), SV-Nr. → gelöscht). Geschehnis, Stunden, Lohn bleiben, nur die Person wird unidentifizierbar.
  • Art. 20 Portabilität: JSON-Schema personendaten-v1.schema.json definiert Struktur; Export enthält alle Felder, nichts was auf andere Personen verweist (Lohn-Summen statt LODAS-Datei).
  • Art. 30 VVT: Liste aller Verarbeitungen als eigene Tabelle; Excel-Export mit 10 Pflichtspalten.
  • Art. 32 Sicherheit: ed25519, AES-256-GCM für at-rest, TLS 1.3 für in-transit, KMS für Schlüsselverwaltung, HSM-gesicherte Private Keys, Incident-Response-Prozess (Datenpannen-Workflow V2).
  • DSFA: Pflicht (Art. 35 Abs. 3 b — umfangreiche Verarbeitung besonderer Kategorien, hier: Gesundheitsbezug bei Krank-Tagen, BetrVG-Bezug). DSFA-Dokument wird in der Verfahrensdoku als Anhang generiert, regelmäßig reviewed.
  • Mobile-DSGVO-Wizard ist ESS-Flow → voller BFSG-Anwendungsbereich.
  • Screenreader-Labels auf allen Buttons, Live-Regions bei Status-Wechsel („Antrag eingereicht“).
  • Kontrast: Anthrazit #1A1F2B auf Beige #F5F1EA ≥ 12:1 (weit über AA).
  • Tastatur-Navigation: Wizard durchgehend per Tab, Skip-Links zwischen Schritten.
  • Aktive Pflicht-Erkenntnis: Audit-Log ist Leistungs-/Verhaltenskontroll-geeignet. Wir stellen sicher, dass:
    • Default-Manager-View zeigt nur den Audit zu einer konkret geöffneten Ressource (z. B. „Zeitbuchung #123 — wer hat sie wann geändert?“), nicht aggregiert über MA.
    • Aggregations-Views (Anzahl Events pro MA, Häufigkeit Stempel-Korrekturen, Pausen-Durchschnitt) sind hinter betrvg.aggregation.freigegeben-Flag.
    • Flag-Aktivierung nur durch BR-Freigabe-Workflow (F-W-08), nicht durch Admin-Alleinentscheidung.
    • Jede Aggregations-Aktivierung bekommt einen Audit-Event betrvg.aggregation.freigegeben mit Verweis auf hochgeladenes BR-Beschluss-PDF.
    • Widerruf jederzeit möglich, sofort wirksam, blendet Views aus.
  • Textbaustein „Betriebsvereinbarung Audit-Log“ (Beispiel) wird in der Verfahrensdoku mitgeliefert als Hilfe für den Betrieb.
  • eIDAS Art. 42 (qualifizierter Zeitstempel): RFC 3161-Anker beim qualifizierten VDA ist der einzige Weg zu einem gerichtsfesten externen Anker. Alternativ: Blockchain-Anker (z. B. Bitcoin-Commit via OpenTimestamps) — wir bauen diesen nicht, weil nicht rechtlich anerkannt.
  • BDSG §26 (Beschäftigtendaten): Audit-Zugriff durch Manager zweckgebunden, keine Mitarbeiter-Profilbildung ohne BR-Freigabe.
  • §257 HGB (Handelsbücher): deckungsgleich mit §147 AO behandelt.

# Szenario Erwartetes Verhalten
EC-01 Direkter DB-Eingriff (Ops-User ändert via Raw-SQL eine audit_events-Zeile) Integrity-Check erkennt hash_self != recompute(row), meldet exakte Zeile + Block-Nr. compliance.integrity.broken-Event wird erzeugt. Admin-Alert via E-Mail + SRE-Pager.
EC-02 Mitarbeiter scheidet aus, fordert 6 Monate später Art. 17 System prüft Aufbewahrungsfristen: Lohn-bezogene Tabellen → Pseudonymisierung frühestens 10 Jahre nach Ausscheiden; Zeitbuchungen → 2 Jahre; passkey_credentials → sofort. Antragsteller erhält Bescheid mit den maßgeblichen Fristen.
EC-03 Multi-Tenant-Cross-Read im Audit RLS erzwingt tenant_id-Filter; Versuch, per ID-Guessing fremden Event zu lesen → 404 (nicht 403), kein Audit im fremden Tenant.
EC-04 Tenant-Deletion (Firma gibt Werkszeit auf, will alle Daten gelöscht) Zwei-Stufen-Prozess: Export aller Daten (Art. 20), dann Anonymisierung tenant_id → fixer Hash und Blockade aller Log-ins. Vollständige DB-Löschung erst nach 10 Jahren bei Lohn-Daten möglich; Kunde wird darüber vertraglich aufgeklärt.
EC-05 KMS-Key-Rotation während laufendem Anker Monats-Anker wird mit dem zum Anker-Zeitpunkt gültigen Key signiert. key_version im Anker festgehalten. Verifikation nutzt exakte Version. Alte Public Keys bleiben permanent archiviert.
EC-06 BR-Widerruf einer Aggregations-Freigabe mitten in laufendem Report Reports werden sofort ausgeblendet; bereits erzeugte PDF-Exporte bleiben lokal, werden aber serverseitig entwertet (Download-Links invalidiert). Audit: betrvg.aggregation.widerrufen.
EC-07 100.000+ Events/Tag bei Peak Audit-Writer ist async (Queue), Event-Schreibe-Latenz P95 < 50 ms. Block-Nummer-Sequenz per PostgreSQL Sequence (atomarer nextval), Hash-Berechnung nach Commit in eigener kleiner Transaktion.
EC-08 Clock-Skew (Server-Uhr 5 min falsch) NTP-erzwungen via Chrony; bei Abweichung > 30 s: Alert + Event-Schreibe-Pause bis Sync. Entfernt das Risiko falscher occurred_at.
EC-09 Datenpannen-Erstmeldung außerhalb Werkszeit (Admin meldet per E-Mail statt Flow) Workflow dokumentiert Pflicht-Weg; nachträgliche Nachtragung möglich.
EC-10 DPO wechselt Admin-Rollen-Migration. Retention-Policy-Approvals bleiben mit ursprünglichem DPO im Audit. Neuer DPO muss alle aktiven Policies re-approve innerhalb 30 Tage.
EC-11 Verfahrensdoku-PDF > 20 MB Stream-Rendering als Chunked Upload in S3, Download via presigned URL. Nie in DB.
EC-12 Mitarbeiter stellt Art. 15-Antrag, System ist überlastet Antrag queued, Mitarbeiter erhält Bestätigung mit Fertigstellungs-SLA (max. 30 Tage). Bei Überlast eskaliert Monitoring; Art. 12 Abs. 3 erlaubt einmalige Verlängerung um 2 Monate mit Begründung.
EC-13 Integrity-Check findet historischen Bruch (jemand hat 2023 etwas verändert) Report markiert genau den fraglichen Block, Zeit-Bereich, User. Incident-Response-Playbook: forensische Analyse, ggf. Meldung an Behörden (Art. 33 DSGVO 72 h).

Funktionalität: Compliance & Audit
Hintergrund:
Angenommen ein Tenant "shk-gebruder-schmidt" mit aktivem Modul-Gate "module.kern.compliance-audit"
Und ein Nutzer "Hannes" (Mitarbeiter)
Und ein Nutzer "Markus" (Admin)
Und ein Nutzer "Frau Bengescu" (DPO, extern)
Und der Audit-Log enthält 48.172 Events, 47 Monats-Anker
Szenario: Happy Path — Art. 15 Datenauskunft durch Mitarbeiter
Wenn Hannes in der Mobile-App "Meine Daten (Art. 15)" öffnet
Und den Antrag per Passkey bestätigt
Und die vorausgewählten Kategorien bestätigt
Dann wird ein DSGVO-Antrag mit artikel="15", status="offen" erstellt
Und der Antragsteller erhält eine Bestätigung mit SLA-Datum (30 Tage)
Und ein Audit-Event "dsgvo.antrag.erstellt" wird geschrieben
Wenn Markus "Export generieren" klickt
Dann wird innerhalb 15 min ein ZIP mit personendaten.json, auskunft.pdf und anhaenge/ erzeugt
Und Hannes erhält eine Push-Notification mit Download-Link (presigned URL, 7 Tage)
Und ein Audit-Event "dsgvo.antrag.abgeschlossen" wird geschrieben
Szenario: Grenzfall — Integrity-Check erkennt DB-Manipulation
Angenommen ein Ops-User hat direkt per SQL eine audit_events-Zeile verändert
Wenn Markus "Chain prüfen" klickt
Dann erkennt der Integrity-Check Block 42.118 als korrupt
Und der Report zeigt den exakten Zeitstempel, User und Diff der Manipulation
Und ein Audit-Event "compliance.integrity.broken" wird erzeugt
Und Markus erhält sofort eine E-Mail + SRE-Pager-Alert
Und der Integrity-Check-Report wird als PDF + signiertes JSON gespeichert
Szenario: Grenzfall — GoBD-Kollision mit Art. 17-Löschungsanfrage
Angenommen Hannes (noch im Betrieb) stellt einen Art. 17-Antrag auf komplette Löschung
Wenn Markus den Antrag prüft
Dann zeigt die UI die maßgeblichen Aufbewahrungsfristen pro Tabelle
Und der Antrag wird abgelehnt mit Begründung "§147 AO 10 Jahre, Art. 17 Abs. 3 b DSGVO"
Und Hannes erhält diesen Bescheid als PDF
Und ein Audit-Event "dsgvo.antrag.abgelehnt" mit dieser Begründung wird geschrieben
Szenario: Grenzfall — BR-Widerruf einer Aggregations-View
Angenommen eine Aggregations-View "Durchschnittliche Überstunden je MA" ist seit 2 Monaten aktiv
Wenn Herr Gärtner (BR-Vorsitzender) "Freigabe widerrufen" klickt
Und eine Begründung eingibt
Dann wird die View in allen Dashboards sofort ausgeblendet
Und bereits erzeugte PDF-Exporte werden serverseitig entwertet (Download-Links 403)
Und ein Audit-Event "betrvg.aggregation.widerrufen" wird geschrieben
Und der Admin wird benachrichtigt
Szenario: Grenzfall — Multi-Tenant-Isolation im Audit-Browser
Angenommen ein zweiter Tenant "musterbetrieb-maler" mit eigenen Audit-Events
Wenn Markus (shk-gebruder-schmidt) versucht, per API GET /audit/{id} mit einer fremden Event-ID aufzurufen
Dann antwortet die API mit 404
Und es entsteht KEIN Cross-Tenant-Eintrag
Und der Zugriffsversuch selbst wird in Tenant shk-gebruder-schmidt als audit.read.forbidden geloggt
Szenario: Grenzfall — Monats-Anker-Cronjob nach KMS-Key-Rotation
Angenommen der ed25519-Schlüssel wurde am 01.05.2026 von Version v2025-01 auf v2026-05 rotiert
Wenn der Cronjob am 01.06.2026 00:10 UTC den Mai-Anker erstellt
Dann wird der Anker mit key_version="v2026-05" signiert
Und der April-Anker bleibt mit key_version="v2025-01" verifizierbar
Und der Integrity-Check kann beide Versionen validieren

  • U-01 — JCS-Kanonisierung (RFC 8785) auf 1.000 Zufalls-Objekten, Re-Roundtrip muss identisches Byte-Output liefern.
  • U-02 — SHA-256-Chain: Genesis-Block + 1.000 Blöcke, Chain-Break-Detection an Block 500 durch Mutation.
  • U-03 — ed25519-Sign/Verify mit KMS-Mock, 100 Samples.
  • U-04 — Merkle-Tree-Bau auf 10.000 Hashes, Root-Stabilität.
  • U-05 — Retention-Policy-Resolver: Gegeben ein MA mit Austritts-Datum und Tabellen-Policies, berechne Löschungs-/Pseudonymisierungs-Datum korrekt.
  • U-06 — SLA-Counter DSGVO: Gegeben Antrags-Datum, Feiertage, Wochenenden → korrekte Deadline nach Art. 12 Abs. 3.
  • U-07 — Pseudonymisierungs-Hash mit Tenant-Salt: identische Namen in verschiedenen Tenants → unterschiedliche Hashes.
  • U-08 — Mobile-DSGVO-Wizard Dart-Widget: Passkey-Re-Auth erzwungen, Back-Button-Verhalten.
  • W-01 — Mobile Audit-Trail-List Golden-Test (de-DE + en-US), Dark + Light Theme.
  • W-02 — DSGVO-Wizard Schritt-Navigation, Fokus-Reihenfolge.
  • W-03 — Admin Audit-Browser Table-Sort, Cursor-Pagination.
  • I-01 — Multi-Tenant-Isolation für alle 17 Compliance-Endpunkte (DOD §2.2).
  • I-02 — RLS: Mitarbeiter sieht nur eigene Audit-Events.
  • I-03 — Trigger-Test: UPDATE auf audit_events schlägt fehl (Permission denied).
  • I-04 — Monats-Anker-Cronjob: schreibt atomar Anker + Audit-Event; bei Fehler Rollback beides.
  • I-05 — DSGVO-Antrag-Dedup: Zweiter offener Antrag gleichen Typs → 409.
  • I-06 — Retention-Policy-Änderung ohne DPO-Co-Sign → 403.
  • E-01 — Happy Path Art. 15 aus §12 (Mobile + Web).
  • E-02 — Integrity-Check mit 100k Events in < 30 s (Last-Szenario).
  • E-03 — Visuelle Regression: Verfahrensdoku-PDF-Rendering Golden-Test.
  • E-04 — Accessibility: axe-core auf allen Compliance-Screens + Flutter-Semantics-Test für Mobile-DSGVO-Flow.
  • E-05 — BR-Freigabe End-to-End: Admin-Antrag → BR-Genehmigung → View sichtbar → Widerruf → View weg.

13.5 Compliance-Tests (Suite apps/api/test/compliance/audit/)

Abschnitt betitelt „13.5 Compliance-Tests (Suite apps/api/test/compliance/audit/)“
  • C-01Hash-Chain-Bruch-Detection: gezielt eine Zeile manipulieren, Integrity-Check muss in < 1 s Bruch erkennen (DOD §2.2 Pflicht-Test).
  • C-02 — ed25519-Verifikation: Monats-Anker mit absichtlich falscher Signatur → Detection.
  • C-03 — GoBD-Verfahrensdoku-Template-Abdeckung: Jedes GoBD-Pflichtkapitel (Verantwortliche, Datenerfassung, Datenaufbewahrung, Internal Control, …) erscheint im Generator-Output.
  • C-04 — DSGVO-Exporteinheit: Art. 15-Paket enthält alle Pflichtfelder, keine Daten Dritter.
  • C-05 — Art. 17-GoBD-Kollisions-Test: Löschungs-Antrag auf Lohn-pflichtige Daten → korrekte Ablehnung + Maskierungs-Hinweis.
  • C-06 — Retention-Engine-Test: 5-Jahres-alter Datensatz mit 2-Jahres-Frist wird als „zur Pseudonymisierung fällig“ markiert.
  • C-07 — S3 Object Lock: Attempt zu DELETE auf Anker-Objekt → AWS-SDK-Fehler.
  • 20× Re-Run der Cronjob-Tests (Zeitbezug) grün.
  • 20× Re-Run der Integrity-Check-Tests mit variierender Event-Anzahl.

Nicht-Ziel Begründung
Automatische Meldung an Landesdatenschutzbehörde (Datenpannen-Workflow Art. 33) V2. In MVP wird nur intern dokumentiert; Meldung an Behörde manuell.
Blockchain-Anker (Bitcoin, Ethereum) Rechtlich nicht als qualifizierter Zeitstempel anerkannt. RFC 3161 bei VDA ist die richtige Lösung.
Vollständige Pentest-Dokumentation Liegt im CISO-Bereich, nicht im Produkt. Verfahrensdoku verlinkt auf Pentest-Reports.
Zero-Knowledge-Proofs für Audit-Verifikation Overengineering für B2B-Handwerk. Standard-ed25519 + Merkle-Tree reicht.
Multi-Region-Replikation für Hash-Chain MVP: single-region eu-central-1. V2 bei Bedarf Cross-Region-Read-Replica.
Automatisierte DSFA-Erstellung DSFA bleibt manuelle Arbeit des DPO; wir liefern nur die Datengrundlage.
Benutzerdefinierte Audit-Events über Plugin-API V2; MVP nur festes Event-Katalog.
Real-time Audit-Stream für SIEM (Splunk, QRadar) V1.5 via Webhook-Events + Webhook-Receiver. MVP: Events nur in Werkszeit-DB.

Risiko / Annahme Impact Wahrscheinlichkeit Gegenmaßnahme
Hash-Chain-Performance bei Peak (> 500 Events/s) hoch mittel Queue-based Audit-Writer (SQS), Benchmark mit Seed-Tenant-Load vor Release
RFC 3161-VDA hat Service-Downtime zum 31.12. mittel niedrig Fallback auf zweiten VDA; monatlicher Smoke-Test; Notariats-Anker ist V1.5, MVP reicht interner Anker
KMS-Key-Verlust (Region-Ausfall) sehr hoch sehr niedrig Multi-Region-Key-Replikation, Backup-KMS in eu-west-1
Rechtliche Neuinterpretation Art. 17 (BGH-Urteil, das GoBD-Vorrang kippt) hoch niedrig Jährlicher Rechts-Review durch Fachkanzlei; Architektur erlaubt Policy-Wechsel ohne Code-Release
Betriebsrat verweigert jegliche Aggregations-View niedrig mittel Features bleiben funktionsfähig ohne Aggregations-Views; nur statistische Berichte entfallen
Mitarbeiter-Masse-DSGVO-Anträge als Störungs-Taktik (100 Anträge in einer Woche) mittel niedrig Rate-Limit pro Antragsteller (1 pro 90 Tage, Art. 12 Abs. 5 erlaubt bei „offensichtlich unbegründet“ Ablehnung), Queue
Jahres-Audit-Volumen überschreitet S3 Object Lock Limits niedrig sehr niedrig S3 skaliert praktisch unbegrenzt; Kosten-Monitoring

  • Vorbedingung: kern/09-auth-self-service liefert Rollen-Model + Passkey-Re-Auth für DSGVO-Bestätigung.
  • Schnittstelle zu: AWS KMS (ed25519-Keys), AWS S3 Object Lock (werkszeit-audit-eu-central-1-Bucket), Webhook-Bus (§3.12), RFC 3161-VDA (V1.5).
  • Wird konsumiert von: allen Werkszeit-Features, weil jedes mutierende Endpoint audit_events schreibt. DATEV (kern/10) nutzt den Monatsschluss-Lock aus diesem Feinkonzept. Reporting (kern/07) nutzt audit_events als Quelle für Änderungs-Reports.

Status: TBD — zu klären mit Sales / Product-Owner.

DOR §1.1.1 verlangt: „Mindestens ein zahlender Design-Partner ist namentlich dokumentiert, der das Feature konkret fordert.“

Kandidaten-Profile:

  • Handwerksbetrieb 50–150 MA mit externem Datenschutzbeauftragten + aktivem Betriebsrat
  • Betrieb, der kürzlich eine Betriebsprüfung durchlaufen hat und die Schmerzpunkte aus erster Hand kennt
  • Ggf. Steuerkanzlei mit GoBD-Beratungsgeschäft, die Compliance als USP will

Validierungs-Fragen:

  1. Wie viele Stunden kostet Sie ein Art. 15-Auskunftsantrag heute?
  2. Haben Sie eine aktuelle Verfahrensdokumentation (Stand < 12 Monate)?
  3. Gab es schon einmal Diskussionen mit dem BR über Software, die „zu viel weiß“?
  4. Wären Sie bereit, das Feature als erster Kunde 60 Tage im Beta zu testen, inklusive Penetrationstest durch Ihren IT-Prüfer?

Build-Sequenz:

  1. Datenmodell (audit_events, audit_anchors, dsgvo_antraege, retention_policies, vvt_eintraege) + RLS + UPDATE/DELETE-Revoke.
  2. Audit-Writer-Bibliothek (@werkszeit/audit) — jede mutierende Operation ruft sie auf. Alle Feature-Teams integrieren sie vor allem anderen.
  3. Hash-Chain-Berechnung + Test gegen DB-Trigger-Manipulation.
  4. Monats-Anker-Cronjob + KMS-ed25519-Integration.
  5. Integrity-Check-Endpoint + PDF-Report-Generator.
  6. DSGVO-Antrags-Flow (Mobile Wizard + Web Dashboard + Export-Generator).
  7. Retention-Policy-Editor + DPO-Co-Sign-Workflow.
  8. Verfahrensdoku-Generator.
  9. VVT-Export.
  10. BR-Freigabe-Workflow (V1) + Notariats-Anker (V1.5) + Datenpannen-Workflow (V2).

Risiko-Reihenfolge:

  • Hash-Chain-Integrität ist die Essenz. Wenn die kaputt ist, ist das gesamte Compliance-Versprechen kaputt.
  • DSGVO-Art. 17 mit GoBD-Kollision — falsche Reihenfolge wäre ein Rechtsrisiko.
  • Audit-Writer-Latenz — wenn der Writer Mutations blockiert, wird die ganze App langsam.

Streich-Kandidaten bei Zeitnot:

  • BR-Freigabe-Workflow → V1.
  • Notariats-Anker → V1.5.
  • VVT-Excel-Export → V1 (JSON reicht MVP).
  • Mobile-Audit-Trail offline-Cache → V1.

Stop-the-Bus-Triggers:

  • Integrity-Check-Test schlägt fehl → Merge blockiert, SRE-Pager.
  • Multi-Tenant-Bleed-Test im Audit schlägt fehl → Deploy blockiert.
  • Art. 17-Löschung ohne DPO-Approval möglich → Sofort-Patch.
  • ed25519-Sign-Aufruf findet Private Key außerhalb KMS → Security-Incident.

Was uns 2027 dankbar macht:

  • Das zentrale @werkszeit/audit-Modul wird zum Differenzierungs-Merkmal gegenüber SaaS-Konkurrenten. Wer jemals eine Betriebsprüfung mit Werkszeit durchlaufen hat, bleibt — weil der Wechsel die Verfahrensdoku und den externen Anker neu aufsetzen würde.
  • Der DSGVO-Self-Service senkt Support-Tickets dauerhaft um einen Faktor ~5 gegenüber manueller Bearbeitung.
  • Die Retention-Engine ist die Grundlage, um später Multi-Jurisdiction-Compliance (Österreich, Schweiz) anzubauen, ohne den Audit-Log neu zu denken.

Feinkonzept-Version 1.0 · Stand 2026-04-20 · Autor: Senior Consultant · Review: Tech-Lead + CISO + DPO

Für Entwickler — API-Endpoints16
MethodePfadAuthZweck
POST/v1/kern/compliance/anchor/{year}/{month}bearerAuthCreate or fetch the monthly audit anchor (system scope)
GET/v1/kern/compliance/auditbearerAuthList audit events (cursor-paginated)
GET/v1/kern/compliance/audit/{id}bearerAuthFetch a single audit event (incl. hash fields)
POST/v1/kern/compliance/audit/integrity-checkbearerAuthStart an async hash-chain integrity check
GET/v1/kern/compliance/audit/integrity-check/{id}bearerAuthFetch integrity-check job status and report
POST/v1/kern/compliance/br/aggregation-requestbearerAuthRequest Betriebsrat approval for a new aggregation view
POST/v1/kern/compliance/br/aggregation-request/{id}/freigebenbearerAuthRecord the Betriebsrat's decision on an aggregation request
GET/v1/kern/compliance/retentionbearerAuthList retention policies
PUT/v1/kern/compliance/retention/{id}bearerAuthUpdate a retention policy (requires DPO co-sign)
GET/v1/kern/compliance/verfahrensdokubearerAuthGenerate and download the Verfahrensdokumentation PDF
GET/v1/kern/compliance/vvtbearerAuthList the Verzeichnis von Verarbeitungstätigkeiten (Art. 30)
PUT/v1/kern/compliance/vvt/{id}bearerAuthUpdate a VVT entry
POST/v1/kern/dsgvo/antragbearerAuthFile a DSGVO Art. 15/17/20 request (self-service)
GET/v1/kern/dsgvo/antrag/{id}bearerAuthFetch DSGVO request status
POST/v1/kern/dsgvo/antrag/{id}/export-generierenbearerAuthGenerate the Art. 15/20 export bundle (admin/DPO)
POST/v1/kern/dsgvo/antrag/{id}/pseudonymisierenbearerAuthExecute the Art. 17 pseudonymisation flow