syncova-backup/docs/reports.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

10 KiB

Berichte (Phase 17)

Neun Berichte, drei Formate, kein gespeicherter Bericht.

Ein Bericht ist das Dokument, das die Anlage verlässt. Er landet in einer Tabellenkalkulation, in einer Präsentation und in einem Prüfordner. Was darin steht, wird Monate später gelesen, ohne dass jemand nachfragen kann — und eine erfundene Zahl überlebt dort jede mündliche Erläuterung, mit der man sie hätte einordnen können. Das ist der Grund für die beiden Entscheidungen, die dieses Paket tragen.

Ein Modell, drei Sichten

Ein Bericht ist eine Sammlung aus Kennzahlen und Tabellen. CSV, JSON und PDF sind drei Sichten auf dieselbe Struktur; kein Bericht weiß, in welchem Format er ausgegeben wird.

Wären die Formate in die Berichte eingebaut, müsste jeder der neun dreimal geschrieben werden — und beim zehnten vergisst jemand eines davon. Der praktische Beleg steht in report.go: Die Zellen einer Tabelle sind bereits als Text aufbereitet. Die Formatierung einer Datenmenge gehört zum Bericht, nicht zum Ausgabeformat, sonst zeigten CSV und PDF dieselbe Zahl verschieden.

Ausnahme mit Absicht: Die Kennzahlen gehen als Rohwert ins CSV. Eine Tabellenkalkulation soll rechnen können, und „1,4 GiB" ist Text.

Eine ungemessene Zahl ist keine Null

Jede Kennzahl trägt is_known. Ist sie nicht gesetzt, gibt es keinen Wert — sondern eine Begründung.

Fall Falsch Richtig
Zeitraum ohne Lauf Erfolgsquote 100 % nicht bestimmbar, „ohne Lauf gibt es keine Quote"
Repository ohne hinterlegte Kapazität Auslastung 100 % nicht bestimmbar
Kein Wiederherstellungspunkt 0 % verschlüsselt nicht bestimmbar, „es gibt keinen"
RTO ohne Wiederherstellung geschätzt aus Datenmenge ÷ Durchsatz nicht gemessen

Im CSV bleibt die Wertspalte leer und die Anmerkung trägt den Grund. Eine leere Zelle lässt sich nicht versehentlich summieren; eine Null wird summiert, gemittelt und in ein Diagramm gezeichnet.

Umgekehrt gilt dasselbe: Eine gemessene Null bleibt sichtbar. „Null Läufe" ist eine Aussage und darf nicht mit „nicht gemessen" verwechselt werden.

Die neun Berichte

Bericht Zeitbezug Besonderheit
Tagesbericht 24 h Beantwortet die Frage, die morgens zuerst gestellt wird
Wochenbericht 7 Tage Zusätzlich nach Tagen aufgeschlüsselt — zeigt den Auftrag, der immer freitags scheitert
Monatsbericht 30 Tage Zusätzlich nach Aufträgen aufgeschlüsselt
Gescheiterte Sicherungen 7 Tage Teilfehler stehen ausdrücklich mit darin
Repository-Kapazität Zustand Löschschutz gemessen, nicht behauptet
Wiederherstellungen 30 Tage Die einzige Quelle gemessener Wiederherstellungszeiten
Sicherheit Zustand Nutzt das Security Center, rechnet nicht selbst
Für Prüfungen Zustand Messwerte, keine Urteile
RPO und RTO Zustand Der ehrliche Härtefall, siehe unten

Die drei Sicherungsberichte teilen sich eine Implementierung. Sie unterscheiden sich nur in der Aufschlüsselung — sie getrennt zu schreiben hieße, dieselbe Erfolgsquote dreimal zu berechnen und beim nächsten Fund an zwei Stellen zu vergessen.

Ein Zustandsbericht bekommt keinen Zeitraum. Die Belegung der Repositories wird nicht historisiert; einen Bericht „vom letzten Dienstag" kann es nicht geben. Wer trotzdem einen Zeitraum angibt, bekommt ihn ignoriert — ihn anzunehmen und nicht auszuwerten wäre die freundlichste Art zu lügen.

RPO lässt sich messen, RTO nicht

Der Abstand zur letzten erfolgreichen Sicherung steht in der Datenbank. Die Zeit, die eine Wiederherstellung bräuchte, kennt niemand, bevor sie stattgefunden hat.

In der RTO-Spalte steht deshalb entweder eine Messung aus einer tatsächlich durchgeführten Wiederherstellung oder ein ausdrückliches „nicht gemessen" — nie eine Hochrechnung aus Datenmenge und Durchsatz. Eine solche Schätzung wäre die bequemste Zahl des ganzen Berichts und die einzige, auf die sich im Ernstfall niemand verlassen könnte.

Der Bericht sagt das auch: Für einen Auftrag mit RTO-Vorgabe ohne Messung ist die Einhaltung unbekannt — nicht erfüllt und nicht verletzt.

Der Bericht für Prüfungen täuscht nichts vor

PROMPT.md §73 ist an dieser Stelle ausdrücklich: „Syncova soll keine Compliance-Zertifizierung vortäuschen." Der Bericht liefert deshalb Messwerte und keine Urteile — kein „erfüllt", kein Haken, keine Norm. Der erste Hinweis unter dem Bericht sagt es wörtlich: Dieser Bericht ist keine Zertifizierung und ersetzt keine.

„Kopie an einem zweiten Ort" erscheint als nicht messbar, weil Backup Copy nicht umgesetzt ist. Weder null noch hundert Prozent wären wahr.

PDF ist selbst geschrieben

Der Plan sagt „PDF where practical". Die bequeme Auslegung wäre, es für nicht praktikabel zu erklären — aber ein Bericht für eine Prüfung wird als PDF verlangt, nicht als CSV.

Gebraucht wird nur der einfachste Teil des Formats: Text in einer der vierzehn Standardschriften, die jeder Betrachter mitbringt. Keine Einbettung, keine Bilder, keine Transparenz.

Drei Dinge kosteten trotzdem Arbeit:

  • Die Zeichenbreiten von Helvetica liegen als Tabelle im Code. Ohne sie ließe sich weder umbrechen noch eine Spalte bemessen; mit geschätzten Breiten überlappen Tabellenspalten, sobald ein Wert länger wird als erwartet.
  • Die Kodierung. Die Standardschriften werden mit /WinAnsiEncoding eingebunden, jedes Zeichen ist ein Byte. Ohne Umwandlung zeigt der Betrachter „Sicherungslÿufe" — und zwar erst beim Empfänger, nicht beim Erzeuger.
  • Die Querverweistabelle. Sie ist der einzige Teil eines PDF, bei dem ein Fehler nicht auffällt, solange man die Datei nur ansieht: Ein falscher Offset bricht das Dokument erst beim Empfänger, und dann heißt es, der Bericht sei beschädigt angekommen. TestPDFCrossReferenceOffsetsArePrecise prüft jeden Verweis auf einen tatsächlichen Objektbeginn.

Fund bei der Sichtprüfung: Ein gleichmäßiger Stauchfaktor für zu breite Tabellen kürzte „TEILWEISE FEHLGESCHLAGEN" zu „TEILWEISE FEHL…", weil daneben ein langer Pfad stand — ausgerechnet der Wert, wegen dessen man den Bericht liest. Jetzt wird eine gemeinsame Obergrenze gesucht: Spalten darunter bleiben unangetastet, nur die breitesten geben ab.

Fund bei derselben Prüfung: **keine Zertifizierung** stand mit Sternchen im PDF. Das Modell ist formatunabhängig, sein Text darf deshalb keine Auszeichnung tragen. Ein Test hält das fest.

API

GET  /api/v1/reports              # Katalog der neun Berichte  (reports.read)
POST /api/v1/reports/generate     # erzeugt und liefert aus     (reports.read)
{ "type": "daily_backup", "from": "…", "to": "…", "format": "pdf" }

JSON kommt in der üblichen Antworthülle, CSV und PDF als Datei mit Content-Disposition. Der Dateiname wird bereinigt, bevor er in den Kopf geht — ein Zeilenumbruch darin ließe sich zum Einschleusen weiterer Kopffelder nutzen.

Berichte werden nicht gespeichert. Ein abgelegter Bericht veraltet mit jedem Tag, ohne dass sich an ihm etwas ändert — dieselbe Überlegung wie bei der Recovery Assurance (Phase 10) und der Sicherheitsbewertung (Phase 15). Wer einen Bericht aufbewahren will, lädt ihn herunter; die Datei trägt ihren Erzeugungszeitpunkt bei sich.

Jeder Abruf wird auditiert (REPORT_GENERATED). Ein Bericht liest nur und verändert nichts — protokolliert wird er trotzdem, weil er ein Datenexport ist: Der Bericht für Prüfungen und der Sicherheitsbericht nennen die Schwachstellen der Anlage in geordneter Form, und wer eine solche Datei aus dem System trägt, gehört zu den Fragen, die ein Prüfer als erstes stellt.

Scheitert das Protokollieren, wird der Bericht trotzdem ausgeliefert — anders als bei einer destruktiven Handlung wäre es die falsche Abwägung, ein Leserecht wegen eines Protokollfehlers zu verweigern.

Oberfläche

Die Berichtsseite stand seit Phase 12 als benannte Lücke im Menü. Sie zeigt den Katalog, eine Vorschau im selben Modell wie die Datei und zwei Schaltflächen für CSV und PDF.

Der Download geht über downloadApiFile neben requestApi: Eine Datei trägt keine Antworthülle. Durch requestApi geschickt, versuchte dieser ein PDF als JSON zu lesen und meldete eine unverständliche Antwort — obwohl alles in Ordnung ist. Der Fehlerfall geht dagegen sehr wohl durch die Hülle, deshalb wird der Inhaltstyp geprüft: Sonst landete eine Fehlermeldung als „bericht.pdf" im Download-Ordner, und der Anwender sähe statt einer Meldung eine kaputte Datei.

Nachweis

Gegen die laufende Anlage geführt:

  • 27 von 27 Kombinationen aus neun Berichten und drei Formaten erzeugt.
  • Das PDF von CoreGraphics gerendert (macOS Quick Look nutzt dieselbe Engine wie Vorschau) und angesehen — Umlaute, Tabellen, Seitenumbruch, Hinweise.
  • Eine echte Wiederherstellung über die API ausgeführt, damit der RTO-Teil nicht nur Code ist: Vorher „nicht gemessen, 0 Messungen", danach „RTO-Vorgabe 1 h 0 min, gemessene Dauer 36 ms, 1 Messung".
  • Ein Zeitraum ohne jeden Lauf (2024) erzeugte keine 100-Prozent-Quote, sondern drei ausdrücklich unbestimmbare Kennzahlen mit Begründung.
  • Der Auditeintrag REPORT_GENERATED steht mit Art, Format und Zeitraum in der Datenbank.

Fund im Nachweis: „Längste Laufzeit: 0" stand als Messung da, während „Mittlere Laufzeit" korrekt als unbestimmbar galt. Die Datenbank liefert dank COALESCE eine Null, und im Bericht sah sie aus wie ein Lauf, der in null Sekunden durchlief. TestEmptyPeriodProducesNoFabricatedNumbers hält jetzt alle drei Kennzahlen fest.

Fund vor dem Nachweis: Die Abfrage des Berichts für Prüfungen nutzte users.is_active — eine Spalte, die es nie gab (das Schema führt status). Der Bericht wäre bei jedem Abruf gescheitert.

Bekannte Grenzen

  • Die Liste einzelner Läufe ist auf 500 Einträge begrenzt. Die Begrenzung wird im Bericht ausgesprochen; die Kennzahlen im Überblick zählen alle Läufe. Eine stillschweigend gekürzte Liste liest sich wie eine vollständige.
  • Kein Zeitplan, keine Zustellung. Berichte werden abgerufen, nicht zugestellt. Der Plan verlangt das für diese Phase nicht; die Zustellwege aus Phase 14 (E-Mail, Webhook) sind vorhanden und ließen sich anbinden.
  • Die Vorschau zeigt 25 Zeilen je Tabelle. Die heruntergeladene Datei enthält alle; die Vorschau sagt, wie viele sie zeigt.
  • PDF kennt keine Textauszeichnung. Fett gesetzt sind nur Überschriften, Spaltenköpfe und Kennzahlenwerte — der Fließtext ist einheitlich.