docs: spec PROJ-12 — automatic retention-based deletion for left members (GDPR)
Configurable per-organization retention period, daily job, reuses the existing hard-delete path from PROJ-3 instead of introducing a new one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
This commit is contained in:
1 parent
ffe2d450eb
commit
4cb2284ad9
2 files changed
+44
-1
No files matched your search
+3
-1
@@ -23,11 +23,13 @@
|
||||
| PROJ-9 | Synapse-Integration (Health Check, User Provisioning, Raum-Sync) | Deployed | [PROJ-9-synapse-integration-health-check.md](PROJ-9-synapse-integration-health-check.md) | 2026-09-11 |
|
||||
| PROJ-10 | Deployment (Docker Compose + Synapse Installer) | Deployed | [PROJ-10-deployment.md](PROJ-10-deployment.md) | 2026-09-11 |
|
||||
| PROJ-11 | Chat (Matrix Client-Server API) | Planned | [PROJ-11-chat.md](PROJ-11-chat.md) | 2026-09-11 |
|
||||
| PROJ-12 | Automatische Löschfrist für ausgetretene Mitglieder (DSGVO) | Planned | [PROJ-12-mitglieder-loeschfrist.md](PROJ-12-mitglieder-loeschfrist.md) | 2026-09-11 |
|
||||
|
||||
Noch nicht gestartet (siehe [docs/PRD.md](../docs/PRD.md) Core Features): dedizierte Tenant-Isolation-Tests. P2 (Medien-Anhänge, Push) bewusst aus PROJ-11 ausgeklammert, siehe dessen Non-Goals.
|
||||
|
||||
Nachträge 2026-09-11 (commit bc4d749): Passwort-Reset → [[PROJ-1]], Mitglied hart löschen → [[PROJ-3]].
|
||||
Nachtrag 2026-09-11 (commit ffe2d45): API-Zugriff bei suspended/left sofort entziehen (nicht nur Matrix) → [[PROJ-3]].
|
||||
|
||||
<!-- Add features above this line -->
|
||||
|
||||
## Next Available ID: PROJ-12
|
||||
## Next Available ID: PROJ-13
|
||||
@@ -0,0 +1,41 @@
|
||||
# PROJ-12: Automatische Löschfrist für ausgetretene Mitglieder (DSGVO)
|
||||
|
||||
**Status:** Planned
|
||||
**Created:** 2026-09-11
|
||||
|
||||
## Summary
|
||||
Mitglieder mit Status `left` (Austritt) werden aktuell unbegrenzt aufbewahrt — [[PROJ-3]] sperrt nur den API-/Matrix-Zugriff, löscht aber keine Daten. Das verletzt den DSGVO-Grundsatz der Speicherbegrenzung (Art. 5 Abs. 1 lit. e, Art. 17): sobald kein Zweck mehr besteht (keine Mitgliedschaft, keine gesetzliche Aufbewahrungspflicht für reine Mitgliederdaten ohne Beitrags-/Buchhaltungsbezug — siehe PRD Non-Goals), müssen die Daten gelöscht werden. Dieses Feature führt eine vereinskonfigurierbare Aufbewahrungsfrist ein, nach der `left`-Mitglieder automatisch über den bestehenden `DELETE /api/members/:memberId`-Pfad ([[PROJ-3]]) hart gelöscht werden.
|
||||
|
||||
## Dependencies
|
||||
- Requires: [[PROJ-3]] (Mitgliederverwaltung) — nutzt den bestehenden harten Lösch-Pfad (Matrix-Erase, Cascade-Deletes, Audit-Log) 1:1, nur automatisch statt manuell ausgelöst
|
||||
- Requires: [[PROJ-2]] (Verein anlegen) — Frist wird pro Organisation konfiguriert
|
||||
|
||||
## User Stories
|
||||
1. Als Vereinsadmin will ich pro Verein festlegen, nach wie vielen Tagen ausgetretene Mitglieder automatisch gelöscht werden, damit ich die Satzung/Aufbewahrungspraxis meines Vereins abbilden kann.
|
||||
2. Als Vereinsadmin will ich einen sinnvollen Standardwert vorfinden, falls ich die Einstellung nicht bewusst ändere, damit DSGVO-Konformität nicht von mir vergessen werden kann.
|
||||
3. Als System will ich täglich prüfen, welche `left`-Mitglieder die Frist ihrer Organisation überschritten haben, und sie automatisch löschen.
|
||||
4. Als Vereinsadmin will ich im Audit-Log sehen, dass ein Mitglied automatisch (nicht manuell) gelöscht wurde, um den Vorgang nachvollziehen zu können.
|
||||
|
||||
## Acceptance Criteria
|
||||
- [ ] `organizations`-Tabelle bekommt Feld `memberRetentionDays` (integer, not null, Default 90)
|
||||
- [ ] Neues UI-Feld in den Organisationseinstellungen zum Ändern der Frist (nur Org-Admin)
|
||||
- [ ] Täglicher Hintergrund-Job (analog `startReconciliationWorker`-Pattern in `sync-worker.ts`) prüft `members` mit `status = 'left'`, deren `updatedAt` (Zeitpunkt des Status-Wechsels) älter ist als `organizations.memberRetentionDays`
|
||||
- [ ] Betroffene Mitglieder werden über denselben Pfad wie manuelles Löschen entfernt (Matrix-Erase, Cascade-Deletes für Team-Zuordnungen/Rollen/Eltern-Verknüpfungen)
|
||||
- [ ] Audit-Log-Eintrag `action: "member.auto_deleted"` mit `actorUserId: null` (System-Aktion, kein menschlicher Akteur), Metadata enthält die angewandte Frist
|
||||
- [ ] Keine Benachrichtigung an Admin vor der Löschung (bewusst still im Hintergrund)
|
||||
- [ ] Reaktivierung vor Fristablauf (Status zurück auf `active`) bricht die geplante Löschung ab — Job muss den aktuellen Status zum Ausführungszeitpunkt prüfen, nicht nur einmalig einplanen
|
||||
|
||||
## Edge Cases
|
||||
- `members.updatedAt` existiert aktuell nicht im Schema (nur `createdAt`) — muss ergänzt werden, sonst kein verlässlicher Zeitpunkt für "seit wann `left`"
|
||||
- Mitglied wird mehrfach `left`→`active`→`left` — Frist zählt ab dem letzten Wechsel zu `left`, nicht kumulativ
|
||||
- Sehr kurze Frist (z.B. 0 oder negative Werte) — Validierung auf Minimum (z.B. ≥ 1 Tag) im Backend
|
||||
- Job läuft während laufender manueller Löschung desselben Mitglieds — `DELETE`-Route ist idempotent genug (member existiert nicht mehr → 404), Job sollte 404 vom eigenen internen Aufruf abfangen statt zu crashen
|
||||
- Synapse nicht erreichbar während Auto-Löschung — gleiches Verhalten wie beim manuellen Löschen: Matrix-Erase-Fehler wird geloggt, DB-Löschung läuft trotzdem durch (siehe bestehendes `try/catch` in [[PROJ-3]])
|
||||
|
||||
## Non-Goals (für dieses Feature)
|
||||
- Self-Service-Löschantrag durch das Mitglied selbst (Art. 17-Betroffenenrecht als eigener Flow) — separates Feature
|
||||
- Aufbewahrung von Beitrags-/Buchhaltungsdaten (10-Jahres-Frist nach §147 AO) — laut PRD Non-Goal (kein Buchhaltungsmodul), betrifft dieses Feature nicht
|
||||
- Anonymisierung statt Löschung als Alternative — MVP macht hartes Löschen, kein Soft-Anonymisieren
|
||||
|
||||
## Notes
|
||||
Direkt aus DSGVO-Rückfrage im Chat entstanden, kein formaler Requirements-Workshop nötig — Scope ist klar umrissen durch bestehenden Lösch-Pfad aus [[PROJ-3]].
|
||||
Reference in new issue
Block a user