6 Commits

Author SHA1 Message Date
Micha 0723eccca1 Sync repo map, audit TODO and migration log
Pull repo map up to include FAMILY_VIEW_DASHBOARD,
RESTORE_DRILL_ROUTINE, IMMICH_RESTORE_TEST, FRITZBOX_PORT_
CORRECTION_PLAN and OFFSITE_BACKUP_OPTIONS.

Mark Sprint 2 'Komodo bootstrap', Sprint 3 'Family-View',
Sprint 4 'Family onboarding' and Sprint 7 'Quarterly restore drill'
as done with explicit completion notes. Carry the FRITZBox UPnP
finding forward; tag the second offsite item as decision-pending.

Add two doc-only migration log entries for the bootstrap/family/
onboarding/drill sprint and for the FRITZBox/offsite preparation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 20:21:37 +02:00
Micha 3bfecdd291 Add FRITZBox correction plan and offsite options
Two operator decision documents, doc-only, no live action:

- docs/FRITZBOX_PORT_CORRECTION_PLAN.md prepares the three open
  router items: remove 80/tcp (no HTTP-01 in use), do not add
  222/tcp while Tailscale remains the operator path, deactivate
  the UPnP self-exposure from PC-192-168-178-71. Every step waits
  for operator go.

- docs/OFFSITE_BACKUP_OPTIONS.md compares rsync.net, BorgBase EU2
  and rotating cold disk for a second offsite target. Recommends
  rsync.net or cold disk; BorgBase EU2 is explicitly not
  recommended because it does not separate the provider risk.
  No provider booked, no costs triggered.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 20:21:03 +02:00
Micha c4fd4154db Document quarterly restore drill routine
New docs/RESTORE_DRILL_ROUTINE.md introduces a three-stage model:
weekly freshness check, monthly/bimonthly mini-restores, quarterly
DR sanity check. Tracks confirmed mini-restores (Vaultwarden, Gitea,
Paperless 2026-05-07; Immich 2026-05-27) and rotates services by
quarter Q1-Q4. Includes ten-point DR sanity check and abort rules
that point at the drift runbook. No host schedule is created; the
existing ops/restore-tests/schedule.md now references this routine
as the source for quarterly assignment.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 20:15:43 +02:00
Micha dddb33d900 Finalize family onboarding before invitation
Set status to final pre-invitation, soften the 2FA section to
app-specific 2FA (no SSO promise while Authelia-OIDC stays parked),
add a 'bewusst nicht versprochen' block (no single sign-on, no
24/7 SLA, no hotline support, no data sharing), and refine the
2FA loss guidance.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 20:06:38 +02:00
Micha 8eac93c1a5 Add Family-View dashboard specification
New docs/FAMILY_VIEW_DASHBOARD.md specifies the homelab-family-view
Grafana dashboard: 8 panels covering endpoints up, Borg freshness,
cert days, critical containers, disk usage, endpoint table, cert
table and container status. Includes PromQL queries, thresholds,
layout grid, datasource references, build order and smoke test.
Dashboard JSON is intentionally not created yet because the
Borg-stale / cert-expiry / container-down metrics from Sprint 3
are still pending.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 20:06:30 +02:00
Micha cfa02ce627 Document Komodo bootstrap in linear stages
Add explicit stages A-F to docs/SERVICES_RECOVERY.md: host/docker
baseline, repo source, secrets order, Komodo start, web/GitOps
validation, tier stack rollout. Recovery anchor is ops/komodo/
docker-compose.yml; the self-stack is explicitly not the anchor.
Link DISASTER_RECOVERY Phase 4 stage 3 to the new bootstrap section
and the stack-env-only secrets section in SECRETS_MAP.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 20:01:20 +02:00
11 changed files with 692 additions and 31 deletions
+6 -6
View File
@@ -54,7 +54,7 @@ Kontext bewusst gesichert, bevor weitere Live-Aenderungen passieren:
| erledigt | `docs/STORAGE_LAYOUT.draft.md` finalisieren | Datei als `docs/STORAGE_LAYOUT.md` Active v1.4 gefuehrt; Draft-Blocker entfernt |
| erledigt (Baseline) | Disk- und Share-TBDs eintragen | Disk-Modelle, Seriennummern, Groessen, Filesysteme und Share-Cache-Settings aus `docs/HARDWARE_INVENTORY.md` und Host-Readout 2026-05-27 uebernommen; Retention-/Schwellen-Kalibrierung bleibt Folgeaufgabe |
| erledigt (Skript + Host-Test) | Gitea-Repo-Mirror-Mechanik definieren | `ops/borg-ui/scripts/gitea-bundle-mirror.sh` erzeugt verifizierte Bundles unter `/mnt/user/backups/git-bundles/gitea`; Host-Erstlauf 2026-05-26: 4 Bundles, Checksums OK, `homelab-infra.bundle` klonbar und `git fsck` sauber; Schedule bleibt offen |
| offen | Komodo-Bootstrap-Pfad beschreiben | Kaltstart ohne laufendes Komodo ist dokumentiert |
| erledigt (Doku) | Komodo-Bootstrap-Pfad beschreiben | `docs/SERVICES_RECOVERY.md` enthaelt linearen Bootstrap in Stufen A-F mit Recovery-Anker `ops/komodo/docker-compose.yml`, expliziter Abgrenzung zum Self-Stack, Secret-Reihenfolge und Validierungs-Kommandos; `docs/DISASTER_RECOVERY.md` Stufe 3 verlinkt auf Bootstrap-Pfad. Trockenlauf-Skript bleibt als offene Folgeaufgabe. |
| erledigt | Immich-Restore-Test planen | Testumfang, Datenpfade und Smoke-Test-Kriterium sind in `docs/IMMICH_RESTORE_TEST.md`, `ops/restore-tests/immich-plan.md` und `ops/restore-tests/immich-runbook.md` festgehalten; erster Host-Lauf am 2026-05-27 erfolgreich |
## Sprint 3 - Restore und Monitoring
@@ -65,18 +65,18 @@ Kontext bewusst gesichert, bevor weitere Live-Aenderungen passieren:
| in Arbeit (Regeln vorbereitet) | Borg-Stale-Alert bauen | Textfile-Metrik `homelab_borg_last_completed_timestamp_seconds` und Prometheus-Regeln vorbereitet; Host-Smoke 2026-05-27 schreibt Metriken, aktueller Borg-Status `completed_with_warnings`; Abschluss nach Host-Schedule + Prometheus-Reload/Testalert |
| in Arbeit (Regeln vorbereitet) | TLS-Cert-Expiry-Alert bauen | Blackbox-Regeln fuer 21-/7-Tage-Schwellen vorbereitet; Abschluss nach Prometheus-Reload/Testalert |
| in Arbeit (Regeln vorbereitet) | Container-Down-Alert bauen | Textfile-Metrik `homelab_critical_container_running{name=...}` und Alert vorbereitet; Host-Smoke 2026-05-27: alle gelisteten kritischen Container `1`; Abschluss nach Host-Schedule + Prometheus-Reload/Testalert |
| offen | Family-View Dashboard definieren | Uptime, Backup-Frische, Cert-Tage, Disk-Fuellung auf einer Seite |
| erledigt (Spezifikation) | Family-View Dashboard definieren | `docs/FAMILY_VIEW_DASHBOARD.md` enthaelt Layout, PromQL-Queries, Thresholds und Build-Reihenfolge fuer ein `homelab-family-view`-Dashboard. JSON wird bewusst erst angelegt, sobald Borg-Stale-/Cert-Expiry-/Container-Down-Metriken stabil live sind und ein manueller Build im Grafana-UI das Layout bestaetigt hat. |
## Sprint 4 - Familien- und Betriebsdoku
| Status | Aufgabe | Ergebnis |
|---|---|---|
| in Arbeit | Familien-Onboarding schreiben | Familienverstaendlich (kein Tech-Jargon); deckt Nextcloud, Immich, Vaultwarden, Mealie, Paperless, Plex, Passwort-/2FA-Verlust und WAN-/DNS-Stoerungen ab; finaler Stand vor Wochenend-Einladung |
| erledigt (final vor Einladung) | Familien-Onboarding schreiben | `docs/FAMILY_ONBOARDING.md` ist final redigiert: familienverstaendliche Sprache, App-eigene 2FA statt SSO-Versprechen, neuer "Bewusst nicht versprochen"-Block (kein Einheits-Login, kein 24/7-SLA, kein Hotline-Support, keine Datenweitergabe), konkrete Was-tun-Anleitungen. Einladungstermin bleibt Operator-Aufgabe. |
| erledigt (Baseline) | Capacity-/Lifecycle-Review erstellen | Cache 6 %, Array/User-Shares 33 %, lokale Backups 2.2G; H:/-Nearline-Bewertung ergaenzt; externe Cold-Storage-Groessen bleiben offen |
| erledigt | USV-Test oder USV-Entscheidung | Operator-Entscheidung 2026-05-26: aktuell keine USV-Anschaffung; Power-Loss-Risiko wird bewusst akzeptiert und dokumentiert |
| erledigt (Baseline) | H:/ als zusaetzliches lokales Backupziel bewerten | Als zweite Nearline-Kopie und Freeze-Sicherung sinnvoll; kein Offsite-Ersatz, kein CIFS-Hard-Mount am Unraid; Pull-Modell vom Windows-PC ist der getestete Weg (siehe `docs/CAPACITY_AND_LIFECYCLE.md`) |
| in Arbeit (vorbereitet) | H:/ Groesse und Pull-Schedule festschreiben | Groesse erfasst: 8.0T NTFS, 3.91T belegt, 4.10T frei, `Healthy`; `docs/H_DRIVE_NEARLINE_PULL.md` und `ops/h-drive-nearline/pull-critical-backups.ps1` vorbereitet; SMB-Quelle erreichbar; empfohlener Schedule taeglich 05:30, Task noch nicht aktiviert |
| erledigt (Abweichung dokumentiert) | FRITZ!Box-Portfreigaben gegen Repo-Soll abgleichen | UI am 2026-05-27 geprueft: aktiv sind `80/tcp` und `443/tcp` auf `192.168.178.58`; `222/tcp` fehlt. Korrektur ist ein separater produktiver Router-Schritt nach Operator-Freigabe |
| erledigt (Abweichung dokumentiert) | FRITZ!Box-Portfreigaben gegen Repo-Soll abgleichen | UI am 2026-05-27 geprueft: aktiv sind `80/tcp` und `443/tcp` auf `192.168.178.58`; `222/tcp` fehlt; zusaetzlich UPnP-Selbstfreigabe durch `PC-192-168-178-71`. Korrektur ist ein separater produktiver Router-Schritt nach Operator-Freigabe; Vorbereitung in `docs/FRITZBOX_PORT_CORRECTION_PLAN.md` |
## Sprint 5 - Auth und Frontdoor, bewusst zuletzt
@@ -103,8 +103,8 @@ In diesem Audit-Zyklus werden diese Punkte **nicht** umgesetzt. Sie sind dokumen
| Status | Aufgabe | Bemerkung |
|---|---|---|
| offen | Zweites echtes Off-site (F-03) | H:/ ersetzt das **nicht**; rsync.net / BorgBase EU2 / Cold-Wechselplatte mit fester Rotation pruefen |
| offen | Restore-Lab-Drill quartalsweise dokumentieren | Routine festlegen, nicht ad-hoc |
| offen (Entscheidungsvorlage) | Zweites echtes Off-site (F-03) | H:/ ersetzt das **nicht**; Optionen rsync.net / BorgBase EU2 / Cold-Wechselplatte in `docs/OFFSITE_BACKUP_OPTIONS.md` gegenuebergestellt, Empfehlung rsync.net oder Cold-Platte; Operator-Entscheidung und Budget-Freigabe stehen aus |
| erledigt (Routine dokumentiert) | Restore-Lab-Drill quartalsweise dokumentieren | `docs/RESTORE_DRILL_ROUTINE.md` definiert Drei-Stufen-Modell (Freshness woechentlich / Mini-Restore monatlich-bimonatlich / DR-Sanity quartalsweise), Quartals-Belegung Q1-Q4 mit Dienst-Rotation, Immich 2026-05-27 als bestaetigter Erstlauf gefuehrt, 10-Punkte-Sanity-Check, kein Host-Schedule angelegt. `ops/restore-tests/schedule.md` verweist jetzt auf Drill-Routine. |
## Offene Host-Werte
+7 -1
View File
@@ -276,7 +276,13 @@ Ziel:
### Stufe 3 - Deploy-System
8. `ops/komodo/`
8. `ops/komodo/` - **Kaltstart-Anker, kein Auto-Deploy**
Komodo wird in dieser Stufe bewusst **nicht** ueber Gitea-Webhook deployed. Der vollstaendige Bootstrap-Pfad ist in `docs/SERVICES_RECOVERY.md` Abschnitt "Komodo Bootstrap" als lineare Stufen A-F dokumentiert. Hier in der DR-Reihenfolge gilt der Einstiegspunkt:
- Recovery-Anker ist `ops/komodo/docker-compose.yml` aus dem Repo (lokaler Clone, GitHub-Mirror oder `homelab-infra.bundle`-Restore).
- Komodo-Stack-ENV-Werte (`KOMODO_*`) sind Stack-ENV-only und werden aus Vaultwarden oder externer Notiz wiederhergestellt (siehe `docs/SECRETS_MAP.md` Abschnitt "Stack-ENV-only Secrets - Restore-Wege").
- Erst nach erfolgreicher Validierung der Komodo-Web-UI und Periphery-Verbindung werden in den naechsten Stufen die produktiven Stacks aufgenommen.
Ziel:
+23 -8
View File
@@ -1,10 +1,10 @@
# Familien-Willkommen - KalliLab CORE
Status: Entwurf 2026-05-26. Zielgruppe: Familie. Kein Technik-Wortschatz noetig.
Status: **Final-Stand vor Wochenend-Einladung** (2026-05-27). Zielgruppe: Familie. Kein Technik-Wortschatz noetig.
Diese Seite richtet sich an alle, die zuhause unsere eigenen Apps nutzen wollen. Du brauchst kein Technikwissen. Wenn etwas unklar ist, frag Michi.
Diese Seite richtet sich an alle, die zuhause unsere eigenen Apps nutzen. Du brauchst kein Technikwissen. Wenn etwas unklar ist: einfach Michi fragen.
Wir laden die Familie am Wochenende ein, sobald alles fertig ist. Bis dahin ist diese Seite **Entwurf** — Details koennen sich noch aendern.
Du musst nichts auswendig lernen. Wenn du nur eine Sache aus dieser Seite mitnimmst: **Passwoerter gehoeren in Vaultwarden, nicht auf Zettel und nicht in den Browser.**
---
@@ -37,7 +37,10 @@ Beim ersten Mal bekommst du von Michi:
- deinen Benutzernamen (in der Regel dein Vorname klein geschrieben)
- ein Start-Passwort, das du beim ersten Login aenderst
- bei einigen Apps spaeter zusaetzlich einen **Zweitfaktor** (eine kleine 6-stellige Zahl aus einer App)
Wenn eine App selbst einen **Zweitfaktor** anbietet (zum Beispiel Nextcloud), bekommst du dafuer am Anfang gemeinsam mit Michi eine kleine Authentifizierungs-App eingerichtet. Das ist eine extra Schicht: zusaetzlich zum Passwort tippst du beim Login eine 6-stellige Zahl aus dieser App ein.
> Hinweis: Ein **einheitliches 2FA fuer alle Apps gleichzeitig** ist noch nicht eingerichtet. Aktuell hat jede App ihre eigene Anmeldung. Das bleibt erst einmal so. Wenn sich da etwas aendert, sagt Michi rechtzeitig Bescheid.
**Bitte:**
@@ -76,8 +79,9 @@ Das ist die App, die deinen Eltern wahrscheinlich am meisten bringt.
### "Ich habe mein 2FA verloren (neues Handy, App geloescht)."
- Michi schreiben. Er kann den Zweitfaktor zuruecksetzen, sobald er dich persoenlich identifiziert.
- Michi schreiben. Er kann den Zweitfaktor in der betroffenen App fuer dich zuruecksetzen, sobald er dich persoenlich identifiziert.
- Wir richten den Zweitfaktor dann neu auf dem neuen Handy ein.
- Wenn moeglich: vor einem Handy-Wechsel kurz Bescheid sagen, dann koennen wir 2FA vorher umziehen, statt zuruecksetzen.
### "Das Handy-Foto-Backup ist stehen geblieben."
@@ -132,11 +136,22 @@ Michi laesst es dich wissen, wenn ein Wartungsfenster geplant ist.
## Offene Inhalte (Operator-Notiz)
Diese Punkte gehoeren in das Wochenend-Onboarding-Gespraech und sind nicht Teil dieser Familien-Seite:
| Status | Aufgabe |
|---|---|
| offen | Pro Familien-Konto Benutzernamen und Start-Passwort an die Person uebergeben |
| offen | 2FA-App-Empfehlung pro Person festlegen (z. B. 2FAS / Aegis / Bitwarden Authenticator) |
| offen | Pro Familien-Konto Benutzernamen und Start-Passwort persoenlich uebergeben (Vaultwarden Familien-Organisation als Uebergabeweg) |
| offen | 2FA-App-Empfehlung pro Person festlegen (zum Beispiel Bitwarden Authenticator, Aegis, 2FAS) |
| offen | Vaultwarden Familien-Organisation einrichten und Mitglieder einladen |
| offen | Immich Mobile Backup mit jedem Familien-Geraet testen |
| offen | Immich Mobile Backup mit jedem Familien-Geraet einmal gemeinsam ausprobieren |
| offen | Scan-/Inbox-Anleitung fuer Paperless ergaenzen, sobald der Workflow final ist |
| offen | Einladungstermin Wochenende mit konkretem Datum festlegen |
## Bewusst nicht versprochen
Damit niemand spaeter enttaeuscht ist, hier kurz, was die Seite **nicht** verspricht:
- Es gibt aktuell **kein** Einheits-Login fuer alle Apps. Jede App hat ihre eigene Anmeldung. Eine Vereinheitlichung ist als Idee notiert, aber zeitlich noch nicht geplant.
- Es gibt **keine** Garantie, dass eine App 24/7 verfuegbar ist. Es ist ein Heim-Server, kein Rechenzentrum.
- Es gibt **keinen** automatischen Support per Hotline. Probleme gehen an Michi.
- Es gibt **kein** Werbe-Konto und keinen Versand deiner Daten an Externe. Alles bleibt zuhause.
+194
View File
@@ -0,0 +1,194 @@
# Family-View Dashboard - Spezifikation
Status: **Spezifikation (Doku-only)**, kein Grafana-JSON in diesem Schritt.
Audit-Bezug: `docs/AUDIT_2026-05-25.md` Finding **F-08** (Alerts/Sichtbarkeit) und das Sprint-3-TODO "Family-View Dashboard definieren" aus `docs/AUDIT_2026-05-25_TODO.md`.
## Zweck
Ein Grafana-Dashboard, das beim Morgen-Check in unter 30 Sekunden zeigt, ob das Homelab gesund ist. Zielgruppe ist primaer der Operator. Wenn die Familie es zufaellig anschaut, soll niemand erschrecken: ueberall gruene Felder bedeuten "alles in Ordnung", ohne dass man die Technik dahinter verstehen muss.
Das Dashboard ist die Konsolidierung des morgendlichen Pruefablaufs:
- Sind die wichtigsten Apps erreichbar?
- Hat das Backup gestern Nacht funktioniert?
- Wann laufen die Zertifikate aus?
- Sind die Disks ausreichend frei?
- Laufen die kritischen Container?
## Abgrenzung
Diese Datei beschreibt nur Layout, Datenquellen und PromQL-Queries. Die JSON-Datei `monitoring/grafana/dashboards/family-view.json` wird **bewusst noch nicht** angelegt, weil:
- Es noch keine Live-Pruefung gegen die echte Grafana-Instanz gab.
- Die Alert-Regeln (Borg-Stale, Cert-Expiry, Container-Down) sind laut `docs/AUDIT_2026-05-25_TODO.md` Sprint 3 selbst noch im "in Arbeit (Regeln vorbereitet)"-Status.
- Bei einem halbgaren Dashboard-JSON entstehen mehr Wartungsfragen als Klarheit.
Die JSON-Datei wird angelegt, sobald (a) die genannten Metriken stabil verfuegbar sind und (b) ein erster manueller Build im Grafana-UI das Layout bestaetigt hat.
## Datenquellen
Authoritativ ist `monitoring/grafana/provisioning/datasources/datasources.yml`. Das Dashboard nutzt nur die schon provisionierten Datasources:
- `Prometheus` - Blackbox, node-exporter, cAdvisor, Traefik-Metrics
- optional `Loki` - Log-Volume-Spike als Zusatz-Panel
- bewusst nicht: `InfluxDB 3 Core` (das ist Home-Assistant-/Ecowitt-Sicht, nicht Homelab-Health)
## Layout (4x4 Grid, mobile-vertraeglich)
| Zeile | Panel | Breite (Grafana w) | Hoehe (Grafana h) |
|---|---|---:|---:|
| 1 | Endpoints up (Stat, gross gruen/rot) | 12 | 5 |
| 1 | Backup heute Nacht (Stat) | 6 | 5 |
| 1 | Naechster Cert-Ablauf (Stat, Tage) | 6 | 5 |
| 2 | Kritische Container running (Stat-Liste) | 12 | 6 |
| 2 | Disk-Fuellung (Bargraph, je Mountpoint) | 12 | 6 |
| 3 | Endpoint-Tabelle (Table: Host, Status, Latenz) | 24 | 8 |
| 4 | Cert-Tage-Tabelle (Table: Host, Tage bis Ablauf) | 12 | 6 |
| 4 | Container-Status-Tabelle (Table: kritischer Container, Running, letztes Restart) | 12 | 6 |
Dashboard-Metadaten:
- UID: `homelab-family-view`
- Title: `Homelab / Family View`
- Tags: `homelab`, `family-view`, `morning-check`
- Refresh: `30s`
- Default-Zeitfenster: `now-24h` bis `now`
- Folder: `Homelab`
## Panel-Spezifikation
### Panel 1: Endpoints up
- Type: `stat`
- Title: `Apps online`
- Query: `sum(probe_success{job="blackbox-http"})`
- Anzeige: gruene Zahl bei Gesamtzahl, Wechsel auf rot wenn `< Soll-Anzahl`.
- Threshold: Soll-Anzahl wird aus `monitoring/blackbox/blackbox.yml` und Prometheus-Scrape-Liste abgeleitet (zum Doku-Zeitpunkt 19 HTTPS-Ziele laut `docs/MIGRATION_LOG.md` 2026-05-25-Monitoring-Konsolidierung).
- Subtitel im Panel: `von <N> erreichbar`. (Soll-Wert wird beim Bau aus dem aktuellen Target-Count gesetzt; nicht hartcoden.)
### Panel 2: Backup heute Nacht
- Type: `stat`
- Title: `Borg-Lauf`
- Query (sobald Borg-Stale-Metrik im Textfile-Collector live ist):
```promql
(time() - homelab_borg_last_completed_timestamp_seconds) / 3600
```
- Einheit: `h`
- Threshold:
- 0-26 h gruen
- 26-30 h gelb
- >30 h rot
- Subtitel im Panel: `Stunden seit letztem completed-Lauf`.
- Fallback bis Metrik live: Panel zeigt `n/a`, Doku-Hinweis in der Beschreibung.
### Panel 3: Naechster Cert-Ablauf
- Type: `stat`
- Title: `Cert laeuft in`
- Query:
```promql
min((probe_ssl_earliest_cert_expiry{job="blackbox-http"} - time()) / 86400)
```
- Einheit: `d` (Tage)
- Threshold:
- >14 gruen
- 7-14 gelb
- <7 rot
- Subtitel: `Tage bis kleinste Restlaufzeit aller geprueften Hosts`.
### Panel 4: Kritische Container running
- Type: `stat`
- Title: `Kritische Container`
- Query (sobald Container-Up-Metrik live ist):
```promql
sum(homelab_critical_container_running)
```
- Threshold: erwartete Anzahl gruen, jeder fehlende Container rot.
- Subtitel: `von <N> erwartet`. Erwartete Liste pflegen wir in `services/posture-check` / Textfile-Exporter (siehe `docs/AUDIT_2026-05-25_TODO.md` Sprint 3 "Container-Down-Alert").
- Fallback: cAdvisor-Query als Naeherung, solange Textfile-Metrik noch nicht produktiv ist:
```promql
count(rate(container_last_seen{name=~"traefik|authelia|postgresql17|Redis|gitea|komodo-core|komodo-mongo|komodo-periphery|monitoring-prometheus|monitoring-grafana|monitoring-loki|monitoring-alertmanager"}[5m]) > 0)
```
### Panel 5: Disk-Fuellung
- Type: `bargauge`
- Title: `Disk-Fuellung`
- Query:
```promql
100 * (1 - node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"})
```
- Anzeige: pro Mountpoint, sortiert absteigend.
- Threshold:
- <70 gruen
- 70-85 gelb
- >85 rot
- Subtitel: `Prozent belegt`.
### Panel 6: Endpoint-Tabelle
- Type: `table`
- Title: `Endpoint-Status`
- Spalten:
- Host (Instance)
- Status (`UP` / `DOWN`)
- Antwortzeit (probe_duration_seconds)
- Queries:
- `probe_success{job="blackbox-http"}` -> Mapping: `1` -> `UP` (gruen), `0` -> `DOWN` (rot)
- `probe_duration_seconds{job="blackbox-http"}` -> Sekunden
- Sortierung: DOWN oben, dann nach Antwortzeit absteigend.
### Panel 7: Cert-Tage-Tabelle
- Type: `table`
- Title: `Cert-Tage bis Ablauf`
- Spalten: Host, Tage
- Query:
```promql
(probe_ssl_earliest_cert_expiry{job="blackbox-http"} - time()) / 86400
```
- Sortierung: aufsteigend (am ehesten ablaufende oben).
- Color-Mapping wie Panel 3.
### Panel 8: Container-Status-Tabelle
- Type: `table`
- Title: `Kritische Container`
- Spalten: Container, Running (1/0), letztes Restart (Sekunden seit Start)
- Queries:
- sobald Textfile-Metrik live: `homelab_critical_container_running` (Label `name`)
- Fallback aus cAdvisor: `container_start_time_seconds{name=~"<Whitelist>"}`
- Sortierung: nicht-running zuerst.
## Spaeter ergaenzbar (nicht Teil der ersten Version)
- Loki-Log-Volume-Spike-Panel
- node_exporter Memory-Saturation-Panel
- Plex-Sessions (nur wenn Plex-Exporter eingerichtet ist; aktuell nicht geplant)
- Immich Asset-Wachstum (eigenes Dashboard, nicht Family-View)
## Build-Reihenfolge fuer den spaeteren JSON
Wenn das JSON gebaut wird, bitte in dieser Reihenfolge:
1. Sicherstellen, dass alle Metriken aus den Queries oben in Prometheus auffindbar sind (`/graph` Smoke-Test).
2. Dashboard in Grafana-UI manuell zusammenklicken; Layout an dieser Spezifikation entlang.
3. JSON exportieren, in `monitoring/grafana/dashboards/family-view.json` ablegen.
4. Provisioning-Provider laesst die Datei automatisch laden (siehe `monitoring/grafana/provisioning/dashboards/dashboards.yml`).
5. Bei jeder Schema-Aenderung Doku hier nachziehen, damit Spec und JSON nicht driften.
## Smoke-Test nach Aktivierung
- Dashboard laedt unter `https://monitoring.kaleschke.info/d/homelab-family-view/`.
- Alle 8 Panels rendern ohne `No data`.
- Im Normalbetrieb erscheinen Panel 1-5 vollstaendig gruen.
- Ein bewusster Test-Stale-Borg oder ein Container-Stop laesst die zugehoerigen Panels auf gelb/rot wechseln.
## Was das Dashboard NICHT ersetzt
- ntfy-Alerts: das Dashboard ist passiv (Pull), ntfy ist aktiv (Push). Beide sind notwendig.
- DR-Doku: `docs/DISASTER_RECOVERY.md` bleibt die Recovery-Quelle.
- Restore-Tests: `docs/RESTORE_DRILL_ROUTINE.md` ist die Kadenz, die das Dashboard nicht ersetzt.
- Familien-Onboarding: `docs/FAMILY_ONBOARDING.md` ist die Doku fuer die Familie, dieses Dashboard ist Operator-Tool.
+94
View File
@@ -0,0 +1,94 @@
# FRITZ!Box Portfreigaben - Korrektur-Vorbereitung
Status: **Vorbereitung (Doku-only)**, 2026-05-27. Keine Router-Aenderung.
Audit-Bezug: `docs/AUDIT_2026-05-25_TODO.md` Sprint 4 ("FRITZ!Box-Portfreigaben gegen Repo-Soll abgleichen") und `docs/NETWORK_INVENTORY.md`.
## Aktueller FRITZ!Box-Befund (2026-05-27)
Aus dem Operator-Live-Check der FRITZ!Box-UI:
- aktiv: `80/tcp` -> `192.168.178.58`
- aktiv: `443/tcp` -> `192.168.178.58`
- fehlt: `222/tcp` -> `192.168.178.58` (Gitea-SSH)
- zusaetzlich gemeldet: eine Portfreigabe durch das Geraet `PC-192-168-178-71` (Selbst-Freigabe per UPnP)
## Soll-Stand laut Repo
`docs/NETWORK_INVENTORY.md` definiert genau zwei aktive Portfreigaben:
| Erwartete Freigabe | Ziel | Begruendung |
|---|---|---|
| `443/tcp` -> `192.168.178.58:443` | Traefik HTTPS | einziger Public-HTTPS-Einstieg, Wildcard-Zert ueber Cloudflare-DNS-Challenge |
| `222/tcp` -> `192.168.178.58:222` | Gitea SSH | Git-SSH-Push/Pull; dokumentierte Ausnahme |
Port `80/tcp` ist im Cloudflare-DNS-Challenge-Modell **nicht** notwendig.
## Drei Korrektur-Punkte
### 1. `80/tcp` entfernen
Begruendung:
- ACME laeuft als Cloudflare-DNS-Challenge (`traefik/docker-compose.yml`), nicht als HTTP-01.
- Traefik leitet intern jeden HTTP-Request auf `https://` weiter; ein WAN-`80`-Listener bietet keinen Mehrwert, oeffnet aber einen zusaetzlichen Angriffsvektor (Header-/Method-Scanning, Open-Redirect-Versuche bevor TLS terminiert).
- Innerhalb des LAN funktioniert die Browser-Auto-HTTPS-Umleitung weiter ueber AdGuard-DNS.
Empfehlung: WAN-Eintrag `80/tcp` in FRITZ!Box-UI **entfernen** nach Operator-Go.
Validierung nach Aenderung:
```bash
# WAN-seitiger Test, idealerweise von einem Geraet im Mobilfunknetz oder Tailscale-Exit-Node
curl -sI http://kaleschke.info/ # erwartet: Connection refused / Timeout
curl -sI https://vault.kaleschke.info/ # erwartet: HTTP/2 200 oder Authelia-Redirect
```
### 2. `222/tcp` ergaenzen (nur wenn externes Git-SSH wirklich gewuenscht)
Frage an Operator: Wird `git@git.kaleschke.info -p 222` von extern gebraucht? Hinweise:
| Pro `222/tcp` extern | Contra `222/tcp` extern |
|---|---|
| Push/Pull vom unterwegs-Laptop ohne Tailscale | Tailscale ist schon der Operator-Pfad, deckt das voll ab |
| GitHub-Mirror-Bootstrap funktioniert dann auch ohne Tailscale | GitHub-Push-Mirror laeuft automatisch von Gitea aus, braucht kein WAN-SSH |
| Externe Webhooks gegen Git push (nicht in Nutzung) | weniger Angriffsflaeche fuer SSH-Brute-Force |
Empfehlung: `222/tcp` **nicht** ergaenzen, solange Tailscale stabil verfuegbar ist. Stattdessen `docs/NETWORK_INVENTORY.md` und `HOMELAB_ARCHITECTURE_MASTER_V2.md` darauf abgleichen, dass Gitea-SSH bewusst LAN/Tailscale-only ist.
Wenn Operator entscheidet, `222/tcp` doch extern zu oeffnen: zusaetzlich SSH-Login auf Key-only setzen, Brute-Force-Limits in Gitea pruefen, `docs/NETWORK_INVENTORY.md` "Erwartete Freigabe"-Tabelle aktualisieren.
### 3. UPnP-Selbstfreigabe von `PC-192-168-178-71` deaktivieren
Begruendung:
- Geraete-initiierte Portfreigaben (UPnP) sind ausserhalb der Repo-Sollkonfiguration.
- Welcher Port von welchem Programm geoeffnet wurde, ist aus der FRITZ!Box-UI heraus nicht versionierbar.
- Wenn der Port gebraucht wird, gehoert er als bewusste Operator-Freigabe in `docs/NETWORK_INVENTORY.md`.
Empfehlung in zwei Stufen:
1. FRITZ!Box-UI: in den Geraete-Details fuer `PC-192-168-178-71` die aktuelle Selbstfreigabe-Liste pruefen und mit dem Operator besprechen.
2. Wenn der Port nicht gebraucht wird: Selbstfreigabe deaktivieren. Optional: UPnP global pro Geraet abschalten ("Selbststaendige Portfreigaben fuer dieses Geraet erlauben" abwaehlen).
## Schutzregeln
- Keine Router-Aenderung ohne ausdrueckliches Operator-Go.
- Nach jeder Aenderung: `docs/NETWORK_INVENTORY.md` Abschnitt "FRITZ!Box (WAN -> Host)" gegen den neuen UI-Stand abgleichen.
- Aenderung in `docs/MIGRATION_LOG.md` als kurzer Eintrag dokumentieren (was/warum/Validierung).
- Bei `80/tcp`-Entfernung kurz prufen, ob irgendein externer Dienst noch HTTP-01 nutzen wollte (sollte nicht der Fall sein).
## Nicht Teil dieser Vorbereitung
- FRITZ!OS-Update 8.21 -> aktuell. Das ist eigenes Service-Fenster und braucht WAN/Tailscale-Aufbau-Beobachtung.
- IPv6-Exposure. Wenn Telekom IPv6 zustellt und Apps via Cloudflare-AAAA erreichbar sind, kann WAN-Filter pro Port noetig werden. Separater Doku-Punkt in `docs/NETWORK_INVENTORY.md` Offene Entscheidungen.
- Mobilfunk-Failover-Stick. Bewusst nicht eingerichtet.
## Offene Punkte
| Status | Punkt | Naechster Schritt |
|---|---|---|
| offen (Operator-Go) | `80/tcp` entfernen | FRITZ!Box-UI, danach `docs/NETWORK_INVENTORY.md` aktualisieren |
| offen (Operator-Go) | `222/tcp` Entscheidung | Operator klaert: extern noetig oder Tailscale-only |
| offen (Operator-Go) | UPnP-Freigabe `PC-192-168-178-71` | FRITZ!Box-UI Geraete-Detail pruefen |
| offen | FRITZ!OS 8.21 Update | Service-Fenster, separat geplant |
| offen | IPv6-Exposure pruefen | bei naechstem WAN-Touch mit erfassen |
+21
View File
@@ -17,6 +17,27 @@ Dieses Dokument ist nur noch ein historischer Verlauf. Der aktuelle operative Ab
## Historische Meilensteine
### 2026-05-27 - Vorbereitungsdokumente FRITZ!Box-Korrektur und Off-site-Optionen
- Reine Doku-Aenderung; kein Router-, Provider- oder Host-Eingriff.
- `docs/FRITZBOX_PORT_CORRECTION_PLAN.md` neu angelegt: Korrektur-Plan fuer drei Punkte (`80/tcp` entfernen, `222/tcp` nicht ergaenzen solange Tailscale stabil, UPnP-Selbstfreigabe `PC-192-168-178-71` deaktivieren). Operator-Go ausstehend; jede UI-Aenderung wird gesondert dokumentiert.
- `docs/OFFSITE_BACKUP_OPTIONS.md` neu angelegt: Entscheidungsvorlage fuer zweites Off-site-Ziel mit drei Optionen (rsync.net Borg-Plan, BorgBase EU2, rotierende Cold-Platte). Bewertung gegen Provider-Trennung, Standort, Preis und Konto-Risiko; Empfehlung rsync.net oder Cold-Platte; BorgBase EU2 explizit nicht empfohlen wegen gleichem Anbieter. Kein Provider gebucht, keine Kosten ausgeloest.
- `docs/REPO_MAP.md` um beide neuen Dokumente ergaenzt.
- `docs/AUDIT_2026-05-25_TODO.md` Sprint 4 (FRITZ!Box) und Sprint 7 (Off-site) mit Verweis auf die neuen Vorbereitungsdokumente nachgezogen.
### 2026-05-27 - Doku-Sprint Bootstrap / Family-View / Onboarding / Drill-Routine
- Reine Doku-Aenderung; kein Compose-, Secret-, Host- oder Router-Eingriff.
- `docs/SERVICES_RECOVERY.md` "Komodo Bootstrap" auf linearen Stufenpfad A-F ausgebaut: Recovery-Anker bleibt `ops/komodo/docker-compose.yml`, Self-Stack ist explizit kein Anker, Secret-Restore-Reihenfolge verweist auf `docs/SECRETS_MAP.md` Stack-ENV-only-Sektion, Validierungs-Kommandos ergaenzt. Trockenlauf-Idee als Folgeaufgabe eingetragen, kein Repo-Skript dafuer.
- `docs/DISASTER_RECOVERY.md` Phase 4 Stufe 3 verweist explizit auf den Bootstrap-Pfad in `docs/SERVICES_RECOVERY.md` und auf die Stack-ENV-only-Sektion in `docs/SECRETS_MAP.md`.
- `docs/FAMILY_VIEW_DASHBOARD.md` neu angelegt: Spezifikation fuer `homelab-family-view`-Dashboard, 8 Panels (Endpoints up, Borg-Frische, Cert-Tage, Kritische Container, Disk-Fuellung, Endpoint-Tabelle, Cert-Tabelle, Container-Tabelle), PromQL-Queries, Thresholds, Build-Reihenfolge. Bewusst noch **kein** `monitoring/grafana/dashboards/family-view.json` angelegt, weil Borg-Stale-/Cert-Expiry-/Container-Down-Metriken laut Sprint 3 noch "in Arbeit" sind.
- `docs/FAMILY_ONBOARDING.md` final redigiert: Status auf "Final-Stand vor Wochenend-Einladung", 2FA-Beschreibung auf App-eigene 2FA praezisiert (kein SSO-Versprechen, weil Authelia-OIDC weiter geparkt ist), neuer "Bewusst nicht versprochen"-Block (kein Einheits-Login, kein 24/7-SLA, kein Hotline-Support, keine Datenweitergabe).
- `docs/RESTORE_DRILL_ROUTINE.md` neu angelegt: Drei-Stufen-Modell (Freshness woechentlich / Mini-Restore monatlich-bimonatlich / DR-Sanity quartalsweise), Quartals-Kadenz Q1-Q4 mit Dienst-Rotation, bestaetigte Mini-Restores (Vaultwarden, Gitea, Paperless 2026-05-07; Immich 2026-05-27), 10-Punkte-Sanity-Check, Abbruch-Regel mit Verweis auf `docs/GITOPS_DRIFT_RUNBOOK.md`. Kein Host-Schedule angelegt, nur Doku.
- `ops/restore-tests/schedule.md` verweist jetzt auf `docs/RESTORE_DRILL_ROUTINE.md` als Quelle der Quartals-Belegung.
- `docs/REPO_MAP.md` um `docs/FAMILY_VIEW_DASHBOARD.md`, `docs/RESTORE_DRILL_ROUTINE.md` und `docs/IMMICH_RESTORE_TEST.md` ergaenzt.
- `docs/AUDIT_2026-05-25_TODO.md` aktualisiert: Sprint 2 "Komodo-Bootstrap-Pfad beschreiben", Sprint 3 "Family-View Dashboard definieren", Sprint 4 "Familien-Onboarding schreiben" und Sprint 7 "Restore-Lab-Drill quartalsweise dokumentieren" jeweils auf "erledigt".
- Geparkte Punkte bleiben unveraendert: Authelia-2FA/OIDC/CrowdSec, Nextcloud-Haertung, Hermes (Review 2026-07-25), USV-Anschaffung.
### 2026-05-27 - Immich Restore-Smoke-Test praktisch verifiziert (F-11)
- Erster echter Immich-Restore-Smoke-Test gegen das produktive Borg-Archiv erfolgreich: `Tägliche-Sicherung-2026-05-27T04:30:06.778`, Report `/mnt/user/backups/restore-reports/immich-2026-05-27.md`.
+104
View File
@@ -0,0 +1,104 @@
# Zweites Offsite-Backup-Ziel - Entscheidungsvorlage
Status: **Entscheidungsvorlage (Doku-only)**, 2026-05-27. Kein Provider gebucht, keine Kosten ausgeloest.
Audit-Bezug: `docs/AUDIT_2026-05-25.md` Finding **F-03** und `docs/AUDIT_2026-05-25_TODO.md` Sprint 7.
## Zweck
Aktuell laeuft das Off-site-Backup bei genau einem Anbieter (**Hetzner Storage Box**). Diese Vorlage stellt drei realistische Optionen fuer ein **zweites** Off-site-Ziel gegenueber. Die Entscheidung trifft der Operator; dieses Dokument trifft sie nicht.
`H:/` am Windows-PC bleibt **kein** Offsite-Ersatz (siehe `docs/CAPACITY_AND_LIFECYCLE.md`). H:/ ist Nearline-Kopie am gleichen Standort.
## Anforderungen an das zweite Ziel
| Anforderung | Begruendung |
|---|---|
| **anderer Anbieter als Hetzner** | Provider-Risiko (Account-Sperre, Insolvenz, Region-Outage) wird sonst nicht reduziert |
| **anderer physischer Standort** als Unraid-Host | Brand-/Diebstahl-/Wasser-Schutz |
| **Borg-kompatibel** (SSH + Repo-Layout) | bestehendes Borg-UI-Setup soll wiederverwendbar bleiben |
| **bezahlbar im Privatrahmen** | grobe Zielgroesse: < 10 EUR / Monat fuer ~1 TB |
| **stabil ueber Monate** | wenig Eingriff, keine taeglichen Quirks |
| **keine Konto-Komplexitaet** | 2FA-Recovery muss sauber, ohne Telefon-Nummer-Hacks etc. moeglich sein |
## Drei Optionen
### Option A - rsync.net Borg-Plan
- **Was:** seit Jahren etablierter Anbieter mit dediziertem Borg-Plan (eingebaute `borg`-Binary, SSH-Account, Snapshot-Schutz).
- **Zielanbindung:** `borg init --encryption=repokey-blake2 user@hostname:repo`.
- **Standort:** Schweiz/USA (je nach Konto-Region).
- **Preis (Stand 2026, grob):** ~10-15 USD pro Monat fuer 1 TB; Mindestbestelldauer keine.
- **Vorteile:** Borg-First, ZFS-Snapshots als zusaetzlicher Schutz vor Repo-Loeschung, sehr lange Track-Record, keine SMB-/CIFS-Quirks.
- **Nachteile:** USD-Preis, Standort ausserhalb EU je nach Konto, etwas teurer als reine Storage Boxes.
- **Konto-Risiko:** unabhaengig von Hetzner-Konto.
### Option B - BorgBase EU2 (zweite Region beim gleichen Borg-Spezialisten)
- **Was:** BorgBase ist Borg-as-a-Service mit mehreren Regionen.
- **Zielanbindung:** identisch zu bestehendem Borg-UI-Pfad.
- **Standort:** zweite EU-Region als EU1.
- **Preis (Stand 2026, grob):** ~10 EUR / Monat fuer ~1 TB.
- **Vorteile:** identisches Borg-Modell, geringer Lernaufwand, einfache Web-UI.
- **Nachteile:** Anbieter-Risiko nicht vollstaendig getrennt (gleicher Anbieter, andere Region). Bei Account-Sperre/Insolvenz waeren beide Ziele gleichzeitig betroffen.
- **Konto-Risiko:** **gleicher** Anbieter, **anderes** Geo-Risiko.
### Option C - Rotierende Cold-Wechselplatte ausser Haus
- **Was:** zweite externe HDD (z. B. 2x WD Elements 4 TB), die im monatlichen Wechsel zwischen Heim-LAN und vertrauter dritter Person (Familie, Schliessfach, Buero) rotiert.
- **Zielanbindung:** Borg-Repo lokal auf der eingesteckten Platte; Backup-Lauf nur wenn die Platte gerade vor Ort ist.
- **Standort:** echtes Ausserhaus, dazwischen offline (Air-Gap).
- **Preis (einmalig):** zwei Platten ~250 EUR, keine laufenden Kosten.
- **Vorteile:** echtes Air-Gap, keine Provider-Abhaengigkeit, keine Bandbreitenfrage, keine Konto-Risiken.
- **Nachteile:** manuelle Disziplin noetig, Recovery-Zeit haengt davon ab, dass die Platte gerade erreichbar ist. Nicht so taggenau wie Cloud-Borg.
- **Konto-Risiko:** keines (keine Provider-Bindung).
## Bewertung gegen die Anforderungen
| Anforderung | Hetzner (Ist) | Option A rsync.net | Option B BorgBase EU2 | Option C Cold-Platte |
|---|---|---|---|---|
| Anderer Anbieter | - | ja | nein (gleicher) | ja (keiner) |
| Anderer Standort | - | ja | ja | ja |
| Borg-kompatibel | ja | ja | ja | ja |
| Preis < 10 EUR/Monat | ja | grenzwertig | ja | einmalig ~250 EUR |
| Stabilitaet | hoch | hoch | hoch | Operator-Disziplin abhaengig |
| 2FA/Konto-Recovery | OK | OK | OK | n/a |
## Empfehlung (nicht Entscheidung)
Wenn der Operator die geringste Bedienung bei maximalem Provider-getrennten Schutz will: **Option A rsync.net**. Echte Anbieter-Trennung gegenueber Hetzner, Borg-First-Anbieter, ZFS-Snapshot-Schutz, keine zusaetzliche Hardware noetig.
Wenn Air-Gap und Null-Provider-Abhaengigkeit am wichtigsten sind und der Operator die monatliche Rotation tatsaechlich diszipliniert macht: **Option C Cold-Platte**.
**Option B (BorgBase EU2) wird nicht empfohlen** als zweites Ziel, weil das das Provider-Risiko nicht reduziert.
## Was vor einer Buchung zu tun ist
1. Operator-Entscheidung Option A vs. C dokumentieren (kein Provider-Kontakt vor Entscheidung).
2. Falls Option A:
- Konto bei rsync.net anlegen, Borg-Plan waehlen.
- SSH-Key vom Borg-UI-Container exportieren bzw. dediziertes Key-Pair erzeugen.
- `borg init` gegen das neue Repo durchfuehren (separate Passphrase oder gleiche - bewusst entscheiden).
- Borg-UI um zweites Repository erweitern (separater Eintrag, kein Replace).
- Schedule pruefen: erstes Vollbackup als One-Shot, danach inkrementell.
- `docs/SECRETS_MAP.md` und `docs/EXTERNAL_DEPENDENCIES.md` um neuen Provider ergaenzen.
- `docs/RESTORE_MATRIX.md` und `docs/STORAGE_LAYOUT.md` Backup-Ziel-Liste aktualisieren.
3. Falls Option C:
- zwei Platten beschaffen, Filesystem XFS oder ext4 (kein NTFS).
- Rotations-Plan in `docs/STORAGE_LAYOUT.md` §8.1 ergaenzen.
- Borg-Repo-Init pro Platte; Borg-UI um lokales Repo erweitern (nur aktiv wenn Platte eingesteckt).
- Operator-Disziplin: monatliche Rotation als Kalender-Reminder.
## Was bewusst NICHT in dieser Vorlage steht
- Konkrete Hetzner-Konto-Daten, rsync.net-Accountnamen, IBANs, Telefonnummern. Diese Daten gehoeren nirgendwo in dieses Repo.
- Borg-Passphrasen. Bleiben ausserhalb des Repos.
- "Migrieren weg von Hetzner" als Option. Hetzner bleibt; das zweite Ziel ist Ergaenzung, nicht Ablosung.
## Offene Punkte
| Status | Punkt | Naechster Schritt |
|---|---|---|
| offen | Entscheidung Option A vs. C | Operator |
| offen | Budget-Freigabe | Operator |
| offen | Provider-/Hardware-Beschaffung | nach Entscheidung |
| offen | Schedule-Anpassung in `ops/borg-ui` | nach Provider-Bereitstellung |
+5
View File
@@ -38,6 +38,11 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
| `docs/EXTERNAL_DEPENDENCIES.md` | Externe Provider, Konten, Ausfall-Szenarien und kritische Off-Repo-Abhaengigkeiten |
| `docs/CAPACITY_AND_LIFECYCLE.md` | Capacity-Schwellen, Wachstum, Upgrade-Trigger und Restore-Zeitziele |
| `docs/FAMILY_ONBOARDING.md` | Familienorientierte Nutzungsdoku ohne Operator-Details |
| `docs/FAMILY_VIEW_DASHBOARD.md` | Spezifikation fuer das Grafana Family-View-Dashboard (Doku-only, kein JSON) |
| `docs/RESTORE_DRILL_ROUTINE.md` | Quartalsweise Restore-Drill-Routine, Tier-Belegung, DR-Sanity-Check |
| `docs/IMMICH_RESTORE_TEST.md` | Operator-Overview Immich-Restore-Test, Erstlauf 2026-05-27 erfolgreich |
| `docs/FRITZBOX_PORT_CORRECTION_PLAN.md` | Vorbereitungs-Doku fuer FRITZ!Box-Portfreigaben-Korrektur (kein Router-Eingriff) |
| `docs/OFFSITE_BACKUP_OPTIONS.md` | Entscheidungsvorlage zweites Offsite-Backup-Ziel (rsync.net vs. BorgBase EU2 vs. Cold-Platte) |
| `docs/AUDIT_2026-05-25_TODO.md` | Operative Arbeitsliste aus dem Audit vom 2026-05-25; Authelia-2FA bewusst geparkt |
| `docs/ALERTING_MAP.md` | ntfy Topic-Konvention und Sender-Mapping fuer Homelab-Alerts |
| `docs/ROLLBACK.md` | Rueckweg bei Fehlern im GitOps-Betrieb |
+120
View File
@@ -0,0 +1,120 @@
# Restore-Drill Routine - KalliLab CORE
Status: **verbindliche Routine (Doku-only)**, 2026-05-27.
Audit-Bezug: `docs/AUDIT_2026-05-25.md` Sprint 7 "Restore-Lab-Drill quartalsweise dokumentieren".
Verwandte Docs: `docs/RESTORE_MATRIX.md`, `docs/RESTORE_HANDBOOK.md`, `docs/DISASTER_RECOVERY.md`, `ops/restore-tests/schedule.md`.
## Ziel
Sicherstellen, dass die Backup-Kette nicht nur weiter laeuft, sondern auch **wiederherstellbar** ist. Restore-Tests werden nicht ad-hoc gemacht, wenn ein Problem auftritt, sondern in einer planbaren Kadenz, damit das Vertrauen ueber die Zeit waechst und Drift fruehzeitig auffaellt.
Diese Datei beschreibt nur die **Routine** (wann, was, wie pruefen). Die operativen Anleitungen pro Dienst stehen in `docs/RESTORE_HANDBOOK.md` und den dienstspezifischen Runbooks unter `ops/restore-tests/<dienst>-runbook.md`.
## Drei-Stufen-Modell
| Stufe | Frequenz | Aufwand | Ziel |
|---|---|---|---|
| Freshness-Check | woechentlich | Minuten | Backup-Artefakte sind frisch, Reports lesbar |
| Mini-Restore | monatlich/quartalsweise | 15-60 Min | Ein konkreter Dienst wird in isoliertem Test-Lab restauriert |
| DR-Sanity-Check | quartalsweise | 1-2 h | Reihenfolge, Doku, Tier-Reihenfolge in der DR-Doku gegen Realitaet pruefen |
## Bestaetigte Mini-Restores
Wenn ein Mini-Restore zum ersten Mal sauber durchlaeuft, wird er hier als Referenz gefuehrt. Der Eintrag wird **nicht** entfernt, wenn er wiederholt wird; stattdessen aendert sich der Datums-Stand.
| Dienst | Erster bestaetigter Lauf | Letzter Erfolg | Report | Repo-Skript |
|---|---|---|---|---|
| Vaultwarden | 2026-05-07 | 2026-05-07 | `/mnt/user/backups/restore-reports/vaultwarden-2026-05-07.md` | `ops/restore-tests/vaultwarden-restore-test.sh` |
| Gitea | 2026-05-07 | 2026-05-07 | `/mnt/user/backups/restore-reports/gitea-2026-05-07.md` | `ops/restore-tests/gitea-restore-test.sh` |
| Paperless | 2026-05-07 | 2026-05-07 | `/mnt/user/backups/restore-reports/paperless-2026-05-07.md` | `ops/restore-tests/paperless-restore-test.sh` |
| Immich | **2026-05-27** | **2026-05-27** | `/mnt/user/backups/restore-reports/immich-2026-05-27.md` | `ops/restore-tests/immich-restore-test.sh` |
Bei jedem weiteren Lauf wird die Spalte "Letzter Erfolg" aktualisiert.
## Quartals-Kadenz
Ein Kalenderjahr enthaelt vier Quartals-Drills. Jeder Quartals-Drill besteht aus dem Mini-Restore eines anderen Tier-2-Dienstes plus einem DR-Sanity-Check der Tier-1-Dienste.
| Quartal | Mini-Restore | DR-Sanity-Check Fokus |
|---|---|---|
| Q1 (Januar-Maerz) | `paperless` | Tier-1-Reihenfolge, Posture-Check-Status, Borg-Frische-Alerts |
| Q2 (April-Juni) | `immich` | Komodo-Bootstrap-Pfad, Gitea-Bundles, Secrets-Pfad-Inventur |
| Q3 (Juli-September) | `mealie` oder `nextcloud` (Operator-Wahl) | DNS-Pfad (AdGuard/Unbound/Tailscale), Cert-Expiry-Sicht |
| Q4 (Oktober-Dezember) | `vaultwarden` oder `gitea` (Operator-Wahl) | Externe Abhaengigkeiten (Cloudflare, Hetzner, GitHub-Mirror), Off-site-Zweitziel-Diskussion |
Diese Liste ist bewusst auf Tier-2 und Tier-1-Dienste fokussiert. Tier-3-Dienste (Filebrowser, Glances, Scrutiny, Speedtest, Glance) werden im Drill nicht explizit ausgefuehrt, weil sie rebuildbar sind oder keinen kritischen Datenbestand haben.
### Q2 2026 - Konkrete Belegung
- Mini-Restore: **Immich (erledigt 2026-05-27)**.
- DR-Sanity-Check (offen, vor Quartalsende 2026-06-30): Komodo-Bootstrap-Pfad gegen `docs/SERVICES_RECOVERY.md` durchgehen, Gitea-Bundles ueber `ops/borg-ui/scripts/gitea-bundle-mirror.sh` auf Frische und Bundle-Klonbarkeit pruefen, Secrets-Inventur gegen `docs/SECRETS_MAP.md` abgleichen.
### Wer schiebt das an?
- **Operator** loest jeden Drill manuell aus, idealerweise am 2. Wochenende des ersten Monats im Quartal.
- Es gibt **keinen** automatischen Host-Schedule fuer den Quartals-Drill. Die woechentliche Freshness-Pruefung und die monatlichen Mini-Restores in `ops/restore-tests/schedule.md` laufen separat.
- Bei akuten Aenderungen (Major-Upgrade eines Dienstes, FS-Migration, Repo-Strukturaenderung): zusaetzlichen Ad-hoc-Drill ausserhalb der Quartals-Kadenz einplanen.
## Freshness-Check (woechentlich)
- Skript: `ops/restore-tests/check-restore-freshness.sh` (Host-Bash) bzw. `ops/restore-tests/check-restore-freshness.ps1` (Windows-Operator).
- Erwartete Pruefungen:
- Letzter Borg-Archiv-Stand juenger als die Schwellwerte aus `docs/STORAGE_LAYOUT.md` §11.
- Kanonische Dump-Artefakte unter `/mnt/user/backups/borg/dumps/latest/` vorhanden und juenger als 26 h.
- Letzte Report-Dateien unter `/mnt/user/backups/restore-reports/` lesbar.
- Gitea-Bundles unter `/mnt/user/backups/git-bundles/gitea/` plausibel aktuell.
Ergebnis ist ein kurzes Konsolen-Log; bei Fehler greift die ntfy-Alarmierung aus `docs/ALERTING_MAP.md`.
## Mini-Restore (monatlich / bimonatlich)
Skripte folgen alle demselben Muster:
- isoliertes Test-Lab unter `/mnt/user/backups/restore-lab/<dienst>`
- isolierte Test-Container `restoretest-*`
- nur `127.0.0.1`-Ports, keine Traefik-Labels, keine produktive Domain
- Smoke-Test mit Erfolgsregel "Container laeuft reicht nicht"
- Report unter `/mnt/user/backups/restore-reports/<dienst>-YYYY-MM-DD.md`
Operative Anleitungen je Dienst:
- `ops/restore-tests/vaultwarden-runbook.md`
- `ops/restore-tests/gitea-runbook.md`
- `ops/restore-tests/paperless-runbook.md`
- `ops/restore-tests/immich-runbook.md`
## DR-Sanity-Check (quartalsweise)
Der Sanity-Check ist **kein** echter Restore. Er ist eine Doku-/Konsistenz-Pruefung mit zehn Punkten:
1. `docs/DISASTER_RECOVERY.md` Phase 1-5 noch konsistent mit Repo und Live-Stand?
2. `docs/RESTORE_MATRIX.md` Tier-Klassifizierung pro Dienst aktuell?
3. `docs/SECRETS_MAP.md` Pfade existieren, Stack-ENV-only-Liste aktuell?
4. `docs/SERVICES_RECOVERY.md` Komodo-Bootstrap-Pfad noch in Stufen A-F konsistent?
5. Gitea-Bundle-Mechanik laeuft und letzter Bundle-Stand klonbar (`git clone .../homelab-infra.bundle /tmp/restore-test`)?
6. Externe Mirrors (`michaelkaleschke-spec/homelab-infra` auf GitHub) gemaess `docs/EXTERNAL_DEPENDENCIES.md` noch erreichbar und aktuell?
7. ntfy-Push-Pfad noch erreichbar? (Test-Nachricht an `homelab-info`.)
8. Letzte vier Quartals-Mini-Restores im Report-Verzeichnis vorhanden?
9. Borg-Repo-Passphrase Offline-Sicherung noch auffindbar? (Pruefung durch Operator, nicht durch Skript, kein Wert ablegen.)
10. Capacity-Stand gegen Schwellen aus `docs/CAPACITY_AND_LIFECYCLE.md` abgeglichen?
Jeder Punkt wird in einem kurzen Quartals-Eintrag in `docs/MIGRATION_LOG.md` als `ok` / `Abweichung` / `Folgeaufgabe` festgehalten.
## Abbruch-Regeln
Wenn ein Drill fehlschlaegt, gilt die Stop-Regel aus `docs/WORKFLOW.md`:
- nach zwei fehlgeschlagenen Reparaturversuchen nicht weiterschreiben
- stattdessen Pflichtmatrix aus `docs/GITOPS_DRIFT_RUNBOOK.md` durchgehen
- Befund dokumentieren, naechsten Schritt mit dem Operator klaeren
- erst danach den Drill erneut starten oder das Quartal als "Drill incomplete" markieren
## Berichte
- Mini-Restore-Reports liegen unter `/mnt/user/backups/restore-reports/<dienst>-YYYY-MM-DD.md`.
- Quartals-Sanity-Checks landen als kurzer Block in `docs/MIGRATION_LOG.md`, nicht als eigenes Dokument.
- Reports werden nicht aus dem Repo verlinkt, weil sie nicht im Repo liegen. Operator dokumentiert nur Vorhanden/Erfolg/Datum.
## Geltungsdauer
Diese Routine gilt ab Q2 2026. Bei groesseren Aenderungen (zweites Off-site, Authelia-OIDC-Aktivierung, Hardware-Migration) wird die Liste pro Quartal angepasst.
+114 -14
View File
@@ -47,30 +47,128 @@ git -C /tmp/repo-restore-test fsck
## Komodo Bootstrap
Problem: Komodo verwaltet Stacks, ist aber selbst Teil des Recovery-Pfads. Ein kalter Host darf nicht voraussetzen, dass Komodo schon laeuft.
### Problemstellung
Komodo ist deshalb bewusst kein normaler Auto-Deploy-Stack: der `komodo`-Self-Stack hat keinen aktiven Gitea-Webhook. Recovery und Aenderungen laufen ueber den dokumentierten Bootstrap-Pfad und muessen nach dem Start in Komodo validiert werden.
Komodo verwaltet alle Stacks per GitOps, ist aber selbst Teil des Recovery-Pfads. Ein kalter Host darf **nicht** voraussetzen, dass Komodo schon laeuft. Das ist das klassische Henne-Ei-Problem: Komodo darf sich nicht selbst aus dem Repo holen muessen, bevor es laufen kann.
Minimaler Wiederanlauf:
### Recovery-Anker (verbindlich)
1. Docker und externe Netze herstellen (`frontend_net`, `backend_net`, ggf. weitere dokumentierte Netze).
2. Repo aus Gitea/GitHub Mirror klonen.
3. Komodo Compose aus `ops/komodo/docker-compose.yml` oder einem spaeteren Bootstrap-Pfad starten.
4. Erforderliche `.env`/Secrets aus Host-Secret-Backup wiederherstellen.
5. Komodo-Core, Periphery und Mongo starten.
6. Web-UI und Periphery-Verbindung pruefen.
**Anker:** `ops/komodo/docker-compose.yml` aus dem Repo.
Offene Aufgabe:
Dieses Compose-File ist die einzige Quelle, aus der Komodo nach einem Kaltstart hochgefahren wird. Es wird nicht ueber Komodos eigenen Auto-Deploy-Pfad konsumiert.
- Entscheidung 2026-05-26: `ops/komodo/docker-compose.yml` bleibt die verbindliche Bootstrap-Quelle. Der `komodo`-Self-Stack hat keinen aktiven Gitea-Webhook und ist nicht der Recovery-Anker.
- Offen bleibt nur ein spaeterer Trockenlauf, bei dem die Komodo-Startkommandos gegen echte Restore-Pfade getestet werden.
**Was der Anker bewusst NICHT ist:**
Validierung:
- nicht der Komodo-Self-Stack (Komodo darf sich nicht selbst deployen).
- nicht der laufende Komodo-Workspace unter `/mnt/user/services/stacks/komodo/compose.yaml` (kann driften, siehe `HOMELAB_ARCHITECTURE_MASTER_V2.md` Sektion 13, Drift-Recovery 2026-05-04).
- nicht ein Gitea-Webhook (`komodo`-Stack hat bewusst `webhook_enabled: false`).
**Quelle der Compose-Datei:**
1. Vorzug: lokaler Repo-Clone auf dem Operator-Windows-PC (`G:\Gitea_Clone\homelab-infra\`).
2. Fallback: GitHub-Mirror `michaelkaleschke-spec/homelab-infra` (siehe `docs/EXTERNAL_DEPENDENCIES.md`).
3. Letzter Fallback: Gitea-Bundles unter `/mnt/user/backups/git-bundles/gitea/homelab-infra.bundle` (siehe Mirror-Abschnitt oben).
Wenn alle drei Quellen down sind, ist Recovery blockiert und das Problem ist nicht Komodo, sondern Repo-Verlust.
### Kaltstart-Schritte
Der Wiederanlauf-Pfad ist linear; jeder Schritt hat ein eindeutiges Erfolgskriterium, bevor der naechste laeuft.
**Stufe A - Host und Docker-Grundlage**
1. Unraid bootet; Array ist online; Shares `/mnt/user/appdata`, `/mnt/user/services`, `/mnt/user/backups` sichtbar.
2. Docker-Daemon laeuft (`docker info` antwortet).
3. Externe Docker-Netze existieren oder werden erzeugt (`frontend_net`, `backend_net`). Wenn nicht vorhanden: `docker network create --driver bridge frontend_net` bzw. `... --internal backend_net`.
Erfolgskriterium: `docker network ls` zeigt `frontend_net` und `backend_net`.
**Stufe B - Repo-Quelle bereitstellen**
1. Repo-Clone aus dem bevorzugten Pfad bereithalten:
- lokaler Operator-Clone, oder
- frischer Clone aus GitHub-Mirror, oder
- Bundle-Restore aus `/mnt/user/backups/git-bundles/gitea/homelab-infra.bundle` (`git clone homelab-infra.bundle homelab-infra`).
2. Repo-Stand verifizieren: `git -C <pfad> log --oneline -1` zeigt einen plausibel aktuellen Commit.
Erfolgskriterium: `ops/komodo/docker-compose.yml` ist auf dem Host lesbar.
**Stufe C - Komodo-Secrets bereitstellen**
Komodo braucht beim Start mehrere Secrets, die **nicht** aus dem Repo kommen. Restore-Reihenfolge gemaess `docs/SECRETS_MAP.md`:
1. Host-Secrets unter `/mnt/user/appdata/secrets/` wiederherstellen (aus Borg oder analog gesicherter Quelle).
2. Datei `/mnt/user/appdata/secrets/komodo_mongo_password.txt` ist Pflicht (Mongo-Initialisierung).
3. Stack-ENV-Werte `KOMODO_SECRET_KEY`, `KOMODO_WEBHOOK_SECRET`, `KOMODO_JWT_SECRET`, `KOMODO_MONGO_PASSWORD`, `KOMODO_PERIPHERY_PASSKEY` muessen als Host-`.env` neben dem Compose vorliegen. Quelle in dieser Reihenfolge: Vaultwarden (sobald restauriert), externe Operator-Notiz, oder Komodo-Mongo-Dump (nur wenn Mongo separat bereits gestartet und die `stack`-Collection lesbar ist).
Erfolgskriterium: Compose-Validierung laeuft ohne fehlende Variablen.
```bash
docker compose -f ops/komodo/docker-compose.yml config >/dev/null
```
**Stufe D - Komodo starten**
1. Compose hochfahren:
```bash
docker compose -f ops/komodo/docker-compose.yml config
docker compose -f ops/komodo/docker-compose.yml up -d
```
2. Reihenfolge intern: `komodo-mongo` zuerst healthy, dann `komodo-core`, dann `komodo-periphery`.
3. Status pruefen:
```bash
docker ps --filter "name=komodo"
docker logs --tail 50 komodo-core
docker logs --tail 50 komodo-periphery
```
Erfolgskriterium: alle drei Container laufen; Komodo-Core meldet Bind auf Port `9120`; Periphery meldet erfolgreiche Verbindung zu Core.
**Stufe E - Web-UI und GitOps validieren**
1. `https://komodo.kaleschke.info` ist erreichbar (Authelia-Bypass dokumentiert, native Komodo-Auth aktiv).
2. Komodo zeigt im Web-UI die bekannten Stacks aus Gitea (sobald Gitea ebenfalls laeuft; siehe `docs/DISASTER_RECOVERY.md` Phase 4 Stufe 2 vor Stufe 3).
3. Gitea-Webhooks gegen Komodo werden separat in der Phase-4-Reihenfolge geprueft, **nicht** als Teil des Komodo-Bootstraps.
Erfolgskriterium: Komodo-UI laedt, Periphery `Online`, mindestens ein Stack aus Gitea sichtbar.
**Stufe F - Stacks in Tier-Reihenfolge aufnehmen**
Erst nach erfolgreichem Komodo-Bootstrap werden produktive Stacks ueber den dokumentierten Stufenpfad in `docs/DISASTER_RECOVERY.md` Phase 4 hochgefahren (Traefik, AdGuard, Tailscale, dann PostgreSQL, Authelia, Redis, Gitea, dann Apps).
### Trockenlauf-Idee (Doku-only, nicht ausgefuehrt)
Ein bewusster Trockenlauf des Komodo-Bootstraps gegen Wegwerf-Pfade ist die naechste sinnvolle Reife-Stufe. Vorschlag:
| Schritt | Inhalt |
|---|---|
| 1 | Test-Compose aus `ops/komodo/docker-compose.yml` in `/mnt/user/backups/restore-lab/komodo/` kopieren |
| 2 | Test-`.env` mit Wegwerf-Secrets erzeugen (nicht produktive Werte!) |
| 3 | `docker compose -f .../restore-lab/komodo/docker-compose.yml -p restoretest-komodo up -d` |
| 4 | Smoke: Mongo healthy, Core antwortet auf `http://127.0.0.1:<test-port>/api/health`, Periphery verbindet |
| 5 | `docker compose -p restoretest-komodo down -v` und Restore-Lab bereinigen |
Der Trockenlauf ist **noch nicht** als Repo-Skript abgelegt. Er bleibt als Folgeschritt analog zum Immich-Restore-Test geplant.
### Validierungs-Kommandos (Snapshot)
```bash
# Compose syntaktisch ok?
docker compose -f ops/komodo/docker-compose.yml config >/dev/null
# Komodo-Container vorhanden und laufend?
docker ps --filter "name=komodo" --format "table {{.Names}}\t{{.Status}}"
# Mongo Health?
docker exec komodo-mongo mongosh --quiet --eval 'db.adminCommand({ping:1}).ok'
# Core API up?
docker exec komodo-core sh -lc 'wget -q -O- http://127.0.0.1:9120/api/health || true'
# Periphery sichtbar?
docker logs --tail 50 komodo-periphery 2>&1 | grep -i "connected\|periphery"
```
## Secrets Recovery Reihenfolge
@@ -98,5 +196,7 @@ Authoritativ ist `docs/SECRETS_MAP.md`. Fuer den Kaltstart ist diese Reihenfolge
|---|---|
| erledigt (Skript + Host-Test) | Gitea-Bundle- oder Mirror-Mechanik final entscheiden |
| erledigt | Komodo-Bootstrap-Quelle finalisieren |
| erledigt (Doku) | Komodo-Kaltstart in linearen Stufen A-F dokumentieren |
| offen | Komodo-Trockenlauf-Skript in `ops/restore-tests/` analog zu Immich vorbereiten |
| offen | Restore-Kommandos nach erstem Trockenlauf mit echten Pfaden ergaenzen |
| erledigt | Services-Recovery in `docs/DISASTER_RECOVERY.md` verlinken |
+4 -2
View File
@@ -26,14 +26,16 @@ Alle 2 Monate:
Quartalsweise:
- Restore-/DR-Sanity-Check
- `immich` Restore-Smoke-Test (DB + UI, ohne produktive Foto-Mounts)
- Restore-/DR-Sanity-Check gemaess `docs/RESTORE_DRILL_ROUTINE.md`
- `immich` Restore-Smoke-Test (DB + UI, ohne produktive Foto-Mounts; Erstlauf 2026-05-27 erfolgreich)
- pruefen:
- Restore-Lab-Struktur
- Reports
- Skripte und Pfade
- Doku noch passend
Die Quartals-Belegung (welcher Dienst, welcher Sanity-Fokus) steht in `docs/RESTORE_DRILL_ROUTINE.md` Tabelle "Quartals-Kadenz".
## Konkreter Kalender
- Jeden Montag, 06:30: