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
5.8 KiB
Markdown
111 lines
5.8 KiB
Markdown
# Aufbau des Codes
|
|
|
|
Dieses Dokument beschreibt den **aktuell implementierten** Stand. Die angestrebte Gesamtarchitektur steht in [SYNCOVA_ARCHITECTURE.md](../SYNCOVA_ARCHITECTURE.md).
|
|
|
|
## Schichtung
|
|
|
|
```text
|
|
apps/web Weboberfläche (React + TypeScript)
|
|
| HTTP (gleicher Ursprung, im Entwicklungsbetrieb über Proxy)
|
|
v
|
|
apps/api Control-Plane-API (Go)
|
|
|
|
|
v
|
|
packages/platform Querschnittsdienste: config, logging, database, health
|
|
|
|
|
v
|
|
PostgreSQL ausschließlich Control Plane, niemals Backup-Nutzdaten
|
|
```
|
|
|
|
Die Abhängigkeitsrichtung ist strikt: `packages/` kennt `apps/` nicht. Damit können Agent und Worker später dieselben Querschnittsdienste nutzen, ohne die API einzubinden.
|
|
|
|
## packages/platform
|
|
|
|
| Paket | Verantwortung |
|
|
| --- | --- |
|
|
| `config` | Konfiguration aus Umgebungsvariablen, vollständige Validierung |
|
|
| `logging` | strukturiertes Logging, Correlation IDs, Secret-Redaction |
|
|
| `database` | PostgreSQL-Pool, Health-Check, Migrationsverwaltung |
|
|
| `health` | Health Engine über alle Komponenten |
|
|
|
|
### config
|
|
|
|
Alle Werte stammen aus `SYNCOVA_*`-Umgebungsvariablen. Zwei Entscheidungen prägen das Paket:
|
|
|
|
- **Secrets besitzen keinen Standardwert.** Das Datenbankpasswort ist als nicht exportiertes Feld abgelegt, damit es nicht versehentlich serialisiert wird. Zugriff besteht nur über `ConnectionString()` (mit Passwort) und `RedactedConnectionString()` (ohne).
|
|
- **Validierungsfehler werden gesammelt.** `Load()` meldet alle Probleme gemeinsam, statt beim ersten abzubrechen.
|
|
|
|
Sicherheitsrelevante Voreinstellungen: Listener nur auf `127.0.0.1`, CORS leer, Body-Limit 1 MiB, Wildcard-Herkunft grundsätzlich unzulässig, unverschlüsselte Herkunft in der Produktion abgelehnt.
|
|
|
|
### logging
|
|
|
|
`slog` mit JSON-Ausgabe. Die Redaction hängt an `ReplaceAttr` und greift damit für **jedes** Attribut — der einzige Ort, an dem sie nicht vergessen werden kann. Erkannt werden Feldnamen, die auf ein Geheimnis hindeuten (`password`, `token`, `secret`, `connection_string` und weitere), unabhängig von Schreibweise und Präfix.
|
|
|
|
### health
|
|
|
|
Jede Komponente meldet einen expliziten Zustand; der Gesamtzustand ist der schlechteste Einzelzustand. Drei Eigenschaften sind bewusst so gebaut:
|
|
|
|
- Eine Prüfung ohne gemeldeten Zustand gilt als **kritisch**, nicht als gesund.
|
|
- Eine Zeitüberschreitung gilt als kritisch, statt den Endpunkt zu blockieren.
|
|
- Ein Panic in einer Prüfung wird abgefangen und als kritisch gemeldet.
|
|
|
|
Nur als kritisch registrierte Komponenten beeinflussen die Readiness. Eine ausgefallene Nebenkomponente nimmt den Dienst also nicht aus dem Verkehr, erscheint aber weiterhin ehrlich im Bericht.
|
|
|
|
## apps/api
|
|
|
|
### Middleware-Reihenfolge
|
|
|
|
```text
|
|
Recovery → Correlation → SecurityHeaders → CORS → BodyLimit → AccessLog → Router
|
|
```
|
|
|
|
Recovery liegt außen, damit auch Fehler innerer Schichten erfasst werden. Correlation folgt unmittelbar, damit jede weitere Schicht bereits eine ID zum Loggen hat.
|
|
|
|
### Request-ID und Correlation ID
|
|
|
|
Die **Request-ID** wird immer serverseitig als UUID vergeben. Die **Correlation ID** darf der Aufrufer über `X-Correlation-ID` vorgeben, um eine Operation über Systemgrenzen zu verfolgen — aber nur, wenn der Wert eine gültige UUID ist. Andernfalls wird er verworfen, damit keine fremden Zeichenketten in die Logs gelangen.
|
|
|
|
### Antworthülle
|
|
|
|
```json
|
|
{ "data": {}, "meta": { "request_id": "uuid" } }
|
|
{ "error": { "code": "...", "message": "...", "request_id": "uuid" } }
|
|
```
|
|
|
|
`APIError` trennt die ausgelieferte Darstellung von der internen Ursache: Letztere wird über `WithCause` mitgeführt, erscheint im Log und **nie** in der Antwort.
|
|
|
|
Maßgeblich für den Client ist die Hülle, nicht der HTTP-Status. Beide tragen unterschiedliche Aussagen: der Status beschreibt den Betriebszustand, die Hülle den Inhalt. `GET /api/v1/health` nutzt genau das und meldet einen kritischen Systemzustand mit **503**, liefert dabei aber den vollständigen Bericht als Nutzlast — das externe Monitoring schlägt an, und die Oberfläche kann trotzdem anzeigen, *was* kaputt ist.
|
|
|
|
### Health-Endpunkte
|
|
|
|
| Endpunkt | Prüft | Zweck |
|
|
| --- | --- | --- |
|
|
| `GET /health/live` | nur den Prozess | Neustartentscheidung |
|
|
| `GET /health/ready` | alle kritischen Komponenten | Verkehrszuteilung |
|
|
| `GET /api/v1/health` | alle Komponenten einzeln | Diagnose |
|
|
|
|
Liveness prüft bewusst **keine** Abhängigkeiten: sonst würde eine kurzzeitig nicht erreichbare Datenbank einen Neustart des Dienstes auslösen und das Problem verschlimmern.
|
|
|
|
## Datenbank
|
|
|
|
PostgreSQL ist die Control-Plane-Datenbank und enthält niemals Backup-Nutzdaten. Beide Zugriffswege — Verbindungspool und Migrationsläufe — nutzen denselben `pgx`-Treiber. Das ist eine bewusste Festlegung: `golang-migrate` würde sonst seinen eingebauten `lib/pq`-Treiber verwenden, der andere SSL-Modi kennt, womit dieselbe Konfiguration je nach Kommando funktionieren oder scheitern würde.
|
|
|
|
Migrationen sind über `embed` ins Binary eingebettet und laufen ausschließlich über `syncova-migrate`. Der API-Dienst prüft beim Start nur, ob der Schemastand zur Programmversion passt.
|
|
|
|
## apps/web
|
|
|
|
Aufbau nach PROMPT.md §104:
|
|
|
|
```text
|
|
src/
|
|
api/ HTTP-Client mit Fehlerbehandlung
|
|
components/ wiederverwendbare Bausteine des Designsystems
|
|
features/ fachliche Bereiche (aktuell: health)
|
|
styles/ Design-Token
|
|
types/ Typen des API-Vertrags
|
|
```
|
|
|
|
Der Client `requestApi` kapselt die Antworthülle und liefert Fehler stets als `ApiError`. Eine Antwort außerhalb des Vertrags führt zu einem klaren Fehler statt zu einem stillschweigend leeren Ergebnis.
|
|
|
|
Die Statusfarben (grün/gelb/orange/rot/blau) sind ausschließlich Zuständen vorbehalten. Ein Zustand wird nie allein über Farbe vermittelt, sondern immer zusätzlich als Text — Farbe allein wäre für farbfehlsichtige Anwender unzugänglich.
|