# Recovery Engine ## Status Umgesetzt sind Vorabprüfung, Wiederherstellung von Dateien und Ordnern über die API, Sitzungen mit Prüfpunkt, Schutz gegen versehentliches Überschreiben und die Ausführungsschleife. Nicht umgesetzt: Plattenwiederherstellung, vollständige Systemwiederherstellung und Proxmox-VM-Wiederherstellung. Für Proxmox fehlt der `ArchiveTransport` (siehe `proxmox.md`); ohne ihn kommt schon die Sicherung nicht zustande. ## Die Vorabprüfung ist der Kern > Ein Backup gilt erst als vertrauenswürdig, wenn Integrität geprüft und Wiederherstellbarkeit nachgewiesen wurde. Ein Nachweis, der erst im Ernstfall erbracht wird, ist keiner. `POST /restores/validate` prüft deshalb **ohne etwas zu schreiben**, ob eine Wiederherstellung gelingen kann: | Prüfung | Gewicht bei Befund | | --- | --- | | Manifest lesbar | verhindernd | | Abschlussvermerk vorhanden | verhindernd | | **Jeder benötigte Block vorhanden** | verhindernd | | Datenschlüssel des Repositorys auffindbar | verhindernd | | Zielverzeichnis erreichbar, Elternverzeichnis vorhanden | verhindernd | | Freier Platz ausreichend (5 % Aufschlag) | verhindernd | | Ziel nicht leer, ohne Zustimmung | verhindernd | | Ziel wird überschrieben, mit Zustimmung | Warnung | | Backup unverschlüsselt | Warnung | | Schlüsselversion weicht ab | Warnung | | Blockprüfung übersprungen | Hinweis | **Die Blockprüfung ist der eigentliche Nachweis.** Ein Manifest allein belegt nur, dass jemand einmal etwas gesichert hat — nicht, dass die Daten noch da sind. Ein versehentlich aufgeräumtes Verzeichnis, ein unvollständig kopiertes Repository, ein fehlgeschlagenes Prune: Alles das fällt hier auf und nicht erst, wenn man es braucht. Real geprüft: Nach dem Entfernen eines einzigen Blocks meldet die Prüfung ``` can_proceed: False NICHT WIEDERHERSTELLBAR: 1 Hindernisse, 1 fehlende Bloecke. [blocking] 1 von 16 benoetigten Bloecken fehlen im Repository. betroffen: daten/datei-8.bin ``` Die **betroffene Datei** wird genannt, nicht nur eine Zahl. „Ein Block fehlt" hilft niemandem weiter; „daten/datei-8.bin lässt sich nicht wiederherstellen" schon. Wird die Blockprüfung übersprungen (`skip_deep_check`), steht das als Hinweis im Bericht. Sonst hielte man einen halben Nachweis für einen ganzen. **Die Prüfung läuft zweimal:** einmal beim Anlegen des Auftrags und erneut unmittelbar vor dem Schreiben. Der Bericht am Auftrag kann Minuten oder Tage alt sein; in der Zwischenzeit kann ein Block verschwunden oder das Ziel gefüllt worden sein. Der Bericht wird am Auftrag gespeichert — später muss belegbar sein, was vor dem Start bekannt war. ## Drei Hürden vor dem Überschreiben Eine Wiederherstellung an einen belegten Ort richtet sich gegen Daten, die es noch gibt — und die könnten genau die sein, die man eigentlich retten will. 1. **Ohne `overwrite_existing` wird ein nicht leeres Ziel abgelehnt.** 2. **`restores.overwrite` ist eine eigene Berechtigung**, nicht in `restores.execute` enthalten. Wer wiederherstellen darf, darf nicht automatisch überschreiben. 3. **`confirm_overwrite` muss den Zielpfad wörtlich wiederholen.** Ein zweites Feld neben dem Kennzeichen ist keine Umständlichkeit: Ein versehentlich gesetztes Kennzeichen in einem Skript oder einer Vorlage reicht damit nicht aus, um Daten zu vernichten. Wer den Pfad abtippt, hat ihn gelesen. Läuft der Schalter ins Leere — das Ziel ist leer —, entfällt die Bestätigung. Ein Ritual ohne Anlass gewöhnt nur das Wegklicken an. Jede Wiederherstellung wird auditiert, das Überschreiben unter eigener Aktion (`RESTORE_OVERWRITE_REQUESTED`) und zusätzlich als Warnung im Protokoll. ## Sitzungen und Prüfpunkte Ein Abbruch bei 90 Prozent darf nicht bedeuten, dass alles von vorn beginnt — bei mehreren Terabyte wäre das der Unterschied zwischen Stunden und Tagen, und zwar in einer Lage, in der ohnehin schon etwas schiefging. Jeder Auftrag bekommt eine Sitzung mit Prüfpunkt. Der Prüfpunkt hält **einen einzigen Pfad**: den zuletzt vollständig zurückgeschriebenen. Das genügt, weil die Objekte im Manifest fest sortiert sind — alles lexikographisch davor ist erledigt. Eine Liste wäre bei einer Million Dateien unbrauchbar groß. **Der Prüfpunkt entsteht erst, nachdem die Datei vollständig und unter ihrem endgültigen Namen am Platz liegt.** Ein Prüfpunkt auf eine halb geschriebene Datei wäre schlimmer als keiner: Die Fortsetzung übersprünge sie als erledigt. Geschrieben wird er nicht bei jeder Datei, sondern höchstens alle paar Sekunden — bei einer Million kleiner Dateien wären das sonst eine Million Schreibvorgänge in die Datenbank. Der Preis ist, dass eine Fortsetzung wenige Sekunden Arbeit wiederholt. Bei einer Fortsetzung greift der Schutz gegen ein volles Zielverzeichnis **nicht**: Dort liegt notwendigerweise der bereits zurückgeschriebene Teil. Ihn über `overwrite_existing` auszuhebeln wäre falsch — das erlaubte zugleich das Überschreiben fremder Daten. Die Endzahlen beschreiben den **gesamten** Vorgang, nicht nur den letzten Versuch: drei Objekte aus dem ersten Lauf plus zwei aus dem zweiten ergeben fünf. ## Kein automatischer Wiederholungsversuch Anders als eine Sicherung wird eine gescheiterte Wiederherstellung **nicht** automatisch erneut gestartet. Ein zweiter Lauf in ein halb gefülltes Zielverzeichnis kann Daten beschädigen, die der erste Versuch bereits am Platz hatte. Der Betreiber soll hinsehen, bevor erneut geschrieben wird — die Sitzung mit ihrem Prüfpunkt bleibt dafür offen. Das gilt auch für verwaiste Aufträge: Stirbt ein Control-Server mitten in einer Wiederherstellung, wird der Auftrag nach fünf Minuten als `SCHEDULER_LOST` gescheitert vermerkt, aber nicht erneut eingereiht. Die Sitzung überlebt. ## Nebenläufigkeit **Standard ist eine Wiederherstellung zur Zeit.** Sie läuft im Ernstfall, und dann zählt die Geschwindigkeit *einer* Wiederherstellung, nicht der Durchsatz mehrerer: Wer zwei gleichzeitig laufen lässt, halbiert die Geschwindigkeit derjenigen, auf die alle warten. Ein Teilindex verhindert zwei gleichzeitige Wiederherstellungen in **dasselbe Ziel**. Sie schrieben sich gegenseitig zu — und zwar ohne dass es auffiele, weil beide erfolgreich endeten. **Das Repository wird schreibgeschützt geöffnet.** Eine Wiederherstellung liest nur und kann deshalb neben einer laufenden Sicherung stattfinden, statt auf deren Schreibsperre zu warten. Im Ernstfall wartet niemand gern. ## Übergangene Objekte sind ein Teilfehler Wie bei den Sicherungen: Eine Wiederherstellung, die zwei Dateien nicht zurückschreiben konnte, ist unvollständig. Sie darf nicht als Erfolg dastehen — der Anwender glaubte sonst, alles sei wieder da. Die Datenbank erzwingt das über ein CHECK. ## API ``` POST /api/v1/restores/validate restores.read prüft, schreibt nichts POST /api/v1/restores restores.execute 202: reiht ein, auditiert GET /api/v1/restores restores.read Liste mit Pagination GET /api/v1/restores/{id} restores.read inkl. Prüfpunkt bei Abbruch POST /api/v1/restores/{id}/cancel restores.execute auditiert ``` Die Vorabprüfung braucht nur das **Leserecht**: Sie schreibt nichts und soll niedrigschwellig sein — wer den Zustand der Backups beurteilen soll, muss sie ausführen können. ```json { "backup_id": "…", "target_path": "/wiederhergestellt", "path_prefix": "dokumente/2026", "overwrite_existing": true, "confirm_overwrite": "/wiederhergestellt" } ``` `path_prefix` beschränkt auf einen Teilbaum — der häufigste Fall im Betrieb ist nicht die vollständige Wiederherstellung, sondern eine einzelne versehentlich gelöschte Datei. Die Präfixgrenze achtet auf Verzeichnisse: `dokumente` trifft nicht auch `dokumentation`. `POST /restores` antwortet mit **202**, nicht 201: Der Auftrag ist eingereiht, die Daten sind noch nicht zurück. Beim Abbruch wird ausdrücklich gesagt, dass bereits zurückgeschriebene Daten am Ziel liegen bleiben — sonst hielte man das Ziel für unberührt. ## Nachgewiesen Gegen den laufenden Dienst, 11,4 MiB in 12 Objekten: | Schritt | Ergebnis | | --- | --- | | Vorabprüfung | „Wiederherstellbar: 9 Dateien, 11.4 MiB", 16 Blöcke geprüft, 0 fehlend | | Quelle gelöscht, über die API wiederhergestellt | `succeeded`, 12 Objekte in 0,11 s | | Vergleich | **bitgenau identisch** inkl. Rechten (600), Symlink, leerem Verzeichnis | | Volles Ziel ohne Zustimmung | abgelehnt | | `overwrite_existing` ohne Bestätigung | abgelehnt, nennt den zu wiederholenden Pfad | | Falscher Bestätigungswert | abgelehnt | | Mit korrekter Bestätigung | 202, überschrieben, im Protokoll als Warnung | | Ein Block entfernt | `can_proceed: false`, betroffene Datei benannt, Auftrag mit 422 abgelehnt | ## Offene Punkte - **Keine Platten- und Systemwiederherstellung.** Sie setzen Provider voraus, die noch nicht liefern. - **Proxmox-VM-Wiederherstellung** ist im Provider umgesetzt, aber ungeprüft und ohne `ArchiveTransport` nicht erreichbar. - **Keine Fortsetzung über die API.** Der Prüfpunkt liegt vor und die Schleife nutzt ihn, aber es gibt keinen Endpunkt, der einen gescheiterten Auftrag erneut einreiht — nur den Weg über die Datenbank. - **Kein `GET /restores/{id}/logs` und `/metrics`** (SYNCOVA_API.md §13). - **Keine Wiederherstellung in der Oberfläche.** Sie geht nur über die API. - **Kein Wiederherstellungstest als eigener Auftrag.** Das ist Phase 10 — und der Punkt, an dem aus „prüfbar" ein regelmäßiger Nachweis wird.