archive: Auftragstabelle statt Marker, exklusiver Abschluss, Quarantäne, Restore über Payload-Identität und signierte Exporte, Schlüsselrotation (Umsetzungsaudit 29.09.: A-01 bis A-15, OPT-6, 5.1 d; 02.10.: O04, O05, O07, O08)

- A-01/A-02/A-04: archive_pending_jobs je Vorgang (ArchivePendingQueue),
  Depublikationsstand am Auftrag festgehalten, Nachholer ohne Schlüssel
  FAILURE; archive_pending_at nur noch Sammelmarker
- O07: Direktweg und Nachholer beanspruchen den Auftrag unter Zeilensperre
- O08: fehlende Snapshot-Datei → Verweis auf den image_removed-Eintrag oder
  offener Auftrag, nie ein still unvollständiger Nachweis
- A-03/A-06: Sweep verschiebt in die Quarantäne, Pause beim Restore,
  archive:reregister; Restore-Abgleich löscht Anwendungskopien erneut
- O04/O05: Restore ordnet über den Payload-Pfad zu; unsignierte Exporte nur
  mit --accept-unsigned, reregister bricht bei ungültigem/fehlendem Export ab
- A-05, A-07 bis A-15: Altbestand als möglicherweise veröffentlicht, Hold-
  Historie, Zweck per POST, Nachweise gelöschter Bilder nur mit
  compliance:manage, Anhänge auf ihrer Disk, Schlüssel-ID und
  ARCHIVE_PREVIOUS_KEYS, HMAC per HKDF, archive:verify-export
- 5.1 d: Depublikation im Meldeverfahren trägt legal_request_id

PressReleaseService.php enthält zusätzlich K-01 (Prüfung im Publish-Lock),
N19 (archive() gibt Kopien frei) und N09 (markImagesPublished nur für
auslieferbare Bilder), weil die Datei ungeteilt committet ist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-10-02 13:09:01 +00:00
parent c82f279fef
commit 60650baeb8
29 changed files with 2918 additions and 412 deletions

View file

@ -24,8 +24,26 @@ return [
'disk' => env('ARCHIVE_DISK', 'archive'),
'key' => env('ARCHIVE_KEY'),
/*
| Schlüsselrotation (Umsetzungsaudit 29.09.2026, A-15): frühere Schlüssel,
| kommagetrennt (base64:…). Jeder Eintrag trägt die ID seines Schlüssels;
| entschlüsselt wird mit dem passenden. Einen früheren Schlüssel erst
| entfernen, wenn der Systemstatus keine Einträge mit ihm mehr meldet.
*/
'previous_keys' => array_values(array_filter(array_map('trim', explode(',', (string) env('ARCHIVE_PREVIOUS_KEYS', ''))))),
'cipher' => 'AES-256-CBC',
'retention_years' => (int) env('ARCHIVE_RETENTION_YEARS', 3),
'tombstone_export_path' => 'tombstones/latest.json',
/*
| Orphan-Sweep (A-03): Payloads ohne Zeile wandern in die Quarantäne
| (`quarantine/JJJJ-MM-TT/…`) und fallen erst nach quarantine_days.
| sweep_paused setzt den Sweep hart aus (zusätzlich zum Schalter
| `archive:sweep-orphans --pause`, den der Restore-Abgleich setzt).
*/
'quarantine_days' => (int) env('ARCHIVE_QUARANTINE_DAYS', 30),
'sweep_paused' => (bool) env('ARCHIVE_SWEEP_PAUSED', false),
];