syncova-backup/CHANGELOG.md
Jerrit Fritzsche d94debac4d
Some checks failed
CI / Backend (Go) (push) Failing after 30s
CI / Frontend (React/TypeScript) (push) Successful in 46s
CI / Sicherheitsprüfungen (push) Successful in 27s
Dokumentation und Aenderungsliste fuer rc9
Die Sicherungsart steht in backup-engine.md, weil dort der Unterschied zwischen
voll und inkrementell erklaert ist — mit der Einordnung, die am haeufigsten
verwechselt wird: Der Platzbedarf steigt bei "immer voll" nicht nennenswert,
die Laufzeit schon.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 17:48:06 +02:00

403 lines
19 KiB
Markdown

# Änderungen
## V1 — Release Candidate 9, 18. August 2026
### Sicherungsart je Auftrag
Bisher entschied die Anlage allein: Liegt ein Elternbackup vor, wird
inkrementell gesichert. Jetzt wählbar —
- **inkrementell** (Standard, bisheriges Verhalten),
- **immer voll**, oder
- inkrementell **mit festem Volltag**, etwa „immer freitags".
Der Wochentag wird in der **Zeitzone des Zeitplans** bestimmt. Rechnete der
Server in UTC, bekäme ein Betreiber in Berlin seine Vollsicherung am
Donnerstagabend und wunderte sich, warum sie freitags fehlt.
**Der Platzbedarf steigt bei „immer voll" nicht nennenswert** — unveränderte
Blöcke werden dedupliziert. Was steigt, ist die Laufzeit. Das steht so in der
Maske, weil es die häufigste Verwechslung ist.
Migration 000014 mit drei CHECKs. Der dritte lehnt „immer voll" zusammen mit
einem Wochentag ab: Dann ist ohnehin jeder Lauf voll.
### Behoben
- **Das Aufnahme-Token eines Agenten zeigte „undefined".** Das Feld heißt
`token`, nicht `enrollment_token` — Letzteres ist der Name im *Anfrage*körper
der Registrierung. Der dritte Formfehler dieser Art; alle konsumierten
Endpunkte sind jetzt gegen den laufenden Dienst abgeglichen statt aus der
Struktur abgeleitet.
### Aufnahmedialog mit Anleitung
Vollständige Anleitung für **Linux und Windows**, umschaltbar, mit fertig
ausgefüllten Befehlen — Serveradresse und Token eingesetzt, jeder Schritt
einzeln kopierbar. Eine Anleitung mit Platzhaltern führt zuverlässig dazu, dass
jemand `<token>` wörtlich einsetzt.
Dazu die beiden Stolperstellen: `--state` erwartet eine **Datei**, und der
Agent braucht Schreibzugriff auf das Repository.
### update.sh rüstet die Wiederherstellungsfläche nach
Sie kam mit rc8 dazu; eine Anlage aus einer älteren Fassung hat sie nicht. Ohne
sie scheitert jede Wiederherstellung an `ProtectSystem=strict`. `update.sh`
legt sie jetzt an und trägt sie in `ReadWritePaths` ein — ein Schritt, den man
von Hand ausführen muss, wird übersehen und fällt erst im Ernstfall auf.
## V1 — Release Candidate 8, 18. August 2026
Wiederherstellung von Dateien und Ordnern mit Auswahl statt Textfeld — und die
Erklärung, warum vorher gar keine Wiederherstellung funktionierte.
### Warum keine Wiederherstellung ging
Nicht die Rechte des Zielverzeichnisses, sondern die Härtung des Dienstes: Er
läuft mit `ProtectSystem=strict` und `ReadWritePaths` nur auf Repository und
Sicherungsordner. Jedes Ziel außerhalb endete mit `mkdir: permission denied` —
und zwar **nach** der Vorabprüfung, an der unangenehmsten Stelle. `/tmp`
scheiterte anders: Mit `PrivateTmp=yes` hat der Dienst ein eigenes `/tmp`, und
was dort landet, ist von außen unsichtbar.
`setup.sh` legt jetzt `/srv/syncova-restore` an und trägt es ein;
`--wiederherstellungsziel` ergänzt weitere. Der Ort liegt unter `/srv`, weil
`/var/lib` auf der Sperrliste des Zielschutzes steht — beide Regeln zugleich zu
erfüllen lässt genau `/srv` übrig.
### Auswahl statt Textfeld
- **Ordnerbaum für das Ziel.** Er meldet je Verzeichnis, ob der Dienst dort
schreiben darf — **gemessen** durch eine Probedatei, nicht aus den Rechtebits
abgeleitet. Gesperrte Orte werden gezeigt, nicht versteckt: Sonst bliebe
offen, warum ein Pfad fehlt.
- **Browser für den Backup-Inhalt.** Ordner **und** einzelne Dateien lassen
sich zurückholen. Der Baum entsteht aus den Pfaden, nicht aus
Verzeichniseinträgen — ein Manifest kann eine Datei enthalten, deren
Elternordner nicht als eigener Eintrag vorliegt.
Zwei neue Endpunkte, der eingefrorene Vertrag ist entsprechend erweitert:
`GET /filesystem/browse` und `GET /backups/{id}/contents`.
### Behoben
- **„can't access property toLocaleString, chunks_checked is undefined"** beim
Integritätslauf. Die Ergebnisse liegen unter `details`, und die Felder heißen
`missing_chunks`/`corrupted_chunks`. Betrifft alle vier Prüfendpunkte — sie
tragen dieselbe Hülle. Derselbe Fehler wie zuvor bei `/retention-policies`:
die Antwortform angenommen statt geprüft. Alle konsumierten Endpunkte sind
jetzt gegen den laufenden Dienst abgeglichen.
### Geist Mono liegt im Paket
Drei Schnitte, 128 KB, OFL-Lizenz dabei. Ausgeliefert vom eigenen Ursprung —
das verlangt die CSP, und ein Backup-Server, dessen Oberfläche von der
Erreichbarkeit eines CDN abhängt, wäre auch ohne CSP falsch.
### Bekannte Grenze
**Eine Auswahl je Lauf**, kein Mehrfachhaken. Eine Liste ausgewählter Pfade
kennt die API nicht; mehrere Läufe hintereinander ergäben mehrere Ausgänge, und
ein „teilweise fehlgeschlagen" ließe sich dann nicht mehr erklären.
## V1 — Release Candidate 7, 18. August 2026
Behebt einen Absturz, macht die Sitzung brauchbar und stellt das Aussehen um.
### Behoben
- **`/retention` zeigte einen schwarzen Bildschirm.** `GET /retention-policies`
liefert ein Objekt `{policies, predefined}` — als einziger von zehn geprüften
Listenendpunkten. Die Oberfläche behandelte es als Array; `map` gibt es auf
einem Objekt nicht, React hängte den ganzen Baum aus. Der Regressionstest
füttert jetzt die **echte** Antwortform; ein Test mit einem Array hätte den
Fehler nie gefunden, und genau das war passiert.
- **Jedes Neuladen führte zurück zur Anmeldung.** Die Tokens lagen nur im
Arbeitsspeicher.
- **Ein Fehler in einer Komponente schwärzte die ganze Konsole.** Jetzt sitzt
eine Fehlergrenze um den Seiteninhalt: Menü und Kopfzeile bleiben stehen, der
Fehlertext ist lesbar und kopierbar.
### Sitzung
Sie überlebt ein Neuladen und endet nach **30 Minuten** — gerechnet als frühere
von zwei Grenzen: einer harten Obergrenze ab Anmeldung, die keine Interaktion
verschiebt, und einer Untätigkeitsgrenze. Die verbleibende Zeit läuft neben
„Abmelden" und wird unter fünf Minuten auffällig.
Sechs Tests halten die Grenzen fest, zwei davon durch Mutation als fangend
bestätigt: Wer beim Vermerken einer Interaktion die Obergrenze mitverschiebt,
macht aus „30 Minuten" ein „unbegrenzt, solange die Maus wackelt".
### Aussehen
Farben, Radien, Schatten und Schrift aus dem Preset `b5vnF8SMi`: violett als
Handlungsfarbe, Radius 0, schattenlos, durchgehend Geist Mono. Die
**Statusfarben bleiben** — das Preset kennt keine, und ohne sie ließe sich ein
Teilfehler nicht von einem Erfolg unterscheiden.
Dazu **Umlaute auf allen Seiten** (vorher durchgehend ae/oe/ue/ss), **33
Erläuterungen von Absätzen auf einen Satz gekürzt** und neue Module: acht
Schnellzugriffe auf der Übersicht sowie sechs mitgelieferte
Aufbewahrungsvorlagen als Kacheln — die lieferte der Server schon immer mit,
die Oberfläche warf sie bisher weg.
### Unverändert offen
Windows-Dienst, systemd-Einheit des Agenten und der Proxmox-Bootmeilenstein sind
gebaut, aber nie auf echter Hardware gefahren. Geist Mono wird nicht
mitgeliefert; ohne die Schrift auf dem Gerät greift die System-Monospace.
## V1 — Release Candidate 6, 18. August 2026
**Die Weboberfläche ist eine vollständige Verwaltungskonsole geworden.**
Vorher erreichte sie 23 von 95 fachlichen Endpunkten; schreibend waren es neun,
vier davon An- und Abmeldung. Real verwaltbar war: einen Auftrag anlegen, einen
Bericht erzeugen, eine Meldung bestätigen. Das war ein Leseinstrument mit
Assistent, keine Konsole. **Jetzt sind es 89 von 95.**
### Neu bedienbar
- **Wiederherstellung** als vierstufiger Assistent — vorher nur über `curl`. Die
Vorabprüfung ist ein eigener Schritt, weil sie den Unterschied zwischen
Hoffnung und Nachweis macht: Sie schreibt nichts und stellt fest, ob **jeder
benötigte Block noch da ist**. Die drei Hürden vor dem Überschreiben sind
sichtbar umgesetzt; läuft die dritte ins Leere, entfällt sie.
- **Wiederherstellungspunkte** mit Bewertung, Legal Hold, Fristverlängerung,
Löschung und Ransomware-Einschätzung.
- **Prüfung** mit allen fünf Prüfarten. Zustand und Ergebnis stehen
nebeneinander: Eine gescheiterte Prüfung ist kein Befund am Backup.
- **Repositories** mit Integritätslauf, Katalog-Neuaufbau, Gesundheitsprüfung und
gemessener Durchsetzungsstufe.
- **Aufbewahrung** mit Regeln und Vorschau vor dem Löschen.
- **Proxmox** — neun Endpunkte, die vorher gar keine Oberfläche hatten — und
**Agenten** samt einmaliger Anzeige des Aufnahme-Tokens.
- **Benutzer, Rollen, Benachrichtigungswege, eigener zweiter Faktor.**
### Neues Design
Tailwind v4 und Radix-Primitive nach shadcn-Muster, alles gebündelt. Dark Mode,
einklappbare Seitenleiste, Bedienung auf Tablets. Achtzehn Seiten in fünf
Bereichen.
Die tragende Entscheidung ist keine Frage des Aussehens: **Die Zuordnung der
Fachbegriffe auf die fünf Statusfarben liegt an genau einer Stelle.** Verteilt
über die Seiten erschiene früher oder später irgendwo `partial_failure` grün —
und ein Betreiber hält einen Teilfehler dann für einen Erfolg. Ein unbekannter
Serverzustand wird neutral dargestellt, niemals grün.
### Funde beim Nachweis
- **Die Einstufung heißt `successful`, nicht `unverified`.** Das ist die
gefährlichste Stelle der Oberfläche: `successful` bedeutet „der Lauf ist
durchgelaufen" — nicht „wiederherstellbar". Es ist deshalb neutral, nicht
grün. Regressionstest vorhanden.
- **`describeApiError` warf die genauere Servermeldung weg** und ersetzte sie
durch einen allgemeinen Satz. Ein `SERVICE_UNAVAILABLE` mit „Für diesen Bericht
ist keine Sicherheitsprüfung eingerichtet" wurde zu „Der Dienst ist derzeit
nicht vollständig verfügbar" — der Betreiber hätte den Fehler bei seiner Anlage
gesucht. Jetzt hat die Servermeldung Vorrang.
- **Einem Fehler nach einer Handlung fehlte `role="alert"`.** Ein Screenreader
hätte ihn nicht angesagt.
- **Der Geheimnis-Scanner griff korrekt** beim Platzhaltertext des
SSH-Schlüsselfelds. Gekennzeichnet, statt das Muster aufzuweichen.
### Aufgeräumt
Ein Stylesheet statt sieben; das ausgelieferte CSS fällt von 54 auf 30 KB.
Entfernt, weil ersetzt: `JobsPanel`, `StatusIndicator`, `PageState`.
### Unverändert offen
Windows-Dienst, systemd-Einheit des Agenten und der Proxmox-Bootmeilenstein sind
gebaut, aber nie auf echter Hardware gefahren. Dazu ohne Oberfläche: sechs
Detail-Endpunkte (ihre Daten stehen in den Listen), Live-Fortschritt
(`/api/v1/events/stream` ist auch serverseitig nicht umgesetzt) und der Simple
Mode.
## V1 — Release Candidate 5, 18. August 2026
Die Oberfläche richtet sich jetzt mit ein. Bisher endete `setup.sh` mit einer
laufenden API auf `127.0.0.1:8080` und der Aufgabe, einen Webserver von Hand
davorzusetzen — der häufigste Punkt, an dem eine Einrichtung liegen blieb.
- **`setup.sh` richtet nginx und ein selbst signiertes Zertifikat ein.** Das
Zertifikat gilt für den Rechnernamen, den vollständigen Namen und **jede**
globale IPv4-Adresse des Servers (`subjectAltName` — moderne Browser lesen
den `CN` nicht mehr), 3650 Tage. Der SHA-256-Fingerabdruck wird genannt, damit
er sich beim ersten Aufruf im Browser vergleichen lässt.
- **Die API bleibt an `127.0.0.1:8080` gebunden.** Erreichbar ist sie nur durch
nginx hindurch. Sie stattdessen auf alle Schnittstellen zu legen wäre der
kürzere Weg und der falsche: Die Verschlüsselung ließe sich dann umgehen,
indem man Port 8080 direkt anspricht.
- **Die Firewall wird gemeldet, nicht geändert.** `ufw` und `firewalld` werden
erkannt und ihr Zustand ausgegeben; geöffnet wird nichts. Eine Einrichtung,
die selbsttätig einen Port ins Netz öffnet, hebelt genau die Entscheidung aus,
für die jemand die Firewall aufgesetzt hat.
- **Scheitert die Oberfläche, scheitert nicht die Einrichtung.** Die
Konfiguration wird mit `nginx -t` geprüft, **bevor** sie übernommen wird; hält
sie nicht, wird sie entfernt, der Grund genannt und der Nachholweg gezeigt.
Die Anlage läuft in jedem Fall.
- **Nachträglich einrichten:** `sudo /opt/syncova/setup.sh --weboberflaeche`.
Auslassen: `--ohne-weboberflaeche`.
Ein Fund beim Erproben:
- **`http2 on;` gibt es erst ab nginx 1.25.1.** Debian 12 liefert 1.22, wo
HTTP/2 ein Parameter von `listen` ist. Die neue Schreibweise ergibt dort
„unknown directive http2", und nginx startet nicht. Die Fassung wird jetzt
gelesen und die passende Schreibweise erzeugt.
## V1 — Release Candidate 4, 18. August 2026
Behebt [Issue #2](https://git.jfritzsche.de/jf/syncova-backup/issues/2): Die
Einrichtung brach mit „Der Dienst meldet sich nicht als betriebsbereit" ab,
obwohl der Dienst einwandfrei lief.
- **Die Bereitschaftsprüfung hängt nicht mehr an `curl`.** Sie weicht auf `wget`
aus und zuletzt auf die Bash selbst (`/dev/tcp`), die überall vorhanden ist.
Auf einem schlanken Serverabbild ist `curl` nicht installiert — das ist der
Normalfall, nicht die Ausnahme. Ein Einrichtungsskript darf nicht
voraussetzen, was es nicht selbst mitbringt.
- Betrifft `setup.sh`, `update.sh` und `diagnose.sh`. Der Diagnosebericht nennt
jetzt zusätzlich, **womit** er gemessen hat.
## V1 — Release Candidate 3, 17. August 2026
Behebt [Issue #1](https://git.jfritzsche.de/jf/syncova-backup/issues/1): Die
Einrichtung brach ab, wenn das Repository unter `/tmp` liegen sollte.
- **Ein flüchtiger Ablageort wird abgelehnt** — `/tmp`, `/var/tmp`, `/dev/shm`,
`/run` und jedes tmpfs. `systemd-tmpfiles` räumt dort auf, ein tmpfs ist nach
einem Neustart leer: Die Sicherungen verschwänden von selbst, ohne Meldung,
bis jemand sie braucht. Geprüft wird **vor** der Datenbankeinrichtung, damit
ein unbeaufsichtigter Lauf in Sekunden scheitert statt nach Minuten.
Für Wegwerf-Umgebungen: `SYNCOVA_SETUP_ALLOW_VOLATILE_REPOSITORY=ja` — dann
weicht `PrivateTmp`, sonst könnte der Dienst nicht starten.
- **Der Abbruch zeigt jetzt den Grund.** Kommt der Dienst nicht hoch, liefert
`setup.sh` die letzten Journalzeilen gleich mit und erklärt `226/NAMESPACE`.
Vorher verwies er nur auf `journalctl` — und beim Rückbau war der Dienst dann
schon weg.
## V1 — Release Candidate 2, 17. August 2026
Ergänzt gegenüber `rc1`:
- **`diagnose.sh`** liegt jetzt im Paket und wird von `setup.sh` und `update.sh`
nach `/opt/syncova/diagnose.sh` gelegt. Es sammelt in einem Zug, was für eine
Fehlersuche gebraucht wird — Fassungen, Dateisystem des Repositorys,
Schemastand, letzte nicht erfolgreiche Läufe mit Fehlercode und Fehlerklasse,
gemessene Durchsetzungsstufe. Es liest nur und entfernt Geheimnisse aus jeder
Ausgabe.
- **Fehlervorlage** unter `.gitea/ISSUE_TEMPLATE/`.
Zwei Korrekturen, beide beim Erproben gefunden:
- **PostgreSQL 15 genügt.** Die Anleitung verlangte 17; Debian 12 liefert 15,
und darauf lief alles bis zum echten Sicherungslauf. `setup.sh` prüft die
vorgefundene Fassung jetzt und lehnt ältere als 15 ab, statt sie
stillschweigend zu nehmen.
- **`SYNCOVA_ADMIN_PASSWORD` gibt es nicht.** Die Anleitung nannte diese
Umgebungsvariable; das Passwort kommt über die Standardeingabe. Korrigiert.
`rc1` bleibt abrufbar, ist aber überholt.
## V1 — 14. August 2026
Die erste Fassung. Sie sichert Dateisysteme unter Linux und Windows sowie
Gäste eines Proxmox-VE-Verbunds, prüft die Wiederherstellbarkeit und weist sie
nach.
### Was sie kann
**Sichern und Wiederherstellen.** Inhaltsabhängige Blockfindung, Deduplizierung
auch über verschlüsselte Bestände hinweg, zstd, AES-256-GCM, Streaming-Pipeline
mit Gegendruck. Zusatzsicherungen tragen ein vollständiges Manifest — ein
Restore liest genau eine Datei, es gibt keine Kette aufzulösen. Wiederherstellung
mit Vorabprüfung, Prüfpunkt und Fortsetzung nach Abbruch.
**Ein Repository, das ohne die Anlage auskommt.** Inhaltsadressierte Blöcke,
atomares Commit-Protokoll, Katalogaufbau allein aus den Manifesten. Fällt der
Control-Server samt Datenbank aus, lässt sich beides aus dem Repository
zurückholen.
**Vertrauen wird nachgewiesen, nicht behauptet.** Fünf Prüfarten bis zum
tatsächlichen Wiederherstellungstest, eine Einstufung, die nur mit
durchgeführtem Test auf `recoverable` steigt, und eine Bewertung, in die
Unbekanntes niemals als gut eingeht.
**Löschschutz mit gemessener Durchsetzungsstufe.** Was das Betriebssystem
nachweislich verhindert, wird gemeldet — nicht, was die Einstellung verspricht.
Dazu Legal Hold, Fristverlängerung ohne Verkürzungsmöglichkeit und
Aufbewahrungsregeln.
**Oberfläche, Kennzahlen, Meldungen, Berichte, Security Center** und eine
Ransomware-Heuristik, die meldet und niemals selbst handelt.
### Was sie ausdrücklich nicht kann
- **Kapazitätsprognose.** Die Kennzahl erscheint mit Begründung statt mit einer
Null.
- **Kopie an einen zweiten Ort (Backup Copy).** Nicht umgesetzt; das Security
Center führt den Bereich als ungeprüft und rechnet ihn nicht ein.
- **Changed Block Tracking bei Proxmox.** Proxmox gibt geänderte Blöcke nicht
über die REST-API heraus. Eine Zusatzsicherung eines Gasts spart deshalb
Platz, aber keine Lesezeit.
- **Erweiterte Attribute, POSIX-ACLs und SELinux-Kontexte.** Gehen bei einer
Sicherung verloren.
- **Harte Verknüpfungen** werden aufgelöst: Der Inhalt kommt vollständig
zurück, die Verknüpfung nicht.
- **VMware, Hyper-V, Kubernetes, M365, Object Storage, Synthetic Full.** Nicht
Teil dieser Fassung.
### Was gebaut, aber nicht auf echter Hardware gefahren wurde
Diese Punkte sind vollständig umgesetzt und gegen Nachbauten geprüft. Was fehlt,
ist die Ausführung auf der jeweiligen Plattform — und bis dahin gelten sie
nicht als freigegeben:
- **Der Windows-Dienst.** Er übersetzt für Windows und ist `vet`-sauber, wurde
aber nie geladen. Der Kommandozeilenweg und die gesamte Auftragsausführung
sind plattformunabhängig nachgewiesen.
- **Die systemd-Einheit.** Inhaltlich korrigiert, nie auf einem Linux-System
geladen.
- **Proxmox.** Entdecken, Sichern, Prüfen und bitgenaues Zurückschreiben laufen
durch — gegen einen Nachbau der API. **Ob eine wiederhergestellte Maschine
startet, ist ungeprüft.**
- **Der SSH-Zugriffsweg auf Proxmox-Knoten.** Die Fingerabdruckprüfung ist
getestet, eine echte Verbindung gab es nie.
### Einrichtung, Aktualisierung, Entfernung
Das Linux-Paket bringt drei Skripte mit:
- **`setup.sh`** richtet eine Anlage vollständig ein — PostgreSQL auf Wunsch
mit, Dienstkonto, Schlüssel, Schema, erster Administrator, gehärtetes
Repository, systemd-Einheit. Bricht ein Schritt ab, wird zurückgebaut, was
dieser Lauf angelegt hat; Vorgefundenes bleibt unangetastet.
- **`update.sh`** sichert **zuerst** Datenbank und Konfiguration, hält den
Dienst an, tauscht die Programme, migriert mit der neuen Fassung und startet.
Kommt der Dienst danach nicht hoch, holt es die vorige Fassung zurück.
Repository und Verschlüsselungsschlüssel werden nie angefasst.
- **`uninstall.sh`** entfernt standardmäßig **nur** Dienst und Programme.
Datenbank, Repository und Konfiguration bleiben liegen; jede dieser drei
Löschungen verlangt ein wörtlich getipptes Bestätigungswort an einem
Terminal.
### Eingefrorene Verträge
Ab dieser Fassung sind API (102 Endpunkte), Migrationen, Backup-Format und
Repository-Protokoll festgeschrieben. Jede Abweichung schlägt in einer Prüfung
an; Einzelheiten in `docs/release-candidate.md`.
### Bekannte Grenzen
- Das Manifest liegt vollständig im Speicher — rund 200 Byte je Blockverweis,
also etwa 1,5 GiB bei 10 TB Quelldaten.
- Bei vielen kleinen Dateien begrenzt `fsync` den Durchsatz auf rund 100 Dateien
je Sekunde. Das ist der Preis des Commit-Protokolls und kein Fehler.
- Weitergeleitete IP-Header werden ignoriert; hinter einem Reverse Proxy steht
im Auditprotokoll dessen Adresse.