Enterprise-Backup-, Recovery-, Verification-, Security- und Monitoring-Plattform fuer Proxmox VE, Windows, Linux und Dateisysteme. Der Leitsatz, der fast jede Entscheidung erklaert: Ein Backup gilt erst als vertrauenswuerdig, wenn Integritaet geprueft und Wiederherstellbarkeit nachgewiesen wurde. Deshalb steigt ein Wiederherstellungspunkt erst nach einem tatsaechlich durchgefuehrten Restore-Test auf "recoverable", und Unbekanntes geht in keine Bewertung als "gut" ein. Umfang (Phasen 0-23): - Repository Engine: inhaltsadressierte Bloecke, atomares Commit-Protokoll, Katalogaufbau allein aus den Manifesten — ohne Datenbank - Backup Engine: inhaltsabhaengiges Chunking, Deduplizierung trotz Verschluesselung, zstd, AES-256-GCM, Streaming mit Gegendruck - Agenten fuer Windows und Linux mit Auftragsabholung (Pull-Modell) - Proxmox-Provider mit beiden Zugriffswegen auf die Sicherungsarchive - Scheduler, Recovery Engine mit Pruefpunkt, Verification, Unveraenderlichkeit - Weboberflaeche, Kennzahlen, Meldungen, Berichte, Security Center, Ransomware-Heuristik (meldet, handelt nie) - Disaster Recovery, Haertung, Leistungsmessung, Chaos Testing - Eingefrorene Vertraege fuer API, Migrationen, Backup-Format und Repository - Auslieferungspaket fuer linux/amd64, linux/arm64 und windows/amd64 Nicht enthalten und als solches gekennzeichnet: Kapazitaetsprognose, Backup Copy, Changed Block Tracking bei Proxmox, erweiterte Attribute und ACLs. Gebaut, aber nie auf echter Hardware gefahren: der Windows-Dienst, die systemd-Einheit und der verpflichtende Proxmox-Meilenstein — ob eine wiederhergestellte VM startet, ist ungeprueft. Einzelheiten in CHANGELOG.md und docs/release-candidate.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
11 KiB
Prüfung und Recovery Assurance
Phase 10. Der Teil der Anlage, der die Frage beantwortet, die das ganze Produkt trägt: Kann ich mich auf dieses Backup verlassen?
Ein Backup, dessen Wiederherstellbarkeit nie geprüft wurde, ist eine Vermutung. Die Prüfung macht daraus eine Aussage — und wo sie fehlt, sagt die Anlage das, statt die Lücke mit einer freundlichen Zahl zu füllen.
Die fünf Prüfarten
Sie unterscheiden sich in Aussagekraft und Kosten erheblich. Das ist kein Nachteil, sondern der Zweck: Eine Anlage, die nur die teuerste Prüfung kennt, wird selten geprüft.
| Art | Was sie liest | Was sie beweist |
|---|---|---|
manifest |
nur das Manifest | Das Verzeichnis ist stimmig. Über die Daten sagt sie nichts. |
chunk_presence |
Verzeichniseinträge der Blöcke | Alle Blöcke sind da. Ihr Inhalt wurde nicht gelesen. |
chunk_integrity |
jeden Block vollständig | Alle Blöcke sind da und unbeschädigt. |
chain |
die Manifeste der ganzen Kette | Kein Glied zwischen Voll- und Zuwachssicherung fehlt. |
restore_test |
alles, und schreibt es zurück | Das Backup ist wiederherstellbar. |
Nur die letzte Zeile ist ein Nachweis. Die übrigen sind Indizien — gute, aber Indizien. Genau deshalb wiegt der Wiederherstellungstest in der Bewertung am schwersten und hängt an einem eigenen Recht.
Ohne Angabe der Art wird chunk_integrity gewählt: die schwächste Prüfung, die
überhaupt etwas über die Daten aussagt. manifest als Vorgabe wäre bequem und
wertlos.
Die Blockprüfung braucht keinen Schlüssel
Sie vergleicht die gespeicherte Form gegen ChunkReference.StoredDigest,
nicht gegen die Chunk-Kennung. Die Kennung beschreibt bei einem verschlüsselten
Repository den Klartext; eine Prüfung gegen sie meldete jeden Block als
beschädigt. Diesen Fehler hatte der Integritätsscan der Phase 2 (siehe
docs/repository.md), und die Prüfmaschine hätte ihn wiederholt.
Der Nebeneffekt ist wichtiger als die Fehlerbehebung: Eine Integritätsprüfung läuft dadurch auch dort, wo der Datenschlüssel nicht vorliegt.
Der Wiederherstellungstest
Er legt ein Wegwerfziel über os.MkdirTemp an, schreibt das Backup dorthin
zurück, vergleicht jede Datei gegen den ContentHash im Manifest und räumt
anschließend restlos auf (defer os.RemoveAll). Er braucht den Datenschlüssel —
liegt er nicht vor, wird die Prüfung abgelehnt statt scheinbar ausgeführt. Ein
Vergleich von Geheimtext gegen Klartext-Prüfsummen schlüge fehl und meldete ein
gesundes Backup als beschädigt.
Ein auf einen Teilbaum beschränkter Test hebt die Einstufung nicht auf
recoverable: Er hat nicht das Backup geprüft, sondern einen Teil davon.
Einstufung
Fünf Stufen, vier davon auf einer Leiter:
failed → successful → verified → recoverable
corrupted (außerhalb)
| Stufe | Bedeutung |
|---|---|
failed |
Der Sicherungslauf ist gescheitert. |
corrupted |
Eine Prüfung fand Beschädigungen oder fehlende Blöcke. |
successful |
Der Lauf ist abgeschlossen. Ob sich die Daten zurückholen lassen, wurde nicht geprüft. |
verified |
Die Blöcke sind vorhanden und unbeschädigt. |
recoverable |
Das Backup wurde zurückgeschrieben und verglichen. Nachweislich wiederherstellbar. |
corrupted steht bewusst außerhalb der Leiter: Es ist keine Zwischenstufe,
sondern ein Ausschlussgrund.
Die Einstufung wird von zwei Datenbank-Constraints gestützt, nicht nur von der Anwendungslogik:
CHECK (classification <> 'recoverable' OR last_restore_test_at IS NOT NULL)
CHECK (classification <> 'verified' OR last_verified_at IS NOT NULL)
Die stärkste Aussage der Anlage lässt sich damit nicht ohne ihren Nachweis vergeben — auch nicht durch einen Fehler in einer künftigen Codeänderung.
Rangfolge beim Fortschreiben
Eine bestandene Blockprüfung hebt auf verified, stuft aber nie von
recoverable herab: Der Wiederherstellungstest bleibt die stärkere Aussage.
Ein Befund löscht die bisherigen Nachweise
Findet eine Prüfung Beschädigungen, werden last_verified_at,
last_restore_test_at und die gemessene Wiederherstellungsdauer auf NULL
gesetzt. Der Grund stammt aus dem Nachweis dieser Phase: Ohne diesen Schritt
stand das Backup nach Behebung des Schadens sofort wieder als recoverable da —
auf Grundlage eines Tests, der vor dem Schaden lief. Wer den Nachweis will,
muss ihn wiederholen.
Ein Fehlschlag ist kein Befund
Konnte die Prüfung nicht laufen — Repository nicht erreichbar, Control-Server
abgestürzt —, wird der Auftrag als failed vermerkt und die Einstufung des
Backups bleibt unberührt. Dass die Prüfung nicht stattfand, sagt nichts über die
Daten. Alles andere wäre ein Fehlalarm, und ein Prüfwerkzeug, das grundlos Alarm
schlägt, wird bald nicht mehr ernst genommen.
Bewertung (Recovery Assurance Score)
Zehn Eingangsgrößen nach SYNCOVA_IMPLEMENTATION_PLAN.md §12:
| Größe | Gewicht |
|---|---|
| Wiederherstellungstest | 25 |
| Integritätsprüfung | 20 |
| Aktualität | 15 |
| Wiederherstellungspunkt (RPO) | 10 |
| Wiederherstellungsdauer (RTO) | 10 |
| Unveränderlichkeit | 5 |
| Zweiter Standort | 5 |
| Verschlüsselung | 5 |
| Repository-Zustand | 3 |
| Auffälligkeiten | 2 |
Die Gewichte folgen einer Überzeugung: Der Wiederherstellungstest wiegt am schwersten, weil er als einziger etwas nachweist. Verschlüsselung und Unveränderlichkeit schützen die Daten, sagen aber nichts darüber, ob sie noch da sind.
Unbekannt zählt niemals als gut
Jede Größe trägt ein is_known. Eine nie durchgeführte Messung bekommt null
Punkte und wird als ungemessen geführt. Bei mehr als drei unbekannten Größen
meldet is_trustworthy false und die Zusammenfassung sagt ausdrücklich, dass die
Prozentzahl eine Vermutung ist. missing_measurements nennt die fehlenden
Messungen — das ist die eigentliche Handlungsanweisung.
Beschädigt heißt null Prozent
Ein Backup mit Befund und ein gescheiterter Lauf bekommen 0 %, unabhängig davon, wie gut die übrigen Größen aussehen.
Der Fall stammt aus dem Nachweis dieser Phase: Ein Backup mit einem einzigen beschädigten Block kam auf 70 %, weil Aktualität, Verschlüsselung und ein früherer Wiederherstellungstest weiterhin zählten. Diese Zahl liest sich wie „weitgehend in Ordnung". Ein Backup, aus dem sich ein Block nicht mehr lesen lässt, ist aber nicht zu 70 % wiederherstellbar, sondern nicht wiederherstellbar.
Ebenso zählt ein früherer Wiederherstellungstest nach einem Befund nicht mehr: Er bezieht sich auf einen Zustand des Repositorys, den es nachweislich nicht mehr gibt.
Berechnet, nicht gespeichert
GET /backups/{id}/assurance berechnet die Bewertung bei jedem Aufruf neu. Sie
hängt am Alter der Messungen und veraltet damit von selbst — ein gespeicherter
Wert würde mit jedem Tag falscher, ohne dass sich etwas ändert. Der Wert in
backups.assurance_score ist nur ein Abbild für Übersichten.
Ausführung
Die Prüfungen laufen in einer eigenen Schleife neben Sicherung und Wiederherstellung. Sie mit den Sicherungen zu vermengen wäre falsch: Eine Prüfung wird nie wiederholt, weil sie fehlschlug, und sie darf eine laufende Sicherung nicht verdrängen — beide lesen denselben Datenträger. Standardmäßig läuft eine Prüfung gleichzeitig.
- Übernahme über
FOR UPDATE SKIP LOCKED: Mehrere Control-Server greifen nicht nach demselben Auftrag. - Ein Teilindex verhindert zwei gleichzeitige Prüfungen desselben Backups. Sie läsen dieselben Daten doppelt, ohne mehr festzustellen. Der zweite Versuch bekommt 409.
- Lebendmeldung alle 30 Sekunden; nach 30 Minuten ohne Meldung gilt die Prüfung als verwaist und wird beim nächsten Start freigegeben. Ohne diesen Schritt sperrte der Teilindex das Backup dauerhaft gegen jede weitere Prüfung.
- Das Repository wird schreibgeschützt geöffnet: Eine Prüfung darf nichts anfassen und soll neben einer laufenden Sicherung stattfinden können.
Endpunkte
| Methode | Pfad | Recht |
|---|---|---|
| GET | /api/v1/verification |
verification.read |
| POST | /api/v1/verification |
verification.write |
| GET | /api/v1/verification/{id} |
verification.read |
| GET | /api/v1/verification/{id}/results |
verification.read |
| POST | /api/v1/verification/{id}/cancel |
verification.write |
| GET | /api/v1/backups/{id}/assurance |
backups.read |
Ein restore_test verlangt zusätzlich verification.restore_test. Er schreibt
zwar nur an einen Wegwerfort, liest aber das gesamte Backup und belastet damit
Datenträger und Leitung.
Der vollständige Bericht steht unter /results und nicht am Auftrag: Bei einem
großen Backup trägt er viele Befunde, und eine Liste von Aufträgen bliebe damit
nicht mehr überschaubar. Liegt kein Bericht vor, antwortet der Endpunkt mit 404
statt mit einem leeren Ergebnis — ein leeres Ergebnis sähe aus wie ein sauberes.
Beispiel
# Integritätsprüfung anstoßen
curl -X POST http://127.0.0.1:8080/api/v1/verification \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"backup_id":"<uuid>","verification_type":"chunk_integrity"}'
# Ergebnis abholen
curl http://127.0.0.1:8080/api/v1/verification/<id>/results -H "Authorization: Bearer $TOKEN"
# Bewertung des Backups
curl http://127.0.0.1:8080/api/v1/backups/<uuid>/assurance -H "Authorization: Bearer $TOKEN"
Nachgewiesener Lebenszyklus
Gegen den laufenden Dienst mit einem echten Repository durchgespielt (420 KB, drei Objekte, verschlüsselt):
| Schritt | Einstufung | Bewertung |
|---|---|---|
| Backup, ungeprüft | successful |
35 % — drei Größen ungemessen |
nach chunk_integrity |
verified |
55 % |
nach restore_test |
recoverable |
90 % |
ein Byte in einem Block gekippt, chunk_integrity |
corrupted |
0 % |
restore_test auf dem beschädigten Backup |
corrupted |
0 % |
Block zurückgespielt, chunk_integrity |
verified |
55 % |
restore_test wiederholt |
recoverable |
90 % |
Der Befund nannte die betroffene Datei (rechnung.txt), nicht nur die Zahl der
Blöcke. Die verbleibenden 10 % fehlen für Unveränderlichkeit und eine Kopie an
einem zweiten Ort — beides ist noch nicht umgesetzt und wird deshalb mit null
Punkten geführt, nicht wohlwollend geschätzt.
Bekannte Grenzen
- Keine Prüfrichtlinien.
verification_policiesausSYNCOVA_DATABASE.mdexistiert noch nicht; Prüfungen werden von Hand oder über die API angestoßen, nicht nach Zeitplan. Der Endpunkt/verification-policiesfehlt entsprechend. - Kein zweiter Standort.
HasOffsiteCopyist festfalse. Die Größe hier wohlwollend anzunehmen wäre die bequeme und falsche Entscheidung. - Auffälligkeiten werden aus der Zahl übergangener Objekte des Laufs abgeleitet, nicht aus einer Verlaufsanalyse.
- Der Wiederherstellungstest schreibt vollständig zurück. Bei einem 10-TB-
Backup braucht das entsprechend Platz und Zeit;
RestoreTestWorkingDirectorylegt den Ort fest.