syncova-backup/docs/release-candidate.md
Jerrit Fritzsche 610719c316
Some checks failed
CI / Backend (Go) (push) Failing after 3m7s
CI / Frontend (React/TypeScript) (push) Successful in 37s
CI / Sicherheitsprüfungen (push) Successful in 44s
Syncova Backups V1
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>
2026-08-17 09:10:54 +02:00

14 KiB

Release Candidate (Phase 22)

Phase 22 tut zwei Dinge: Sie friert die Verträge ein und fährt die Prüfungen, die man vor einer Auslieferung genau einmal ernsthaft macht.

Was „eingefroren" hier bedeutet

Nicht: „bitte nicht mehr ändern." Sondern: Eine Änderung muss eine bewusste Handlung sein. Vier Verträge stehen deshalb in Dateien, die man anfassen muss, und in Tests, die bei jeder Abweichung anschlagen.

Vertrag Wo er steht Was der Test fängt
API apps/api/internal/httpapi/contract_routes.txt (102 Endpunkte) neuer, entfallener oder anders berechtigter Endpunkt
Migrationen migrations/checksums.txt nachträglich geänderte oder entfernte Migration, fehlende Gegenrichtung, Lücke in der Nummerierung
Backup-Format packages/backupformat/testdata/container-v1.syncova Format-, Abschnitts- und Kennzeichenwerte; ein Container von gestern, der heute nicht mehr lesbar ist
Repository packages/repository/testdata/frozen-repository/ Verzeichnis- und Dateinamen, Manifestversion, Lesbarkeit, Katalogaufbau ohne Datenbank

Warum der API-Vertrag den Quelltext auswertet

http.ServeMux gibt seine Routen nicht heraus. Ein Test, der die bekannten Adressen anfragt, bemerkt eine hinzugefügte nicht — und eine neue Route ist die häufigste Vertragsänderung überhaupt. Der Test liest deshalb router.go mit go/parser und zieht Methode, Pfad und geforderte Berechtigung heraus.

Die Berechtigung gehört ausdrücklich dazu. Eine stillschweigend gelockerte Prüfung ist die gefährlichste Änderung, die es gibt: Der Endpunkt funktioniert weiter, nur dürfen ihn plötzlich mehr Leute aufrufen. Beides — Lockerung und Wegfall — ist mit einer Mutation nachgewiesen.

Warum eine ausgelieferte Migration sich nie ändern darf

Datenbanken, die eine Migration angewandt haben, führen sie nicht erneut aus; golang-migrate merkt sich nur die Versionsnummer. Eine nachträgliche Änderung wirkt deshalb ausschließlich auf neue Installationen. Das Ergebnis sind zwei Schemata mit derselben Versionsnummer, und der Unterschied fällt erst auf, wenn eine Abfrage auf einer der beiden Anlagen scheitert.

Wer etwas ändern will, schreibt eine neue Migration. Immer.

Warum Fixture-Dateien und kein Rundlauftest

Ein Rundlauftest schreibt und liest mit demselben Code. Er bleibt grün, wenn sich beide Seiten gemeinsam ändern — also genau in dem Fall, den man fürchtet. Die eingefrorenen Dateien sind mit Formatversion 1 geschrieben und liegen im Quellbestand; sie werden vom Code von heute gelesen.

Neu erzeugen lassen sie sich nur mit ausdrücklichem Schalter:

SYNCOVA_WRITE_FIXTURE=ja go test ./packages/backupformat -run TestWriteFrozenContainerFixture
SYNCOVA_WRITE_FIXTURE=ja go test ./packages/repository   -run TestWriteFrozenRepositoryFixture

Sie versehentlich mitlaufen zu lassen hieße, den Vertrag jedes Mal auf den aktuellen Stand zu heben — und damit genau die Prüfung abzuschalten, um die es geht.

Der Upgrade-Test

Der einzige Test dieser Anlage, den es bis Phase 22 nie gab. Bisher wurde jedes Schema von Grund auf angelegt; damit prüft man ausschließlich die Neuinstallation. Ein Kunde macht etwas anderes: Er hat Daten und spielt eine neue Version darüber.

Der Unterschied ist nicht theoretisch:

  • Eine Migration, die eine Spalte mit NOT NULL ohne Vorgabewert ergänzt, läuft auf einer leeren Tabelle einwandfrei durch und scheitert auf einer gefüllten.
  • Eine nachträgliche CHECK-Regel prüft auch die Zeilen, die schon da sind.

Der Ablauf ist der einer echten Aktualisierung: Schemastand 4 herstellen, Benutzer, Repositories, Aufträge, Quellen mit Ausschlussmustern, Läufe, Kette und ein abgeschlossenes Backup anlegen — dann die neuen Migrationen darüber.

Geprüft wird danach die Zeilenzahl und der Inhalt. Eine Migration, die jede Zeile behält und dabei ein Feld leert, bliebe sonst unbemerkt.

Eine Absicherung im Test selbst weist einen leeren Ausgangsbestand zurück. Ohne sie wäre der ganze Test ein Placebo: Ein Vergleich „vorher gleich nachher" ist auf leeren Tabellen immer erfüllt.

Beide Fehlerarten sind mit einer Mutation nachgewiesen — eine löschende Migration und eine NOT NULL-Spalte ohne Vorgabewert.

Der Rollback-Test

Ein Rollback ist die unangenehmste Zusage einer Datenbank. Geprüft ist:

  • Genau ein Schritt. Ein versehentlicher Rücklauf auf Version 0 löschte die gesamte Control Plane; deshalb geht down immer nur einen Schritt und verlangt in der Produktion SYNCOVA_MIGRATE_CONFIRM_DOWN=yes.
  • Daten älterer Stände bleiben unberührt. Ein Rückschritt, der nebenbei Aufträge oder Backups mitnimmt, wäre ein Datenverlust ohne Ansage.
  • Der Weg nach vorn bleibt offen. Eine Anlage, die sich zurücknehmen, aber nicht wieder aufbauen lässt, ist verloren.
  • Alle Rückrichtungen laufen auf einer gefüllten Datenbank — bis Version 0 und wieder hoch. Eine down-Migration wird typischerweise nie ausgeführt, bis zu dem Tag, an dem sie gebraucht wird; dann steht sie unter Zeitdruck und mit Daten davor.

Was ein Rollback nicht kann

Daten in Tabellen, die es vorher nicht gab, sind danach weg. Der Test hält das ausdrücklich fest: Ein eingerichteter Virtualisierungsverbund überlebt den Rückschritt seiner Migration nicht, und nach dem erneuten Vorlauf steht die Tabelle leer da. Die Zugangsdaten sind nicht wiederherstellbar.

Ein Rollback ersetzt keine Sicherung der Datenbank. Wer eine Aktualisierung fahren will, sichert vorher — der Rückschritt ist die zweite Verteidigungslinie, nicht die erste.

Der vollständige Durchlauf

Gefahren gegen den laufenden Dienst, 30 MiB inkompressible Daten in 61 Dateien mit Unterordnern und einem symbolischen Verweis:

Schritt Ergebnis
Repository eintragen über POST /repositories, Kennung aus dem Descriptor gelesen
Auftrag anlegen und starten 202, Lauf succeeded, 30,0 MiB gelesen, 68 Objekte
Integritätslauf 69 Blöcke geprüft, 0 fehlend, 0 beschädigt, 0 verwaist
Prüfung mit Wiederherstellungstest completed / clean, Einstufung recoverable, 70 %
Quelle gelöscht 61 Dateien vernichtet
Vorabprüfung durchführbar, 68 Objekte, 69 Blöcke, keine Befunde
Wiederherstellung succeeded, 68 Objekte, 0 übergangen, 31 457 320 Byte in 0,41 s
Vergleich 61 von 61 Dateien bitgenau, Symlink und Rechte erhalten

Zwei Funde des Durchlaufs

Ein Repository ließ sich über die API gar nicht anlegen. SYNCOVA_API.md §8 verlangt POST, PATCH, GET /{id}, test, health-check, integrity-scan und rebuild-catalog — vorhanden war ausschließlich die Liste. Repositories kamen bis dahin über SQL in die Control Plane. Aufgefallen ist das erst hier, weil jeder frühere Nachweis die Zeile selbst eingetragen hat. Die Anlage war über ihre eigene API nicht in Betrieb zu nehmen.

Die sieben Endpunkte sind ergänzt. POST /repositories legt dabei nichts an, sondern übernimmt: Es öffnet das vorhandene Repository, liest dessen Kennung aus dem Descriptor und trägt es ein. Ein Datenbankeintrag ohne Repository dahinter wäre ein Ziel, das erst um zwei Uhr nachts als nicht vorhanden auffällt — real geprüft, die Antwort verweist auf syncova-repo create.

Der Ort eines Repositorys war nicht eindeutig. Zwei Einträge konnten auf dasselbe Verzeichnis zeigen. Das ist kein Schönheitsfehler: Zwei Aufträge hielten sie für verschiedene Ziele, rissen sich um dieselbe Schreibsperre, und die Kennzahlen zählten denselben Speicher doppelt. Migration 000013 setzt die Eindeutigkeit; bestehende Doppelungen lassen die Aktualisierung scheitern, weil nur ein Betreiber weiß, welcher Eintrag der richtige ist.

Disaster Recovery

Auf einer leeren Datenbank mit dem neuen Schemastand 13, allein aus dem Repository:

11 Repositories, 12 Aufträge, 6 Konten übernommen
0 aktive Aufträge, 0 aktive Konten

Die beiden Nullen sind das Wesentliche. Nichts läuft von selbst wieder an: Ein Zeitplan, der nachts anspringt, könnte auf eine halb wiederhergestellte Anlage schreiben.

Security-Review

Gegen die Entwicklungsdatenbank: 29 von 88 Punkten (33 %), Einstufung unzureichend, 8 kritische Befunde. Das ist das richtige Ergebnis für diese Umgebung — kein gehärtetes Repository, keine gemessene Durchsetzungsstufe, Konten mit Löschrecht ohne zweiten Faktor, unverschlüsselte Wiederherstellungspunkte aus früheren Nachweisen.

Der Satz daneben ist der Kern: „Solange sie bestehen, ist die Anlage nicht sicher zu betreiben — die Prozentzahl daneben ändert daran nichts." Ein kritischer Befund deckelt die Einstufung, unabhängig von der Zahl.

Dazu die Prüfungen aus make check: Abhängigkeiten (govulncheck: keine Schwachstelle), Statik (go vet, staticcheck), Geheimnissuche im gesamten Quellbestand.

Performance-Review

Apple M1, 8 Kerne, lokale SSD, inkompressible Daten, ohne Verschlüsselung — dieselben Bedingungen wie in Phase 20, damit die Zahlen vergleichbar sind:

Szenario Phase 20 Phase 22
Eine große Quelle (512 MiB) 151,9 MiB/s 179,5 MiB/s
Zweiter Lauf über unveränderte Daten 662,8 MiB/s 636,7 MiB/s
Viele kleine Dateien 107 Dateien/s 100 Dateien/s

Keine Regression. Der zweite Lauf bleibt rund viermal schneller — die unabhängige Bestätigung der Aussage aus Phase 6: Der Gewinn einer Zusatzsicherung ist Zeit, nicht Speicher.

Die Streaming-Pipeline hält weiterhin: 256 MiB Quelle bei 256 MiB Speichergrenze, Höchststand 182,5 MiB.

Akzeptanzmatrix (§26)

Abgehakt wird nur, was tatsächlich gefahren wurde. Eine Zeile, die man abhakt, weil der Code vorhanden ist, ist die teuerste Zeile des ganzen Dokuments.

Backup

Zeile Nachweis
✅ Linux file backup Phase 6, Sonderdateien und Rechte; Phase 22 E2E
⬜ Windows file backup Der Agent übersetzt für Windows und wurde nie dort ausgeführt. Kommandozeilenweg und Auftragsausführung sind plattformunabhängig nachgewiesen, der Dienstwrapper nicht
⚠️ Proxmox VM backup vollständig gegen einen API-Nachbau, nie gegen einen echten Verbund
✅ incremental Phase 6 und 8; zweiter Lauf 0,0 MiB statt 38,1 MiB
✅ deduplication Phase 4; zweiter Lauf 636,7 MiB/s gegen 179,5
✅ compression Phase 4, zstd mit Marker für nicht verkleinerbare Blöcke
✅ encryption Phase 4, AES-256-GCM mit abgeleiteter Nonce
✅ retry Phase 8, nur bei transient/network/repository/source
✅ resume Phase 9 und 18, Fortsetzung nach SIGKILL bei 496 von 1500 Dateien
✅ bandwidth control Phase 8, gemessen: 19,1 MiB in 0,37 s ohne Grenze gegen 18,17 s bei 1 MiB/s

Recovery

Zeile Nachweis
✅ file restore Phase 9; Phase 22 E2E bitgenau
✅ folder restore Phase 22 E2E, 61 Dateien in Unterordnern
⚠️ Proxmox restore Archiv bitgenau auf den Knoten zurückgeschrieben und qmrestore angestoßen — ob die Maschine bootet, ist ungeprüft
⚠️ alternate-host restore im Provider umgesetzt (--node), nur gegen den Nachbau gefahren
✅ restore validation Phase 9, Vorabprüfung mit Blockprüfung; im E2E gefahren
✅ interrupted restore recovery Phase 18, Prüfpunkt und Fortsetzung nach SIGKILL

Repository

Zeile Nachweis
✅ local repository durchgehend
✅ hardened repository Phase 11, rm -rf → 15 verweigerte Löschungen
✅ integrity scan Phase 2; Phase 22 über die API
✅ corruption detection Phase 10 und 21; Phase 22 Fixture mit gekipptem Byte
✅ catalog rebuild Phase 2, 18 und 22 (Fixture ohne indexes/)
✅ immutable retention Phase 11, Verlängern ja, Verkürzen nie

Security

Zeile Nachweis
✅ RBAC Phase 19, sechs schreibende Zugriffe als Viewer → 403; Phase 7 erneut
✅ MFA Phase 1, gegen alle zehn RFC-6238-Testvektoren, mit Replay-Schutz
✅ audit Phase 1, append-only per Datenbank-Trigger
✅ TLS Phase 19, mit TLS 1.3 nachgewiesen; halbe Konfiguration wird beim Start abgelehnt
✅ secret protection Phase 19 Geheimnissuche; Phase 7 real geprüft, dass kein Token im Klartext in der Zeile steht
✅ privilege boundaries Phase 5, Agent-Token gegen /users → 401; Phase 11, backups.delete getrennt von immutability.manage

Monitoring

Zeile Nachweis
✅ dashboard Phase 12, 9 von 10 Kennzahlen mit Datengrundlage
✅ metrics Phase 13
✅ graphs Phase 13, zwölf Diagramme über sieben Zeiträume
✅ alerts Phase 14, zehn Regeln, Zustellung gegen echten SMTP- und HTTP-Server
⬜ capacity forecast nicht umgesetzt. Die Kennzahl erscheint mit Begründung statt mit einer Null
✅ health Phase 0, Liveness ohne Abhängigkeiten, Readiness mit

Reliability

Zeile Nachweis
✅ control server restart Phase 8, SIGTERM bricht laufende Vorgänge ab und schreibt ihr Ergebnis
✅ database restart Phase 21, /health/live bleibt 200, Dienst 2 s nach Rückkehr wieder bereit
✅ repository restart Phase 21, volle Platte → REPOSITORY_FULL, null Manifeste
⚠️ agent restart Betriebsschleife und Wiederaufnahme nachgewiesen; der Windows-Dienst wurde nie geladen
⚠️ network interruption Rückzugsverhalten des Agenten geprüft, aber nur lokale Repositories — nicht neu ausgelöst
✅ corrupted data Phase 21, beschädigtes Manifest: Wiederherstellung bricht ab, 0 Dateien im Ziel

Zusammenfassung

32 von 38 Zeilen erbracht, 2 nicht umgesetzt, 4 nur gegen Nachbauten.

Die vier Zeilen mit ⚠️ und die Windows-Zeile hängen alle an derselben Sache: fehlender Hardware. Sie lassen sich hier nicht erbringen, und sie zu behaupten wäre die eine Sorte Fehler, die dieses Produkt nicht machen darf.

Was vor der Auslieferung bleibt

  1. Windows: Dienst laden, Start, Stopp, Neustart und die Rechte des Dienstkontos prüfen.
  2. Linux: deployment/syncova-agent.service laden und systemd-analyze verify fahren.
  3. Proxmox: den verpflichtenden Meilenstein zu Ende gehen — Test-VM löschen, wiederherstellen, booten, validieren.
  4. Netzunterbrechung gegen ein entferntes Repository auslösen.
  5. Capacity Forecast umsetzen oder als bewusste Auslassung in die Freigabe schreiben.

Punkte 1 bis 4 sind Ausführung, kein Bau. Punkt 5 ist eine Entscheidung.