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>
111 lines
6.7 KiB
Markdown
111 lines
6.7 KiB
Markdown
# Backup-Format
|
|
|
|
Dieses Dokument beschreibt den **aktuell implementierten** Stand nach Phase 3.
|
|
|
|
## Zweck
|
|
|
|
Der Syncova Backup Container ist die **portable** Form eines Backups: eine einzelne, in sich geschlossene Datei mit allem, was zur Wiederherstellung nötig ist. Er dient der Übertragung an ein zweites Repository (PROMPT.md §17), der Archivierung und dem Austausch zwischen Installationen.
|
|
|
|
Das Repository (Phase 2) legt ein Backup **verteilt** ab — Chunks und Manifest getrennt. Der Container fasst dasselbe Backup in **eine** Datei. Beide Formen sind selbstprüfend und brauchen keine Datenbank.
|
|
|
|
## Aufbau
|
|
|
|
```text
|
|
+--------------------------------------------------+
|
|
| Magic "SYNCOVA1" 8 Byte |
|
|
| Formatversion 2 Byte (Big Endian) |
|
|
| Headerlänge 4 Byte |
|
|
| Header JSON |
|
|
+--------------------------------------------------+
|
|
| Abschnitt 1: Typ 1 | Flags 1 | Länge 8 | SHA 32 |
|
|
| Inhalt |
|
|
| Abschnitt 2 ... |
|
|
+--------------------------------------------------+
|
|
| Footer-Magic "SYNFOOT1" 8 Byte |
|
|
| Footerlänge 4 Byte |
|
|
| Footer JSON |
|
|
+--------------------------------------------------+
|
|
```
|
|
|
|
Alle Mehrbyte-Zahlen sind Big Endian. Die Byte-Reihenfolge ist Teil des Formats und darf nicht von der Rechnerarchitektur abhängen.
|
|
|
|
**Abschnittstypen:** Manifest (1), Chunk-Verzeichnis (2), Blockzuordnung (3), Datenblöcke (4), Quellenangaben (5), Integritätsangaben (6). Die Zahlenwerte sind Teil des Vertrags und dürfen **niemals** neu belegt werden — ein bestehender Container würde sonst falsch gedeutet.
|
|
|
|
## Der Footer steht am Ende
|
|
|
|
Das ist die tragende Entscheidung des Entwurfs. Erst am Ende stehen alle Prüfsummen fest — und genau daraus folgt die Aussagekraft:
|
|
|
|
> Ein abgebrochener Schreib- oder Übertragungsvorgang hinterlässt einen Container **ohne** Footer. Er wird dadurch zuverlässig als unvollständig erkannt.
|
|
|
|
Erst `Close()` macht einen Container gültig. Wer den Vorgang vorher abbricht, hinterlässt Bruchstücke — aber nie ein Gebilde, das sich als vollständiges Backup ausgibt.
|
|
|
|
## Versionsverhandlung
|
|
|
|
Der Leser kennt `MinimumReadableVersion` und `MaximumReadableVersion`. Ein Container außerhalb dieser Spanne wird **nicht angetastet**, und die Meldung unterscheidet die Richtung:
|
|
|
|
- **zu neu** → „Bitte Syncova aktualisieren" (ein Update hilft)
|
|
- **zu alt** → Hinweis auf die Mindestversion (ein Update hilft nicht)
|
|
|
|
Ein falsch interpretiertes Backup ist schlimmer als ein nicht gelesenes.
|
|
|
|
## Erweiterbarkeit
|
|
|
|
Jeder Abschnitt trägt seine Länge. Eine spätere Programmversion darf deshalb neue Abschnitte ergänzen — eine ältere überspringt sie, ohne sie zu deuten.
|
|
|
|
Die Grenze zieht `FlagRequired`:
|
|
|
|
| Abschnitt | Verhalten einer älteren Version |
|
|
| --- | --- |
|
|
| unbekannt, optional | wird übersprungen, Container bleibt gültig |
|
|
| unbekannt, **erforderlich** | Verarbeitung wird **verweigert** |
|
|
|
|
Ohne diese Unterscheidung würde eine alte Version einen Container lesen, dem wesentliche Teile fehlen, und Vollständigkeit vortäuschen (PROMPT.md §140).
|
|
|
|
## Streaming und aufgeschobene Prüfsumme
|
|
|
|
Der Datenbereich kann beliebig groß werden und darf nie vollständig in den Arbeitsspeicher (PROMPT.md §80). Er wird deshalb als Datenstrom geschrieben — was einen Konflikt erzeugt:
|
|
|
|
> Die Prüfsumme eines Abschnitts steht erst fest, wenn er vollständig geschrieben ist. Sein Kopf ist zu diesem Zeitpunkt längst in der Ausgabe.
|
|
|
|
Der naheliegende Ausweg — den Kopf nachträglich überschreiben — verlangt eine rückspulbare Senke und schlösse Pipes und Netzwerkziele aus. Stattdessen trägt ein solcher Abschnitt `FlagDeferredDigest`: sein Kopf-Digest bleibt leer und wird beim Lesen nicht herangezogen.
|
|
|
|
**Es entsteht dadurch keine Prüflücke.** Die Unversehrtheit ist doppelt gesichert:
|
|
|
|
1. Jeder einzelne Chunk trägt seine Prüfsumme im Chunk-Verzeichnis; der Import prüft jeden Block gegen seine Kennung.
|
|
2. Die Gesamtprüfsumme im Footer deckt Header **und** alle Abschnittsinhalte ab.
|
|
|
|
## Was die Integritätsprüfung abdeckt
|
|
|
|
| Angriff / Fehler | Erkannt durch |
|
|
| --- | --- |
|
|
| Einzelnes Bit verfälscht | Abschnitts-Prüfsumme, sonst Gesamtprüfsumme |
|
|
| Container abgeschnitten | fehlender Footer |
|
|
| Abschnitt entfernt | Abschnittszahl im Footer |
|
|
| Abschnitt **samt passender Prüfsumme** ausgetauscht | Gesamtprüfsumme im Footer |
|
|
| Manifest ausgetauscht | Manifest-Hash im Footer |
|
|
| Abschlussvermerk auf `false` gesetzt | ausdrückliche Prüfung |
|
|
| Fremde Datei | Magic Bytes |
|
|
|
|
Prüfsummenvergleiche laufen in konstanter Zeit — eine Prüfsumme ist ein Sicherheitsmerkmal, kein bloßer Vergleichswert.
|
|
|
|
## Export und Import
|
|
|
|
```bash
|
|
syncova-repo export --path <repo> --backup <id> --out backup.syncova
|
|
syncova-repo inspect --in backup.syncova # prüfen ohne einzulesen
|
|
syncova-repo import --path <ziel-repo> --in backup.syncova
|
|
```
|
|
|
|
**Export** liest jeden Chunk über `ReadChunk` — die Integritätsprüfung greift also, bevor ein Block in den Container gelangt. Fehlt ein Chunk oder ist er beschädigt, bricht der Export ab und das Kommando **löscht die angefangene Zieldatei**. Ein halber Container wäre eine Falle: er sähe aus wie ein Backup, wäre aber keines.
|
|
|
|
**Import** macht das Manifest erst sichtbar, **nachdem** alle Abschnitte, Prüfsummen und der Abschlussvermerk stimmen. Ein fehlgeschlagener Import hinterlässt allenfalls Chunks — nie ein scheinbar gültiges Backup. Bereits vorhandene Chunks werden dabei erkannt: dieselbe Deduplizierung wie beim Sichern.
|
|
|
|
**Inspect** liest den Container vollständig (und prüft damit alle Prüfsummen), ohne etwas zu schreiben. Der Exit-Status ist bei einem unbrauchbaren Container ungleich 0, damit ein Skript oder Monitoring daran anschlägt.
|
|
|
|
## Grenzen des aktuellen Stands
|
|
|
|
- **Chunks sind unkomprimiert und unverschlüsselt.** Die Felder `encryption` und `compression` sind bereits Teil des Header-Formats und überstehen die Rundreise — die Verarbeitung folgt in Phase 4. Eine spätere Ergänzung der Felder wäre ein Formatbruch, deshalb stehen sie jetzt schon dort.
|
|
- **Der Import lädt den Datenbereich vollständig in den Speicher.** Für Container jenseits weniger Gigabyte ist das nicht tragbar; der Weg über `ChunkIndexEntry.ContainerOffset` ist vorbereitet, aber noch nicht als Datenstrom umgesetzt.
|
|
- **Die Blockzuordnung (`SectionBlockMap`) ist definiert, wird aber noch nicht geschrieben.** Sie wird für Disk-Images gebraucht, bei denen logische Bereiche auf Chunks abgebildet werden müssen.
|
|
- **Kein Wiederaufsetzen einer abgebrochenen Übertragung.** Ein unvollständiger Container muss vollständig neu erzeugt werden.
|