# 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 --backup --out backup.syncova syncova-repo inspect --in backup.syncova # prüfen ohne einzulesen syncova-repo import --path --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.