# Konzept — Konfigurierbarer Backup-Zielspeicher (Backend + Lokal) (certvia) > Status: **Konzept/Entscheidungsvorlage** (kein Code). Produkt: **certvia**. Folge-Feature der Backup-Lane. Ziel: den Zielspeicher für Backup-/DSGVO-Artefakte **im Betreiber-Portal konfigurierbar** machen und **lokale (persistente) Speicherung** als vollwertige Option anbieten — nicht nur über Env. ## 1. Ist-Zustand (`src/server/storage/backup-store.ts`) - Es gibt bereits eine **`BackupStore`-Abstraktion** (`put/get/list/remove`) mit zwei echten Implementierungen: **`S3BackupStore`** (MinIO/S3) und **`LocalBackupStore`** (echte Byte-Persistenz). - **Aber:** Die Wahl trifft `createBackupStore()` **einmalig beim Prozessstart, rein Env-basiert**: alle `S3_*` gesetzt → S3, sonst lokaler Ordner (`BACKUP_LOCAL_DIR` bzw. `/.backups`). Exportiert als **statisches Singleton** `backupStore`. - **Nur 3 Nutzer:** `src/server/backup/export.ts`, `restore.ts`, `ops.ts`. - **Schwächen:** (a) nicht im Backend wählbar; (b) der lokale Ordner liegt im Container → beim Redeploy **flüchtig**; (c) S3-Config nur als Env, nicht pro Betreiber pflegbar. ## 2. Zielbild - **Betreiber wählt im Portal** das Ziel: **Lokal** oder **S3/MinIO**, inkl. Config, mit „Verbindung testen". - **Lokal ist persistent** (gemountetes Volume), nicht flüchtig. - Env bleibt als **Fallback** funktionsfähig (Rückwärtskompatibilität). ## 3. Datenmodell (`PlatformSetting`, Singleton erweitern) Aktuell nur `mfaRequired`. Ergänzen: - `backupTarget String @default("local")` — `local` | `s3` - `backupLocalDir String?` — Pfad des lokalen Ziels (muss auf ein **gemountetes** Volume zeigen) - `backupS3Endpoint / backupS3Bucket / backupS3Region / backupS3AccessKey String?` - `backupS3SecretKeyEnc String?` — S3-Secret **verschlüsselt at-rest** über `src/server/secret-crypto.ts` (`encryptSecret`/`decryptSecret`, Schlüssel `MFA_ENC_KEY`/`AUTH_SECRET`) — **nie** Klartext in der DB. Migration additiv (nullable, Default `local`). Kein Backfill nötig. ## 4. Store-Factory umbauen - `backupStore`-Singleton → **`getBackupStore(): Promise`**: liest `PlatformSetting`, baut den passenden Store, entschlüsselt den S3-Key. - **Präzedenz:** DB-Config (wenn `backupTarget` gesetzt/vollständig) → **sonst** Env (`S3_*` / `BACKUP_LOCAL_DIR`) → **sonst** lokaler Default `.backups`. So bleibt bestehendes Env-Deployment lauffähig. - **Caching + Invalidierung:** Store memoisieren, bei Änderung der Backup-Settings invalidieren (Version/Timestamp aus `PlatformSetting.updatedAt`). - Die **3 Call-Sites** (`export.ts`, `restore.ts`, `ops.ts`) von `backupStore` auf `await getBackupStore()` umstellen. - **Fail-secure:** unvollständige S3-Config → klarer Fehler (nicht still auf lokal fallen, wenn `backupTarget=s3` gewählt wurde). ## 5. Betreiber-UI - Neue Seite im Plattform-Portal, z. B. **`/admin/backup`** (oder Abschnitt in den Plattform-Einstellungen). - Gated: **`requirePlatformFullAdmin` + MFA-Step-up** (`assertPlatformStepUp`) — Betreiber-Config mit Credentials. - Felder: Ziel-Radio (Lokal/S3), je nach Wahl die Config; **„Verbindung testen"** (Probe-`put`+`get`+`remove` eines winzigen Test-Keys) mit klarer Rückmeldung; Speichern über eine Action analog `setPlatformMfaRequired` (`platformSetting.upsert`, S3-Secret vor dem Schreiben verschlüsseln). ## 6. Persistenz für „Lokal" (Compose) - In `docker-compose.coolify.yml` ein **persistentes Volume** ergänzen (analog `pgdata`/`miniodata`): `backups:` und in **app + worker** unter dem Pfad aus `backupLocalDir` mounten (z. B. `/app/.backups`). Ohne Mount bleibt Lokal flüchtig. - Doku-Hinweis: `backupLocalDir` muss innerhalb des gemounteten Pfads liegen. ## 7. Sicherheit - S3-Secret **nur verschlüsselt** in der DB (`secret-crypto`). - Config-Bearbeitung nur **Full-Admin + Step-up**, auditiert. - **`BACKUP_ENC_KEY`** (Artefakt-Verschlüsselung, `src/server/backup/crypto.ts`) bleibt **getrennt** vom Zielspeicher — Verschlüsselung des Inhalts ≠ Wahl des Speicherorts. - Umgebungs-Secret-Kohärenz (Restore) unverändert: `BACKUP_ENC_KEY`/`PASSWORD_PEPPER`/`MFA_ENC_KEY` sind Env, nicht Artefakt (siehe `KONZEPT-backup-restore.md` §9). ## 8. Tests - `scripts/test-backup-*` erweitern: Store-Auflösung aus DB-Config (local & s3), Präzedenz DB→Env→Default, „Verbindung testen"-Pfad, Fail-secure bei unvollständiger S3-Config. - Export→Restore end-to-end gegen **beide** Backends. ## 9. Sofort testbar (ohne Umbau) Schon heute: ohne `S3_*` fällt der Store auf **lokal** zurück → Export/Restore/DSGVO laufen (Artefakte im Container-`.backups`, **flüchtig**). Für einen schnellen Funktionstest genügt: **backup-worker + Redis** (vorhanden) + `BACKUP_ENC_KEY` (Fallback `AUTH_SECRET`). Der Umbau macht das Ziel **wählbar** und **persistent**. ## 10. Offene Entscheidungen - Eigene Seite `/admin/backup` vs. Abschnitt in bestehenden Plattform-Einstellungen. - Mehrere Ziele/Profile (Primär + Offsite) — jetzt 1 Ziel, Mehrfachziele als Phase 2. - Ob der lokale Pfad frei wählbar ist oder auf den gemounteten Volume-Pfad festgelegt wird (empfohlen: fest, um Fehlkonfiguration zu vermeiden).