Trim documentation to active runbooks

This commit is contained in:
2026-05-31 23:26:12 +02:00
parent ab8bfea7c8
commit 4e34582008
42 changed files with 193 additions and 4886 deletions
+37 -196
View File
@@ -1,213 +1,54 @@
# AI Context # AI Context
Stand: 2026-05-04 Stand: 2026-05-31
Diese Datei ist fuer KI-Agenten gedacht, die das Homelab-Repo schnell verstehen muessen. Sie ersetzt nicht die Detaildokumente, sondern fasst Zielbild, Betriebsmodell und Risiken zusammen. Kurzer Kontext fuer KI-Agenten. Nicht als Ersatz fuer die echten Runbooks lesen.
## Kurzfassung ## Systembild
Dieses Repository beschreibt ein Unraid-basiertes Homelab namens `Kallilabcore`. Der Betrieb folgt GitOps: Gitea `origin/master` ist die Quelle der Wahrheit, der lokale Clone ist Arbeitskopie, Komodo deployed aus Gitea, Docker Runtime und Host sind Ergebnis, nicht Bearbeitungsort. - Host: Unraid `Kallilabcore`
- Betriebsmodell: GitOps mit Gitea `origin/master` als Sollzustand
- Deploy: Komodo zieht aus Gitea und startet Compose-Stacks
- Ingress: Traefik; WAN-seitig bewusst nur `443/tcp`
- Secrets: nie im Repo, meist unter `/mnt/user/appdata/secrets/`
- Backup: Borg plus host-seitige Dumps; Hetzner ist Offsite, H:/ ist lokale Nearline-Kopie
Traefik ist der zentrale Web-Einstieg fuer HTTP(S). Admin-/Ops-UIs liegen entweder hinter Authelia/secure headers oder sind als Ausnahme dokumentiert. Secrets liegen ausserhalb des Repos auf dem Host, meistens unter `/mnt/user/appdata/secrets/`. ## Vor jeder Aenderung lesen
## Zielbild des Homelabs 1. `HOMELAB_ARCHITECTURE_MASTER_V2.md`
2. `docs/WORKFLOW.md`
3. betroffene Compose-Datei
4. bei Service-Fragen `docs/SERVICE_CATALOG.md`
5. bei Restore/DR `docs/DISASTER_RECOVERY.md` und `docs/RESTORE_MATRIX.md`
- stabile Compose-first Infrastruktur ## Harte Regeln
- keine produktiven Dockerman-/Ad-hoc-Container als Dauerzustand
- Traefik als einziger oeffentlicher Web-Eingang
- GitOps ueber Gitea + Komodo
- klare Trennung von Web-Netz, Backend-Netz und app-internen Netzen
- saubere Backup-/Restore-Faehigkeit ueber Borg, Dumps und dokumentierte Pfade
- keine stillen Host-Hotfixes ohne Repo-/Doku-Abgleich
## Architekturblocke
### Ingress / Netzwerk
- `traefik` nimmt 80/443 entgegen.
- Docker-Labels definieren Service-Routing.
- `traefik/dynamic/*` bleibt nur fuer Middlewares, TLS und Dashboards.
- Neue Service-Routen gehoeren nicht in den File-Provider.
### DNS / Remote
- AdGuard Home beantwortet LAN-DNS und nutzt Unbound als Upstream.
- Tailscale stellt Remote-Zugang bereit und nutzt `network_mode: host`.
### GitOps
- Gitea hostet das Repo unter `git.kaleschke.info`.
- Komodo ist Stack-Manager und Deploy-Consumer.
- Komodo Periphery braucht Docker-Socket und `/mnt/user/services` Mount, um Stacks reproduzierbar zu deployen.
- Neue produktive Komodo-Stacks aus `Micha/homelab-infra` muessen einen aktiven Gitea->Komodo-Webhook auf die aktuelle Stack-ID haben; Ausnahmen wie deaktivierte/pausierte Stacks muessen dokumentiert werden.
- Der `komodo`-Self-Stack ist eine dokumentierte Ausnahme ohne aktiven Gitea-Webhook; Bootstrap/Recovery laeuft ueber `docs/SERVICES_RECOVERY.md`.
### Identity / Security
- Authelia stellt ForwardAuth fuer viele Admin-UIs bereit.
- Authelia nutzt GMX SMTP fuer Identity-/2FA-Benachrichtigungen; Passwort liegt als Host-Secret `authelia_smtp_password.txt`.
- Vaultwarden ist ein separater Passwort-Tresor.
- Komodo ist bewusst nicht pauschal hinter Authelia, weil UI, API, Webhooks und Periphery-WebSocket sonst leicht gebrochen werden koennen.
- Komodo-Compose, Komodo-Secrets und Komodo-Runtime nur gemeinsam mit dem Betreiber aendern; `KOMODO_WEBHOOK_SECRET` ist bewusst getrennt von `KOMODO_SECRET_KEY`. Gitea-Webhooks nicht pauschal vereinheitlichen: einzelne Komodo-Stacks koennen eigene per-Stack-Webhook-Secrets haben.
### Apps
Wichtige Apps sind Paperless, Immich, Mealie, Mail Archiver, Nextcloud, ntfy, Vaultwarden und Gitea. Admin-/Ops-Tools sind u. a. Glance, Komodo, Borg UI, Filebrowser, code-server, Glances, Scrutiny, Speedtest, Monitoring Grafana und Hermes Agent.
### Hermes Agent — Architektur und Ops-Monitor
Hermes laeuft nach Model C (siehe `ops/hermes-agent/README.md`):
- `hermes-gateway` als Docker-Container auf dem Unraid-Host, intern auf `hermes_net:8642`
- Terminal-Befehle werden via SSH auf eine dedizierte Linux-VM ausgefuehrt
- VM-IP: `192.168.178.143`, SSH-User: `hermes`
- Repo-Clone auf der VM: `/srv/hermes-workspace/homelab-infra/`
**Fuer KI-Agenten wichtig:** Das Hermes-Terminal laeuft auf der VM, nicht auf dem Unraid-Host.
`/mnt/user/...`-Pfade sind von der VM aus nicht direkt erreichbar.
Docker-CLI ist auf der VM nicht installiert — fuer Homelab-Checks wird `check_health.py` verwendet.
**Ops-Monitor (homelab-ops-monitor):**
- Skill: `ops/hermes-agent/skills/homelab-ops-monitor.md`
- Script: `ops/hermes-agent/scripts/check_health.py` — prueft Services via HTTP, keine externen Deps
- Wissensbasis: `ops/hermes-agent/services.json` — maschinenlesbare Ableitung aus `docs/SERVICE_CATALOG.md`
- Check-Strategie: HTTP GET fuer URL-basierte Services, interne Services (DBs, Redis) als `"internal"` markiert
- ntfy-Topic fuer Alerts: `homelab-alerts` auf `https://ntfy.kaleschke.info`
Nach Aenderungen an `services.json` oder `check_health.py`: `git pull` auf der VM ausfuehren.
- Mail Archiver ist ein Hybrid-Dienst mit `frontend_net` fuer IMAP/Traefik und `backend_net` fuer PostgreSQL; die Web-UI liegt hinter Authelia und behaelt zusaetzlich App-eigene Auth.
### Monitoring / Metriken
- Zielzustand ist ein zentraler Stack `monitoring/` unter `https://monitoring.kaleschke.info`.
- `monitoring-grafana` ist die zentrale UI fuer Prometheus, Loki und InfluxDB 3 Core.
- `monitoring-prometheus` sammelt Infrastruktur-Metriken; `monitoring-loki` + `monitoring-promtail` sammeln Docker-Logs.
- `monitoring-influxdb3-core` ist nicht public und nicht im `frontend_net`.
- Home Assistant schreibt ueber LAN-only Port 8181 nach InfluxDB, gebunden ueber `INFLUXDB_BIND_IP`.
- Ein `401 Unauthorized` von InfluxDB ohne Token ist beim Reachability-Test ein Erfolgssignal.
- Die frueheren Altstaende `ops/loki` und `ops/grafana-influxdb` wurden aus dem aktiven Repo entfernt; fuer Monitoring immer `monitoring/` verwenden, Rollback nur ueber Git-Historie.
- Uptime Kuma ist entfernt; HTTP-Verfuegbarkeit laeuft ueber Blackbox Exporter, Prometheus-Alerts und `Homelab / Availability` in Monitoring Grafana.
## Deployment-Logik
Normalfall:
1. lokaler Clone synchronisieren
2. betroffene Dokumente und Compose-Datei lesen
3. minimal aendern
4. lokal validieren
5. committen und pushen
6. Komodo-Webhook / Deploy beobachten
7. Runtime testen
8. Doku aktualisieren
Wichtig: Komodo-Web-Editor ist nicht der Bearbeitungsort. Wenn Komodo und Git voneinander abweichen, zuerst Git und Komodo Workspace pruefen, nicht live herumprobieren.
Beim Anlegen neuer produktiver Stacks ist der Gitea->Komodo-Webhook Pflicht. Nach dem Anlegen muss ein Test-Push oder Test-Delivery zeigen, dass Gitea die aktuelle Komodo-Stack-ID erreicht.
## Netzwerkmodell
| Netzwerk | Bedeutung |
|---|---|
| `frontend_net` | Web-/Proxy-Netz fuer Traefik-geroutete Dienste und Dienste mit Internetbedarf |
| `backend_net` | internes Netz fuer shared PostgreSQL, Redis und Backends |
| `dns_net` | AdGuard + Unbound |
| app-interne Netze | Isolation von App + DB/Cache, z. B. Immich, Mealie, Nextcloud, Monitoring |
| `host` | nur dokumentierte Sonderfaelle wie Tailscale/Plex |
Regeln:
- Keine Secrets zitieren oder ins Repo schreiben.
- Keine produktiven Host-Hotfixes ohne Repo-Abgleich.
- Datenbanken nie ins `frontend_net`. - Datenbanken nie ins `frontend_net`.
- Admin-UIs nur mit Traefik + Middleware oder dokumentierter Ausnahme. - Direkte Host-Ports sind Ausnahme.
- Direkte Host-Ports sind Ausnahme, nicht Default. - Traefik dynamic config und Authelia Host-Config sind manuelle Sync-Ausnahmen.
- Runtime-Netznamen koennen Compose-Projektpraefixe bekommen, z. B. `monitoring_monitoring_influx_lan`. - Bei Drift zuerst Git, Gitea, Komodo Workspace, Docker Runtime und Host getrennt pruefen.
- Nach zwei fehlgeschlagenen Reparaturversuchen stoppen und `docs/GITOPS_DRIFT_RUNBOOK.md` nutzen.
## Security-Modell ## Bekannte Ausnahmen
- Secrets nie ins Git. - Traefik: Host-Ports 80/443, WAN-Freigabe nur 443
- Werte niemals zitieren, auch nicht aus `.env`, Stack ENV, Logs oder Screenshots. - Gitea: SSH auf Host-Port 222, keine WAN-Freigabe
- Secret-Dateien bevorzugt unter `/mnt/user/appdata/secrets/`. - AdGuard: DNS 53 direkt; Admin nur auf Tailscale-IP `100.80.98.33:8082`
- `_FILE`-Varianten bevorzugen, falls Image sie unterstuetzt. - Tailscale und Plex: Host-Netz
- Wenn `_FILE` nicht unterstuetzt wird, Komodo Stack Environment Variables verwenden.
- Docker-Socket, `privileged: true`, Host-Netz und breite Mounts sind nur mit dokumentierter Begruendung akzeptabel.
Bekannte Ausnahmen:
- Traefik: 80/443
- Gitea: SSH 222
- AdGuard: DNS 53 direkt; Admin 8082 ist bewusst ohne Traefik/2FA, aber auf Tailscale-IP `100.80.98.33` begrenzt
- Tailscale: Host-Netz, `NET_ADMIN`, `NET_RAW`, `/dev/net/tun`
- Scrutiny: privileged - Scrutiny: privileged
- Komodo: Docker-Socket, native Auth - Komodo/Periphery: Docker-Socket-Zugriff
- InfluxDB: LAN-only 8181 fuer Home Assistant Writer - InfluxDB 3 Core: `127.0.0.1:8181`, Root-User-Ausnahme dokumentiert
- `monitoring-influxdb3-core`: `user: "0"` als dokumentierte Host-Appdata-Permissions-Ausnahme
- Traefik dynamic config: manueller Host-Sync
## Backup- und Restore-Modell ## Aktuelle Restpunkte
Borg sichert kritische Appdaten, Secrets, Traefik-State und Dump-Artefakte. Datenbank-Restore soll bevorzugt ueber Dumps laufen, nicht ueber rohe Live-DB-Verzeichnisse. Authoritativ: `docs/AUDIT_2026-05-25_TODO.md`.
Wichtige Pfade: Kurzfassung:
- `/mnt/user/appdata` - naechsten Borg-Lauf nach den Dumps vom 2026-05-31 21:42 bestaetigen
- `/mnt/user/appdata/secrets` - Alt-Volumes fruehestens ab 2026-06-02 freigeben
- `/mnt/user/services` - Hetzner-Account-Hygiene und Borg `append-only` pruefen
- `/mnt/user/documents` - FRITZ!Box-Servicefenster fuer Update, Config-Backup und IPv6-Exposure planen
- `/mnt/user/photos` - Auth-/OIDC-/CrowdSec-/Hermes-Themen bewusst geparkt
- `/mnt/user/backups/borg/dumps/latest`
Dump-Skript:
- `ops/borg-ui/scripts/pre-backup-dumps.sh`
- soll auf dem Unraid Host laufen
- soll nicht als Borg-UI Inline-Hook behandelt werden, solange die Architektur nicht bewusst geaendert wird
Disaster Recovery folgt einer Bootstrap-Reihenfolge:
1. Traefik, AdGuard, Tailscale
2. PostgreSQL, Authelia, Redis, Gitea
3. Komodo
4. kritische Apps
5. restliche Apps/Ops inklusive Hermes Agent
## Typische Arbeitsweise im Repo
- Fuer Fragen zuerst `HOMELAB_ARCHITECTURE_MASTER_V2.md` lesen.
- Fuer operative Aenderungen `docs/WORKFLOW.md` lesen.
- Fuer Service-Details `docs/SERVICE_CATALOG.md` und die Compose-Datei lesen.
- Fuer Drift `docs/GITOPS_DRIFT_RUNBOOK.md` nutzen.
- Fuer Rollback `docs/ROLLBACK.md` nutzen.
- Fuer Restore `docs/DISASTER_RECOVERY.md` und `docs/RESTORE_MATRIX.md` nutzen.
KI-Agenten sollen konservativ arbeiten: keine indirekten Live-Aenderungen, keine Deployments, keine Commits, keine Host-Schreibbefehle, wenn der Benutzer nur Analyse oder Doku verlangt.
## Bekannte Risiken und Altlasten
- Traefik dynamic config muss manuell auf den Host synchronisiert werden; Komodo deployed diese Dateien nicht automatisch.
- `backend_net` und app-interne Netze muessen bei Runtime-Problemen live geprueft werden, weil Compose-Projektpraefixe Netznamen veraendern koennen.
- Authelia `configuration.yml` ist Repo-Baseline fuer nicht geheime Einstellungen, wird aber nicht automatisch von Komodo auf den Host kopiert; die produktive Host-Datei kann OIDC-/Secret-Konfiguration enthalten. Bei Auth-Aenderungen Repo-Baseline, Host-Config und Compose-Middlewares pruefen und nicht blind ueberschreiben.
- Authelia nutzt PostgreSQL, aber bewusst kein Redis-Session-Backend; Redis ist kein Authelia-Bootstrap-Blocker.
- Authelia-Notifier ist SMTP; bei Auth-Aenderungen Host-Config backupen, `authelia validate-config` ausfuehren und erst danach neu starten.
- `paperless-ngx` nutzt fuer DB/Redis bewusst Stack ENV statt `_FILE`.
- `glance-docker-socket-proxy`, `glances` und `komodo-periphery` nutzen Docker-/Socket-Zugriff; Zugriff bewusst behandeln.
- `borg-ui` und `filebrowser` haben breite Mounts; bei Hardening nicht ad hoc, sondern gezielt vorgehen.
- `scrutiny` ist privilegiert und hat Device-Mounts.
- `Plex-Media-Server` ist im Architekturziel als Host-Sonderfall dokumentiert, aber nicht als Repo-Compose-Stack enthalten.
- Echte `stack.env`- und `.env`-Dateien gehoeren nicht ins Repo; fuer Hermes liegt nur `ops/hermes-agent/stack.env.example` im Git.
- Einige Images nutzen mutable Tag plus Digest. Das friert den aktuellen Digest ein, ist aber kein automatisches Upgrade-Modell.
- Stateful Images werden bevorzugt als Minor-/Patch-Tag plus Digest gepinnt; Redis-Caches bleiben bewusst ungedigestet.
## Arbeitsregel bei Unsicherheit
Nicht raten. Erst diese Reihenfolge:
1. Repo-Doku lesen
2. Compose-Datei lesen
3. Git-Stand pruefen
4. Komodo Workspace pruefen
5. Docker Runtime pruefen
6. Host-Listener / echten Request pruefen
7. genau eine abweichende Ebene benennen
Wenn zwei Reparaturversuche scheitern: keine weiteren Schreibbefehle, Pflichtmatrix aus `docs/GITOPS_DRIFT_RUNBOOK.md` ausfuellen.
-34
View File
@@ -1,34 +0,0 @@
# Alerting Map
Stand: 2026-05-23
Ziel: Alle problemrelevanten Homelab-Meldungen landen auf einem Handy-Topic.
> Die Prometheus-Alarmregeln im Detail (Trigger, Schwellen, Severity,
> Handlungshinweis, Luecken-Analyse) stehen in `docs/ALERT_RULES.md`.
## ntfy Topics
| Topic | Zweck |
|---|---|
| `homelab-alerts` | Alles, was Aufmerksamkeit braucht: Prometheus, Docker-Events, Posture, Zertifikate/Token, Compose-Drift, Borg-Pre-Hook-Fehler und Restore-Fehler |
| `homelab-info` | Optionale Erfolgsmeldungen, z. B. erfolgreiche Restore-Testlaeufe |
## Sender
| Sender | Pfad | Problem-Topic | Hinweis |
|---|---|---|---|
| Prometheus / Alertmanager | `monitoring/alertmanager/alertmanager.yml`, `monitoring/alertmanager-ntfy-bridge/bridge.py` | `homelab-alerts` | Zentrale Monitoring-Alerts via Bridge |
| Posture Check | `services/posture-check/posture-check.sh` | `homelab-alerts` | Warning und Critical gehen auf dasselbe Handy-Topic |
| Cert / Token Check | `services/posture-check/cert-token-check.sh` | `homelab-alerts` | Prueft produktive HTTPS-Domains und Cloudflare Token |
| Compose Runtime Drift | `services/posture-check/compose-runtime-drift.sh` | `homelab-alerts` | Meldet Abweichungen zwischen Repo-Compose und Runtime-Image |
| Docker Critical Events | `services/posture-check/docker-critical-events.sh` | `homelab-alerts` | Meldet Docker `die`, `oom` und `kill` Events |
| Borg Pre-Hook | `ops/borg-ui/scripts/pre-borg.sh` | `homelab-alerts` | Meldet Fehler vor Borg, z. B. Posture-, Dump- oder Restore-Freshness-Fehler |
| Restore Jobs | `ops/restore-tests/run-restore-job-with-ntfy.sh` | `homelab-alerts` | Erfolg geht an `homelab-info`, Fehler immer an `homelab-alerts` |
## Konvention
- `NTFY_BASE_URL` zeigt standardmaessig auf `https://ntfy.kaleschke.info`.
- Neue Problem-Alerts sollen `homelab-alerts` nutzen.
- Erfolgsmeldungen sind optional und sollen nicht in `homelab-alerts` landen, ausser sie sind bewusst als Lebenszeichen gewuenscht.
- Blackbox-Endpoint-Alerts sollen bekannte WAN-/Provider-Sammelausfaelle zusammenfassen, damit kurze DSL-Reconnects keine ntfy-Flut pro Domain erzeugen.
+40 -117
View File
@@ -1,129 +1,52 @@
# Alert Rules # Alert Rules
Stand: 2026-05-30 Stand: 2026-05-31
Zentrale Nachschlagetabelle aller Prometheus-Alarmregeln plus Bewertung, ob die Diese Datei beschreibt die produktiven Alarmwege und wichtigsten Regeln. Die
Abdeckung sinnvoll und vollstaendig ist. Konfiguration selbst liegt in `monitoring/prometheus/alerts.yml` und in den
Skripten unter `services/posture-check/`.
- **Authoritative Quelle der Regeln:** `monitoring/prometheus/alerts.yml` ## Alarmwege
- **Topic-/Sender-Konvention:** `docs/ALERTING_MAP.md`
- Alle Prometheus-Alarme laufen ueber Alertmanager →
`monitoring/alertmanager-ntfy-bridge/bridge.py` → ntfy-Topic `homelab-alerts`.
> Diese Datei ist **Doku**, nicht die Konfiguration. Wer eine Regel aendert, | Weg | Quelle | Ziel |
> aendert `monitoring/prometheus/alerts.yml`, pusht nach Gitea und laesst Komodo
> deployen bzw. Prometheus neu laden. Danach diese Tabelle nachziehen.
## Zwei Alarm-Pfade nebeneinander
Nicht jeder Homelab-Alarm kommt aus Prometheus. Wer "fehlt da was?" beantworten
will, muss beide Pfade zusammen lesen:
| Pfad | Quelle | Beispiele |
|---|---|---| |---|---|---|
| **Prometheus / Alertmanager** | `monitoring/prometheus/alerts.yml` | Erreichbarkeit, Zertifikate, Disk/RAM, Borg-Metriken, Critical-Container | | Prometheus / Alertmanager | `monitoring/prometheus/alerts.yml` | ntfy `homelab-alerts` |
| **Posture-Check / ntfy-direkt** | `services/posture-check/*` | NVMe-SMART, Cert/Token-Check, Compose-Runtime-Drift, Docker `die`/`oom`/`kill`, Authelia-Drift, Borg-Pre-Hook, Restore-Jobs | | Posture Check | `services/posture-check/posture-check.sh` | ntfy `homelab-alerts` |
| Cert / Token Check | `services/posture-check/cert-token-check.sh` | ntfy `homelab-alerts` |
| Compose Runtime Drift | `services/posture-check/compose-runtime-drift.sh` | ntfy `homelab-alerts` |
| Docker Critical Events | `services/posture-check/docker-critical-events.sh` | ntfy `homelab-alerts` |
| Borg Pre-Hook | `ops/borg-ui/scripts/pre-borg.sh` | ntfy `homelab-alerts` |
| Restore Jobs | `ops/restore-tests/run-restore-job-with-ntfy.sh` | Fehler `homelab-alerts`, Erfolg `homelab-info` |
Beide enden auf `homelab-alerts`. Der Posture-Pfad ist in `docs/ALERTING_MAP.md` ## Prometheus-Regeln
tabelliert; er wird hier nur referenziert, nicht dupliziert.
## Prometheus-Regeltabelle | Alarm | Ausloeser | Severity | Aktion |
|---|---|---|---|
Severity-Routing der Bridge: `critical` und `warning` gehen beide auf | `HomelabExternalConnectivityDown` | mindestens 5 HTTP-Ziele down | warning | WAN/DNS/Provider pruefen, nicht jede Domain einzeln jagen |
`homelab-alerts` (kein eigenes Topic je Severity). | `HomelabEndpointDown` | einzelnes HTTP-Ziel down | critical | Dienst, Traefik-Route und Backend pruefen |
| `HomelabEndpointSlow` | Endpoint >5s | warning | Dienstlast oder Backend-Latenz pruefen |
### Gruppe `homelab-availability` | `HomelabCertificateExpiresSoon` | Cert <21 Tage | warning | ACME/Traefik-Renewal beobachten |
| `HomelabCertificateExpiresCritical` | Cert <=7 Tage | critical | Renewal sofort pruefen |
| Alarm | Trigger (PromQL, gekuerzt) | Schwelle / `for` | Severity | Was tun | | `HomelabDiskAlmostFull` | Filesystem >85% | warning | Platz schaffen oder Schwelle pruefen |
|---|---|---|---|---| | `HomelabDiskCritical` | Filesystem >95% | critical | Sofort Platz schaffen |
| `HomelabExternalConnectivityDown` | `sum(probe_success{blackbox-http}==0) >= 5` | ≥5 Endpunkte / 8m | warning | WAN/DNS/Provider pruefen, nicht pro Domain jagen — Sammelausfall | | `HomelabHighMemoryUsage` | MemAvailable <10% | warning | Speicherfresser identifizieren |
| `HomelabEndpointDown` | `probe_success==0` (einzeln, nicht im Sammelausfall) | 1 Endpunkt / 8m | critical | Betroffenen Dienst/Traefik-Route pruefen | | `HomelabTraefik5xx` | >=5 5xx je Service in 5 Minuten | warning | betroffenes Backend pruefen |
| `HomelabEndpointSlow` | `probe_duration_seconds > 5` | >5s / 5m | warning | Dienst-/Backend-Last pruefen, oft transient | | `HomelabTextfileExporterStale` | Textfile-Exporter >2h alt | warning | Host-Cron pruefen |
| `HomelabCertificateExpiresSoon` | Restlaufzeit 721 Tage | <21d & >7d / 30m | warning | ACME/Traefik-Renewal beobachten | | `HomelabBorgMetricsMissing` | Borg-Metrik fehlt | critical | Textfile-Exporter oder Borg-UI pruefen |
| `HomelabCertificateExpiresCritical` | Restlaufzeit ≤7 Tage (oder abgelaufen) | ≤7d / 15m | critical | Renewal sofort erzwingen/pruefen | | `HomelabBorgBackupStale` | letztes Borg-Backup >30h | warning | Backup-Lauf nachholen/pruefen |
| `HomelabBorgLastJobFailed` | letzter Borg-Job fehlgeschlagen | critical | Borg-UI-Job-Log pruefen |
### Gruppe `homelab-host` | `HomelabBorgLastJobCompletedWithWarnings` | letzter Borg-Job mit Warnungen | warning | Warnung im Borg-UI-Job lesen |
| `HomelabCriticalContainerDown` | kritischer Container fehlt | critical | Komodo/Docker-Status pruefen |
| Alarm | Trigger (PromQL, gekuerzt) | Schwelle / `for` | Severity | Was tun | | `HomelabPrometheusTargetDown` | Scrape-Ziel down | critical | node-exporter/cadvisor/blackbox/traefik pruefen |
|---|---|---|---|---|
| `HomelabDiskAlmostFull` | `100*(1-avail/size) > 85` (ohne tmpfs/overlay) | >85% / 10m | warning | Mountpoint aufraeumen / erweitern |
| `HomelabDiskCritical` | `100*(1-avail/size) > 95` (ohne tmpfs/overlay) | >95% / 5m | critical | Sofort Platz schaffen — Writes drohen zu scheitern (DB, appdata, Cache) |
| `HomelabHighMemoryUsage` | `100*(1-MemAvailable/MemTotal) > 90` | >90% / 10m | warning | Speicherfresser identifizieren, ggf. Container-Limit (F-19) |
| `HomelabTraefik5xx` | `increase(traefik_service_requests_total{5..}[5m]) >= 5` je Service | ≥5 / 2m | warning | Backend des betroffenen Service pruefen |
### Gruppe `homelab-backup-and-containers`
| Alarm | Trigger (PromQL, gekuerzt) | Schwelle / `for` | Severity | Was tun |
|---|---|---|---|---|
| `HomelabTextfileExporterStale` | `time()-last_run > 2h` | >2h / 15m | warning | `export-prometheus-textfile.sh`-Cron auf Host pruefen |
| `HomelabBorgMetricsMissing` | `absent(borg_last_completed_ts)` | fehlt / 15m | critical | Textfile-Export oder borg-ui pruefen |
| `HomelabBorgBackupStale` | `time()-borg_last_completed_ts > 30h` | >30h / 15m | warning | Letztes Borg-Backup nachholen/pruefen |
| `HomelabBorgLastJobFailed` | `borg_last_success != 1` | ≠1 / 15m | critical | Borg-UI-Job-Log pruefen, Backup wiederholen |
| `HomelabBorgLastJobCompletedWithWarnings` | `borg_last_job_warning == 1` | =1 / 15m | warning | Warnung im Borg-UI-Job lesen |
| `HomelabCriticalContainerDown` | `homelab_critical_container_running == 0` | =0 / 5m | critical | Container neu starten / Komodo-Stack pruefen (`name`-Label) |
Die Liste der ueberwachten Critical-Container steht in Die Liste der ueberwachten Critical-Container steht in
`services/posture-check/export-prometheus-textfile.sh` (`CRITICAL_CONTAINERS`). `services/posture-check/export-prometheus-textfile.sh`.
### Gruppe `homelab-meta` ## Bekannte Luecken
| Alarm | Trigger (PromQL, gekuerzt) | Schwelle / `for` | Severity | Was tun | - Kein externer Dead-Man's-Switch fuer Prometheus/ntfy-Bridge. Optional spaeter
|---|---|---|---|---| ueber Uptime-Kuma Push-Monitor oder Healthchecks.io.
| `HomelabPrometheusTargetDown` | `up == 0` | =0 / 5m | critical | Scrape-Ziel (node-exporter/cadvisor/blackbox/traefik) pruefen — Metriken sind sonst still | - Kein Inode-Alarm. Bei Paperless/Immich spaeter sinnvoll, aber aktuell kein
dokumentierter Vorfall.
## Bewertung: Sind die Alarme sinnvoll? - Container-Memory-Limits werden erst nach realen Peak-Daten gesetzt; OOM/kill
wird bereits ueber `docker-critical-events.sh` gemeldet.
Insgesamt solide. Die Erreichbarkeits-Gruppe ist gut entworfen — der
Sammelausfall-Trick (`>=5` Endpunkte als ein Warning, Einzelausfall als
Critical) verhindert eine ntfy-Flut bei kurzen DSL-Reconnects. Borg ist mit vier
Regeln (fehlende Metrik, veraltet, fehlgeschlagen, mit Warnungen) gut
abgedeckt.
Anmerkungen / Feinschliff (kein Handlungsdruck):
- **`HomelabDiskAlmostFull` ohne Array-Filter.** Der `fstype!~"tmpfs|overlay"`-
Filter schliesst keine bewusst vollen Unraid-Array-Disks aus. Eine
Datengrab-Disk, die dauerhaft bei 90 % liegt, erzeugt einen Dauer-Warning.
Bei Bedarf per `mountpoint`-Filter auf die wirklich kritischen Pfade
(`/`, appdata-/services-Cache) eingrenzen.
- **`HomelabEndpointSlow` >5s** ist grosszuegig und damit eher ruhig — okay als
bewusste Wahl, faengt aber keine schleichende 34s-Degradierung.
- **`HomelabHighMemoryUsage` 90 %** ist auf einem Host mit ZFS/Unraid-Cache
schnell erreicht (Cache zaehlt nicht als „available" je nach Messung); die
Verwendung von `MemAvailable` ist hier korrekt und mildert das.
## Bewertung: Fehlt etwas? (Luecken, priorisiert)
### Hoch — erledigt 2026-05-30
1. ~~Kein `up == 0` auf Scrape-Targets~~**`HomelabPrometheusTargetDown`**
umgesetzt (Gruppe `homelab-meta`). Faellt node-exporter/cadvisor/blackbox/
traefik aus, feuert jetzt nach 5 Minuten ein Critical.
2. ~~Kein Disk-Critical-Tier~~**`HomelabDiskCritical`** bei >95 % umgesetzt
(Gruppe `homelab-host`), zusaetzlich zum bestehenden Warning bei >85 %.
### Mittel — sinnvoll, aber kein Notstand
3. **Dead-Man's-Switch.** Faellt Prometheus oder die ntfy-Bridge selbst aus,
feuert gar kein Alarm — strukturell blind. Eine immer feuernde
Watchdog-Regel plus externer „Heartbeat fehlt"-Waechter (z. B. Uptime-Kuma
Push-Monitor oder Healthchecks.io) schliesst die Luecke. Bewusst leichtes
Gewicht, weil Posture-Check/Borg-Pre-Hook teilweise unabhaengig laufen.
4. **Inode-Erschoepfung.** Paperless/Immich erzeugen viele kleine Dateien;
`node_filesystem_files_free` kann vor dem Byte-Limit knapp werden. Niedrige
Wahrscheinlichkeit, billiger Alarm.
### Bewusst nicht in Prometheus (anderer Pfad deckt ab)
- **NVMe-SMART-Verschleiss** → `check_nvme_smart` im Posture-Check (ntfy direkt).
- **Compose-Runtime-Drift / Authelia-Drift** → Posture-Check (ntfy direkt).
- **Docker `oom`/`die`/`kill`** → `docker-critical-events.sh` (ntfy direkt) —
dies ist auch der Detektionspfad fuer den ersten echten OOM-Vorfall, der F-19
(Container-Memory-Limits) ausloesen wuerde.
- **Cert/Token-Health jenseits TLS-Ablauf** → `cert-token-check.sh`.
## Stand
Die zwei Hoch-Luecken sind seit 2026-05-30 in `alerts.yml` umgesetzt. Naechster
optionaler Schritt waere der Dead-Man's-Switch ueber einen externen Heartbeat-
Waechter; ohne familienkritischen Anlass aber nicht eilig.
+29 -129
View File
@@ -1,137 +1,37 @@
# Audit TODO 2026-05-25 # Audit-Restliste 2026-05-25
Quelle: `docs/archive/2026-05/AUDIT_2026-05-25.md` Status: **kompakte Restliste**. Die erledigten Sprint-Tabellen und langen
Audit-Snapshots wurden aus der Arbeitskopie entfernt; Detailhistorie liegt in Git.
Status: Arbeitsliste fuer die Umsetzung. Authelia-2FA/OIDC, CrowdSec und Nextcloud-2FA-Haertung bleiben ganz hinten und werden bewusst nicht in diesem Audit-Zyklus angefasst. ## Aktuell offene Punkte
## Leitplanken | Prioritaet | Punkt | Naechster Schritt |
- Authelia-2FA-ACL, Authelia-OIDC und CrowdSec werden in diesem Audit-Zyklus **nicht** umgesetzt (Operator-Vorgabe 2026-05-26).
- Keine Live-riskanten Bind-/Port-Aenderungen ohne vorher erfasste Host-Werte, insbesondere Tailscale-IP.
- Hermes-Agent ist geparkt, nicht entfernt; Review-Deadline 2026-07-25.
- USV-Anschaffung ist verschoben; Power-Loss-Risiko ist als Operator-Entscheidung 2026-05-26 bewusst akzeptiert.
- Borg-Passphrase ist offline gesichert (bestaetigt 2026-05-26).
- H:/ ist evaluiert als zweite lokale Nearline-Kopie, nicht als Offsite-Ersatz (siehe `docs/CAPACITY_AND_LIFECYCLE.md`).
- Familien-Einladung ist fuer das Wochenende **nach** Erreichen des finalen Stands geplant; Family-Onboarding muss familienverstaendlich werden, nicht technisch.
- Jede produktive Aenderung bekommt Validierung und Rollback-Hinweis.
## Naechster Startpunkt 2026-05-26
Kontext bewusst gesichert, bevor weitere Live-Aenderungen passieren:
1. Host-Schedule fuer Gitea-Bundles und Restore-Freshness pruefen.
2. FRITZ!Box-Portfreigaben (UI) gegen Repo-Soll abgleichen (`443/tcp` + `222/tcp`).
3. H:/ Pull-Workflow festlegen.
4. Family-Onboarding-Doku familienverstaendlich umarbeiten, vor der Wochenend-Einladung.
5. Authelia 2FA/OIDC und CrowdSec weiterhin nicht anfassen; bleibt bewusst der letzte Block.
## Sprint 0 - Inventar und Baseline
| Status | Aufgabe | Ergebnis |
|---|---|---| |---|---|---|
| erledigt | Hardware-Inventar ausfuellen | CPU, RAM, Mainboard, BIOS, NIC, Controller, Disks, SMART und Capacity-Baseline erfasst; USV ist als nicht validiert dokumentiert | | P0 | Borg-Lauf nach den Dumps vom 2026-05-31 21:42 bestaetigen | Nach dem naechsten regulaeren Lauf pruefen, ob die frischen Dumps im Hetzner-Borg-Archiv liegen |
| erledigt | Netzwerk-Inventar ausfuellen | Host-IP, Gateway, Tailscale-IP, AdGuard-Bind und FRITZ!Box-Baseline (7590, FRITZ!OS 8.21, Telekom DSL 87/36, 36 Geraete, Gast-WLAN inaktiv, Ausfallschutz inaktiv, 2 Portfreigaben aktiv) erfasst; IPv6 und FRITZ!OS-Update bleiben Operator-Folgeaufgaben | | P0 | Alt-Volumes nach Burn-in freigeben | Nicht vor 2026-06-02 loeschen; vorher aktiven Pfad und letzten Borg-Lauf pruefen |
| erledigt (Baseline) | Externe Abhaengigkeiten dokumentieren | `docs/EXTERNAL_DEPENDENCIES.md` enthaelt bekannte Provider, Kritikalitaet, Ausfallplaene; Account-Recovery-Codes/Zahlungswege bleiben Off-Repo-Operatorcheck | | P1 | Hetzner-Account-Hygiene | Starkes einzigartiges Passwort, Backup-Zahlungsweg und Login-Benachrichtigungen extern bestaetigen |
| erledigt (Baseline) | Services-Recovery-Pfade beschreiben | `docs/SERVICES_RECOVERY.md` enthaelt Gitea-/Komodo-/Secrets-Sonderpfade; Gitea-Bundle-Mechanik ist umgesetzt und per Host-Erstlauf validiert | | P1 | Borg `--append-only` fuer Hetzner pruefen | Rollback-faehigen Test fuer `borg serve --append-only` in Hetzner `authorized_keys` planen |
| erledigt | Baseline-Tag setzen | `audit-2026-05-25-baseline` ist lokal und remote vorhanden | | P1 | FRITZ!Box-Servicefenster | FRITZ!OS-Update, FRITZ!Box-Konfig-Backup und IPv6-Exposure-Pruefung gemeinsam erledigen |
| erledigt | Policy-Check neu ausfuehren | SEC001-Warnings aus altem Report sind nicht mehr aktuell | | P2 | Family-View Dashboard als JSON bauen | Erst nach manuellem Grafana-Layout-Check; Metriken fuer Borg, Certs und Critical-Container sind vorhanden |
| P2 | Family-Onboarding praktisch starten | Familienkonten, Vaultwarden-Organisation und Immich-Mobile-Backup gemeinsam einrichten |
## Sprint 1 - Nicht-kontroverse Sicherheits- und Repo-Hygiene ## Bewusst geparkt
| Status | Aufgabe | Ergebnis | | Punkt | Entscheidung |
|---|---|---| |---|---|
| erledigt | Borg-Passphrase analog sichern | Operator bestaetigt am 2026-05-26: Passphrase ist offline gesichert und ohne Host/Vaultwarden wiederherstellbar | | Authelia 2FA fuer Operator-UIs | In diesem Zyklus nicht umgesetzt; erst mit finaler Auth-Policy |
| erledigt (repo) | AdGuard Admin-Bind vorbereiten | Tailscale-IP `100.80.98.33` erfasst, Compose-Soll geaendert | | Authelia OIDC fuer Apps | Geparkt bis klare Familien-/SSO-Entscheidung |
| erledigt | AdGuard Admin-Port auf Tailscale-IP binden | Live validiert: `ss -ltnp` zeigt `100.80.98.33:8082`, DNS auf Port 53 funktioniert, LAN-Zugriff auf `192.168.178.58:8082` schlaegt fehl | | CrowdSec vor Traefik | Erst nach Auth-Policy neu bewerten |
| erledigt | Alte Monitoring-Verzeichnisse entfernen | `ops/grafana-influxdb/` und `ops/loki/` sind aus dem aktiven Repo entfernt; Rollback erfolgt ueber Git-Historie | | Nextcloud 2FA/Brute-Force-Haertung | Gemeinsam mit OIDC/Familienkonten entscheiden |
| erledigt | Komodo/Gitea-Restdrift bereinigen | alter Komodo-Stack `grafana` ist inert und ohne Repo-Pfad/Webhook; Gitea-Hook `35` und `komodo`-Self-Hook `11` sind inaktiv; aktive Gitea-Hooks haben keine Fehlstatus | | Hermes-Agent | NAS-Stack bleibt deaktiviert; Review-Deadline 2026-07-25 |
| erledigt | Policy-Warnings triagieren | Plex Host-Netz und digest-gepinnte mutable Tags sind dokumentierte Info-Ausnahmen; `monitoring-influxdb3-core` als Root-Ausnahme bleibt bewusst als Warning sichtbar | | USV | Anschaffung verschoben; Power-Loss-Risiko bewusst akzeptiert |
| Zweites Off-site-Ziel | Bewusst nicht umgesetzt; neu bewerten bei Hetzner-Problemen, stark wachsendem Datenwert oder geaenderter Betreiber-Praeferenz |
## Sprint 2 - Storage und Recovery verbindlich machen ## Zuletzt geschlossen
| Status | Aufgabe | Ergebnis | - Immich-, Paperless-, Gitea- und Vaultwarden-Restore-Pfade sind belegt.
|---|---|---| - H:/ Nearline-Pull laeuft seit 2026-05-28 als Windows Scheduled Task.
| erledigt | `docs/STORAGE_LAYOUT.draft.md` finalisieren | Datei als `docs/STORAGE_LAYOUT.md` Active v1.4 gefuehrt; Draft-Blocker entfernt | - FRITZ!Box-Portfreigaben sind bereinigt: WAN-seitig bleibt `443/tcp`.
| 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 | - InfluxDB 3 Core ist effektiv nur auf `127.0.0.1:8181` gebunden.
| erledigt | 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 live seit 2026-05-27 ueber User-Script `gitea-bundle-mirror-6h` (`10 */6 * * *`); Bundles werden mit `chmod 644` geschrieben damit der Nearline-Pull sie greift. | - Renovate ist produktiv, Major-Updates werden bewusst manuell entschieden.
| erledigt (Doku + Skript + Erstlauf) | 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 unter `ops/restore-tests/komodo-bootstrap-*` seit 2026-05-29 vorhanden, Erstlauf 2026-05-30 erfolgreich (siehe Sprint 8 Eintrag). | - Policy-Check bleibt ohne Criticals; bekannte Root-Ausnahmen sind dokumentiert.
| 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
| Status | Aufgabe | Ergebnis |
|---|---|---|
| erledigt | Immich-Restore-Test implementieren | Echter Host-Lauf 2026-05-27 erfolgreich: Borg-Archiv `Tägliche-Sicherung-2026-05-27T04:30:06.778`, `immich.dump` extrahiert, isolierter pgvecto-rs-Postgres importiert, Immich-Server ohne ML gestartet, HTTP `200`, Login-Marker ok, `11977` Assets und `1` User im Test-DB-Check; Report `/mnt/user/backups/restore-reports/immich-2026-05-27.md` |
| erledigt | Borg-Stale-Alert bauen | Cron `*/5 * * * *` (`export-prometheus-textfile-5min` User-Script) schreibt `homelab.prom`; node-exporter scraped, Prometheus laedt Regel `HomelabBorgBackupStale` aktiv. Live 2026-05-27 20:33 reloaded; `lastConfigTime: 2026-05-27T18:33:06Z`; Smoke-Query `(time() - homelab_borg_last_completed_timestamp_seconds)/3600 = 16h`. Borg-Job-Warning ist aktuell `pending` (Letzter Lauf `completed_with_warnings`). |
| erledigt | TLS-Cert-Expiry-Alert bauen | Regeln `HomelabCertificateExpiresSoon` (21d) und `HomelabCertificateExpiresCritical` (7d) sind in `alerts.yml` aktiv und nach Prometheus-Reload geladen. Smoke `inactive` (keine Cert <21d). |
| erledigt | Container-Down-Alert bauen | `HomelabCriticalContainerDown` aktiv; Live-Smoke 2026-05-27 `sum(homelab_critical_container_running) = 30`, alle aktuellen Critical-Container `1`. Aktualisierung alle 5 Min ueber Cron. |
| 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 |
|---|---|---|
| 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; zweites Off-site/Cold-Storage bewusst nicht umgesetzt |
| 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`) |
| erledigt 2026-05-28 | H:/ Groesse und Pull-Schedule festschreiben | Groesse erfasst: 8.0T NTFS, 3.91T belegt, 4.10T frei, `Healthy`. Erster echter Pull 2026-05-27 20:45 erfolgreich: 19 Borg-Dumps + 10 Gitea-Bundle-Files unter `H:\kallilab-nearline-backups`. `unraid-flash-config.*` bewusst ausserhalb Scope (`/XF`-Exclude, Restore aus Hetzner-Borg). Windows Scheduled Task `KalliLab H Drive Nearline Pull` laeuft seit 2026-05-28 taeglich 05:30. |
| erledigt 2026-05-28 | FRITZ!Box-Portfreigaben gegen Repo-Soll abgleichen | Bereinigt: `80/tcp` entfernt (Mobilfunk-validiert: HTTP timeout, HTTPS weiter erreichbar). `222/tcp` bleibt bewusst nicht eingerichtet (Tailscale-only-Linie). UPnP-Selbstfreigabe-Recht fuer `PC-192-168-178-71` (VONETS-Bridge, vermutlich SolarEdge-Wechselrichter) deaktiviert. Aktiver Endstand: ausschliesslich `443/tcp -> 192.168.178.58`. Details in `docs/archive/2026-05/FRITZBOX_PORT_CORRECTION_PLAN.md`. |
## Sprint 5 - Auth und Frontdoor, bewusst zuletzt
In diesem Audit-Zyklus werden diese Punkte **nicht** umgesetzt. Sie sind dokumentiert, damit sie bei einer kuenftigen Policy-Entscheidung sofort priorisiert werden koennen.
| Status | Aufgabe | Begruendung der Parkung |
|---|---|---|
| geparkt | Authelia 2FA fuer Operator-UIs erweitern (F-04) | Operator-Vorgabe 2026-05-26: keine Auth-Aenderungen in diesem Zyklus |
| geparkt | Authelia OIDC fuer Apps pruefen (F-13) | Operator-Vorgabe 2026-05-26: keine Auth-Aenderungen in diesem Zyklus |
| geparkt | CrowdSec vor Traefik pruefen (F-14) | Operator-Vorgabe 2026-05-26: erst nach finaler Auth-Policy |
| geparkt | Nextcloud-2FA-/Brute-Force-Haertung dokumentieren (F-18) | beruehrt Auth-Policy fuer Familien-Konten; gemeinsam mit OIDC-Entscheidung |
## Sprint 6 - Geparkte Apps und Folgeentscheidungen
| Status | Aufgabe | Naechster Pruefschritt |
|---|---|---|
| geparkt | Hermes-Agent (F-06) — Operator-Entscheidung produktiv vs. entfernen | Review-Deadline **2026-07-25**; bis dahin bleibt der NAS-Stack deaktiviert, das Repo-Verzeichnis erhalten, Dashboard-Domain und ACL-Eintrag unveraendert |
| erledigt 2026-05-28 | paperless-gpt / BentoPDF Nutzungsentscheidung | Operator behaelt beide. **paperless-gpt** bleibt bis Paperless-NGX 3.0 (erwartete native KI-Features); danach neu bewerten. **BentoPDF** bleibt als situatives Tool (Resource-Footprint ~4 MB). Beide ohne aktive Traefik-Zugriffe in der letzten Woche, aber bewusste Behalten-Entscheidung mit Begruendungs-Anker im SERVICE_CATALOG. |
| erledigt 2026-05-28 | Plex Remote Access in UI deaktivieren falls nur LAN/Tailscale (F-17) | Beim Versuch entdeckt: Server war seit 18.05. unclaimed und Bibliotheken leer. Reclaim als `Xeridos` via inline `PLEX_CLAIM`-Token, danach Bibliotheken (`/data/movies` 1.4 TB, `/data/Heimatfilme` 300 GB) neu angelegt und Remote Access deaktiviert (`PublishServerOnPlexOnlineKey=0`, Plex-Relay aus). Details in `HOMELAB_ARCHITECTURE_MASTER_V2.md` Sektion 13. |
| erledigt | `infra/redis` Doku-Etikett korrigieren (F-16) | SERVICE_CATALOG, REPO_MAP, MASTER (Sektion 13) und DISASTER_RECOVERY Bootstrap-Stufe 2 auf "primaer Paperless-Redis" praezisiert; keine Compose-Aenderung |
| erledigt | Paperless-DBPass DR-Restore-Reihenfolge in DR-Doc (F-20) | DISASTER_RECOVERY 6.2.1 (Restore-Quellen fuer Stack-ENV-Werte) ergaenzt; SECRETS_MAP um Abschnitt "Stack-ENV-only Secrets - Restore-Wege" mit Reihenfolge Komodo-Mongo-Dump -> Vaultwarden -> externe Notiz erweitert; Paperless, Immich, Mail-Archiver, Speedtest, Komodo, Hermes und Glance je mit Restore-Quelle dokumentiert |
## Sprint 8 - Reife der Stack-Hygiene (2026-05-29)
| Status | Aufgabe | Ergebnis |
|---|---|---|
| erledigt 2026-05-29 | Healthchecks fuer Tier-1 (F-15) | postgresql17 (`pg_isready`), Redis (`redis-cli ping` mit Auth), Vaultwarden (`curl /alive`), Gitea (`wget /api/healthz`), Traefik (`traefik healthcheck --ping`, `--ping=true` in CLI), Authelia (`wget /api/health`, weil v4.39 `helper health-check` entfernt hat); komodo-mongo war bereits gepinnt healthy. Live-Smoke: alle 6 healthy nach Recreate. Postgres- und Gitea-Stack-Workspace waren Komodo-seitig zurueckgeblieben (124 bzw. 52 commits behind); manuell per `cp` + `docker compose up -d` synchronisiert. |
| erledigt 2026-05-29 | Monitoring-Stack Digest-Pinning (F-07) | 9 Container in `monitoring/docker-compose.yml` per Tag@sha256 gepinnt: prometheus, alertmanager, alertmanager-ntfy-bridge (python:3.13-alpine), blackbox-exporter, loki, promtail, grafana, node-exporter, cadvisor. Digests aus dem aktuell laufenden Container ausgelesen, damit der Pin den Live-Stand reflektiert. influxdb3-core war bereits gepinnt. |
| erledigt 2026-05-29 (Skript) / 2026-05-30 (Erstlauf) | Komodo-Bootstrap-Trockenlauf-Skript (F-09 Rest) | `ops/restore-tests/komodo-bootstrap-{compose.test.yml,test.sh,plan.md,runbook.md}` analog zum Immich-Restore-Test angelegt. Test-Compose nutzt dieselben Image-Digests wie Produktion, isoliert unter Project `restoretest-komodo`, Test-Periphery ohne docker.sock-Mount, Test-Port nur `127.0.0.1:19120`. Wegwerf-Secrets im Compose. **Erstlauf 2026-05-30 erfolgreich**: Result `SUCCESS`, alle 5 Checks gruen — compose config valid, Test-Mongo healthy (6s), Mongo authenticated ping ok, Komodo Core HTTP `200`, Test-Periphery container state `running`. Report unter `/mnt/user/backups/restore-reports/komodo-bootstrap-2026-05-30.md`. Produktive Komodo-Container, Mongo-Datadir und Secrets nicht beruehrt. Damit ist `ops/komodo/docker-compose.yml` als Recovery-Anker belegt tauglich (nicht mehr nur angenommen). |
| erledigt 2026-05-29 | Renovate-Bot gegen Gitea (F-12) | Live: Service-Account `renovate` (uid 2, kein Admin) angelegt, Collaborator Write auf `Micha/homelab-infra`, PAT in `/mnt/user/appdata/secrets/renovate_token.txt` (chmod 600). Cron `renovate-six-hourly` (`20 */6 * * *`) live in `/etc/cron.d/root`. Erstlauf 2026-05-29 erfolgreich: 5 PRs (mongo digest+minor, postgres digest+minor, minor-and-patch-updates gruppiert), 1 Dependency-Dashboard-Issue, 8 Branches. Komodo-Major durch packageRule deaktiviert wie erwartet. Architektur-Detail: Repo-Config in `renovate.json`, Bot-Config in `ops/renovate/bot-config.js` (Renovate liest die im Repo nur als Repo-Config, Bot-Settings dort triggern "forbidden/disabled"). |
| erledigt 2026-05-30 | Authelia Repo<->Host Drift-Check (F-10) | `services/authelia-diff.sh` vergleicht die `access_control:`-Sektion zwischen Repo-Baseline und Host-Datei (Default; per env `AUTHELIA_DIFF_SECTIONS` erweiterbar). OIDC-Clients/Identity-Provider und Secret-Werte bleiben bewusst aussen vor. Exit-Codes: 0 = ok, 1 = Drift, 2 = Datei fehlt, 3 = Sektion fehlt, 4 = Werkzeug fehlt. Posture-Check ruft das Skript als Check `authelia_config_drift` auf (`SKIP_AUTHELIA_DRIFT=1` skippt, `AUTHELIA_DIFF_SCRIPT` ueberschreibt den Pfad); Drift wird als Warning gemeldet, nicht Critical. Smoke-Test lokal: identische Files -> rc=0, ACL-Drift im Domain-Eintrag -> rc=1 mit unified diff. WORKFLOW.md hat jetzt eine eigene Pflicht-Sektion "Ausnahme: Authelia configuration.yml" analog zur Traefik-Dynamic-Sektion. Pflicht-Setup auf dem Host: Repo-Spiegel unter `/mnt/user/services/homelab-infra/`. |
## Sprint 7 - Off-site und 3-2-1
| Status | Aufgabe | Bemerkung |
|---|---|---|
| erledigt 2026-05-28 (bewusst nicht umgesetzt) | Zweites echtes Off-site (F-03) | Operator-Entscheidung 2026-05-28: kein zweites Off-site. 3-2-1 ist mit Live + lokalem Borg + Hetzner + H:/-Nearline erfuellt; ein zweites Off-site wuerde nur den Fall "Hetzner-Account verloren" zusaetzlich abdecken, Aufwand/Kosten unverhaeltnismaessig fuer Familien-Homelab. Statt dessen drei Hetzner-Haertungen als Folge-TODOs (siehe `docs/OFFSITE_BACKUP_OPTIONS.md` Beschluss-Block). Review-Trigger sind dort definiert. |
| offen (Operator-Aufgabe) | Hetzner-Account-Hygiene ohne 2FA | Starkes, einzigartiges Passwort in Vaultwarden + Backup-Zahlungsweg + Login-Benachrichtigungen per E-Mail. 2FA bewusst nicht (analog USV: Risiko bewusst akzeptiert, Aufwand ueberwiegt Risiko-Reduktion fuer Familien-Homelab). |
| offen (Folge-Sprint) | Borg `--append-only` auf Hetzner setzen | Aktuell laeuft Repo `appdata-critical` im Mode `full`, `custom_flags` leer. Setup server-seitig in Hetzner `~/.ssh/authorized_keys` mit `command="borg serve --append-only"`. Schuetzt gegen Ransomware, die client-seitig Borg-Credentials abgreifen koennte. |
| erledigt 2026-05-28 | H:/-Pull als Windows Scheduled Task | Task `KalliLab H Drive Nearline Pull` registriert: taeglich 05:30, `RunLevel Limited`, `AllowStartIfOnBatteries`, `StartWhenAvailable`, `ExecutionTimeLimit 2h`. Naechster Lauf 2026-05-29 05:30. Erstlauf manuell 2026-05-27 20:45 erfolgreich, Task-Setup in `docs/H_DRIVE_NEARLINE_PULL.md` aktualisiert (RunLevel-Enum-Fix `LeastPrivilege` -> `Limited`). |
| 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
Diese Werte muessen am Unraid-Host erhoben werden, bevor die entsprechenden Aenderungen sauber umgesetzt werden:
```bash
hostname
cat /proc/cpuinfo | awk '/model name|flags/ {print; if(/flags/) exit}'
free -h
dmidecode -t baseboard | head -30
ip -br link
tailscale ip -4
lsblk -o NAME,SIZE,MODEL,SERIAL,FSTYPE,MOUNTPOINT,VENDOR
df -h /mnt/cache /mnt/disk1 /mnt/user
smartctl -a /dev/nvme0n1 | head -80
smartctl -a /dev/sdb | head -80
```
+1 -1
View File
@@ -15,7 +15,7 @@ Dieses Dokument haelt Wachstum, Schwellenwerte und Upgrade-Trigger fest. Es verh
| User Shares gesamt | 5.5T | 1.8T | 3.7T | 80 % Planung / 90 % Aktion | gruen, entspricht aktuell Disk1 | | User Shares gesamt | 5.5T | 1.8T | 3.7T | 80 % Planung / 90 % Aktion | gruen, entspricht aktuell Disk1 |
| Backups lokal | 5.5T geteilter Array-Space | 2.2G unter `/mnt/user/backups` | 3.7T Share-frei | Review bei Borg-/Dump-Wachstum | lokal nicht unabhaengig vom Array | | Backups lokal | 5.5T geteilter Array-Space | 2.2G unter `/mnt/user/backups` | 3.7T Share-frei | Review bei Borg-/Dump-Wachstum | lokal nicht unabhaengig vom Array |
| Hetzner Borg | extern / Storage Box | nicht repo-seitig gemessen | nicht repo-seitig gemessen | Borg-Stale-Alert + Account-Review | einziges echtes Off-site-Ziel | | Hetzner Borg | extern / Storage Box | nicht repo-seitig gemessen | nicht repo-seitig gemessen | Borg-Stale-Alert + Account-Review | einziges echtes Off-site-Ziel |
| Externe Cold-Platte | nicht vorhanden | - | - | Review nur bei Trigger | bewusst nicht beschafft, siehe `docs/OFFSITE_BACKUP_OPTIONS.md` | | Externe Cold-Platte | nicht vorhanden | - | - | Review nur bei Trigger | bewusst nicht beschafft; zweites Off-site erst bei Hetzner-Problemen, stark wachsendem Datenwert oder geaenderter Betreiber-Praeferenz |
Pruefkommando: Pruefkommando:
+1 -1
View File
@@ -44,7 +44,7 @@ Authoritativ ist `docs/SECRETS_MAP.md`. Diese Liste markiert nur externe Abhaeng
- Lokales Borg-Repo und aktuelle Dumps pruefen. - Lokales Borg-Repo und aktuelle Dumps pruefen.
- Keine destruktiven Host-Aenderungen starten, solange Off-site unklar ist. - Keine destruktiven Host-Aenderungen starten, solange Off-site unklar ist.
- H:/ Nearline-Pull als schnelle lokale Zweitkopie fuer kritische Restore-Artefakte nutzen. - H:/ Nearline-Pull als schnelle lokale Zweitkopie fuer kritische Restore-Artefakte nutzen.
- Zweites Off-site-Ziel nur bei Review-Trigger aus `docs/OFFSITE_BACKUP_OPTIONS.md` neu bewerten. - Zweites Off-site-Ziel nur neu bewerten bei Hetzner-Problemen, stark wachsendem Datenwert oder geaenderter Betreiber-Praeferenz.
### Cloudflare Account/DNS gestoert ### Cloudflare Account/DNS gestoert
-194
View File
@@ -1,194 +0,0 @@
# Family-View Dashboard - Spezifikation
Status: **Spezifikation (Doku-only)**, kein Grafana-JSON in diesem Schritt.
Audit-Bezug: `docs/archive/2026-05/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.
+1 -1
View File
@@ -19,7 +19,7 @@ Dieses Dokument beschreibt die physische Basis des Homelabs. Es ist die Grundlag
| Unraid-Version | 7.2.4 | | Unraid-Version | 7.2.4 |
| Rolle | Single-Host Homelab, Docker Compose via Komodo | | Rolle | Single-Host Homelab, Docker Compose via Komodo |
| Boot-Medium | Samsung Flash Drive, 59.8G, FAT32 | | Boot-Medium | Samsung Flash Drive, 59.8G, FAT32 |
| Flash-Backup | In Borg-Scope aufgenommen, siehe `docs/MIGRATION_LOG.md` | | Flash-Backup | In Borg-Scope aufgenommen, siehe `docs/RESTORE_MATRIX.md` |
## CPU ## CPU
-78
View File
@@ -1,78 +0,0 @@
# Immich Restore Test
Status: **erfolgreich live verifiziert** (2026-05-27)
Audit-Bezug: `docs/archive/2026-05/AUDIT_2026-05-25.md` Finding **F-11**
## Zweck
Schliesst die Audit-Luecke aus F-11: Immich ist der groesste Datentopf (Familien-Fotos), und bisher gibt es im Gegensatz zu Vaultwarden, Gitea und Paperless **keinen** verifizierten Mini-Restore-Test. Dieses Dokument verlinkt die Repo-Artefakte und beschreibt den Ablauf aus Operator-Sicht.
## Repo-Artefakte
| Datei | Zweck |
|---|---|
| `ops/restore-tests/immich-compose.test.yml` | isoliertes Test-Compose: Immich-Postgres mit VectorChord + Redis + Immich-Server, ML weggelassen, `127.0.0.1:12283` |
| `ops/restore-tests/immich-restore-test.sh` | Host-Bash-Skript fuer den Lauf, mit `--what-if` und `--keep-data` Flags |
| `ops/restore-tests/immich-restore-test.ps1` | Plan-Scaffold fuer Windows-Operator-Sicht (kein Live-Run) |
| `ops/restore-tests/immich-plan.md` | Fachlicher Plan: Quellen, Schutzregeln, Smoke-Test-Kriterien, bekannte Risiken |
| `ops/restore-tests/immich-runbook.md` | Konkreter Operator-Ablauf, Fehlerfaelle, Schedule-Vorschlag |
## Was der Test abdeckt
- Extraktion von `local/borg-dumps/latest/immich.dump` aus dem aktuellsten Borg-Archiv
- Import in eine isolierte `ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0` Postgres-Instanz mit demselben Digest wie Produktion
- Start eines isolierten Immich-Server-Containers mit demselben Digest wie Produktion, **ohne** ML-Container und **ohne** Traefik
- Smoke-Test: Login-Seite erreichbar, `asset`- und `"user"`-Tabelle lesbar
- Markdown-Report unter `/mnt/user/backups/restore-reports/immich-YYYY-MM-DD.md`
- Bereinigung von Test-Container und Restore-Lab-Daten nach Erfolg
## Was der Test bewusst NICHT abdeckt
- Wiederherstellung produktiver Foto-Dateien (`/mnt/user/photos/immich`, `/mnt/user/photos/family_archive`). Diese Pfade werden vom Test nicht angefasst und nicht in den Test-Container gemountet.
- Machine-Learning-Container. Spart Image-Pull-Zeit und RAM; ML-Features sind im Smoke-Test irrelevant.
- Echte Login-Flow per API. Smoke-Test prueft nur, dass Login-Seite ausgeliefert wird.
- Asset-Rendering / Thumbnail-Generierung. Ohne Foto-Files erwartet.
- Produktive Domain `immich.kaleschke.info`. Test laeuft ausschliesslich auf `127.0.0.1:12283`.
## Restore-Stufe
Der Test deckt **Stufe 4 (kritische Anwendungen)** aus `docs/DISASTER_RECOVERY.md` Phase 4 fuer Immich ab, allerdings nur DB-Ebene und UI-Smoke. Voll-Restore inklusive Foto-Dateien aus Borg ist eigener Folgeschritt; das Skript bereitet die Restore-Lab-Struktur dafuer vor.
## Erster Lauf und Preflight
| Pruefung | Verantwortlich | Wo |
|---|---|---|
| Dump-Groesse von `immich.dump` bestimmen | erledigt 2026-05-27 | 66M unter `/mnt/user/backups/borg/dumps/latest/immich.dump` |
| Freier Platz unter `/mnt/user/backups/restore-lab/` | erledigt 2026-05-27 | ca. 3.7T frei auf `/mnt/user/backups` |
| Borg-UI-Container laeuft | Operator | `docker ps | grep borg-ui` |
| Trockenlauf mit `--what-if` | erledigt 2026-05-27 | Host-Clone auf `c5d231a`, `bash ops/restore-tests/run-restore-checks.sh immich --what-if` erfolgreich |
| Erster echter Lauf | erledigt 2026-05-27 | Report `/mnt/user/backups/restore-reports/immich-2026-05-27.md`; Archiv `Tägliche-Sicherung-2026-05-27T04:30:06.778`; HTTP `200`; Assets `11977`; User `1` |
## Nach dem ersten erfolgreichen Lauf
1. Report unter `/mnt/user/backups/restore-reports/immich-2026-05-27.md` liegt vor und ist erfolgreich.
2. `docs/RESTORE_MATRIX.md`, `ops/restore-tests/schedule.md`, `docs/AUDIT_2026-05-25_TODO.md` und `docs/MIGRATION_LOG.md` wurden nachgezogen.
3. Quartalsweise Wiederholung einplanen; erster Live-Lauf bleibt bewusst manuell/Operator-kontrolliert, bis mehrere Laeufe stabil waren.
## Schutzregeln
- Skript greift ausschliesslich auf den Restore-Lab-Pfad und den Borg-Extract-Cache zu.
- Produktive Pfade unter `/mnt/user/photos/*`, `/mnt/user/appdata/immich_postgres_vectorchord/` und dem Rollback-Altstand `/mnt/user/appdata/immich_postgres/` werden nicht angefasst.
- Produktive Container `immich_server`, `immich_postgres`, `immich_redis`, `immich_machine_learning` werden nicht gestoppt, nicht beruehrt.
- Borg-Passphrase wird aus `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` gelesen und nicht in Reports, Logs oder Doku geschrieben.
- Test-Container publishen nur auf `127.0.0.1:12283`, nicht auf LAN- oder Tailscale-Interface.
- Keine Traefik-Labels, keine Public-URL fuer Testcontainer.
## Risiken (aus `ops/restore-tests/immich-plan.md`)
- Dump-Groesse und erster `pg_restore`-Lauf sind gemessen: `immich.dump` 66M, echter Smoke-Test erfolgreich am 2026-05-27.
- VectorChord-/pgvector-Extension-Mismatch bei Image-Drift moeglich; Compose pinnt denselben Digest wie Produktion.
- Immich nutzt nach der VectorChord-Migration `vchord 0.4.3` und `vector 0.8.1`; Restore-Tests muessen ein Image mit VectorChord verwenden.
- Immich-Server-Migrations koennen Startup nach Restore verzoegern; Skript pollt 120 s.
- Bei Schema-Drift (z. B. nach Major-Update) koennen einzelne DB-Queries abweichen; das Skript versucht Immich-v2-Singular-Tabellen und aeltere Plural-Fallbacks.
## Naechste Operator-Schritte
1. Quartalsweise Wiederholung nach `ops/restore-tests/schedule.md` einplanen.
2. Bei zukuenftigem Immich-Major-Upgrade den Restore-Test unmittelbar danach einmal manuell ausfuehren.
3. Voll-Restore inklusive Foto-Dateien bleibt ein eigener, deutlich groesserer DR-Drill.
-688
View File
@@ -1,688 +0,0 @@
# Migration Log - Homelab GitOps
Dieses Dokument ist nur noch ein historischer Verlauf. Der aktuelle operative Ablauf steht in `docs/WORKFLOW.md`, das Zielbild in `HOMELAB_ARCHITECTURE_MASTER_V2.md`.
## Aktueller Endstand
- Gitea Online ist der verbindliche Sollzustand.
- Komodo ist der einzige produktive Stack-Manager.
- Portainer CE ist entfernt.
- Firefly, Firefly-Fints und Semaphore sind entfernt.
- `monitoring/` ist der einzige aktive Observability-Stack; alte Repo-Pfade `ops/grafana-influxdb` und `ops/loki` sind entfernt.
- Borg UI ist produktiv, Dump-Automatisierung laeuft host-seitig und ein Restore-Smoke-Test wurde erfolgreich durchgefuehrt.
- GitHub Desktop ist der bevorzugte lokale Workflow fuer `Fetch`, `Pull`, `Commit` und `Push`.
- Mutable Image-Tags sind auf die aktuell laufenden Digests eingefroren.
---
## Historische Meilensteine
### 2026-05-31 - Doku-Restliste bereinigt und Paperless-Restore-Drill nachgezogen
Nach dem Doku-Archiv-Sweep wurden die verbliebenen aktiven offenen Punkte bereinigt: Offsite-Beschluss, H:/-Nearline-Status, Capacity-Zeitziele und Netzwerk-Inventar wurden auf den tatsaechlichen Stand gebracht.
- H:/ Nearline-Pull ist seit 2026-05-28 produktiv; alte offene Beschaffungs-/Schedule-Punkte fuer ein zweites Offsite-Ziel wurden als bewusst nicht umgesetzt bzw. Review-Trigger dokumentiert.
- InfluxDB 3 Core Port `8181` ist effektiv nur auf `127.0.0.1` gebunden; keine LAN-Exposure.
- Echter Paperless-Restore-Drill erfolgreich: Borg-Archiv `Taegliche-Sicherung-2026-05-31T04:30:13.181`, isolierte Testcontainer `restoretest-paperless`, `restoretest-paperless-postgres`, `restoretest-paperless-redis`, HTTP `200`, Login-Marker ok, `32` Dokumente im Test-DB-Check. Report: `/mnt/user/backups/restore-reports/paperless-2026-05-31.md`.
- Cleanup verifiziert: keine `restoretest-paperless*` Container mehr, Restore-Lab-Pfad `/mnt/user/backups/restore-lab/paperless` entfernt.
### 2026-05-31 - Komodo-Mongo Major-Upgrade auf MongoDB 8.0
Komodo-Mongo wurde kontrolliert von `mongo:7.0.34` auf `mongo:8.0.23@sha256:44aa79ae28ff80b56fe58681b66cda9336706df408a5175a6c04988aa54610d3` gehoben. Die Renovate-Branch `renovate/mongo-8.x` wurde nicht direkt gemerged, weil sie veraltet war und Dokumentationsstand aus `master` zurueckgedreht haette; ausserdem schlug sie `8.3.2` vor. Nach Pruefung der MongoDB-Versionierung wurde bewusst die Major-Release-Schiene `8.0.x` gewaehlt.
- Vorabtest: frischer Dump `/mnt/user/backups/borg/dumps/latest/komodo-mongo-pre-major-20260531-142155.archive.gz` wurde in isolierten Container `restoretest-komodo-mongo8-20260531-142155` mit MongoDB `8.0.23` restored; 24 Collections, ca. 88k Objekte, keine ungueltigen `system.buckets`; Report: `/mnt/user/backups/restore-reports/komodo-mongo8-20260531-142155.md`.
- Produktiv-Backups vor Rollout: Compose `/mnt/user/appdata/komodo/_workspace_backups/komodo-compose-before-mongo8-20260531-142411.yaml`, Dump `/mnt/user/backups/borg/dumps/latest/komodo-mongo-pre-major-prod-20260531-142411.archive.gz`.
- Rollout: Host-Mirror `/mnt/user/services/homelab-infra` auf Commit `59b9392` fast-forwarded, Self-Stack-Compose nach `/mnt/user/services/stacks/komodo/compose.yaml` synchronisiert, `komodo-core` fuer den DB-Binary-Wechsel gestoppt, `komodo-mongo` mit `docker compose up -d --no-deps komodo-mongo` recreated und danach Core/Periphery wieder gestartet.
- Smoke nach 5 Minuten: `komodo-mongo` healthy auf `mongo:8.0.23`, `komodo-core` up, `komodo.kaleschke.info` HTTP `200`, Mongo-Ping `1`, Version `8.0.23`, FCV bleibt bewusst `7.0`, Core- und Mongo-Logs in sauberem Nachlauf ohne neue Fehler.
- Watchpoint: FCV nicht sofort auf `8.0` setzen; erst nach Burn-in und expliziter Downgrade-Entscheidung. `renovate.json` begrenzt Komodo-Mongo gezielt auf `8.0.x`, damit Renovate weiter 8.0-Patches liefern kann, aber nicht automatisch auf den 8.2+/8.3-Minor-Track zieht. Renovate-PR #8 wurde mit Kommentar geschlossen, weil die Branch noch `8.3.2` vorschlug.
### 2026-05-31 - Renovate-Cron Folgelauf und Branch-Hygiene
Nach dem Merge der ersten fuenf Renovate-PRs lief der Cron am 2026-05-31 12:20 MESZ erfolgreich (`rc=0`), aber die erledigten Remote-Branches existierten noch. Diese fuenf Branches waren vollstaendig in `origin/master` enthalten und wurden remote geloescht: `renovate/mongo-7.0.32`, `renovate/postgres-17.9`, `renovate/minor-patch-updates`, `renovate/mongo-7.x`, `renovate/postgres-17.x`.
- Manueller Kontrolllauf danach: `bash /mnt/user/services/homelab-infra/ops/renovate/run-renovate.sh`, Ergebnis `rc=0`.
- Neue offene PRs: #7 Grafana 13, #8 Mongo 8, #9 Postgres 18, #10 Redis 8.
- Keine Minor-/Patch-Nachzuegler offen; Dependency Dashboard #6 wurde aktualisiert.
- Verifiziert: `komodo-core` und `komodo-periphery` tauchen in keinem Major-PR-Diff auf; die Renovate-Package-Rule gegen Komodo-Major greift.
- Watchpoint: #7-#10 bleiben reine Major-Entscheidungsvorlagen und werden nicht automatisch gemerged.
### 2026-05-31 - Komodo Deploy-Drift strukturell abgesichert
Nach dem Renovate-Block wurde die Ursache fuer den kurzzeitigen `nextcloud-postgres`-Drift nachgezogen: Komodo hatte den `nextcloud`-Stack beim Postgres-17.10-Deploy gestartet, aber `docker compose up -d` scheiterte zunaechst an einem Docker-Recreate-Namenskonflikt (`nextcloud-postgres` Ersatzcontainer). Der Workspace war dadurch bereits auf dem neuen Commit, waehrend der laufende DB-Container noch das alte Image nutzte. Der spaetere gezielte `docker compose up -d` aus dem aktualisierten Workspace hat den Zwischenzustand sauber aufgeloest; es blieben keine exited/dead Containerreste.
- Komodo-Update-Historie bestaetigt: ein fehlgeschlagener `DeployStack` fuer `nextcloud` mit Compose-Up-Konflikt, danach ein erfolgreicher `DeployStack`.
- Aktueller Runtime-Stand nach Pruefung: `nextcloud`, `nextcloud-postgres`, `nextcloud-redis`, `postgresql17`, `mealie-postgres`, `gitea`, `bentopdf`, `ddns-updater` und Komodo-Self-Stack laufen ohne `unhealthy`-Status; die erwarteten Images stimmen mit den Compose-Dateien ueberein.
- `services/posture-check/export-prometheus-textfile.sh` exportiert jetzt `homelab_gitops_runtime_image_match{name,project,service}` fuer laufende Compose-Container. Die Metrik vergleicht das Image aus `docker compose config --format json` gegen `docker inspect .Config.Image` des laufenden Containers und faengt damit genau den Zustand "Workspace/Compose neu, Runtime alt" ab.
- Neue Prometheus-Regel `HomelabGitOpsRuntimeImageDrift`: feuert als Warning, wenn ein laufender Compose-Container laenger als 10 Minuten nicht dem Compose-Image entspricht.
- Smoke: Exporter-Test in `/tmp/kallilab-textfile-test/homelab.prom` lieferte fuer alle erkannten Compose-Container `homelab_gitops_runtime_image_match = 1`; `promtool check rules` meldete `SUCCESS: 17 rules found`.
- Beim Live-Reload zeigte Prometheus nach dem Git-Pull einen `stale file handle` auf die bind-gemountete `alerts.yml`. Fix: nur `monitoring-prometheus` aus dem aktuellen Monitoring-Workspace per `docker compose up -d --force-recreate --no-deps prometheus` neu erstellt. Danach: `promtool check rules` erfolgreich, Lifecycle-Reload erfolgreich, Regel `HomelabGitOpsRuntimeImageDrift` geladen und `inactive`.
### 2026-05-31 - Renovate PRs #1 bis #5 gemerged und deployed
Die ersten fuenf Renovate-PRs wurden einzeln in `master` uebernommen, mit Policy-Check und Live-Smoke nach den Datenhalter-Aenderungen. Major-Branches wurden bewusst nicht gemerged.
- #1 `renovate/mongo-7.0.32`: Mongo-Digest fuer Komodo uebernommen, Merge-Commit `b8b0af9`.
- #2 `renovate/postgres-17.9`: Postgres-17.9-Digest fuer `postgresql17`, `mealie-postgres` und `nextcloud-postgres` uebernommen, Merge-Commit `db1fa7c`.
- #3 `renovate/minor-patch-updates`: gruppierte Minor-/Patch-Updates uebernommen, Merge-Commit `dde4419`. Danach wurden die Komodo-Workspaces `gitea`, `bentopdf` und `ddns-updater` wegen Drift gezielt gesichert, auf `origin/master` synchronisiert und neu deployed. Backups liegen unter `/mnt/user/appdata/komodo/_workspace_backups/*-workspace-before-renovate-pr3-resync-*.tar.gz`.
- #4 `renovate/mongo-7.x`: Komodo-Mongo von `7.0.32` auf `7.0.34` gehoben, Merge-Commit `076676d`. Da der Komodo-Self-Stack nicht ueber einen normalen Git-Webhook redeployed, wurde vorher ein Compose-Backup (`/mnt/user/appdata/komodo/_workspace_backups/komodo-compose-before-renovate-20260531-125102.yaml`) und ein frischer Mongo-Dump (`/mnt/user/backups/borg/dumps/latest/komodo-mongo-pre-renovate-20260531-125102.archive.gz`) erstellt, danach der Self-Stack kontrolliert aktualisiert.
- #5 `renovate/postgres-17.x`: Postgres von `17.9` auf `17.10` fuer `postgresql17`, `mealie-postgres` und `nextcloud-postgres` gehoben, Merge-Commit `96fcacc`. `postgresql17` und `mealie-postgres` wurden durch Komodo recreated; `nextcloud-postgres` musste aus dem bereits aktualisierten Workspace `/mnt/user/services/stacks/nextcloud/apps/nextcloud` einmal gezielt mit `docker compose up -d` nachgezogen werden.
- `ops/policy-checks/check_repo.ps1` blieb nach den Merge-Commits ohne Criticals; einzige Warning ist weiterhin die dokumentierte InfluxDB-root-Ausnahme.
- Smoke-Beleg nach Settle: `postgresql17` healthy auf `postgres:17.10@sha256:0027bef...`, `mealie-postgres` und `nextcloud-postgres` laufen auf demselben `17.10`-Digest, `mealie.kaleschke.info` HTTP `200`, `cloud.kaleschke.info` HTTP `302`, `git.kaleschke.info` HTTP `200`, `komodo.kaleschke.info` HTTP `200`, keine `unhealthy` Container.
- Gitea listete #1 bis #5 nach den lokalen Merge-Commits noch als offen. Jeder PR bekam per API einen Kommentar mit dem manuellen Merge-Commit und wurde geschlossen, damit Renovate keinen offenen Altstand behaelt.
- Watchpoint: Renovate-Branches `mongo-8.x`, `postgres-18.x`, `redis-8.x` und `major-major-updates` bleiben bewusst ungemerged und brauchen separate Operator-Entscheidung.
### 2026-05-31 - Gitea Komodo-Workspace-Drift bereinigt
Der Komodo-Workspace fuer den `gitea`-Stack unter `/mnt/user/services/stacks/gitea` war auf `1d0cba9` stehengeblieben, 70 Commits hinter `origin/master`, mit 23 untracked Pfaden. Dadurch war die von Docker genutzte Compose-Datei nicht identisch mit dem aktuellen Repo-Stand (`core/gitea/docker-compose.yml`), obwohl der Gitea-Komodo-Webhook aktiv war.
- Vor der Bereinigung wurde der komplette Workspace gesichert: `/mnt/user/appdata/komodo/_workspace_backups/gitea-workspace-before-resync-20260531-122515.tar.gz`.
- Die untracked Pfade wurden einzeln gegen `origin/master` geprueft. 18 Eintraege waren byte-identisch, vier Doku-Dateien waren aeltere Zwischenstaende, und `ops/h-drive-nearline/pull-critical-backups.ps1` war ebenfalls identisch. Es gab keinen nur-im-Workspace-gueltigen neueren Arbeitsstand.
- Danach wurden nur die bekannten untracked Konfliktpfade aus dem Workspace entfernt und `git pull --ff-only origin master` ausgefuehrt. Ergebnis: `stacks/gitea` steht sauber auf `e6a0e9f`, `## master...origin/master`, ohne Dirty-State.
- `docker compose up -d` aus `/mnt/user/services/stacks/gitea/core/gitea` lief ohne Recreate-Zwang; der laufende Container nutzt weiterhin den Stack-Workspace als Compose-Quelle und ist `healthy`.
- Smoke-Test: `docker exec gitea wget -qO- http://localhost:3000/api/healthz` liefert `status: pass`, `https://git.kaleschke.info` liefert HTTP `200`, und die lokale Gitea-API fuer `Micha/homelab-infra` antwortet mit `default_branch: master`.
- Watchpoint: Gitea ist ein Henne-Ei-Stack, weil der Dienst sein eigenes Git-Origin bereitstellt. Webhook und `auto_pull` sind aktiv, aber Workspace-Drift muss bei kuenftigen Gitea-Aenderungen besonders bewusst geprueft werden; kein pauschales `git clean -fd` ohne vorherige Sicherung und Vergleich.
### 2026-05-31 - Komodo 5xx-Spam eingegrenzt: LAN-Client statt Stack-Fehler
`HomelabTraefik5xx` feuerte fuer `service="komodo@docker"`, weil wiederkehrende Komodo-UI-API-Requests ohne gueltige Session (`GET /user`, zeitweise `POST /read/GetCoreInfo`) von Traefik als 500 gezaehlt wurden. Komodo Core selbst loggte keine internen Fehler; die 500-Antwort ist ein Komodo-Auth-Pfad-Bug-on-top, aber nicht die primaere Betriebsstoerung.
- Bestaetigt: Blackbox-Exporter erklaert nur `GET /` alle 15s. Waehrend `monitoring-blackbox-exporter` gestoppt war, verschwanden die `/`-200-Probes, `/user`-500 lief aber weiter.
- Ausgeschlossen: `cert-token-check.sh` prueft keine Komodo-Domain; Komodo Periphery war nach 130s Stop nicht die Quelle; Glance war bereits vorab durch Stop-Test ausgeschlossen.
- Core-Isolation: Bei gestopptem `komodo-core` liefen die Client-Requests weiter, aber Traefik loggte sie als 404 ohne `komodo@docker`-Service. Nach Core-Start wurden dieselben Requests wieder zu `komodo@docker`-500. Damit ist die Quelle ein LAN-/Client-Geraet, nicht Komodo Core als Self-Poll.
- Lokale Client-Suche: Auf dem Windows-Operator-PC `192.168.178.103` bestanden HTTPS-Verbindungen zur WAN-IP `217.249.121.39`. Brave war zunaechst plausibel, weil die Brave-Session alte Komodo-Tabs enthielt; ein Brave-Schluss beendete den 5xx-Takt jedoch nicht. Danach blieb als lokaler Kandidat nur `Codex.exe` mit Verbindung zur WAN-IP. Der in-app Browser zeigte keine offene Seite, daher ist der operative Fix: Codex-App/Thread nach Abschluss schliessen bzw. neu starten; falls der Takt danach wider Erwarten weiterlaeuft, naechster Schritt ist LAN-Geraetesuche am Router/Switch statt Repo-Aenderung.
- Kein Repo-/Komodo-Fix umgesetzt: Monitoring-Regel und Komodo-Compose bleiben unveraendert. Ein Alert-Exclude fuer `komodo@docker` waere nur ein letzter Ausweg und wurde nicht gesetzt.
- Smoke-Beleg waehrend der Eingrenzung: `traefik`, `komodo-core`, `komodo-periphery`, `komodo-mongo` und `monitoring-blackbox-exporter` liefen nach den Stop/Start-Tests wieder; `komodo-mongo` und `traefik` waren healthy.
### 2026-05-30 - Komodo-Bootstrap-Trockenlauf Erstlauf (F-09 Rest abgeschlossen)
Skript ist seit 2026-05-29 vorbereitet, heute erster echter Lauf auf dem Host.
- Aufruf: `bash /mnt/user/services/homelab-infra/ops/restore-tests/komodo-bootstrap-test.sh --keep-data`
- Vorlauf: `--what-if` zur Plan-Verifikation, danach echter Lauf, beides ohne Eingriff in den produktiven Komodo-Stack.
- Ergebnis: `SUCCESS`, alle 5 Smoke-Checks gruen.
- `docker compose config valid: ok`
- `Test-Mongo healthy: ok` (Mongo healthy in ~6 s)
- `Mongo authenticated ping (Test-Creds): ok`
- `Komodo Core HTTP status: 200` (Login-Seite ausgeliefert)
- `Test-Periphery container state: running`
- Report: `/mnt/user/backups/restore-reports/komodo-bootstrap-2026-05-30.md`
- Isolation hielt wie geplant: produktive Container `komodo-mongo`, `komodo-core`, `komodo-periphery` unter Project `komodo` blieben unangetastet, ebenso `/mnt/user/appdata/komodo/{mongo,core,periphery}` und die produktiven `KOMODO_*`-Secrets. Test lief unter Project `restoretest-komodo` mit Wegwerf-Datadir `/mnt/user/backups/restore-lab/komodo/`, Wegwerf-Secrets im Test-Compose und Test-Port nur auf `127.0.0.1:19120`.
- Operator-Klick bewusst nicht von Claude uebernommen: `ssh root@kallilabcore` ist eine Aktionsklasse, die in CLAUDE.md ausdruecklich Operator-Anweisung verlangt. Der Auto-Mode-Classifier hat einen nicht-destruktiven SSH-Probe entsprechend blockiert. Der Operator hat den Befehl im Unraid-Webterminal selbst gestartet.
- Bedeutung: `ops/komodo/docker-compose.yml` ist als Recovery-Anker fuer die Bootstrap-Stufen A-F in `docs/SERVICES_RECOVERY.md` jetzt **belegt** tauglich, nicht mehr nur angenommen tauglich. Image-Digests (mongo:7.0.32, komodo-core:2, komodo-periphery:2) und Mongo-Auth-Schema sind verifiziert.
- Lab-Daten unter `/mnt/user/backups/restore-lab/komodo/` bleiben mit `--keep-data` erhalten, Test-Container wurden im EXIT-Trap sauber abgeraeumt. Operator entscheidet, ob das Lab-Verzeichnis (~300 MB) entfernt wird.
Folgeschritt fuer `docs/RESTORE_DRILL_ROUTINE.md`: Komodo-Bootstrap-Trockenlauf passt zum quartalsweisen DR-Sanity-Check (Q4) oder als wiederholbarer Standalone-Drill. Aktuell kein Host-Schedule, Aufruf bleibt manuell.
### 2026-05-30 - F-10: Authelia Repo<->Host Drift-Check
Der dokumentierte "by-design"-Drift zwischen `security/authelia/configuration.yml` (Repo-Baseline) und `/mnt/user/appdata/authelia/config/configuration.yml` (Host) wird jetzt automatisch ueberwacht. Vorher: Manueller Merge auf den Host war Pflicht, aber keine Pruefung. Eine vergessene ACL-Synchronisation waere erst bei einem Login-Fehler aufgefallen.
- Neues Skript `services/authelia-diff.sh`: extrahiert die `access_control:`-Sektion aus beiden YAMLs per awk-Block-Extractor (Top-Level-Key bis zum naechsten Top-Level-Key), normalisiert Kommentar- und Leerzeilen, vergleicht via `diff -u`. Default-Sektion ist `access_control`, weil das laut F-10 der primaere Drift-Vektor ist; per env `AUTHELIA_DIFF_SECTIONS` koennen weitere Top-Level-Sektionen (`session`, `regulation`, `totp`, ...) ergaenzt werden. OIDC-Clients, Identity-Provider und Secret-Werte bleiben bewusst aussen vor.
- Exit-Code-Schema: 0 = ok, 1 = Drift (Diff auf stdout), 2 = Datei fehlt, 3 = Sektion fehlt, 4 = Werkzeug fehlt. Macht das Skript auch standalone nutzbar (`ssh kallilab "bash /mnt/user/services/homelab-infra/services/authelia-diff.sh"`).
- `services/posture-check/posture-check.sh` ruft das Skript am Ende des Checks-Blocks auf (`check_authelia_config_drift`). Drift wird als **Warning** gemeldet, nicht Critical, weil die produktive Authelia trotz Drift weiter laeuft und die ACL fuer schon angemeldete Sessions weiter wirkt. Skip-Mechanismus: `SKIP_AUTHELIA_DRIFT=1`. Pfad-Override: `AUTHELIA_DIFF_SCRIPT`.
- Pflicht-Setup auf dem Host: Repo-Spiegel unter `/mnt/user/services/homelab-infra/` als read-only-Clone von Gitea `Micha/homelab-infra` mit regelmaessigem `git pull --ff-only`. Default-Pfade des Skripts setzen das voraus. Ohne Repo-Spiegel meldet der Check Warning, weil die Baseline-Datei fehlt - keine stille Inaktivierung.
- Lokaler Smoke-Test 2026-05-30 erfolgreich: identische Files -> rc=0; ACL-Drift im Domain-Eintrag `scrutiny.kaleschke.info -> scrutiny-renamed.kaleschke.info` -> rc=1 mit unified diff, ACL-Block korrekt extrahiert, Kommentar- und Leerzeilen rausgefiltert. False-Positive auf `session.default_redirection_url`-Aenderung korrekt vermieden (gehoert nicht zu `access_control`).
- `docs/WORKFLOW.md` hat jetzt eine eigene Sektion "Ausnahme: Authelia configuration.yml" analog zur Traefik-Dynamic-Sektion. Pflicht-Workflow: 1. Repo-Aenderung + Commit + Push, 2. manueller Merge in die Host-Datei mit Erhalt der OIDC-Sektionen, 3. `docker restart authelia` + Login-Smoke-Test, 4. `services/authelia-diff.sh` muss `exit 0` liefern.
- `docs/REPO_MAP.md` und `docs/SERVICE_CATALOG.md` zeigen das Skript und den neuen Posture-Check-Eintrag.
Operator-Folgeschritt (klein, nicht heute): Repo-Spiegel `/mnt/user/services/homelab-infra/` auf dem Host einrichten und in den vorhandenen `gitea-bundle-mirror-6h`-Plan oder einen eigenen 6h-Cron einbinden, damit das Skript einen aktuellen Vergleichsstand findet.
### 2026-05-29 - Stack-Hygiene Sprint: Healthchecks, Monitoring-Digests, Komodo-Bootstrap-Skript, Renovate-Vorbereitung
Vier Audit-Punkte am Stueck abgearbeitet. Pro Block: Live-Verifikation am Host, Doku im Repo.
**F-15 Tier-1 Healthchecks**
- 6 Tier-1-Stacks bekommen Healthchecks: postgresql17 (`pg_isready`), Redis (`redis-cli ping` mit Auth aus dem mount), Vaultwarden (`curl /alive`), Gitea (`wget /api/healthz`), Traefik (`traefik healthcheck --ping`, vorher `--ping=true` in CLI aktiviert), Authelia (`wget /api/health` - Authelia v4.39 hat `helper health-check` entfernt, daher direkter Endpoint).
- Erste Iteration in Vaultwarden + Authelia schlug fehl: Vaultwarden hat kein `wget`, Authelia kennt das `helper`-Subcommand nicht mehr. Probe per `docker exec` zeigte: Vaultwarden hat `curl`, Authelia hat `wget`. Compose entsprechend nachgezogen, zweiter Lauf gruen.
- Komodo-Stack-Workspaces fuer `postgresql17` (124 commits behind) und `gitea` (52 commits behind) wurden Komodo-seitig nicht automatisch gepullt. Manuell ueber `git pull --ff-only` plus `cp` der aktuellen Compose-Datei aus dem Host-Repo-Clone in den Stack-Workspace synchronisiert, dann `docker compose up -d`. Gitea-Workspace hatte zusaetzlich untracked Doku-Files; nur die im aktuellen Master tracked-en Files entfernt, nicht via `git clean -fd`. Workspace-Drift selbst ist nicht heute Auftrag, aber als Folge-Befund notiert.
- Endstand Live: alle 6 Healthchecks `healthy`.
**F-07 Monitoring-Stack Digest-Pinning**
- 9 Container in `monitoring/docker-compose.yml` per Tag@sha256 gepinnt (prometheus, alertmanager, alertmanager-ntfy-bridge, blackbox-exporter, loki, promtail, grafana, node-exporter, cadvisor; plus zweiter python:3.13-alpine im Bootstrap-Dashboard-Importer). InfluxDB war bereits gepinnt.
- Digests aus den laufenden Containern per `docker inspect ... .Config.Image` + `docker image inspect ... .RepoDigests` ausgelesen, damit die Pins exakt dem Live-Stand entsprechen.
- Kein Recreate ausgeloest, weil die Images identisch sind; nur die Compose-Datei ist jetzt reproduzierbar wie die Tier-1-Stateful-Stacks.
**F-09 Rest - Komodo-Bootstrap-Trockenlauf-Skript**
- `ops/restore-tests/komodo-bootstrap-{compose.test.yml,test.sh,plan.md,runbook.md}` analog zum Immich-Restore-Test-Muster angelegt.
- Test-Compose nutzt dieselben Image-Digests wie Produktion (mongo:7.0.32, komodo-core:2, komodo-periphery:2), isoliert unter Compose-Project `restoretest-komodo`, Test-Port nur `127.0.0.1:19120`, **Test-Periphery ohne docker.sock-Mount und ohne `/mnt/user/services`-Mount** - kann produktive Container nicht managen.
- Wegwerf-Secrets sind im Compose hardcodiert, produktive `KOMODO_*`-Werte werden nicht beruehrt.
- Smoke-Test-Kriterien: docker compose config valid, Mongo healthy, Mongo Auth-Ping ok, Core HTTP 200/302/303/401, Periphery container `running`.
- Erster Lauf bleibt manueller Operator-Schritt.
**F-12 Renovate-Bot (live)**
- Repo-Config in `renovate.json` (Repo-Root): nur extends, packageRules, ignorePaths, manager file patterns, labels, rangeStrategy. Bot-Config separat in `ops/renovate/bot-config.js`: platform, endpoint, autodiscover=false, repositories=["Micha/homelab-infra"], gitAuthor, Concurrent-Limits. Trennung war noetig: Renovate liest die `renovate.json` im Repo als REPO-Config; Bot-Felder darin wurden als "this repo is disabled" fehlinterpretiert (Repository result: forbidden, status: disabled).
- `ops/renovate/run-renovate.sh` als One-Shot-Container-Wrapper. Wichtige Haertungen waehrend des Setups:
- `--add-host git.kaleschke.info:192.168.178.58`: Renovate-Container kann den Hostname sonst nicht aufloesen (`EAI_AGAIN`). Analog zur `extra_hosts`-Loesung in der Komodo-Compose.
- `--env-file` statt `-e RENOVATE_TOKEN=...`: Token war sonst in `ps` und `docker inspect` sichtbar.
- `chmod 0777` auf `/mnt/user/services/renovate/state`: Renovate-Image laeuft als uid 12021 (ubuntu), kann root-owned Mount sonst nicht beschreiben.
- Live-Setup am Host:
- Service-Account `renovate` (uid 2, **kein Admin**) ueber `gitea admin user create` angelegt.
- Collaborator-Status mit Write-Permission auf `homelab-infra` (initialer DB-Insert hat den Gitea-Permissions-Cache nicht aktualisiert; Renovate sah `permissions.push=false` und brach mit "Repository does not permit pull or push" ab; saubere Loesung war Operator-UI-Klick "Entfernen + neu hinzufuegen", was den Cache konsistent aktualisiert; Befund-Bestaetigung via Doku-Studium `lib/modules/platform/gitea/index.ts`: die Push-Check ist hardcoded, kein Bypass moeglich).
- Personal-Access-Token mit Scopes `read:user,write:repository,write:issue`, in `/mnt/user/appdata/secrets/renovate_token.txt` (chmod 600). Token wurde einmal rotiert, weil der Wert beim ersten Erzeugen im SSH-Output sichtbar war.
- User-Script `renovate-six-hourly` mit Cron `20 */6 * * *` live in `/etc/cron.d/root`.
- Erstlauf 2026-05-29 erfolgreich: 5 PRs (mongo digest, mongo 7.0.32->7.0.34, postgres digest, postgres 17.9->17.10, minor-and-patch-updates gruppiert), 1 Issue "Renovate Dependency Dashboard", 8 Branches (drei Major-Branches warten auf naechsten Lauf wegen prConcurrentLimit=5). Komodo-Major-Updates wurden korrekt durch packageRule unterdrueckt.
- `docs/RENOVATE.md` zeigt die ursprueglichen 5 Operator-Schritte fuer Neuaufsetzen bzw. Disaster Recovery.
### 2026-05-29 - Borg-Source `/local/appdata/homepage` verspaetet entfernt + Removal-Checkliste in WORKFLOW
- Befund aus den ersten Tagen scharfer Alert-Pipeline: `HomelabBorgLastJobCompletedWithWarnings` firing fuer die letzten vier Borg-Laeufe (26.05.-29.05.), jeweils Exit-Code 107.
- Ursache im Borg-UI-Logfile `backup_job_39_*.log`: `"/local/appdata/homepage: stat: [Errno 2] No such file or directory"`. Borg-UI-Source-Liste enthielt seit der Homepage-Entfernung am 25.05. weiterhin den Eintrag `/local/appdata/homepage`; der Appdata-Pfad war aber bereits nach `_archive/homepage-removed-2026-05-25/` verschoben.
- Operator hat den Eintrag in der Borg-UI manuell entfernt; Source-Liste jetzt 23 statt 24 Eintraege, `homepage` nicht mehr drin. Naechster Borg-Lauf 2026-05-30 04:30 sollte wieder `completed` ohne Warning sein.
- Backups waren nicht gefaehrdet: trotz Exit-Code 107 wurden alle anderen 23 Quellen sauber archiviert (Stats Job 39: 100.895 Dateien, 26.72 GB Original, 317 MB deduplicated).
- Erkenntnis: bei Stack-Removal wurde die Borg-Source-Liste damals nicht mit-aufgeraeumt. **`docs/WORKFLOW.md`** um neuen Abschnitt "Service-Removal-Checkliste" erweitert, der die Borg-UI-Source-Bereinigung explizit als Pflichtschritt 8 nennt (zusammen mit allen anderen Schritten wie Komodo-Destroy, Gitea-Webhook, Authelia-ACL, Blackbox-Target, Doku).
- Positiv-Befund: die ntfy-Push-Pipeline (Cron `*/5` Textfile-Export -> node-exporter -> Prometheus -> Alertmanager -> ntfy-Bridge), die am 2026-05-27 scharfgeschaltet wurde, hat den Drift binnen 24 h sichtbar gemacht. Das ist der intendierte Mechanismus.
### 2026-05-28 - H:/-Pull als Windows Scheduled Task aktiviert
- Task `KalliLab H Drive Nearline Pull` registriert auf dem Operator-Windows-PC: taeglich 05:30 (nach dem Borg-Dump-Fenster um ca. 04:00), `RunLevel Limited`, `AllowStartIfOnBatteries`, `DontStopIfGoingOnBatteries`, `StartWhenAvailable`, `ExecutionTimeLimit 2 h`. Naechster Lauf 2026-05-29 05:30.
- Repo-Doku `docs/H_DRIVE_NEARLINE_PULL.md` Status auf "produktiv" gesetzt, Register-Snippet auf den tatsaechlich ausgefuehrten Befehl korrigiert (PowerShell-Enum `LeastPrivilege` -> `Limited`; alter Snippet haette beim ersten Aufruf einen Parameter-Binding-Fehler geworfen).
- Verifikation am Windows-PC: `Get-ScheduledTask` zeigt State `Ready`, Trigger-Start 2026-05-28T05:30, RunLevel `Limited`.
- Kein Eingriff am Host noetig; SMB-Quelle und H:/-Ziel waren bereits vorbereitet.
### 2026-05-28 - Zweites Off-site bewusst nicht umgesetzt
- Operator-Bewertung: 3-2-1-Regel ist mit aktueller Topologie erfuellt (Live + lokales Borg-Repo + Hetzner-Borg + H:/-Nearline = 4 Kopien / 3 Medien / 1 Off-site).
- Ein zweites Off-site wuerde **ausschliesslich** das Szenario "Hetzner-Account verloren" zusaetzlich abdecken. Eintrittswahrscheinlichkeit niedrig (etablierter deutscher Anbieter, dokumentierter Zahlungsweg). Aufwand und Kosten unverhaeltnismaessig fuer Familien-Homelab.
- Beschluss in `docs/OFFSITE_BACKUP_OPTIONS.md` mit Review-Triggern dokumentiert; F-03 in `docs/AUDIT_2026-05-25_TODO.md` von "offen" auf "erledigt (bewusst nicht umgesetzt)".
- Stattdessen drei Folge-TODOs zur Haertung der bestehenden Topologie:
- Hetzner-Account-Hygiene: starkes Passwort + Backup-Zahlungsweg + Login-Benachrichtigungen. **Bewusst keine 2FA** (Operator-Entscheidung analog USV-Risiko-Akzeptanz).
- Borg `--append-only` auf Hetzner pruefen. Befund: Repo `appdata-critical` laeuft aktuell im Mode `full`, `custom_flags` leer. Setup waere server-seitig in Hetzner-`authorized_keys`.
- H:/-Pull als Windows Scheduled Task aktivieren (Skript und Doku ready, Erstlauf 2026-05-27 erfolgreich, Task selbst noch nicht angelegt).
- Bewusst NICHT angefasst: laufende Backup-Pipeline (kein Test-Restore, kein Modus-Wechsel ohne Folge-Sprint).
### 2026-05-28 - paperless-gpt und BentoPDF bewusst behalten
- Befund: Beide Container laufen Up 3 days, aber **0 Traefik-Zugriffe in den letzten 7 Tagen** und kein User-LLM-Event in den paperless-gpt-Logs. BentoPDF-Logs zeigen ausschliesslich Docker-Healthchecks.
- Resource-Footprint vernachlaessigbar: `paperless-gpt` 34 MB RAM, `bentopdf` 4 MB RAM, beide 0 % CPU.
- Operator-Entscheidung 2026-05-28 (gegen die Nicht-Nutzung): **beide behalten**.
- `paperless-gpt`: bleibt aktiv bis Paperless-NGX 3.0 verfuegbar ist. Paperless 3.0 wird native KI-Features mitbringen; danach neu entscheiden, ob `paperless-gpt` noch noetig ist oder abgeloest werden kann.
- `bentopdf`: bleibt aktiv als situatives PDF-Werkzeug; Footprint zu klein, um eine harte Entfernung zu rechtfertigen.
- Doku-Anker in `docs/SERVICE_CATALOG.md` ergaenzt, damit die Frage in 6 Monaten nicht erneut als "warum laeuft das?" auftaucht.
### 2026-05-28 - Plex Server Reclaim und Remote Access deaktiviert
- Befund beim Versuch, Remote Access in der Plex-UI zu deaktivieren: Plex-Server war seit 18.05.2026 13:18 nicht mehr mit einem Plex.tv-Account geclaimt. `Preferences.xml` 391 Bytes, ohne `PlexOnlineMail`/`PlexOnlineUsername`/`PlexOnlineToken`. Login als `Xeridos` lieferte "Keine Berechtigung" auf den lokalen Server. Zusaetzlich waren die `library_sections` leer (Backups vom 19./22./28.05. ebenfalls ~370 KB statt MBs); die Bibliotheks-Konfiguration war seit dem 18.05. weg. Filmdateien unter `/mnt/user/media/*` blieben unangetastet (833 Verzeichnisse, ~1.7 TB).
- Reclaim als `Xeridos` durchgefuehrt: Operator-Token via `plex.tv/claim` erzeugt, am Host als Shell-Inline-ENV beim `docker compose up -d --force-recreate plex` mitgegeben. Token wurde **nicht** in `.env`, **nicht** in Compose, **nicht** in Komodo-Stack-ENV geschrieben. Nach Erfolg sauberer zweiter Recreate ohne Token, damit `docker inspect`-Snapshot keinen Token mehr enthaelt. Bash-History defensiv geleert.
- Endstand laut `Preferences.xml`: `PlexOnlineUsername="Xeridos"`, `PlexOnlineMail="michideheld@gmx.de"`, `PlexOnlineHome="1"`, `PublishServerOnPlexOnlineKey="0"` (Remote Access aus).
- Operator hat im Anschluss die Bibliotheken neu angelegt (`/data/movies`, `/data/Heimatfilme`) und Remote Access in der Plex-UI auf "deaktiviert" gesetzt; Metadata-Cache wuchs in den ersten 18 Minuten auf 630 MB.
- Plex bleibt damit strikt LAN/Tailscale-only, konsistent zur FRITZ!Box-Bereinigung vom selben Tag. Smart-TVs (Schlaf-/Wohnzimmer) finden den Server ueber WLAN-LAN per mDNS/Plex-GDM unveraendert.
- `HOMELAB_ARCHITECTURE_MASTER_V2.md` Sektion 13 enthaelt die ausfuehrliche Recovery-Geschichte. `docs/SERVICE_CATALOG.md` und Sektion 7.4 auf "LAN/Tailscale-only, Remote Access aus" praezisiert.
### 2026-05-28 - FRITZ!Box-Portfreigaben bereinigt
- WAN-Soll auf eine einzige Freigabe reduziert: `443/tcp -> 192.168.178.58:443` (Traefik HTTPS).
- `80/tcp` aus FRITZ!Box-UI entfernt. Validierung: Mobilfunk-Test ergibt Timeout auf `http://vault.kaleschke.info`, `https://vault.kaleschke.info` weiter erreichbar; lokal greift Traefik-Redirect 80->443 nach wie vor. Cloudflare-DNS-Challenge braucht kein Port 80.
- `222/tcp` bleibt bewusst nicht eingerichtet. Begruendung: Tailscale ist Operator-Pfad, GitHub-Push-Mirror `michaelkaleschke-spec/homelab-infra` deckt Repo-Bootstrap-Pfad ab, Gitea-Bundles unter `/mnt/user/backups/git-bundles/gitea` decken Offline-Restore ab. `HOMELAB_ARCHITECTURE_MASTER_V2.md` Sektion 10 entsprechend mit "Tailscale-only, bewusst nicht WAN-freigegeben" praezisiert.
- UPnP-Selbstfreigabe-Recht fuer `PC-192-168-178-71` (Hostname `VONETS.COM`, MAC `00:17:13:2F:61:96`) deaktiviert. Identifiziert als VONETS-WiFi-Bridge, vermutlich Bridge-Anbindung zum SolarEdge-Wechselrichter. SolarEdge-Cloud-Sync ist outbound und benoetigt keine UPnP. Aktuell waren 0 Selbstfreigaben aktiv; die Aenderung ist praeventiv gegen kuenftige Anforderungen.
- `docs/NETWORK_INVENTORY.md`, `docs/archive/2026-05/FRITZBOX_PORT_CORRECTION_PLAN.md` und `docs/AUDIT_2026-05-25_TODO.md` Sprint 4 entsprechend nachgezogen.
- Bewusst NICHT angefasst: FRITZ!OS 8.21 Update (Service-Fenster), IPv6-Exposure (separater Folgeschritt), WAN-Ausfallschutz (bewusst aus).
### 2026-05-27 - Monitoring-Alerts live, Gitea-Bundle-Cron live, H:/-Pull live
Drei Audit-TODOs gleichzeitig auf "erledigt" gezogen; alle Aenderungen mit Host-Smoke verifiziert.
**Monitoring-Alerts (Borg-Stale / Cert-Expiry / Container-Down)**
- Auf dem Host neuer User-Script `export-prometheus-textfile-5min` mit Cron `*/5 * * * *` angelegt. Schreibt `/mnt/user/services/posture-check/textfile/homelab.prom`.
- Repo: `services/posture-check/export-prometheus-textfile.sh` setzt jetzt vor dem `mv` per `chmod 644`, damit node-exporter (`nobody:65534`) lesen kann. Vorher `0600 root:root` ? `node_textfile_scrape_error 1`.
- `monitoring-prometheus` wurde einzeln per `docker restart` neu gestartet, um den `stale file handle` auf der gebundenen `alerts.yml` zu loesen. Kein Stack-Down. `promtool check rules` SUCCESS 14 rules, `lastConfigTime 2026-05-27T18:33:06Z`. Aktive Alerts: 1 firing (`HomelabTraefik5xx` aus dem 2026-05-20-Befund), 1 pending (`HomelabBorgLastJobCompletedWithWarnings` durch `completed_with_warnings`-Status des letzten Borg-Laufs).
- Pipeline end-to-end: Textfile-Skript ? node-exporter Textfile-Collector ? Prometheus ? alerts.yml-Regeln.
**Gitea-Bundle-Schedule**
- User-Script `gitea-bundle-mirror-6h` mit Cron `10 */6 * * *` (00:10/06:10/12:10/18:10).
- Repo: `ops/borg-ui/scripts/gitea-bundle-mirror.sh` schreibt Bundles und Sidecars jetzt `chmod 644` statt `600`. Begruendung: Bundle-Inhalt ist Git-Historie ohne Secrets (durch `.gitignore` abgedeckt), nicht sensibler als die uebrigen 0644-Dumps. Damit funktioniert der Nearline-Pull ueber SMB.
- Existierende Bundles wurden manuell auf 0644 angehoben. Erster Cron-Lauf 2026-05-27 18:41 UTC erfolgreich: 4 Bundles, Checksums OK.
**H:/ Nearline-Pull**
- Erster scharfer Lauf 2026-05-27 20:25 zeigte vier Permission-Befunde: `unraid-flash-config.*` (4 Files, by-design 0600), `filebrowser.bolt.dump` (0640, Source erbt), und alle 10 Gitea-Bundle-Files (0600).
- Repo: `ops/h-drive-nearline/pull-critical-backups.ps1` excluded jetzt die `unraid-flash-config.*`-Familie ueber `/XF` (Restore-Quelle bleibt Hetzner-Borg). `ops/borg-ui/scripts/pre-backup-dumps.sh` setzt alle Dumps via `atomic_write` per Default auf 0644, Flash-Config-Familie explizit mit `atomic_write target tmp 600`.
- Existierende Files am Host nachtraeglich auf 0644 angehoben.
- Zweiter Lauf 2026-05-27 20:45: beide Robocopy-Jobs Exit 1, **19 Borg-Dumps + 10 Gitea-Bundle-Files** unter `H:\kallilab-nearline-backups`. Report-Tabellen-Quirk in PowerShell durch `& robocopy @args | Out-Null` behoben (Live-Output landete vorher in `$results`).
- `docs/H_DRIVE_NEARLINE_PULL.md` mit Erstlauf-Befund, gefixter Erwartungs-Liste und expliziter Out-of-Scope-Anmerkung fuer Flash-Config aktualisiert.
- Windows Scheduled Task taeglich 05:30 bleibt **bewusst** offen bis zur Operator-Bestaetigung.
**Was bewusst NICHT angefasst wurde**
- Authelia/OIDC/CrowdSec/Nextcloud-Haertung (geparkt).
- Hermes (Review 2026-07-25).
- USV (Risiko bewusst akzeptiert).
- FRITZ!Box-/Plex-/UI-Punkte (Punkt 5 fuer morgen frueh).
- `unraid-flash-config.tar.gz` bleibt bewusst 0600 und ausserhalb des Nearline-Scopes.
### 2026-05-27 - Vorbereitungsdokumente FRITZ!Box-Korrektur und Off-site-Optionen
- Reine Doku-Aenderung; kein Router-, Provider- oder Host-Eingriff.
- `docs/archive/2026-05/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`.
- Validiert wurden Borg-Extract von `local/borg-dumps/latest/immich.dump`, Import in isolierten `tensorchord/pgvecto-rs:pg14-v0.2.0` Test-Postgres, Start des Immich-Servers ohne ML und ohne Traefik, HTTP `200` auf `127.0.0.1:12283`, Login-Marker, `11977` Assets und `1` User im Test-DB-Check.
- Produktive Container und produktive Foto-Pfade wurden nicht angefasst; Testdaten und Testcontainer wurden nach Erfolg bereinigt.
- Im Lauf wurden Restore-Test-Haertungen umgesetzt: Borg-`known_hosts` aus `/data/known_hosts` wird fuer SSH-Trust genutzt, `completed_with_warnings`-Archive gelten als verwendbare Restore-Quelle, Postgres-Startfenster werden retry-faehig behandelt, Immich-v2-Upload-Marker werden im leeren Test-Mount erzeugt und Smoke-Checks schlagen bei HTTP-/Marker-Fehlern hart fehl.
- `docs/IMMICH_RESTORE_TEST.md`, `docs/RESTORE_MATRIX.md`, `ops/restore-tests/schedule.md` und `docs/AUDIT_2026-05-25_TODO.md` nachgezogen; F-11 ist damit abgeschlossen. Voll-Restore inklusive Foto-Dateien bleibt ein separater DR-Drill.
### 2026-05-27 - FRITZ!Box-Portfreigaben gegen Repo-Soll abgeglichen
- FRITZ!Box-UI `Internet -> Freigaben -> Kallilabcore` geprueft: aktiv sind `HTTP-Server` TCP `80/tcp` und `HTTPS-Server` TCP `443/tcp` auf `192.168.178.58`.
- Repo-Soll aus `docs/NETWORK_INVENTORY.md` ist nur `443/tcp` plus optional gewolltes Gitea-SSH `222/tcp`. Der aktuelle Zustand weicht ab: `80/tcp` ist offen, `222/tcp` fehlt.
- Kallilabcore ist nicht als Exposed Host markiert und erlaubt keine selbststaendige Portfreigabe. `PC-192-168-178-71` erlaubt selbststaendige Portfreigabe, hat aber `0 aktiv`.
- Keine FRITZ!Box-Aenderung vorgenommen. Router-Korrektur bleibt ein produktiver Operator-Schritt nach ausdruecklicher Freigabe.
### 2026-05-26 - Immich Restore-Smoke-Test vorbereitet (F-11)
- `docs/IMMICH_RESTORE_TEST.md` und `ops/restore-tests/immich-plan.md`/`immich-runbook.md` beschreiben den geplanten Immich-Mini-Restore: `immich.dump` aus Borg, isolierter pgvecto-rs-Test-Postgres, Test-Redis, Immich-Server ohne ML, lokaler Port `127.0.0.1:12283`, keine produktiven Foto-Mounts.
- `ops/restore-tests/immich-restore-test.sh`, `immich-restore-test.ps1` und `immich-compose.test.yml` wurden vorbereitet; der Dispatcher kennt `immich --what-if`.
- Lokal verifiziert: Bash-Syntax, `run-restore-checks.sh immich --what-if`, PowerShell-Dispatcher `-Mode immich -WhatIf`, Docker-Compose-Render und Policy-Check. Kein echter Host-Restore, kein Borg-Extract, kein Produktiv-Container-Eingriff.
- Host-Preflight 2026-05-27: Host-Clone per Fast-forward auf `c5d231a`, `immich.dump` 66M, `/mnt/user/backups` ca. 3.7T frei, `run-restore-checks.sh immich --what-if` erfolgreich. Kein echter Restore-Lauf.
- F-11 bleibt fachlich offen bis zum ersten Host-Lauf mit Report unter `/mnt/user/backups/restore-reports/immich-YYYY-MM-DD.md`.
### 2026-05-27 - H:/ Nearline-Pull vorbereitet
- `docs/H_DRIVE_NEARLINE_PULL.md` und `ops/h-drive-nearline/pull-critical-backups.ps1` definieren den Windows-seitigen Pull von `\\192.168.178.58\backups\borg\dumps\latest` und `\\192.168.178.58\backups\git-bundles\gitea` nach `H:\kallilab-nearline-backups`.
- SMB-Quelle `\\192.168.178.58\backups` und H:/ sind erreichbar; `-WhatIf` prueft den Plan ohne Kopie.
- Kein Scheduled Task angelegt und kein echter Kopierlauf gestartet. Empfohlen ist taeglich 05:30 nach dem Borg-Dump-Fenster, Aktivierung erst nach Operator-Sichtpruefung.
### 2026-05-27 - Storage Layout als Active v1.4 gefuehrt
- `docs/STORAGE_LAYOUT.draft.md` wurde zu `docs/STORAGE_LAYOUT.md` umbenannt. Das Dokument war inhaltlich bereits als Active markiert; Version 1.4 entfernt den formalen Draft-Blocker.
- Physikalische Disk-Werte aus `docs/HARDWARE_INVENTORY.md` und Host-Readout uebernommen: Cache Samsung 970 EVO Plus 1.8T XFS, Disk1 WDC WD60EFAX 5.5T XFS auf `md1p1`, Parity TOSHIBA HDWG480 7.3T, Boot Samsung Flash Drive 59.8G FAT32, H:/ als Nearline-Ziel.
- `docs/AUDIT_2026-05-25_TODO.md` Sprint 2 fuer Storage-Layout und Disk-/Share-Baseline auf erledigt gesetzt. Retention-Kalibrierung, Monitoring-Schwellen und RESTORE_MATRIX-Detailklassifikation bleiben normale Folgeaufgaben.
### 2026-05-27 - F-08 Alert-Regeln vorbereitet
- `services/posture-check/export-prometheus-textfile.sh` erzeugt Textfile-Metriken fuer Borg-Backup-Frische und kritische Container unter `/mnt/user/services/posture-check/textfile/homelab.prom`.
- `monitoring/docker-compose.yml` aktiviert den Node-Exporter-Textfile-Collector. `monitoring/prometheus/alerts.yml` enthaelt vorbereitete Alerts fuer Borg-Stale, Borg-Fehlerstatus, Borg-Warnstatus, Textfile-Stale, Critical-Container-Down und TLS-Cert-Expiry 21/7 Tage.
- Host-Smoke 2026-05-27: Skript erzeugt `homelab.prom`, alle gelisteten kritischen Container melden `1`, Borg-Status ist `completed_with_warnings` und wird als Warning statt Critical modelliert.
- Kein Monitoring-Redeploy und kein Scheduled Task in diesem Schritt. Abschluss erfolgt nach Host-Schedule, Prometheus-Reload und Testalert.
### 2026-05-26 - Audit F-16 und F-20 abgeschlossen (Doku-only)
- F-16: `infra/redis`-Etikett auf die Realitaet abgeglichen. `docs/SERVICE_CATALOG.md`, `docs/REPO_MAP.md`, `HOMELAB_ARCHITECTURE_MASTER_V2.md` Sektion 13 und `docs/DISASTER_RECOVERY.md` Bootstrap-Stufe 2 beschreiben Redis jetzt als "primaer Paperless-Redis (App-Cache); historisch als shared angelegt, faktisch nur von Paperless genutzt". Immich, Nextcloud, Mealie eigene Redis-Instanzen; Authelia bewusst ohne Redis. Keine Compose-Aenderung.
- F-20: Restore-Wege fuer Stack-ENV-only Secrets explizit gemacht. Neuer Abschnitt `6.2.1 Restore-Quellen fuer Stack-ENV-Werte` in `docs/DISASTER_RECOVERY.md` (Reihenfolge Komodo-Mongo-Dump -> Vaultwarden -> externe Notiz, Komodo-Sonderfall, Paperless als Hauptanwendung). Neuer Abschnitt `Stack-ENV-only Secrets - Restore-Wege` in `docs/SECRETS_MAP.md` mit Tabelle je Stack (Paperless, Immich, Mail-Archiver, Speedtest, Komodo, Hermes, Glance). Glance-Widget-Tokens explizit als rebuildbar markiert. Konkrete Werte werden nirgendwo dokumentiert.
- `docs/AUDIT_2026-05-25_TODO.md` Sprint 6 entsprechend auf "erledigt" gestellt.
- Kein Eingriff in `ops/borg-ui/scripts/gitea-bundle-mirror.sh`, kein Gitea-Bundle-Trockenlauf, keine SSH-/Host-Pruefung.
### 2026-05-26 - FRITZ!Box-/H:/-/Family-Onboarding-Doku-Update
- `docs/NETWORK_INVENTORY.md` mit FRITZ!Box-Baseline gefuellt: FRITZ!Box 7590, FRITZ!OS 8.21 (Update gemeldet, nicht eingespielt), Telekom DSL ~87/36 Mbit/s, 36 aktive Heimnetz-Geraete, LAN 1-4 verbunden, WLAN `Fritzi`, Gast-WLAN inaktiv, Telefonie/DECT aktiv, Ausfallschutz nicht eingerichtet, USB nicht verbunden, 2 Portfreigaben aktiv. Soll fuer Portfreigaben: nur `443/tcp` und `222/tcp` auf `192.168.178.58`.
- `docs/EXTERNAL_DEPENDENCIES.md` um Telekom-DSL und FRITZ!Box 7590 als WAN-/Router-Abhaengigkeit erweitert; Ausfall-Szenario "Telekom-DSL / FRITZ!Box gestoert" ergaenzt.
- `docs/CAPACITY_AND_LIFECYCLE.md` um Abschnitt "H:/ als zusaetzliches lokales Backup-Ziel" ergaenzt. Bewertung: H:/ ist als zweite lokale Nearline-Kopie und Freeze-Sicherung sinnvoll, aber bewusst **kein** Offsite-Ersatz und **kein** CIFS-Hard-Mount am Unraid (STORAGE_LAYOUT §12.6). Pull-Modell vom Windows-PC bleibt der getestete Weg (vgl. Disk1 Phase 2 Freeze 2026-05-25).
- `docs/FAMILY_ONBOARDING.md` von Tabellen-Entwurf auf familienverstaendlichen Begruessungstext umgestellt: kurze App-Erklaerungen, konkrete Was-tun-Wenn-Anleitungen (Webseite weg, Passwort vergessen, 2FA verloren, Foto-Backup haengt, Browser-Warnung), "Was du nicht musst"-Block, Hinweis fuer geplante Wochenend-Einladung.
- `docs/AUDIT_2026-05-25_TODO.md` Leitplanken aktualisiert: Authelia 2FA/OIDC/CrowdSec und Nextcloud-2FA-Haertung werden in diesem Zyklus nicht angefasst (Operator-Vorgabe). Hermes-Agent geparkt mit Review-Deadline 2026-07-25. USV-Risiko bewusst akzeptiert. Sprint 4 um H:/-Bewertung und FRITZ!Box-Portfreigaben-Abgleich erweitert. Neue Sprints 6 (geparkte Apps) und 7 (Off-site) ergaenzt.
- Keine Live-/Compose-Aenderung in diesem Commit; nur Doku.
### 2026-05-26 - USV-Risiko bewusst akzeptiert
- Operator-Entscheidung: aktuell wird keine USV angeschafft.
- Der Befund bleibt technisch unveraendert: keine funktionierende USV-Abschaltung nachgewiesen. Power-Loss-Risiko fuer Docker-/DB-State und laufende Writes wird bewusst akzeptiert und bei spaeteren Reviews neu bewertet.
### 2026-05-26 - Borg-Passphrase offline gesichert
- Operator bestaetigt: Die Borg-Passphrase ist offline/off-system gesichert und kann ohne Host oder Vaultwarden wiederhergestellt werden.
- Doku aktualisiert nur den Sicherungsstatus; Secret-Wert und Ablageort bleiben bewusst ausserhalb des Repos.
### 2026-05-26 - Gitea-Bundle-Mechanik definiert
- `ops/borg-ui/scripts/gitea-bundle-mirror.sh` ergaenzt: erstellt verifizierte `git bundle`-Artefakte fuer alle bare Gitea-Repositories, schreibt Checksums und einen Markdown-Report.
- Zielpfad ist `/mnt/user/backups/git-bundles/gitea`; dieser Pfad muss in den Borg/off-site Scope aufgenommen und hostseitig geplant werden.
- `docs/SERVICES_RECOVERY.md` und `docs/RESTORE_MATRIX.md` dokumentieren Bundles jetzt als zweite Repo-Bootstrap-Schicht neben dem GitHub-Mirror.
- Host-Erstlauf nach Skript-Fix erfolgreich: 4 Bundles erzeugt (`homelab-infra`, `homelab`, `homepage`, `smart-home-kalli`), Checksums OK, `homelab-infra.bundle` in Restore-Lab geklont und `git fsck` sauber. Offen bleibt die dauerhafte Schedule-Einbindung.
### 2026-05-26 - Audit-Baseline-Tag gesetzt
- Der Stand nach Hardware-/Capacity-Baseline, Policy-Triage und Recovery-Doku wurde als `audit-2026-05-25-baseline` markiert und nach Gitea gepusht.
### 2026-05-26 - Externe Abhaengigkeiten und Services-Recovery baseline dokumentiert
- `docs/EXTERNAL_DEPENDENCIES.md` von Template auf Betreiber-Baseline angehoben: Domain, Cloudflare, Hetzner, GitHub-Mirror, Tailscale, GMX, Let's Encrypt, Registries, Plex und mobile Push-Pfade sind mit Ausfallwirkung und Notfallplan dokumentiert.
- `docs/SERVICES_RECOVERY.md` finalisiert den Komodo-Bootstrap-Anker: `ops/komodo/docker-compose.yml` bleibt verbindlich; der `komodo`-Self-Stack hat keinen aktiven Gitea-Webhook und ist nicht der Recovery-Anker.
- Offene Off-Repo-Betreiberchecks bleiben Account-Besitz, 2FA-Recovery-Codes, Zahlungswege, Borg-Passphrase-Hinterlegung und Gitea-Bundle-/Mirror-Mechanik.
### 2026-05-26 - Policy-Warnings triagiert
- Plex `network_mode: host` wurde in den Policy-Ausnahmen als dokumentierte Discovery-Ausnahme erfasst.
- Mutable Tags bei `ddns-updater`, `glances` und `scrutiny` bleiben wegen vorhandener SHA256-Digests reproduzierbar gepinnt und werden im Policy-Report als Info-Ausnahmen sichtbar gehalten.
- `monitoring-influxdb3-core` bleibt als dokumentierte `user: "0"`-Ausnahme bewusst eine Warning, damit der Hardening-Punkt nicht aus dem Blick faellt.
### 2026-05-26 - Hardware-/Capacity-Baseline abgeschlossen
- Hardware-Inventar auf Host-Befund aktualisiert: BIOS AMI F21 vom 2025-06-19, Intel Raptor Lake SATA AHCI, Samsung NVMe Controller und Realtek RTL8125 2.5GbE mit aktuellem 1G-Link.
- RAM-Baseline dokumentiert: 4x 8 GB DDR4 ohne ECC, gemischte Module, aktuell 2133 MT/s konfiguriert.
- Capacity-Baseline dokumentiert: Cache 1.9T mit 97G genutzt (6 %), Disk1/User-Shares 5.5T mit 1.8T genutzt (33 %), lokale Backups 2.2G unter `/mnt/user/backups`.
- USV-Befund dokumentiert: `apcupsd` ist vorhanden und auf USB vorkonfiguriert, laeuft aber nicht; `apcaccess status` liefert Connection refused und `lsusb` zeigt keine erkannte USV. Power-Loss bleibt damit eine offene Betreiberentscheidung.
### 2026-05-26 - Komodo/Gitea-Restdrift bereinigt
- Der alte Komodo-Stack `grafana` wurde als historischer Altstand inert gemacht: keine Repo-Dateipfade, kein Webhook, keine alte Stack-ENV, keine `missing_files`/`remote_errors`. Rollback bleibt Git-Historie, nicht der alte Komodo-Stack.
- Der Gitea-Hook `35` fuer den alten `grafana`-Stack bleibt inaktiv. Der nicht sinnvolle `komodo`-Self-Hook `11` wurde deaktiviert, weil Komodo selbst nicht per Gitea-Webhook auf `master` deployed wird.
- Ein kurz sichtbarer Komodo-DB-Typfehler durch `updated_at` als Float wurde im selben Kontrollfenster auf nativen Mongo `Long` korrigiert; danach traten keine neuen `invalid type`-Fehler mehr auf.
- Nach der Bereinigung: aktive Gitea-Komodo-Hooks haben `0` Fehlstatus; `komodo-core`, `komodo-periphery`, `komodo-mongo`, `nextcloud` und die aktuellen `monitoring-*` Container laufen weiter.
### 2026-05-26 - Monitoring-Altstaende aus aktivem Repo entfernt
- Die abgeloesten Pfade `ops/grafana-influxdb` und `ops/loki` wurden per `git rm` aus dem aktiven Repo entfernt. `monitoring/` bleibt der einzige Observability-Zielstack.
- Live-Check vor dem Cleanup: nur `monitoring-grafana`, `monitoring-promtail`, `monitoring-influxdb3-core` und `monitoring-loki` laufen; alte Container `grafana`, `influxdb3-core`, `loki` und `alloy` sind nicht vorhanden.
- Rollback erfolgt bei Bedarf ueber Git-Historie, nicht ueber parallel gepflegte Compose-Verzeichnisse.
- Im selben GitOps-Kontrollfenster wurde Gitea-Webhook `35` fuer den alten `grafana`-Rollback-Stack als inaktiv bestaetigt. Der aktive Nextcloud-Hook `36` hatte einen Signaturfehler; sein Secret wurde ohne Ausgabe des Werts aus der Komodo-Stack-Konfiguration zurueck nach Gitea synchronisiert.
### 2026-05-26 - AdGuard Admin-Port auf Tailscale-Soll begrenzt
- Host-Audit per SSH gegen `Kallilabcore` durchgefuehrt: Tailscale IPv4 ist `100.80.98.33`, LAN-IP ist `192.168.178.58/24`, Gateway `192.168.178.1`.
- Repo-Soll fuer `host-services/Adguard/docker-compose.yml` geaendert: DNS `53/tcp+udp` bleibt unveraendert, die Admin-UI bindet nun auf `100.80.98.33:8082:80`.
- Architektur, Service-Katalog, Repo-Map, Netzwerk-Inventar und AI-Kontext wurden an das neue Modell angepasst: keine Traefik-/Authelia-2FA-Umstellung, aber keine LAN-weite Admin-Bindung mehr.
- Live-Deploy wurde nach Fast-Forward des AdGuard-Workspaces auf `5cb4017` mit `docker compose -p adguard ... up -d` ausgefuehrt. Validierung erfolgreich: `ss -ltnp` zeigt `100.80.98.33:8082`, DNS via `@127.0.0.1` und `@192.168.178.58` funktioniert, `http://100.80.98.33:8082/` liefert HTTP 302, `http://192.168.178.58:8082/` ist nicht mehr erreichbar.
- Nachpruefung des GitOps-Pfads: Gitea-Hook `1` zeigt auf Komodo-Stack `69c7b9e26b77cd827811b9d0` und lieferte HTTP 200. Komodo-Deploys fuer AdGuard scheiterten zunaechst im `Git pull` einmal an `.git/index.lock` und danach an `fatal: Cannot rebase onto multiple branches`; der Workspace wurde aufgeraeumt und steht sauber auf `origin/master`.
### 2026-05-26 - Audit-Umsetzung vorbereitet
- Aus `docs/archive/2026-05/AUDIT_2026-05-25.md` wurde `docs/AUDIT_2026-05-25_TODO.md` als operative Arbeitsliste abgeleitet. Authelia-2FA/OIDC bleibt bewusst geparkt und wird erst nach finaler Policy-Entscheidung umgesetzt.
- Neue Inventar- und Betriebsdokumente angelegt: `docs/HARDWARE_INVENTORY.md`, `docs/NETWORK_INVENTORY.md`, `docs/EXTERNAL_DEPENDENCIES.md`, `docs/CAPACITY_AND_LIFECYCLE.md` und `docs/FAMILY_ONBOARDING.md`.
- `docs/SERVICES_RECOVERY.md` beschreibt initial die recovery-kritischen `/mnt/user/services`-Pfade, Gitea-Repo-Mirror-Optionen, Komodo-Bootstrap und Secret-Recovery-Reihenfolge.
- Policy-Check lokal erneut ausgefuehrt: die alten SEC001-Warnings fuer `ddns-updater` und `scrutiny` sind nicht mehr aktuell; verbleibende Warnings betreffen Host-Netz-/User-/Image-Tag-Themen und Altstaende.
### 2026-05-25 - Unraid Flash-Backup in Borg-Scope aufgenommen
- `pre-backup-dumps.sh` erzeugt zusaetzlich zu den DB-Dumps ein sensibles `unraid-flash-config.tar.gz` aus `/boot/config` inklusive SHA256 und Manifest unter `/mnt/user/backups/borg/dumps/latest`.
- Da `/local/borg-dumps` bereits Teil des Borg-Scopes ist, wird das Flash-Konfigurationsartefakt mit dem bestehenden Hetzner/Borg-Backup historisiert. Downloadbare Plugin-Paketarchive unter `/boot/config/plugins/*/` werden aus dem Artefakt ausgeschlossen; Restore-relevante Konfiguration bleibt enthalten.
- Live-Erstlauf erfolgreich: `pre-borg.sh` lieferte `critical_count=0`, Freshness `Critical: 0`, `unraid-flash-config.tar.gz` ist 297 KiB gross, `0600 root:root`, SHA256-Pruefung `OK`, 356 Archiv-Eintraege, Manifest fuer Unraid `7.2.4`. Der Borg-UI-Job `Taegliche Sicherung` ist aktiv und umfasst `/local/borg-dumps`; der naechste planmaessige Hetzner-Lauf nimmt das neue Flash-Artefakt mit. Der Host-Repo-Clone unter `/mnt/user/services/homelab-infra` wurde wegen eines fehlgeschlagenen Fast-Forward-Checkouts frisch geklont; der vorherige Stand liegt archiviert unter `/mnt/user/services/_archive/homelab-infra-pre-refresh-20260525-194209`.
### 2026-05-25 - Monitoring-Zielstack finalisiert und Uptime Kuma entfernt
- `monitoring` und `glance` wurden auf Commit `b6bbca4` deployed; Komodo zeigt fuer beide `latest_hash` = `deployed_hash` = `b6bbca4` ohne `remote_errors`. Die zehn `monitoring-*` Container laufen, `monitoring.kaleschke.info` und `glance.kaleschke.info` leiten anonym zu Authelia, Prometheus ist ready und Loki `/ready` liefert `ready`.
- Alte Monitoring-Altcontainer `grafana`, `influxdb3-core`, `loki` und `alloy` sind in Docker nicht vorhanden; `ops/grafana-influxdb` und `ops/loki` bleiben nur als Rollback-/Migrationsreferenz im Repo. Der noch aktive Gitea-Hook `35` des alten `grafana`-Rollback-Stacks wurde deaktiviert, damit zukuenftige Pushes den Altstand nicht reaktivieren.
- Uptime Kuma wurde durch Blackbox/Prometheus/Grafana ersetzt: aktive Blackbox-Zielliste enthaelt 19 HTTPS-Ziele, `uptime.kaleschke.info` ist dort nicht mehr enthalten und liefert nach Stack-Removal 404. Der Komodo-Stack `uptime-kuma` wurde gestoppt/destroyed/geloescht, Gitea-Webhook `23` deaktiviert, Appdata nach `/mnt/user/appdata/_archive/uptime-kuma-removed-2026-05-25` und der alte Stack-Workspace nach `/mnt/user/services/stacks/_archive/uptime-kuma-removed-2026-05-25` verschoben.
- Authelia-Hostconfig wurde mit Backup `configuration.yml.pre-uptime-removal-20260525-164343.bak` um den toten `uptime.kaleschke.info`-Eintrag bereinigt, validiert und neu gestartet. Prometheus wurde wegen eines `Stale NFS file handle` auf der gebundenen Konfigurationsdatei per Komodo-Restart neu gemountet.
### 2026-05-25 - AdGuard Admin-Port bewusst LAN-direkt belassen
- Strategische Option `adguard.kaleschke.info` hinter Traefik/Authelia-2FA wurde bewertet, aber vom Operator bewusst verworfen, weil der Betriebsweg einfach bleiben soll. AdGuard bleibt als dokumentierte Ausnahme mit DNS `53/tcp+udp` und Admin `8082:80` LAN-direkt; keine Live-Aenderung an AdGuard, Authelia oder Traefik wurde vorgenommen.
### 2026-05-25 - Borg-Passphrase Host-Secret verifiziert
- Erwartete Host-Secret-Datei `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` aus der bestehenden Borg-UI-Repo-Konfiguration erzeugt, mit `root:root` und Modus `600` gesichert und per `borg info` gegen das Hetzner-Borg-Repo verifiziert. Analoge Offline-Hinterlegung bleibt bewusste Operator-Aufgabe; Secret-Wert wurde nicht ausgegeben oder dokumentiert.
### 2026-05-25 - Dashboard auf Glance konsolidiert
- Glance bleibt das einzige Homelab-Dashboard; Homepage wurde aus dem Zielbild entfernt. Authelia-Default-Redirect, Monitoring-Blackbox-Ziele, Cert-Check-Domains und Glance-Konfiguration zeigen nicht mehr auf `home.kaleschke.info`; Homepage wurde via Komodo-API gestoppt/destroyed, der Komodo-Stack geloescht, der alte Gitea-Webhook deaktiviert und Appdata nach `/mnt/user/appdata/_archive/homepage-removed-2026-05-25` verschoben.
### 2026-05-25 - Jellyfin aus Zielbild entfernt
- Plex-Smoke-Test erfolgreich (`/identity` HTTP 200, Container healthy, `/data/movies` und `/photos` sichtbar). Jellyfin wurde repo-seitig entfernt, aus Authelia-Baseline und Zielbild-Doku ausgetragen, via Komodo-API gestoppt/destroyed, der Komodo-Stack geloescht und Appdata nach `/mnt/user/appdata/_archive/jellyfin-removed-2026-05-25` verschoben; Plex bleibt einziger Medienserver.
### 2026-05-25 - Externer Repo-Mirror eingerichtet
- Gitea erlaubt fuer Repo-Migrationen und Mirror-Targets gezielt `github.com` und nutzt explizite externe DNS-Resolver. `Micha/homelab-infra` spiegelt nun als privater GitHub-Push-Mirror nach `michaelkaleschke-spec/homelab-infra`; erster manueller Sync erfolgreich, Gitea `push_mirror.last_error` leer. Token-Werte bleiben ausschliesslich in Gitea/GitHub und werden nicht dokumentiert.
### 2026-05-25 - Audit-Final nachgemessen
- Audit-Restliste erneut live geprueft: runtime-relevanter Stack-Inhalt fuer `gitea`, `borg-ui` und `monitoring` seit `66ee10c` unveraendert; abschliessende Audit-Doku-Commits liegen in Gitea; Monitoring inklusive Loki `/ready` gruen; Borg-Job und 15 kanonische Dump-Artefakte frisch; `docs/archive/2026-05/AUDIT_2026-05-23_FINAL.md` auf den Live-Stand aktualisiert.
### 2026-05-25 - Disk1 Phase 2 abgeschlossen
- Disk1 wurde nach H:-Freeze-Backup und finalem Service-Freeze von NTFS/`ntfs3` auf XFS migriert.
- Restore verifiziert: `media` final 2722 Dateien und 1,800,782,188,226 Bytes mit 0 missing/extra/size mismatch; Tar-Shares und Disk1-Extras aus den H:-Freeze-Archiven wiederhergestellt.
- Docker/Services nach XDG-Runtime-Fix wieder stabil: 49 Container laufend, 0 stopped, 0 unhealthy, 0 starting; Gitea, Komodo, Borg, Jellyfin und Monitoring per Smoke-Test erreichbar.
- Borg-UI meldet den letzten Backup-Job `completed`; `pre-backup-dumps.sh` wurde nach Wiederanlauf erneut ausgefuehrt und 15 kanonische Dump-Artefakte sind juenger als 24 h.
- `posture-check` erwartet Disk1 nun standardmaessig als XFS (`ALLOW_DISK1_NTFS=0`).
### 2026-05-23 - Audit-Endstufe verifiziert
- Lokalen Hardening-Commit `cd650b1` nach Gitea gepusht; Komodo-Workspaces fuer `gitea`, `borg-ui` und `monitoring` stehen auf `cd650b1`.
- Live-Audit in `docs/archive/2026-05/AUDIT_2026-05-23_LIVE.md` dokumentiert: Gitea-Registration geschlossen, Borg-Dumps frisch, Monitoring-Stack aktiv, alte Grafana/Loki-Altcontainer nicht mehr vorhanden.
- Jellyfin und Plex in Architektur, Service-Katalog und Repo-Map nachgetragen; Plex ist jetzt als Repo-Compose-Stack mit dokumentierter Host-Netz-Ausnahme gefuehrt.
- Repo-Hygiene abgeschlossen: `.serena/` ignoriert, leere Verzeichnisse entfernt, Windows-Reinstall-Helfer unter `ops/windows-reinstall/` bewusst versioniert.
### 2026-05-20 - Gitea 5xx-Bursts untersucht und Signup geschlossen
- Live-Befund zu `HomelabTraefik5xx`: kurze externe `POST /`-Bursts auf `gitea@docker` von `103.153.183.69` und `103.153.183.73`, jeweils HTTP 500 in unter 10 ms; normale Gitea-Checks und Git-Reads liefen parallel mit HTTP 200.
- Keine Hinweise auf erfolgreichen Zugriff: Gitea-Container ohne Restart/OOM, nur User `micha`, keine neuen User der letzten 30 Tage, keine neuen Repos, SSH-Keys oder Access-Tokens im Untersuchungsfenster.
- Live-Prometheus lief noch mit der alten Regel `rate(...[5m]) > 0`; die bereits im Repo vorbereitete Regel `increase(...[5m]) >= 5` wurde auf den Live-Mount kopiert und per Prometheus-Reload aktiviert.
- Gitea-Registrierung und OpenID-Signup wurden geschlossen: `DISABLE_REGISTRATION=true`, `REGISTER_EMAIL_CONFIRM=true`, `ENABLE_OPENID_SIGNIN=false`, `ENABLE_OPENID_SIGNUP=false`; Signup-Seite zeigt danach "Registration is disabled", OpenID-Login liefert 403.
### 2026-05-18 - Komodo Webhooks vollstaendig abgeglichen
- Live-Befund auf `Kallilabcore`: Komodo hatte fuer mehrere aktuelle Stacks `webhook_enabled: true`, aber Gitea enthielt noch nicht fuer alle aktuellen Stack-IDs aktive Webhooks.
- In der Gitea-Datenbank wurden aktive Webhooks fuer `monitoring` (`6a08d5297707b0930ab95c72`), `glance` (`6a09d7347707b0930ab96eae`), `grafana` (`69f31ecdf65eb72b757c497d`) und `nextcloud` (`69e519085fd5e8bc51f121f0`) nach dem bestehenden Komodo-Hook-Muster angelegt.
- Stale aktive Gitea-Hooks auf nicht mehr vorhandene bzw. alte Komodo-Stack-IDs wurden deaktiviert.
- Abgleich danach: 30 aktive Gitea-Komodo-Hooks fuer 30 Komodo-Stacks mit aktiviertem Webhook; `hermes` bleibt in Komodo bewusst `webhook_enabled: false`.
- Netzwerkpfad aus dem `gitea`-Container zu `komodo-core:9120` wurde erfolgreich verifiziert; `last_status=0` fuer neue Hooks bleibt bis zum ersten Push erwartbar.
### 2026-05-19 - Posture-Check Host-Version verifiziert
- Ursache fuer wiederholte ntfy-Warnings war nicht mehr die Repo-Logik allein, sondern dass auf dem Unraid-Host noch die alte Skriptversion unter `/mnt/user/services/homelab-infra/services/posture-check/posture-check.sh` ausgefuehrt wurde.
- Host-Skript wurde mit Backup ersetzt und mit `SEND_NTFY=0` direkt auf dem Host verifiziert.
- Ergebnis des echten Host-Laufs: `status: ok`, `critical_count: 0`, `warning_count: 0`.
- Betriebsregel daraus: Bei Host-User-Scripts nach Repo-Aenderungen immer den tatsaechlich ausgefuehrten Host-Pfad und den Live-Output pruefen.
### 2026-05-19 - Borg-Scope fuer GitOps Host Automation erweitert
- Nach den Gitea-/Komodo-Webhook- und Posture-Check-Aenderungen wurde der Backup-Scope um Host-GitOps-Pfade erweitert.
- Borg UI mountet kuenftig `/mnt/user/services` read-only als `/local/services`.
- In `all-important-sources.txt` wurden `/local/services/homelab-infra`, `/local/services/stacks` und `/local/services/posture-check` aufgenommen.
- `pre-backup-dumps.sh` wurde auf dem Host ausgefuehrt; frische Dumps fuer `gitea.sqlite.dump` und `komodo-mongo.archive.gz` liegen unter `/mnt/user/backups/borg/dumps/latest`.
- Wirksam wird der neue `/local/services`-Mount nach Redeploy/Recreate des `borg-ui`-Stacks.
### 2026-05-19 - Traefik-5xx Alert entstoert
- `HomelabTraefik5xx` hatte auf einzelne 5xx-Antworten reagiert, weil die Regel `rate(...[5m]) > 0` nutzte.
- Live-Befund fuer `gitea@docker`: zwei kurze `POST /` mit HTTP 500 von einer externen IP, danach durchgehend erfolgreiche Gitea-Checks; kein Container-Restart.
- Prometheus-Regel auf `increase(...[5m]) >= 5` geaendert, damit einzelne externe Fehlrequests keinen ntfy-Alarm ausloesen.
### 2026-05-17 - Glance Homelab-Dashboard vorbereitet
- `ops/glance` als geschuetztes Homelab-Dashboard unter `glance.kaleschke.info` vorbereitet.
- Glance zeigt HTTP-Monitore fuer Core, Apps und Ops, Docker-Containergruppen, Host-Snapshot und Bookmarks.
- Docker-Status laeuft nicht ueber einen direkten Socket-Mount in Glance, sondern ueber `glance-docker-socket-proxy` auf einem internen `glance_socket_net`.
- Die HTTP-Monitore nutzen oeffentliche URLs als Klickziel und interne `check-url`-Endpunkte auf `frontend_net`, damit Glance nicht vom externen Hairpin-/Auth-Pfad abhaengt.
- Das Immich Community-Widget wurde ergaenzt. Der API-Zugriff nutzt eine interne Service-URL und ein Stack-ENV-Token. Paperless, Scrutiny und Speedtest bleiben Kandidaten fuer einen spaeteren Widget-Pass, sobald die konkrete API-Ausgabe im Glance-Kontext sauber verifiziert ist.
- Das Dashboard-Layout wurde an `ginesjunior11/glance-dashboard-config` angelehnt: dunkleres blaues Theme, Zeitfortschrittsgruppe, farbige Dashboard-Icons, dichter `Homelab Status`, Server-Stats im Hauptbereich und eine zweite Seite `Infrastructure and Media`. Die rechte Home-Spalte zeigt WAN-Infos aus Speedtest Tracker, Speedtest-Livewerte, AdGuard-DNS-Stats, DNS/Ingress-Monitore und eine separate Netzwerk-Containergruppe.
### 2026-05-17 - Monitoring-Zielstack konsolidiert
- `monitoring/` als zentraler Observability-Zielstack fuer Prometheus, Loki, Promtail, Grafana, node-exporter, cAdvisor und InfluxDB 3 Core vorbereitet.
- `monitoring-grafana` nutzt den Repo-Standard `authelia@file,secure-headers@file` und Secrets per Datei statt Klartext-Stack-ENV.
- `monitoring-influxdb3-core` uebernimmt den LAN-only Writer-Endpunkt fuer Home Assistant (`8181` via `INFLUXDB_BIND_IP`).
- `ops/loki` und `ops/grafana-influxdb` sind abgeloeste Altstaende und bleiben nur als Rollback-/Migrationsreferenz im Repo.
### 2026-05-07 - Vaultwarden Restore-Test praktisch verifiziert
- Erster echter Vaultwarden-Mini-Restore gegen das produktive Borg-Repo `hetzner_borg_appdata_critical` erfolgreich durchgefuehrt.
- Restore lief isoliert nach `/mnt/user/backups/restore-lab/vaultwarden`, nicht gegen produktive Pfade.
- Testinstanz `restoretest-vaultwarden` wurde lokal auf `127.0.0.1:18080` gestartet; HTTP 200 und Login-Seite wurden erfolgreich bestaetigt.
- Report wurde unter `/mnt/user/backups/restore-reports/vaultwarden-2026-05-07.md` geschrieben.
- Fuer den praktischen Restore-Pfad wurden zwei hostseitige Voraussetzungen sichtbar und umgesetzt:
- `known_hosts` fuer das Hetzner-Ziel im `borg-ui`-Container
- Host-Secret-Datei `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` fuer kuenftige Restore-Tests
- Testdaten unter `/mnt/user/backups/restore-lab/vaultwarden/data` wurden nach erfolgreichem Lauf wieder bereinigt.
### 2026-05-07 - Gitea Restore-Test praktisch verifiziert
- Erster echter Gitea-Mini-Restore gegen das produktive Borg-Repo `hetzner_borg_appdata_critical` erfolgreich durchgefuehrt.
- Restore lief isoliert nach `/mnt/user/backups/restore-lab/gitea`, nicht gegen produktive Pfade.
- Testinstanz `restoretest-gitea` wurde lokal auf `127.0.0.1:13000` und `127.0.0.1:12222` gestartet.
- HTTP 200, HTML-Titel und lokaler SSH-Port wurden erfolgreich bestaetigt.
- Report wurde unter `/mnt/user/backups/restore-reports/gitea-2026-05-07.md` geschrieben.
- Testdaten unter `/mnt/user/backups/restore-lab/gitea/data` wurden nach erfolgreichem Lauf wieder bereinigt.
### 2026-05-07 - Paperless Restore-Test praktisch verifiziert
- Erster echter Paperless-Mini-Restore gegen das produktive Borg-Repo `hetzner_borg_appdata_critical` erfolgreich durchgefuehrt.
- Restore umfasste sowohl die Dateipfade als auch `postgresql17-paperless.dump` aus dem Borg-Archiv.
- Testinstanzen `restoretest-paperless`, `restoretest-paperless-postgres` und `restoretest-paperless-redis` liefen isoliert ohne Traefik.
- Login-Seite war lokal auf `127.0.0.1:18120` erreichbar.
- Der Dump-Import in Test-Postgres war erfolgreich; die Test-Datenbank enthielt `25` Dokumente.
- Report wurde unter `/mnt/user/backups/restore-reports/paperless-2026-05-07.md` geschrieben.
- Testdaten unter `/mnt/user/backups/restore-lab/paperless` wurden nach erfolgreichem Lauf wieder bereinigt.
### 2026-05-06 - Komodo Webhook Secret getrennt
- `KOMODO_WEBHOOK_SECRET` von `KOMODO_SECRET_KEY` getrennt und als eigene Stack-ENV-Variable dokumentiert.
- Gitea-Komodo-Webhooks mit bisherigem Core-Secret wurden auf den neuen `KOMODO_WEBHOOK_SECRET` umgestellt; bereits individuelle per-Stack-Webhook-Secrets wurden beibehalten.
- Host-`.env`, persistente Komodo-Compose und Gitea-Webhooks wurden als ein gemeinsamer Runtime-Schritt behandelt, damit Auto-Deploys nicht auseinanderlaufen.
- Ein stale Gitea-Webhook auf eine nicht mehr vorhandene Komodo-Stack-ID wurde deaktiviert, nicht geloescht.
### 2026-05-06 - Authelia GMX SMTP Notifier
- Authelia-Notifier von Filesystem-Log auf GMX SMTP (`submission://mail.gmx.net:587`) umgestellt.
- SMTP-Passwort bleibt ausserhalb des Repos unter `/mnt/user/appdata/secrets/authelia_smtp_password.txt`.
- Authelia-Compose erhaelt explizite DNS-Server, weil der SMTP-Startup-Check externe Namen wie `mail.gmx.net` aufloesen muss.
- Repo-Baseline und Host-Config muessen bei Auth-Aenderungen weiter bewusst gemerged und vor Restart validiert werden.
### 2026-05-06 - Hermes DR und Mail-Archiver Authelia
- Hermes Agent in `docs/RESTORE_MATRIX.md` und `docs/DISASTER_RECOVERY.md` mit Restore-Pfaden, Secret-/ENV-Hinweisen und Smoke-Test ergaenzt.
- Mail-Archiver Web-UI hinter `authelia@file,secure-headers@file` gelegt; App-eigene Auth bleibt als zweite Schutzschicht bestehen.
- M10/Komodo blieb unveraendert.
### 2026-05-05 - N-Aufraeum-Sprint
- Obsolete Compose-Top-Level-Felder `version: "3.9"` aus Immich, Mail Archiver und Paperless entfernt.
- Leere `env/domains.env.example` und `env/global.env.example` mit nicht geheimen Beispielwerten gefuellt.
- Veraltete `.keep`-Platzhalter aus Verzeichnissen mit echten Compose-/Repo-Inhalten sowie zwei reine Geister-Verzeichnisse (`host-services/plex`, `infra/dns`) entfernt.
### 2026-05-16 - Backup-Konsistenz und erster Hardening-Schnitt
- SQLite-Dumps fuer Gitea, Vaultwarden, Speedtest Tracker und Filebrowser werden containerseitig als `*.sqlite.dump` erzeugt und per Freshness-Check geprueft; Uptime Kuma wurde am 2026-05-25 aus dem aktiven Dump-Scope entfernt.
- `nextcloud.dump` und die Nextcloud-Userdaten sind als Option A im Borg-Scope dokumentiert.
- Filebrowser mountet keine breite `/mnt/user/appdata`-Flaeche mehr, sondern nur noch Documents, Photos, Projekte sowie eigenen App-State.
- Authelia Argon2id-Parameter in der Repo-Baseline auf `iterations: 3`, `memory: 65536`, `parallelism: 4` gesetzt; produktive Host-Config muss kontrolliert gemerged und mit Test-User validiert werden.
- Redis-Caches wurden auf `redis:7.4-alpine@sha256:...` vereinheitlicht; Nextcloud wurde mit Registry-validiertem Digest gepinnt.
- Eindeutig aufloesbare `latest@sha256`-Images wurden auf konkrete Tags umgestellt: Homepage `v1.12.3`, code-server `4.116.0`, Filebrowser `v2.63.2`, Speedtest Tracker `1.13.12`.
### 2026-05-05 - M3b versionierte App-Images digest-gepinnt
- Versionierte Nicht-Komodo-Images fuer BentoPDF, Mealie, Paperless, Paperless-GPT, AdGuard Home, Grafana, InfluxDB 3 Core und Traefik auf die am Host laufenden, manifest-validierten Digests gepinnt.
- `nextcloud:33.0.2-apache` wurde bewusst nicht in diesem Schritt gepinnt, weil der lokal gelistete Digest nicht als Registry-Manifest fuer `tag@sha256` validierbar war.
- Redis-Caches und Komodo/M10 blieben unveraendert.
### 2026-05-05 - M6/M7/M8 Doku-Konsolidierung
- `hermes.kaleschke.info` als produktive Hermes-Dashboard-Route hinter Traefik + Authelia in Architektur, Repo-Map und Service-Katalog ergaenzt.
- `grafana` und `influxdb3-core` laufen weiterhin als `user: "0"`; das wurde als Host-Appdata-Permissions-Ausnahme dokumentiert und nicht nebenbei geaendert.
- Tailscale-Ausnahme um `NET_ADMIN`, `NET_RAW` und `/dev/net/tun` ergaenzt.
- Komodo-Secret-/Webhook-Themen wurden bewusst nicht geaendert; Komodo-Aenderungen erfolgen nur gemeinsam mit dem Betreiber.
### 2026-05-05 - M3a stateful Digest-Pinning
- PostgreSQL 17 Datenhalter auf `postgres:17.9@sha256:5b96f1a16bd9768b060dd2ffe55cb6225c4d9ef4d214a8b21eb08134869a97e4` gepinnt (`postgresql17`, `mealie-postgres`, `nextcloud-postgres`).
- Immich pgvector-Postgres auf `tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52` gepinnt.
- Komodo Mongo auf `mongo:7.0.32@sha256:32979a1189dfdc44da3f5ed40d910495f5ad8f6f7f77556646f890a30b2d3f56` sowie Komodo Core/Periphery und Gitea auf die am Host laufenden Digests gepinnt.
- Redis-Caches wurden am 2026-05-16 auf `redis:7.4-alpine@sha256:...` vereinheitlicht; Redeploys erfolgen stackweise mit Smoke-Test, nicht parallel.
### 2026-05-04 - Komodo Self-Stack Drift auf persistenten Pfad zurueckgefuehrt
- Drift-Befund: `komodo-core` und `komodo-periphery` liefen aus `/tmp/komodo-core-repair.yml` bzw. `/tmp/komodo-periphery-repair.yml`; `komodo-mongo` verwies auf `/mnt/user/services/stacks/komodo/compose.yaml`, obwohl dieser Pfad fehlte.
- Vor Eingriff wurden die Repair-Dateien und zugehoerigen `/tmp/*.env`-Dateien unter `/mnt/user/appdata/komodo/_drift_backup_2026-05-04/` gesichert.
- Zusaetzlich wurde eine geschuetzte Recovery-ENV unter `/mnt/user/appdata/secrets/_komodo_stack_env_recovery_2026-05-04.env` abgelegt; diese Datei enthaelt Tier-1-Secret-Material und ist kein Dauerzustand.
- Vor dem Reconcile wurde das host-seitige Dump-Skript ausgefuehrt; `komodo-mongo.archive.gz` wurde frisch unter `/mnt/user/backups/borg/dumps/latest/` erzeugt.
- Persistenter Self-Stack wurde unter `/mnt/user/services/stacks/komodo/compose.yaml` aus `ops/komodo/docker-compose.yml` wiederhergestellt; `.env` wurde hostseitig aus der bestehenden Runtime-ENV abgeleitet.
- Der vollstaendige Dry-run haette auch `komodo-mongo` recreated und wurde daher nicht ausgefuehrt. Stattdessen wurden nur `komodo-core` und `komodo-periphery` gezielt mit `--no-deps --force-recreate` aus dem persistenten Pfad neu erstellt; `komodo-mongo` blieb unveraendert healthy.
- Smoke-Tests: `docker compose ls` zeigt fuer `komodo` nur noch `/mnt/user/services/stacks/komodo/compose.yaml`, Mongo pingt `{ ok: 1 }`, `https://komodo.kaleschke.info` liefert HTTP 200, und Periphery meldet sich am Core an.
- Die `/tmp/*repair.yml`-Dateien bleiben vorerst als Altlast erhalten und duerfen erst nach stabiler Laufzeit bewusst entfernt oder ins Drift-Backup verschoben werden.
### 2026-05-04 - Authelia ACL-Drift hostseitig gemerged
- Die produktive Authelia-Config ist groesser als die Repo-Datei, weil sie hostseitige OIDC-/Secret-Konfiguration enthaelt. Die Repo-Datei wurde daher als nicht geheime Baseline eingeordnet und nicht blind auf den Host kopiert.
- Host-Backup vor Aenderung: `/mnt/user/appdata/authelia/config/configuration.yml.bak-20260504-acl-sync`.
- Minimaler Host-Merge: `homepage.kaleschke.info` wurde aus der bypass-Liste entfernt, `komodo.kaleschke.info` aus der 2FA-Liste entfernt, und `default_redirection_url` wurde auf `https://home.kaleschke.info` gesetzt.
- `authelia validate-config` war erfolgreich; Authelia wurde neu gestartet und war danach healthy.
- Smoke-Tests: `home.kaleschke.info` liefert fuer anonyme Requests eine Authelia-Weiterleitung, `komodo.kaleschke.info` bleibt ueber native Komodo-Auth erreichbar.
### 2026-05-04 - Home Assistant InfluxDB LAN-Port und Drift-Runbook
- `influxdb3-core` fuer Home-Assistant-Writer auf LAN-Port `8181` vorbereitet und deployed.
- InfluxDB bleibt ohne Traefik-/Public-Route und haengt nicht im `frontend_net`.
- Fuer aktives Docker Host-Port-Publishing wurde zusaetzlich zum internen `grafana_influx_internal` das Compose-Netz `grafana_influx_lan` ergaenzt.
- Komodo Periphery dauerhaft um `/mnt/user/services:/mnt/user/services` und `frontend_net` ergaenzt, damit Stack-Workspaces und Gitea-Zugriff reproduzierbar funktionieren.
- `docs/GITOPS_DRIFT_RUNBOOK.md` angelegt, um lokale Git-Kopie, Gitea, Komodo Workspace, Docker Runtime und Host-Listener getrennt zu pruefen.
### 2026-03-28 - GitOps-Konsolidierung
- Komodo als primaeren Stack-Manager eingefuehrt.
- Portainer aus dem Zielbild herausgenommen.
- Traefik auf 100% Docker-Labels konsolidiert.
- `diun` entfernt; Update-Monitoring wird ueber Komodo abgedeckt.
### 2026-03-29 - Portainer abgeschaltet
- Portainer CE aus dem produktiven Betrieb entfernt.
- Komodo als alleinigen Stack-Manager festgezogen.
### 2026-04-13 bis 2026-04-15 - Borg-Rollout abgeschlossen
- `critical_infra` erfolgreich nach Borg gesichert.
- Pre-Backup-Dumps host-seitig ueber Unraid User Scripts etabliert.
- Dump-Zielpfad auf `/mnt/user/backups/borg/dumps` umgestellt.
- Restore-Smoke-Test fuer `postgresql17-globals.sql` und `gitea.db` erfolgreich nachgewiesen.
- Monitoring fuer Borg war historisch ueber `ntfy` und Uptime Kuma eingerichtet; seit 2026-05-25 ersetzt durch `ntfy`, Blackbox/Prometheus und Monitoring Grafana.
### 2026-04-15 - Repo- und Betriebsbereinigung
- Firefly, Firefly-Fints und Semaphore aus Repo und Homelab entfernt.
- GitHub Desktop als Standard-Workflow fuer den lokalen Sync festgelegt.
### 2026-04-17 - Sicherheits- und Doku-Abgleich
- `code-server` hinter `authelia@file,secure-headers@file` abgesichert.
- Traefik-Dashboard von `dashboard-auth@file` auf `authelia@file,secure-headers@file` umgestellt; BasicAuth-Hash aus dem Repo entfernt.
- Redis von Klartext in der Compose auf Secret-Datei unter `/mnt/user/appdata/secrets/redis_password.txt` umgestellt.
- Redis-Passwort bewusst **nicht** rotiert; Live-Passwort bleibt vorerst unveraendert.
- `mail-archiver` in der Architektur-Doku an den realen Traefik-Betrieb angepasst.
- `paperless-gpt` von `LOG_LEVEL=debug` auf `info` umgestellt.
- `speedtest-tracker` von `APP_DEBUG=true` auf `false` umgestellt.
- Mutable Image-Tags fuer produktive Stacks auf die aktuell laufenden Digests eingefroren, um Deployments reproduzierbar zu machen.
- `paperless-ngx` bleibt fuer `PAPERLESS_DBPASS` und `PAPERLESS_REDIS` vorerst bewusst bei Stack Environment Variables; keine Live-Migration auf `_FILE`, solange der aktuelle Stand stabil laeuft.
- Disaster-Recovery-Runbook und Restore-Matrix fuer den Totalausfall-/Wiederanlauf-Fall neu dokumentiert.
### 2026-04-19 - paperless-gpt Digest-Pin zurueckgenommen
- Der fuer `paperless-gpt` eingetragene Digest war syntaktisch ungueltig (63 statt 64 Hex-Zeichen) und wurde daher wieder auf `icereed/paperless-gpt:latest` zurueckgesetzt.
- Diese Ruecknahme ist bewusst eng auf einen einzelnen defekten Pin begrenzt und aendert keine anderen Digest-Festschreibungen.
- Die zwischenzeitlichen OCR-/Versions-Experimente fuer `paperless-gpt` wurden wieder auf den einfachen vorherigen Stand zurueckgenommen (`icereed/paperless-gpt:latest`, `VISION_LLM_MODEL=cnshenyang/qwen3-nothink:14b`), um den letzten bekannten Alltagszustand wiederherzustellen.
### 2026-04-19 - Nextcloud und Stirling-PDF vorbereitet
- `apps/nextcloud/docker-compose.yml` als offizieller Docker-Microservice-Stack mit `nextcloud:apache`, eigener PostgreSQL-Datenbank und eigenem Redis vorbereitet.
- Nextcloud folgt dem Repo-Standard `frontend_net` + app-internes Netz, nutzt `_FILE`-Secrets fuer Admin- und DB-Passwort und ist bewusst **nicht** hinter zentraler ForwardAuth, damit WebDAV/CardDAV und native Clients sauber funktionieren.
- `apps/stirling-pdf/docker-compose.yml` als geschuetzter Tool-Stack hinter `authelia@file,secure-headers@file` vorbereitet.
- Stirling-PDF nutzt persistente Pfade fuer `/configs`, `/logs`, `/pipeline`, `/customFiles` und `/usr/share/tessdata`; interne Stirling-Login-Funktion bleibt zugunsten des zentralen Traefik-/Authelia-Zugangs deaktiviert.
### 2026-04-30 - BentoPDF und Grafana/InfluxDB vorbereitet
- `stirling-pdf` repo-seitig durch `bentopdf` ersetzt; Domain `pdf.kaleschke.info` bleibt erhalten.
- BentoPDF laeuft als geschuetztes browserseitiges PDF-Tool hinter `authelia@file,secure-headers@file` und setzt zusaetzlich COOP/COEP-Header fuer SharedArrayBuffer-basierte Office-Konvertierung.
- `ops/grafana-influxdb` als neuer Monitoring-Stack vorbereitet und spaeter in Betrieb genommen.
- Grafana laeuft hinter Traefik + Authelia unter `grafana.kaleschke.info`.
- InfluxDB 3 Core bleibt ohne Public Route und wird ueber eine provisionierte Grafana-Datenquelle angebunden.
- Secrets fuer Grafana-Admin-Passwort, InfluxDB-Admin-Token und Grafana-Datasource-Token sind als Host-Dateien unter `/mnt/user/appdata/secrets/` dokumentiert.
---
## Dauerhafte Learnings
- Kein Live-Editing in Komodo; Git gewinnt immer gegen manuelle Drift.
- Webhooks koennen nach einem Push sofort einen Deploy ausloesen.
- Rollback soll bevorzugt ueber saubere Git-Commits und bekannte Good States erfolgen, nicht ueber History-Rewrites auf `master`.
- Doku soll Endzustaende beschreiben, nicht veraltete Zwischenstaende konservieren.
+1 -1
View File
@@ -84,7 +84,7 @@ Bewusst **nicht** freigegeben:
| `Kallilabcore` (192.168.178.58) | nicht erlaubt | Repo-managed; alle benoetigten Public-Ports sind explizite Freigaben | | `Kallilabcore` (192.168.178.58) | nicht erlaubt | Repo-managed; alle benoetigten Public-Ports sind explizite Freigaben |
| `PC-192-168-178-71` / VONETS-Adapter (192.168.178.71, MAC 00:17:13:2F:61:96) | **2026-05-28 deaktiviert** | wahrscheinlich VONETS-WiFi-Bridge fuer SolarEdge-Wechselrichter; SolarEdge-Cloud-Sync ist ausschliesslich outbound, eingehende Ports sind nicht erforderlich | | `PC-192-168-178-71` / VONETS-Adapter (192.168.178.71, MAC 00:17:13:2F:61:96) | **2026-05-28 deaktiviert** | wahrscheinlich VONETS-WiFi-Bridge fuer SolarEdge-Wechselrichter; SolarEdge-Cloud-Sync ist ausschliesslich outbound, eingehende Ports sind nicht erforderlich |
Sollten neue Geraete UPnP-Selbstfreigaben anfordern, wird das in `docs/MIGRATION_LOG.md` und hier als bewusste Ausnahme dokumentiert oder pro Geraet wieder deaktiviert. Sollten neue Geraete UPnP-Selbstfreigaben anfordern, wird das hier als bewusste Ausnahme dokumentiert oder pro Geraet wieder deaktiviert.
Historischer UI-Befund vor Bereinigung vom 2026-05-27 (`Internet -> Freigaben -> Kallilabcore`): Historischer UI-Befund vor Bereinigung vom 2026-05-27 (`Internet -> Freigaben -> Kallilabcore`):
-76
View File
@@ -1,76 +0,0 @@
# Offsite-Backup-Entscheidung - KalliLab CORE
Status: **Operator-Entscheidung 2026-05-28: bewusst KEIN zweites Off-site.** Doku bleibt als Begruendungs-Anker und fuer zukuenftige Reviews.
Audit-Bezug: `docs/archive/2026-05/AUDIT_2026-05-25.md` Finding **F-03** und `docs/AUDIT_2026-05-25_TODO.md` Sprint 7.
## Beschluss 2026-05-28
Aktuelle Backup-Topologie:
| Kopie | Wo | Medium | Standort |
|---|---|---|---|
| 1 | Live-Daten Unraid (Cache + Disk1) | NVMe + HDD | Heim |
| 2 | Borg-Repo lokal `/mnt/user/backups/borg/` | HDD (Disk1) | Heim |
| 3 | Borg-Repo Hetzner Storagebox | Hetzner | Off-site (DE) |
| 4 | H:/ Nearline-Pull am Windows-PC | externe HDD | Heim |
**3-2-1-Regel ist erfuellt:** 4 Kopien, 3 Medien, 1 Off-site (Hetzner).
Ein zweites Off-site wuerde aktuell nur das Szenario "Hetzner-Account verloren" zusaetzlich abdecken. Operator-Bewertung: Wahrscheinlichkeit niedrig, Aufwand und laufende Kosten unverhaeltnismaessig zur Risikoreduktion fuer ein privates Familien-Homelab.
Statt zweitem Off-site werden Hetzner-Account- und Repo-Haertungen als Folge-TODOs gefuehrt:
1. Hetzner-Account: starkes, einzigartiges Passwort in Vaultwarden + Backup-Zahlungsweg + Login-Benachrichtigungen per E-Mail. **Bewusst keine 2FA** (Operator-Entscheidung 2026-05-28 analog zur USV-Risiko-Akzeptanz).
2. Borg `--append-only`-Mode pruefen. Setup erfolgt server-seitig in Hetzner `~/.ssh/authorized_keys` mit `command="borg serve --append-only"` fuer den Backup-Key.
3. H:/ Pull ist seit 2026-05-28 als Windows Scheduled Task aktiv (Anker `docs/H_DRIVE_NEARLINE_PULL.md`) und kein offener Punkt mehr.
## Review-Trigger
Diese Entscheidung wird neu bewertet, wenn:
- Hetzner-Account-Probleme tatsaechlich auftreten (Payment-Issue, Login-Sperre)
- Hetzner als Anbieter strukturelle Probleme zeigt (Insolvenz-Geruechte, AGB-Aenderungen)
- im Homelab Daten mit deutlich hoeherem Wiederbeschaffungs-Aufwand dazukommen
- Operator-Praeferenzen sich aendern
## Historische Entscheidungsgrundlage
Dieser Abschnitt bleibt als Begruendungs-Anker erhalten. Er ist **keine aktuelle Beschaffungsliste**. Die Entscheidung vom 2026-05-28 bleibt: kein zweites Off-site-Ziel, solange keiner der Review-Trigger eintritt.
`H:/` am Windows-PC bleibt **kein** Offsite-Ersatz (siehe `docs/CAPACITY_AND_LIFECYCLE.md`). H:/ ist Nearline-Kopie am gleichen Standort.
### Option A - rsync.net Borg-Plan
- anderer Anbieter als Hetzner
- Borg-kompatibel per SSH
- ZFS-Snapshot-Schutz als zusaetzlicher Schutz vor Repo-Loeschung
- grob teurer als Hetzner, aber gute Anbieter-Trennung
### Option B - BorgBase EU2
- Borg-kompatibel und einfach
- andere Region, aber nicht vollstaendig getrenntes Anbieter-/Account-Risiko
- deshalb nicht als bevorzugtes zweites Ziel bewertet
### Option C - Rotierende Cold-Wechselplatte ausser Haus
- echte Air-Gap-/Offline-Kopie
- keine Provider-Abhaengigkeit
- manuelle Disziplin noetig
- nicht so taggenau wie Cloud-Borg
## Reaktivierungsplan bei Review-Trigger
1. Review-Trigger konkret benennen und in `docs/MIGRATION_LOG.md` dokumentieren.
2. Operator-Entscheidung Option A vs. C neu bestaetigen.
3. Falls Option A: Provider anlegen, Borg-Repo initialisieren, Borg-UI als zweites Repo ergaenzen, Secrets-/External-/Restore-Doku nachziehen.
4. Falls Option C: zwei Platten beschaffen, Borg-Repos initialisieren, Rotationsplan in `docs/STORAGE_LAYOUT.md` ergaenzen.
## Offene Punkte
| Status | Punkt | Naechster Schritt |
|---|---|---|
| offen | Hetzner-Account-Hygiene | Starkes einzigartiges Passwort, Backup-Zahlungsweg und Login-Benachrichtigung extern bestaetigen |
| offen | Borg `--append-only` fuer Hetzner pruefen | Server-seitige Hetzner-SSH-Konfiguration vorbereiten und Rollback-Pfad dokumentieren |
| erledigt 2026-05-28 | H:/ Nearline-Pull aktivieren | Windows Scheduled Task `KalliLab H Drive Nearline Pull` laeuft taeglich 05:30 |
| bewusst nicht umgesetzt | zweites Off-site-Ziel | Erst bei Review-Trigger neu entscheiden |
-122
View File
@@ -1,122 +0,0 @@
# Post-Migration Burn-in Check - 2026-05-31
Stand: 2026-05-31 21:45 MESZ
## Ergebnis
Der Nachlauf nach den Stateful-Migrationen ist gruen. Es gibt keine offenen Renovate-PRs, keine `unhealthy` Container und die aktuellen Dump-Artefakte wurden nach den Migrationen neu erzeugt und auf Lesbarkeit geprueft.
## Renovate
- Manueller Lauf: `2026-05-31T19-39-01Z`
- Ergebnis: `rc=0`
- Gitea: keine offenen PRs
- Renovate entfernte die verwaisten Branches:
- `renovate/major-major-updates`
- `renovate/postgres-18.x`
- `renovate/redis-8.x`
## Live Burn-in
- `docker ps --filter health=unhealthy`: keine Treffer
- Relevante Laufzeitstaende:
- `postgresql17`: `postgres:18.4`
- `mealie-postgres`: `postgres:18.4`
- `nextcloud-postgres`: `postgres:18.4`
- `immich_postgres`: `ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0`
- `Redis`, `nextcloud-redis`, `immich_redis`: `redis:8.8.0-alpine`
- `monitoring-grafana`: `grafana/grafana:13.0.1`
HTTP-Smoke vom Host:
| Dienst | Status |
|---|---:|
| `https://monitoring.kaleschke.info` | 302 |
| `https://immich.kaleschke.info` | 200 |
| `https://cloud.kaleschke.info` | 302 |
| `https://paperless.kaleschke.info` | 302 |
| `https://mealie.kaleschke.info` | 200 |
| `https://mail.kaleschke.info` | 302 |
Log-Nachlauf:
- `mail-archiver`: fruehere transienten `57P01`-Fehler stammen vom DB-Restart; spaeterer Sync meldet `New: 0, Failed: 0, Deleted: 0`.
- `monitoring-grafana`: nach Härtung keine neuen `level=error`, `permission denied`, `fatal` oder `panic` Treffer.
## Backup- und Dump-Frische
`ops/borg-ui/scripts/pre-backup-dumps.sh` wurde nach den Migrationen erneut ausgefuehrt. Dabei wurde ein Drift behoben: `grafana.sqlite` wird jetzt aus dem aktiven Docker-Volume `monitoring_grafana_data` gelesen, nicht mehr aus dem historischen Pfad `/mnt/user/appdata/grafana`.
Aktuelle relevante Dump-Zeiten:
| Artefakt | Zeit |
|---|---|
| `postgresql17-globals.sql` | 2026-05-31 21:41 |
| `mealie.dump` | 2026-05-31 21:42 |
| `nextcloud.dump` | 2026-05-31 21:42 |
| `immich.dump` | 2026-05-31 21:42 |
| `postgresql17-authelia.dump` | 2026-05-31 21:42 |
| `postgresql17-mailarchiver.dump` | 2026-05-31 21:42 |
| `postgresql17-paperless.dump` | 2026-05-31 21:42 |
| `grafana.sqlite` | 2026-05-31 21:42 |
| `komodo-mongo.archive.gz` | 2026-05-31 21:42 |
Lesbarkeit:
- `pg_restore -l`: ok fuer Mealie, Nextcloud, Immich, Authelia, Mailarchiver, Paperless
- `sqlite3 PRAGMA quick_check`: ok fuer Grafana, Gitea, Vaultwarden, Speedtest, Borg UI
- `gzip -t`: ok fuer `komodo-mongo.archive.gz`
Borg-UI letzter vollstaendiger Backup-Job:
- `#41 completed`, Start `2026-05-31 02:30:13`, Ende `2026-05-31 02:31:26`
- Hinweis: Dieser Borg-Lauf war vor den Tagesmigrationen. Die Dumps sind jetzt frisch; der naechste regulaere Borg-Lauf muss sie off-site aufnehmen.
## Restore-Drill-Vorbereitung
- `run-restore-checks.sh freshness`: Critical `0`, Warnings `0`
- `immich --what-if`: ok, nutzt VectorChord/pgvector-Test-Postgres und Redis 8
- `paperless --what-if`: ok nach Fix der fehlenden Execute-Bits
- Paperless-Restore-Drill wurde am 2026-05-31 erfolgreich nachgezogen: Borg-Archiv `Tägliche-Sicherung-2026-05-31T04:30:13.181`, isolierter PostgreSQL-18-/Redis-8-Testpfad, HTTP `200`, Login-Marker ok, `32` Dokumente im Test-DB-Check. Report: `/mnt/user/backups/restore-reports/paperless-2026-05-31.md`.
Nebenbefund behoben:
- Mehrere Restore-Test-Skripte waren im Git nicht ausfuehrbar. Dateimodus auf `100755` korrigiert.
## Monitoring / Grafana 13
- `/api/health`: `database=ok`, `version=13.0.1`
- SQLite/Unified-Storage-Check:
- `data_source=3`
- `resource` enthaelt `4` Dashboards und `1` Folder
- `unifiedstorage_migration_log=3`
- Grafana-13-Fixes:
- `GF_PLUGINS_PREINSTALL_DISABLED=true`
- leere Provisioning-Verzeichnisse `alerting/` und `plugins/` versioniert
- `user: "0"` gesetzt, damit hostseitige `600 root` Secret-Dateien lesbar bleiben
## Alt-Volumes fuer spaetere Freigabe
Nicht vor Burn-in-Freigabe loeschen.
| Pfad | Zweck | Groesse |
|---|---|---:|
| `/mnt/user/appdata/postgresql17` | Shared PostgreSQL-17-Rollback | 1.8G |
| `/mnt/user/appdata/mealie/postgres` | Mealie PostgreSQL-17-Rollback | 70M |
| `/mnt/user/appdata/nextcloud/postgres` | Nextcloud PostgreSQL-17-Rollback | 71M |
| `/mnt/user/appdata/immich_postgres` | Immich pgvecto.rs-Rollback | 460M |
Aktive Vergleichspfade:
| Pfad | Zweck | Groesse |
|---|---|---:|
| `/mnt/user/appdata/postgresql18` | Shared PostgreSQL 18 aktiv | 2.2G |
| `/mnt/user/appdata/mealie/postgres18` | Mealie PostgreSQL 18 aktiv | 70M |
| `/mnt/user/appdata/nextcloud/postgres18` | Nextcloud PostgreSQL 18 aktiv | 72M |
| `/mnt/user/appdata/immich_postgres_vectorchord` | Immich VectorChord aktiv | 633M |
## Offene bewusste Punkte
- Alt-Volumes bleiben bis zur Erinnerung am 2026-06-02 gesperrt.
- Der naechste regulaere Borg-Lauf soll nachziehen; danach kann die Alt-Volume-Freigabe fundierter entschieden werden.
- Paperless-Restore-Drill ist erledigt; naechster sinnvoller Drill ist Gitea- oder Vaultwarden-Wiederholung gemaess Restore-Drill-Routine.
+2 -15
View File
@@ -2,7 +2,7 @@
Stand: 2026-05-31 Stand: 2026-05-31
Diese Datei trennt aktive Betriebsdokumentation von historischen Snapshots. Neue operative Dokumente duerfen nur in `docs/` liegen, wenn sie heute als Einstieg, Runbook, Inventar oder offene Arbeitsliste gebraucht werden. Erledigte Audits, Chat-Handoffs, Prompt-Dateien und abgeschlossene Plaene gehen nach `docs/archive/YYYY-MM/`. Diese Datei trennt aktive Betriebsdokumentation von historischer Arbeitsdoku. Neue operative Dokumente duerfen nur in `docs/` liegen, wenn sie heute als Einstieg, Runbook, Inventar oder offene Arbeitsliste gebraucht werden. Erledigte Audits, Chat-Handoffs, Prompt-Dateien und abgeschlossene Plaene bleiben in der Git-Historie, aber nicht als dauerhafte Arbeitskopie.
## Pflicht-Einstieg ## Pflicht-Einstieg
@@ -21,7 +21,6 @@ Diese Datei trennt aktive Betriebsdokumentation von historischen Snapshots. Neue
| `DISASTER_RECOVERY.md` | Wiederanlauf nach Host-/Systemausfall | | `DISASTER_RECOVERY.md` | Wiederanlauf nach Host-/Systemausfall |
| `RESTORE_MATRIX.md` | Restore-Quellen, Dumps, Secrets und Smoke-Tests je Dienst | | `RESTORE_MATRIX.md` | Restore-Quellen, Dumps, Secrets und Smoke-Tests je Dienst |
| `RESTORE_HANDBOOK.md` | praktische Restore-Anleitung | | `RESTORE_HANDBOOK.md` | praktische Restore-Anleitung |
| `RESTORE_DRILL_ROUTINE.md` | regelmaessige Restore-Drills |
| `SERVICES_RECOVERY.md` | Gitea-/Komodo-/Services-Bootstrap | | `SERVICES_RECOVERY.md` | Gitea-/Komodo-/Services-Bootstrap |
| `ROLLBACK.md` | Rueckweg bei GitOps-/Deploy-Fehlern | | `ROLLBACK.md` | Rueckweg bei GitOps-/Deploy-Fehlern |
| `GITOPS_DRIFT_RUNBOOK.md` | Pflichtmatrix bei Drift zwischen Git, Komodo, Docker und Host | | `GITOPS_DRIFT_RUNBOOK.md` | Pflichtmatrix bei Drift zwischen Git, Komodo, Docker und Host |
@@ -42,28 +41,16 @@ Diese Datei trennt aktive Betriebsdokumentation von historischen Snapshots. Neue
| Datei | Zweck | | Datei | Zweck |
|---|---| |---|---|
| `ALERT_RULES.md` | Prometheus-/ntfy-Regeln und Handlungslogik | | `ALERT_RULES.md` | Prometheus-/ntfy-Regeln und Handlungslogik |
| `ALERTING_MAP.md` | ntfy Topics und Sender-Konvention |
| `RENOVATE.md` | Self-hosted Renovate gegen Gitea | | `RENOVATE.md` | Self-hosted Renovate gegen Gitea |
| `HOME_ASSISTANT_INFLUXDB_ECOWITT.md` | Home Assistant -> InfluxDB 3 -> Grafana | | `HOME_ASSISTANT_INFLUXDB_ECOWITT.md` | Home Assistant -> InfluxDB 3 -> Grafana |
| `H_DRIVE_NEARLINE_PULL.md` | Windows-H:/ Nearline-Pull fuer kritische Restore-Artefakte | | `H_DRIVE_NEARLINE_PULL.md` | Windows-H:/ Nearline-Pull fuer kritische Restore-Artefakte |
| `IMMICH_RESTORE_TEST.md` | Immich-Restore-Test-Overview |
## Nutzer- und Planungsdoku ## Nutzer- und Planungsdoku
| Datei | Zweck | | Datei | Zweck |
|---|---| |---|---|
| `FAMILY_ONBOARDING.md` | familienverstaendliche Nutzungsdoku | | `FAMILY_ONBOARDING.md` | familienverstaendliche Nutzungsdoku |
| `FAMILY_VIEW_DASHBOARD.md` | Spezifikation fuer das Family-View-Dashboard | | `AUDIT_2026-05-25_TODO.md` | kompakte Restliste aus dem Audit-Zyklus |
| `OFFSITE_BACKUP_OPTIONS.md` | dokumentierte Offsite-Entscheidung und Review-Trigger |
| `AUDIT_2026-05-25_TODO.md` | verbleibende/parkende Aufgaben aus dem Audit-Zyklus |
| `POST_MIGRATION_BURN_IN_2026-05-31.md` | aktueller Burn-in-Nachlauf nach den Stateful-Migrationen |
| `AI_CONTEXT.md` | kompakter Kontext fuer KI-Agenten | | `AI_CONTEXT.md` | kompakter Kontext fuer KI-Agenten |
| `MIGRATION_LOG.md` | historischer Verlauf; kein Primaer-Runbook |
## Archiv
| Pfad | Inhalt |
|---|---|
| `archive/2026-05/` | alte Audits, Chat-Handoffs, Codex-Prompts und erledigte Aktionsplaene aus Mai 2026 |
Windows-Neuaufsetzen-Dokumente liegen nicht mehr in `docs/`, sondern im fachlich passenden Ordner `../ops/windows-reinstall/docs/`. Windows-Neuaufsetzen-Dokumente liegen nicht mehr in `docs/`, sondern im fachlich passenden Ordner `../ops/windows-reinstall/docs/`.
+1 -1
View File
@@ -1,7 +1,7 @@
# Renovate Bot - Self-hosted gegen Gitea # Renovate Bot - Self-hosted gegen Gitea
Status: **live seit 2026-05-29**; PAT, State-Verzeichnis und Cron sind auf dem Host aktiv. Status: **live seit 2026-05-29**; PAT, State-Verzeichnis und Cron sind auf dem Host aktiv.
Audit-Bezug: `docs/archive/2026-05/AUDIT_2026-05-25.md` Finding **F-12**. Audit-Bezug: Mai-2026-Audit Finding **F-12**.
## Zweck ## Zweck
+37 -240
View File
@@ -2,257 +2,54 @@
Stand: 2026-05-31 Stand: 2026-05-31
Diese Datei ist eine technische Landkarte des Repositories. Sie wurde aus Markdown-Dokumenten, `docker-compose.yml`-Dateien, Env-Beispielen, Traefik-Dynamic-Configs, Komodo/Periphery-Dateien und Skripten abgeleitet. Sie beschreibt den Repo-Sollzustand, nicht zwingend den Live-Zustand auf dem Host. Kurzkarte des Repositories. Diese Datei ist bewusst kein zweites Handbuch; fuer
Details gilt immer die betroffene Compose-Datei oder das jeweilige Runbook.
Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennamen, Secret-Namen und Pfade. ## Top-Level
## Ordnerstruktur
| Pfad | Zweck | | Pfad | Zweck |
|---|---| |---|---|
| `apps/` | Produktive Anwendungen und vorbereitete App-Stacks | | `apps/` | produktive Anwendungen und vorbereitete App-Stacks |
| `core/` | Basisdienste, aktuell Gitea | | `core/` | Basisdienste, aktuell Gitea |
| `docs/` | Betriebsdokumentation, Restore, Rollback, GitOps-Regeln | | `docs/` | aktive Betriebsdoku, Restore, Inventare, Arbeitsregeln |
| `env/` | globale nicht geheime Beispiel-Env-Dateien | | `env/` | nicht geheime Beispiel-Env-Dateien |
| `host-services/` | Host-nahe Dienste mit direkten Ports oder Host-Netz | | `host-services/` | host-nahe Dienste mit direkten Ports oder Host-Netz |
| `infra/` | technische Infrastruktur wie PostgreSQL, Redis, DDNS | | `infra/` | technische Infrastruktur wie PostgreSQL, Redis, DDNS |
| `ops/` | Operations-, Backup-, Monitoring- und Admin-Tools | | `monitoring/` | Prometheus, Grafana, Loki, InfluxDB 3 Core |
| `services/` | Host-seitige Betriebsskripte und Recovery-kritische Service-Hilfen | | `ops/` | Admin-, Backup-, Restore- und Wartungswerkzeuge |
| `security/` | Identity/Security-Dienste wie Authelia und Vaultwarden | | `security/` | Authelia, Vaultwarden und Security-Konfiguration |
| `traefik/` | Reverse Proxy Compose und dynamic File-Provider-Konfiguration | | `services/` | Host-seitige Betriebsskripte und Recovery-Hilfen |
| `traefik/` | Reverse Proxy und dynamic File-Provider-Konfiguration |
## Wichtige Dokumente ## Einstiegspunkte
| Datei | Bedeutung | | Datei | Wann lesen |
|---|---| |---|---|
| `README.md` | Einstieg und Kurzueberblick | | `README.md` | Repo-Einstieg |
| `HOMELAB_ARCHITECTURE_MASTER_V2.md` | operative Architektur-Quelle fuer Netzwerk, Zugriff und Ausnahmen | | `HOMELAB_ARCHITECTURE_MASTER_V2.md` | Architektur, Netzmodell, Ausnahmen |
| `docs/README.md` | Doku-Index mit aktiver Doku, Archiv-Regel und Themenclustern | | `docs/WORKFLOW.md` | vor operativen Aenderungen |
| `docs/WORKFLOW.md` | GitOps-/No-Drift-Arbeitsregeln | | `docs/SERVICE_CATALOG.md` | Service-Zweck, Pfade, Besonderheiten |
| `docs/GITOPS_DRIFT_RUNBOOK.md` | Pflichtmatrix fuer Git/Gitea/Komodo/Docker/Host-Drift | | `docs/DISASTER_RECOVERY.md` | echter Wiederanlauf |
| `docs/DISASTER_RECOVERY.md` | Wiederanlauf nach Host-/Systemausfall | | `docs/RESTORE_MATRIX.md` | Restore-Quelle je Dienst |
| `docs/RESTORE_MATRIX.md` | Restore-Quellen, Dump-Artefakte und Smoke-Tests je Dienst | | `docs/SECRETS_MAP.md` | Secret-Namen und Pfade ohne Werte |
| `docs/SERVICES_RECOVERY.md` | Recovery-kritische `/mnt/user/services`-Pfade, Gitea-Mirror und Komodo-Bootstrap | | `docs/GITOPS_DRIFT_RUNBOOK.md` | Git/Gitea/Komodo/Docker/Host-Drift |
| `docs/STORAGE_LAYOUT.md` | verbindliche Storage-/Share-/Pfad-Konstitution | | `docs/AUDIT_2026-05-25_TODO.md` | aktuelle Restliste |
| `docs/HARDWARE_INVENTORY.md` | Hardware-, Disk-, SMART-, USV- und Strom-Inventar |
| `docs/NETWORK_INVENTORY.md` | Router, DNS, Tailscale, Portfreigaben und Netztrennung |
| `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/RENOVATE.md` | Self-hosted Renovate gegen Gitea (Setup + Wartung) |
| `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 |
| `ops/policy-checks/mem-limits-baseline.md` | F-19 Vorbereitungs-Plan fuer Container-Mem-Limits; bewusst nicht vor 7 Tagen Peak-Beobachtung |
| `docs/ALERTING_MAP.md` | ntfy Topic-Konvention und Sender-Mapping fuer Homelab-Alerts |
| `docs/ROLLBACK.md` | Rueckweg bei Fehlern im GitOps-Betrieb |
| `docs/SECRETS_MAP.md` | Secret-Namen, Pfade und Einbindungsarten ohne Werte |
| `docs/HOME_ASSISTANT_INFLUXDB_ECOWITT.md` | Home Assistant -> InfluxDB 3 -> Grafana Ablauf |
| `docs/AI_CONTEXT.md` | Gesamtverstaendnis fuer KI-Agenten |
| `docs/SERVICE_CATALOG.md` | produktiver Service-Katalog |
| `docs/archive/2026-05/` | historische Audits, Handoffs, Codex-Prompts und erledigte Plaene aus Mai 2026 |
## Relevante Nicht-Compose-Dateien ## Wichtige Skripte
| Datei | Zweck / Hinweis | | Datei | Zweck |
|---|---| |---|---|
| `traefik/dynamic/middlewares.yml` | zentrale `secure-headers` und `authelia` ForwardAuth Middleware; manuelle Host-Sync-Ausnahme | | `ops/borg-ui/scripts/pre-backup-dumps.sh` | Dump-Erzeugung vor Borg |
| `traefik/dynamic/dashboards.yml` | leer; File-Provider-Platzhalter | | `ops/borg-ui/scripts/gitea-bundle-mirror.sh` | Gitea-Bundles fuer DR |
| `traefik/dynamic/tls.yml` | leer; File-Provider-Platzhalter | | `ops/restore-tests/run-restore-checks.sh` | Restore-Test-Einstieg |
| `security/authelia/configuration.yml` | versionierte Authelia-Baseline fuer nicht geheime ACL-/Session-/Storage-Einstellungen; manuelle Host-Merge-Pflicht, User-Daten, OIDC-Client-Konfiguration und Secret-Werte bleiben ausserhalb von Git | | `ops/restore-tests/schedule.md` | Restore-Test-Kadenz |
| `monitoring/prometheus/prometheus.yml` | Prometheus Scrape-Konfiguration fuer dedizierten Monitoring-Stack | | `services/posture-check/posture-check.sh` | Host-Posture-Check |
| `monitoring/loki/loki-config.yml` | Loki Filesystem/Retention-Konfiguration fuer dedizierten Monitoring-Stack | | `services/posture-check/export-prometheus-textfile.sh` | Borg-/Container-/Drift-Metriken |
| `monitoring/promtail/promtail-config.yml` | Promtail Docker-Socket-Discovery fuer dedizierten Monitoring-Stack | | `services/authelia-diff.sh` | Authelia ACL Repo-zu-Host-Vergleich |
| `monitoring/grafana/provisioning/*` | Grafana Datasource-/Dashboard-Provisioning fuer Prometheus und Loki | | `ops/h-drive-nearline/pull-critical-backups.ps1` | H:/ Nearline-Pull |
| `ops/glance/config/glance.yml` | Glance Dashboard-Konfiguration fuer Homelab-Monitore, Internet-/DNS-/VPN-Widgets, Community-Widgets, Docker-Containergruppen, Zeitfortschritt, Host-Snapshot, Bookmarks und zweite Infrastruktur-Seite |
| `ops/borg-ui/scripts/pre-backup-dumps.sh` | Host-seitiges Dump-Skript fuer PostgreSQL, SQLite-Container-Dumps und Komodo Mongo |
| `services/posture-check/posture-check.sh` | Host-seitiger Posture-Check fuer Filesystem, Mover-Drift, NVMe-SMART, Fuellstand, Authelia-Repo<->Host-Drift und ntfy-Alarmierung |
| `services/posture-check/export-prometheus-textfile.sh` | Host-seitiger Textfile-Exporter fuer Borg-, Critical-Container- und GitOps-Runtime-Image-Drift-Metriken |
| `services/posture-check/docker-critical-events.sh` | Host-seitiger Docker-Event-Watcher fuer kritische ntfy-Alarme |
| `services/posture-check/posture_check.sh` | Kompatibilitaets-Wrapper fuer die historische Schreibweise aus `STORAGE_LAYOUT.draft.md` |
| `services/authelia-diff.sh` | Vergleicht `access_control:`-Sektion zwischen Repo-Baseline und Host-Datei; wird vom Posture-Check als Check `authelia_config_drift` aufgerufen |
| `ops/hermes-agent/config/hermes/config.yaml` | Hermes Agent Konfiguration mit Env-Platzhaltern |
| `ops/hermes-agent/hermes.env.example` | Beispiel fuer Hermes `.env`; echte Datei liegt auf Host-Appdata |
| `ops/hermes-agent/stack.env.example` | Beispiel fuer Hermes Stack-ENV; echte `stack.env` bleibt host-/komodoseitig und ist per `.gitignore` ausgeschlossen |
| `monitoring/stack.env.example` | `INFLUXDB_BIND_IP` Default `127.0.0.1`; im Zielzustand fuer Home Assistant auf LAN-IP setzen |
| `ops/komodo/stack.env.example` | Komodo Stack-ENV-Beispiel, Secret-Werte nicht enthalten |
## Stack-Inventar ## Arbeitsregel
### Apps Neue Doku nur anlegen, wenn sie dauerhaft als Runbook, Inventar oder Restliste
gebraucht wird. Einmalige Audits, Prompt-Kopien und lange Verlaufsprotokolle
| Stack | Compose | Services / Images | Traefik Hosts | Networks | Ports | Abhaengigkeiten | gehoeren in Git-Commits, nicht als neue Markdown-Dateien.
|---|---|---|---|---|---|---|
| BentoPDF | `apps/bentopdf/docker-compose.yml` | `bentopdf` -> `bentopdfteam/bentopdf:2.8.4` | `pdf.kaleschke.info` | `frontend_net` | keine | Traefik + Authelia; COOP/COEP Middleware |
| Immich | `apps/immich/docker-compose.yml` | `immich-server`, `immich-machine-learning`, `database`, `redis` | `immich.kaleschke.info` | `frontend_net`, `immich_default` | keine | `immich-server` depends on `database`, `redis` |
| Mail Archiver | `apps/mail-archiver/docker-compose.yml` | `mail-archiver` -> `s1t5/mailarchiver@sha256:...` | `mail.kaleschke.info` | `frontend_net`, `backend_net` | keine | shared PostgreSQL via env connection string; Internet fuer IMAP |
| Mealie | `apps/mealie/docker-compose.yml` | `mealie`, `mealie-postgres` | `mealie.kaleschke.info` | `frontend_net`, `mealie_internal` | keine | eigene PostgreSQL im internen Netz |
| Nextcloud | `apps/nextcloud/docker-compose.yml` | `nextcloud`, `nextcloud-postgres`, `nextcloud-redis` | `cloud.kaleschke.info` | `frontend_net`, `nextcloud_internal` | keine | native Nextcloud-Auth; eigene DB und Redis |
| ntfy | `apps/ntfy/docker-compose.yml` | `ntfy` -> `binwiederhier/ntfy:latest@sha256:...` | `ntfy.kaleschke.info` | `frontend_net` | keine | mobile Push via upstream `ntfy.sh` |
| Paperless-ngx | `apps/paperless/docker-compose.yml` | `paperless` -> `ghcr.io/paperless-ngx/paperless-ngx:2.20.10` | `paperless.kaleschke.info` | `frontend_net`, `backend_net` | keine | shared PostgreSQL + Redis; DB/Redis via Stack ENV |
| Paperless-GPT | `apps/paperless-gpt/docker-compose.yml` | `paperless-gpt` -> `icereed/paperless-gpt:v0.24.0` | `paperless-gpt.kaleschke.info` | `frontend_net` | keine | Paperless API, Ollama/LLM config |
| Unbound | `apps/unbound/docker-compose.yml` | `unbound` -> `shaanmajid/unbound:latest@sha256:...` | keine | `dns_net` | keine | Upstream Resolver fuer AdGuard |
### Core / Security / Infra
| Stack | Compose | Services / Images | Traefik Hosts | Networks | Ports | Abhaengigkeiten |
|---|---|---|---|---|---|---|
| Gitea | `core/gitea/docker-compose.yml` | `gitea` -> `docker.gitea.com/gitea:1.25.4@sha256:...` | `git.kaleschke.info` | `frontend_net` | `222:22/tcp` | SQLite in `/data`; SSH-Port ist dokumentierte Ausnahme; `github.com` ist als Mirror-Ziel erlaubt und externe DNS-Resolver sind gesetzt |
| Authelia | `security/authelia/docker-compose.yml` | `authelia` -> `authelia/authelia:latest@sha256:...` | `auth.kaleschke.info` | `frontend_net`, `backend_net` | keine | PostgreSQL 18 Storage im historisch benannten `postgresql17`-Container, Traefik ForwardAuth; bewusst ohne Redis-Session-Backend |
| Vaultwarden | `security/vaultwarden/docker-compose.yml` | `vaultwarden` -> `vaultwarden/server:latest@sha256:...` | `vault.kaleschke.info` | `frontend_net` | keine | Datei-Persistenz, `ADMIN_TOKEN_FILE` |
| ddns-updater | `infra/ddns-updater/docker-compose.yml` | `ddns-updater` -> `ghcr.io/qdm12/ddns-updater:latest@sha256:...` | keine | `frontend_net` | keine | Cloudflare/API-Internetbedarf |
| PostgreSQL 18 | `infra/postgresql17/docker-compose.yml` | `postgresql17` -> `postgres:18.4@sha256:8ff36f3c66371cba71d20ceedccfc3de9669a68737607888c4ef0af93abe8e39` | keine | `backend_net` | keine | shared DB-Cluster; Service-Name bleibt historisch `postgresql17` |
| Redis | `infra/redis/docker-compose.yml` | `Redis` -> `redis:8.8.0-alpine@sha256:09160599abd229764c0fb44cb6be640294e1d360a54b19985ab4843dcf2d90f1` | keine | `backend_net` | keine | primaer Paperless-Redis (App-Cache); historisch als "shared" angelegt, faktisch nur von Paperless genutzt; Passwort-Datei |
### Host Services
| Stack | Compose | Services / Images | Hosts | Networks | Ports / Mode | Abhaengigkeiten |
|---|---|---|---|---|---|---|
| AdGuard Home | `host-services/Adguard/docker-compose.yml` | `adguard` -> `adguard/adguardhome:v0.107.52` | keine Traefik-Route | `dns_net`, `frontend_net` | `53/tcp`, `53/udp`, `100.80.98.33:8082:80/tcp` | Unbound in `dns_net`; DNS-Port 53 ist direkte Ausnahme; Admin 8082 ist auf Tailscale-IP begrenzt |
| Plex | `host-services/plex/docker-compose.yml` | `plex` -> `plexinc/pms-docker:1.43.1.10611-1e34174b1@sha256:...` | keine Traefik-Route | `network_mode: host` | host network | Medienserver; Host-Netz bleibt fuer Discovery / Plex GDM dokumentierte Ausnahme |
| Tailscale | `host-services/tailscale/docker-compose.yml` | `Tailscale-Docker` -> `tailscale/tailscale:stable@sha256:...` | keine | `network_mode: host` | host network | VPN/Remote-Zugang |
### Operations
| Stack | Compose | Services / Images | Traefik Hosts | Networks | Ports | Abhaengigkeiten |
|---|---|---|---|---|---|---|
| Borg UI | `ops/borg-ui/docker-compose.yml` | `borg-ui` -> `ainullcode/borg-ui:latest@sha256:...` | `borg.kaleschke.info` | `frontend_net` | keine | Borg repo, Dump-Scope, Restore-Ziel |
| code-server | `ops/code-server/docker-compose.yml` | `code-server` -> `lscr.io/linuxserver/code-server:4.116.0@sha256:...` | `code.kaleschke.info` | `frontend_net` | keine | Passwort-Datei, Workspace-Mounts |
| Filebrowser | `ops/filebrowser/docker-compose.yml` | `filebrowser` -> `filebrowser/filebrowser:v2.63.2@sha256:...` | `files.kaleschke.info` | `frontend_net` | keine | Documents/Photos/Projekte-Mounts, Admin-UI hinter Authelia |
| Glance | `ops/glance/docker-compose.yml` | `glance` -> `glanceapp/glance:v0.8.4`, `glance-docker-socket-proxy` -> `tecnativa/docker-socket-proxy:v0.4.2` | `glance.kaleschke.info` | `frontend_net`, `glance_socket_net` | keine | Homelab-Dashboard mit Home- und Infrastructure-Seite, Monitor-, Community-, Docker-, Internet-/DNS-/VPN- und Server-Stats-Widgets; aktives Community-Widget: Immich; Docker-API nur ueber internen Socket-Proxy |
| Glances | `ops/glances/docker-compose.yml` | `glances` -> `nicolargo/glances:latest-full@sha256:...` | `glances.kaleschke.info` | `frontend_net` | keine | Rootfs/Docker-Socket fuer Monitoring |
| Monitoring | `monitoring/docker-compose.yml` | `monitoring-prometheus`, `monitoring-alertmanager`, `monitoring-alertmanager-ntfy-bridge`, `monitoring-blackbox-exporter`, `monitoring-loki`, `monitoring-promtail`, `monitoring-grafana`, `monitoring-node-exporter`, `monitoring-cadvisor`, `monitoring-influxdb3-core`, optional `monitoring-grafana-dashboard-importer` | `monitoring.kaleschke.info` | `frontend_net`, `monitoring_net`, `monitoring_influx_lan` | `monitoring-influxdb3-core`: `${INFLUXDB_BIND_IP:-127.0.0.1}:8181:8181` | zentraler Zielstack fuer Prometheus/Loki/Grafana/InfluxDB; Alertmanager sendet via ntfy-Bridge nach `homelab-alerts`; Blackbox ersetzt Uptime-Kuma-Checks nach Parallelphase; Promtail nutzt Docker socket read-only; Dashboard-Importer nur via `bootstrap`-Profil |
| Hermes Agent | `ops/hermes-agent/docker-compose.yml` | `hermes-gateway`, `hermes-dashboard` -> local build from Dockerfile | `hermes.kaleschke.info` via `${HERMES_DASHBOARD_HOST}` | `hermes_net`, dashboard zusaetzlich `frontend_net` | `8642` nur expose intern | SSH runner, Home Assistant optional, LLM provider env; Dashboard hinter Authelia |
| Komodo | `ops/komodo/docker-compose.yml` | `komodo-core`, `komodo-mongo`, `komodo-periphery` | `komodo.kaleschke.info` | `frontend_net`, `komodo_net` | keine | Mongo, Docker socket, `/mnt/user/services` workspace mount, Gitea DNS override |
| Scrutiny | `ops/scrutiny/docker-compose.yml` | `scrutiny` -> `ghcr.io/starosdev/scrutiny:latest-omnibus@sha256:...` | `scrutiny.kaleschke.info` | `frontend_net` | keine | `privileged: true`, device mounts fuer SMART |
| Speedtest Tracker | `ops/speedtest/docker-compose.yml` | `speedtest-tracker` -> `lscr.io/linuxserver/speedtest-tracker:1.13.12@sha256:...` | `speedtest.kaleschke.info` | `frontend_net` | keine | App key/admin env, SQLite/config path |
### Traefik
| Stack | Compose | Service / Image | Hosts | Networks | Ports | Abhaengigkeiten |
|---|---|---|---|---|---|---|
| Traefik | `traefik/docker-compose.yml` | `traefik` -> `traefik:v3.6` | `traefik.kaleschke.info` | `frontend_net`, `backend_net` | `80:80/tcp`, `443:443/tcp` | Docker provider, Cloudflare DNS token secret, dynamic config |
## Traefik Hosts
| Host | Service | Zugriff |
|---|---|---|
| `auth.kaleschke.info` | Authelia | Auth provider / bypass fuer eigene Domain |
| `borg.kaleschke.info` | Borg UI | Traefik + Authelia |
| `cloud.kaleschke.info` | Nextcloud | Traefik, native App-Auth |
| `code.kaleschke.info` | code-server | Traefik + Authelia |
| `files.kaleschke.info` | Filebrowser | Traefik + Authelia |
| `git.kaleschke.info` | Gitea Web | Traefik |
| `glance.kaleschke.info` | Glance | Traefik + Authelia |
| `glances.kaleschke.info` | Glances | Traefik + Authelia |
| `hermes.kaleschke.info` | Hermes Dashboard | Traefik + Authelia |
| `immich.kaleschke.info` | Immich | Traefik, native App-Auth |
| `komodo.kaleschke.info` | Komodo | Traefik, native Komodo-Auth; keine pauschale ForwardAuth |
| `mail.kaleschke.info` | Mail Archiver | Traefik + Authelia + App-Auth |
| `mealie.kaleschke.info` | Mealie | Traefik |
| `monitoring.kaleschke.info` | Monitoring Grafana | Traefik + Authelia |
| `ntfy.kaleschke.info` | ntfy | Traefik |
| `paperless.kaleschke.info` | Paperless-ngx | Traefik |
| `paperless-gpt.kaleschke.info` | Paperless-GPT | Traefik + Authelia |
| `pdf.kaleschke.info` | BentoPDF | Traefik + Authelia + COOP/COEP |
| `scrutiny.kaleschke.info` | Scrutiny | Traefik + Authelia |
| `speedtest.kaleschke.info` | Speedtest Tracker | Traefik + Authelia |
| `traefik.kaleschke.info` | Traefik Dashboard | Traefik + Authelia |
| `vault.kaleschke.info` | Vaultwarden | Traefik |
## Networks
| Network | Typ / Status | Nutzer |
|---|---|---|
| `frontend_net` | external bridge | Web-/Proxy-Netz fuer Traefik und alle gerouteten UIs |
| `backend_net` | external/internal laut Architektur | PostgreSQL 18 (`postgresql17`), Redis 8, Authelia, Paperless, Mail Archiver, Traefik |
| `dns_net` | App-/Host-Netz | AdGuard Home und Unbound |
| `immich_default` | Compose-intern, `internal: true` | Immich Server, ML, Postgres, Redis |
| `mealie_internal` | Compose-intern; Laufzeitname mit Compose-Projektpraefix typischerweise `mealie_mealie_internal` | Mealie und Mealie Postgres |
| `nextcloud_internal` | Compose-intern | Nextcloud, Nextcloud Postgres, Nextcloud Redis |
| `monitoring_net` | Compose-/Stack-Netz bridge | Prometheus, Loki, Promtail, Monitoring-Grafana, node-exporter, cAdvisor; Traefik fuer Metrics-Scrape |
| `monitoring_influx_lan` | Compose-intern bridge | InfluxDB Host-Port-Publishing fuer LAN Writer im zentralen Monitoring-Stack |
| `glance_socket_net` | Compose-intern, `internal: true` | Glance und `glance-docker-socket-proxy`; keine Traefik-Anbindung |
| `komodo_net` | Compose-intern, `internal: true` | Komodo Core, Mongo, Periphery |
| `hermes_net` | Compose-intern bridge | Hermes Gateway/Dashboard |
| `host` | Host-Netz | Tailscale; Plex als Repo-Compose-Stack unter `host-services/plex/` |
## Volumes und Datenpfade
| Bereich | Wichtige Pfade |
|---|---|
| Traefik | `/mnt/user/appdata/traefik/dynamic`, `/mnt/user/appdata/traefik/letsencrypt`, Cloudflare Secret |
| Gitea | `/mnt/user/services/gitea/data` |
| Authelia | `/mnt/user/appdata/authelia/config`, Authelia Secret-Dateien |
| Vaultwarden | `/mnt/user/appdata/vaultwarden`, Admin-Token-Datei |
| PostgreSQL 18 | `/mnt/user/appdata/postgresql18`, Rollback-Altstand `/mnt/user/appdata/postgresql17`, `postgres_password.txt` |
| Redis | `/mnt/user/appdata/redis`, `redis_password.txt` |
| Paperless | `/mnt/user/appdata/paperless-ngx/data`, `/mnt/user/documents/paperless`, `/mnt/user/documents/scans_inbox` |
| Immich | `/mnt/user/photos/immich`, `/mnt/user/photos/family_archive`, `/mnt/user/appdata/immich_postgres_vectorchord`, Rollback-Altstand `/mnt/user/appdata/immich_postgres`, `model-cache` |
| Mealie | `/mnt/user/appdata/mealie/data`, `/mnt/user/appdata/mealie/postgres18`, Rollback-Altstand `/mnt/user/appdata/mealie/postgres` |
| Mail Archiver | `/mnt/user/appdata/mailarchiver/data-protection-keys` |
| Nextcloud | `/mnt/user/appdata/nextcloud/html`, `/mnt/user/documents/nextcloud-data`, `/mnt/user/appdata/nextcloud/postgres18`, Rollback-Altstand `/mnt/user/appdata/nextcloud/postgres`, `/mnt/user/appdata/nextcloud/redis` |
| Plex | `/mnt/user/appdata/plex/config`, `/mnt/user/appdata/plex/transcode`, `/mnt/user/media`, `/mnt/user/photos` |
| ntfy | `/mnt/user/appdata/ntfy` |
| Paperless-GPT | `/mnt/user/appdata/paperless-gpt/data`, `/mnt/user/appdata/paperless-gpt/prompts` |
| AdGuard | `/mnt/user/appdata/adguard/work`, `/mnt/user/appdata/adguard/conf` |
| Tailscale | `/mnt/user/appdata/tailscale` |
| Borg UI | `/mnt/user/appdata/borg-ui/data`, `/mnt/user/appdata/borg-ui/cache`, `/mnt/user/backups/borg/dumps`, selected restore/source mounts |
| code-server | `/mnt/user/appdata/code-server`, `/mnt/user/services/dev` |
| Filebrowser | `/mnt/user/documents`, `/mnt/user/photos`, `/mnt/user/projekte`, Filebrowser database/config paths |
| Glance | Repo-Konfiguration unter `ops/glance/config/glance.yml`; keine produktive Datenpersistenz; Docker-Socket nur am internen Proxy |
| Glances | `/`, Docker socket, `/etc/os-release` |
| Scrutiny | `/mnt/user/appdata/scrutiny/*`, `/run/udev`, selected `/dev/...` disks |
| Speedtest | `/mnt/user/appdata/speedtest-tracker/config` |
| Monitoring | named volumes `prometheus_data`, `loki_data`, `promtail_positions`, `grafana_data`; InfluxDB-Persistenz unter `/mnt/user/appdata/influxdb3/data` und `/mnt/user/appdata/influxdb3/plugins`; Provisioning im Repo unter `monitoring/grafana/provisioning`; Dashboards unter `monitoring/grafana/dashboards`; historische Altstandsdaten unter `/mnt/user/appdata/grafana`, `/mnt/user/appdata/loki` und `/mnt/user/appdata/alloy` nicht blind loeschen |
| Hermes Agent | `/mnt/user/appdata/hermes-agent/data`, `/mnt/user/appdata/hermes-agent/ssh`, SSH private key path |
| Komodo | `komodo_keys`, `/mnt/user/appdata/komodo/core`, `/mnt/user/appdata/komodo/mongo`, `/mnt/user/appdata/komodo/periphery`, `/mnt/user/services` |
## Secrets und Env-Hinweise
| Dienst / Stack | Secret-/Env-Hinweise ohne Werte |
|---|---|
| Traefik | `cloudflare_dns_api_token` als Docker Secret / `CF_DNS_API_TOKEN_FILE` |
| Authelia | JWT, Session, Storage Encryption, Postgres Password via `_FILE` |
| Vaultwarden | `ADMIN_TOKEN_FILE` |
| PostgreSQL 18 | `POSTGRES_PASSWORD_FILE` |
| Redis | Passwort-Datei und Startkommando |
| Paperless | `PAPERLESS_DBPASS`, `PAPERLESS_REDIS` als Komodo Stack ENV |
| Immich | `IMMICH_DB_PASSWORD` Stack ENV; `immich_postgres_password.txt` fuer Postgres |
| Mail Archiver | `MAILARCHIVER_DB_CONNECTION`, `MAILARCHIVER_AUTH_PASSWORD` als Stack ENV |
| Glance | `GLANCE_IMMICH_API_KEY`, `GLANCE_ADGUARD_USERNAME`, `GLANCE_ADGUARD_PASSWORD`, `GLANCE_SPEEDTEST_API_KEY` als Stack ENV fuer Community-/Live-Widgets |
| Speedtest | `APP_KEY`, `ADMIN_PASSWORD` als Stack ENV |
| Nextcloud | Admin User, Admin Password, Postgres Password via Secret-Dateien |
| Komodo | `KOMODO_SECRET_KEY`, `KOMODO_WEBHOOK_SECRET`, `KOMODO_JWT_SECRET`, `KOMODO_MONGO_PASSWORD`, `KOMODO_PERIPHERY_PASSKEY`; Mongo Passwort-Datei |
| Borg UI | Borg Credentials, Admin Login, SSH Keys in persistentem Appdata, nicht im Git |
| Hermes Agent | provider keys, API server key, messaging tokens, Home Assistant token in Host `.env`; SSH private key als Host-Secret |
| Monitoring | `monitoring_grafana_admin_password.txt`, `monitoring_grafana_influxdb_token.txt`, `influxdb3_admin_token.json` fuer zentrale Grafana-/InfluxDB-Funktionen; ersetzt Uptime Kuma fuer HTTP-Verfuegbarkeit |
## Skripte
| Skript | Ausfuehrungsort | Zweck |
|---|---|---|
| `ops/borg-ui/scripts/pre-backup-dumps.sh` | Unraid Host, nicht Borg-UI Inline-Hook | erzeugt aktuelle Dumps unter `/mnt/user/backups/borg/dumps/latest` |
| `services/posture-check/posture-check.sh` | Unraid Host | schreibt `/mnt/user/services/posture-check/last.json` und alarmiert via ntfy bei Warning/Critical |
| `services/posture-check/export-prometheus-textfile.sh` | Unraid Host, Cron/Textfile-Collector | schreibt Borg-, Critical-Container- und GitOps-Runtime-Image-Drift-Metriken fuer Prometheus |
| `services/posture-check/docker-critical-events.sh` | Unraid Host | beobachtet Docker `die`/`oom`/`kill` Events und alarmiert via `homelab-alerts` |
Das Skript liest Secret-Dateien auf dem Host und schreibt Dump-Artefakte. Bei Analyse niemals Secret-Inhalte ausgeben.
## Unsicherheiten / TODOs aus Repo-Sicht
- Echte `stack.env`- und `.env`-Dateien sind per `.gitignore` ausgeschlossen; nur `*.example`-Dateien gehoeren ins Repo.
- Hardware-, Netzwerk- und Provider-Inventare sind initiale Templates und muessen nach einem Host-Audit mit echten Werten gefuellt werden.
- Authelia-2FA-/OIDC-Aenderungen sind nach Audit 2026-05-25 bewusst geparkt und werden nicht als Sofortmassnahme behandelt.
- Authelia `configuration.yml` ist Repo-Baseline fuer nicht geheime Einstellungen, wird aber nicht automatisch von Komodo auf den Host kopiert. Die produktive Host-Datei kann zusaetzliche OIDC-/Secret-Konfiguration enthalten; Aenderungen muessen manuell gemerged und validiert werden.
- `backend_net` ist in der Architektur als `internal: true` beschrieben; einzelne Compose-Dateien referenzieren es external. Live-Netz-Attribute bei Drift-Fragen pruefen.
- Einige Images bleiben trotz Digest-Pin semantisch auf mutable Tags (`latest@sha256`, `release@sha256`). Das ist bewusst dokumentiert, aber bei Updates gesondert pruefen.
- Stateful Datenhalter sind seit 2026-05-05 bevorzugt mit Minor-/Patch-Tag plus Digest gepinnt; PostgreSQL-17-Datenhalter wurden am 2026-05-31 per Dump/Restore auf PostgreSQL 18 gehoben, Redis-Caches auf Redis 8.8, Immich-Postgres bleibt auf PG14 mit VectorChord.
- `scrutiny` bleibt `privileged: true`; dokumentierte Ausnahme, aber weiterhin pruefenswert.
- `tailscale` nutzt Host-Netz, `NET_ADMIN`, `NET_RAW` und `/dev/net/tun` als dokumentierte VPN-Ausnahme.
- `monitoring-influxdb3-core` laeuft aktuell als `user: "0"`; UID/GID-Hardening nur als eigener Sprint.
- Leere `.keep`-Platzhalter wurden entfernt; neue Verzeichnisse sollen erst mit konkretem Inhalt ins Repo.
- `plex` ist als Repo-Compose-Stack unter `host-services/plex/` enthalten; `network_mode: host` bleibt die dokumentierte Discovery-Ausnahme.
- BentoPDF kann je nach Live-Stand vorbereitet statt produktiv sein; Hermes Dashboard ist produktiv unter `hermes.kaleschke.info`.
-123
View File
@@ -1,123 +0,0 @@
# Restore-Drill Routine - KalliLab CORE
Status: **verbindliche Routine (Doku-only)**, 2026-05-27.
Audit-Bezug: `docs/archive/2026-05/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 (teilweise erledigt, Rest vor Quartalsende 2026-06-30):
- Komodo-Bootstrap-Pfad: **erledigt 2026-05-30** durch echten Trockenlauf via `ops/restore-tests/komodo-bootstrap-test.sh --keep-data`, Report `/mnt/user/backups/restore-reports/komodo-bootstrap-2026-05-30.md`, `ops/komodo/docker-compose.yml` als Recovery-Anker belegt.
- Gitea-Bundles ueber `ops/borg-ui/scripts/gitea-bundle-mirror.sh` auf Frische und Bundle-Klonbarkeit pruefen: offen.
- Secrets-Inventur gegen `docs/SECRETS_MAP.md` abgleichen: offen.
### 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.
+5 -1
View File
@@ -140,7 +140,11 @@ Erst nach erfolgreichem Komodo-Bootstrap werden produktive Stacks ueber den doku
### Trockenlauf (als Repo-Skript, bestaetigt) ### Trockenlauf (als Repo-Skript, bestaetigt)
Trockenlauf gegen Wegwerf-Pfade ist seit 2026-05-29 als Repo-Skript abgelegt: `ops/restore-tests/komodo-bootstrap-{compose.test.yml,test.sh,plan.md,runbook.md}`. Aufruf: Trockenlauf gegen Wegwerf-Pfade ist seit 2026-05-29 als Repo-Skript abgelegt:
`ops/restore-tests/komodo-bootstrap-compose.test.yml`,
`ops/restore-tests/komodo-bootstrap-test.sh`,
`ops/restore-tests/komodo-bootstrap-plan.md` und
`ops/restore-tests/komodo-bootstrap-runbook.md`. Aufruf:
```bash ```bash
bash /mnt/user/services/homelab-infra/ops/restore-tests/komodo-bootstrap-test.sh --what-if # nur Plan bash /mnt/user/services/homelab-infra/ops/restore-tests/komodo-bootstrap-test.sh --what-if # nur Plan
+3 -33
View File
@@ -380,38 +380,8 @@ Wenn Hermes-Worker auf weiteren Hosts skaliert: dieser Storage-Layout-Plan gilt
- `docs/AI_CONTEXT.md` — Kontext für AI-Assistenten - `docs/AI_CONTEXT.md` — Kontext für AI-Assistenten
- `docs/HARDWARE_INVENTORY.md` — physische Host-, Disk- und Health-Baseline - `docs/HARDWARE_INVENTORY.md` — physische Host-, Disk- und Health-Baseline
## 18. Changelog ## 18. Status
| Version | Datum | Änderung | Autor | Status: **Active v1.4 seit 2026-05-27**.
|---------|-------|----------|-------|
| 1.0 (Draft) | 2026-05-15 | Initialfassung nach Incident 2026-05-11. Ursprung: NTFS-Cache-Korruption, fehlende Posture-Checks, ungeplante Backup-Strategie. | Operator + AI-Assistenten |
| 1.1 (Draft) | 2026-05-15 | Operator-Review-Feedback eingearbeitet: `system`/`domains` auf `only`, `services` als recovery-kritisch markiert, `data/`-Behandlung pro Stack klassifiziert (statt blanket Exclude), Backup-Tooling Ist/Soll explizit getrennt, Posture-Check zusätzlich bei Boot/vor Backup/nach Mover, Hard Rules 11+12 ergänzt (Restore-Pfad-Pflicht, Posture-vor-Backup), Alarmziel-Optionen benannt, Review-Items in eigene Sektion §20 verschoben. | Operator + AI-Assistenten |
| 1.2 (Draft) | 2026-05-15 | Operator-Entscheidungen #3, #4, #6, #9, #11 eingearbeitet: Backrest abgeschaltet (Borg alleinige Backup-Technologie), persönliche Daten vollständig im Pflicht-Backup-Scope, ntfy als Alarmziel verbindlich, kebab-case-Migration im Rahmen der Recovery-Phase, Mirror-Backup für Gitea-Repo-Inhalte als verbindliche Spec (Implementierung in `SERVICES_RECOVERY.md` zu detaillieren). Offen: Items #1, #2, #5, #7, #8, #10. | Operator + AI-Assistenten |
| 1.3 (Draft) | 2026-05-15 | Operator-Entscheidungen #1, #7, #8 eingearbeitet: Disk-Größen/-Modelle als Deferred via Posture-Check-Detection (kein Blocker), optionale Stacks (Filebrowser, code-server, Speedtest, Scrutiny, Uptime-Kuma) bleiben im Layout und sind produktiv, Network-Verweis auf MASTER_V2 bestätigt. Damit alle akuten Items entschieden. Verbleibend: Items #2, #5, #10 (Retention, Schwellen-Kalibrierung, RESTORE_MATRIX-Klassifikation) — alle als Folge-Aufgaben über Inkraftsetzung hinaus, kein Commit-Blocker. | Operator + AI-Assistenten |
| 1.4 (Active) | 2026-05-27 | Datei von `docs/STORAGE_LAYOUT.draft.md` auf `docs/STORAGE_LAYOUT.md` gehoben; Hardware-/Diskwerte aus `docs/HARDWARE_INVENTORY.md` übernommen; Gitea-Bundle-Mirror und H:/ Nearline-Pull als umgesetzte Folgepfade referenziert. Verbleibend: Retention-Kalibrierung, Monitoring-Schwellen und RESTORE_MATRIX-Detailklassifikation als normale Folgeaufgaben. | Operator + AI-Assistenten |
## 19. Inkraftsetzung Detailhistorie und alte Review-Tabellen liegen in der Git-Historie. Aktuelle Folgepunkte stehen nicht mehr hier, sondern in `docs/AUDIT_2026-05-25_TODO.md`.
Dieses Dokument tritt in Kraft mit dem Commit der finalen Fassung in `master`-Branch des Repos `homelab-infra`. Ab Inkraftsetzung gelten alle Hard Rules; Soft Rules werden im Rahmen der laufenden Recovery-Arbeit etabliert; Posture-Check muss innerhalb von 14 Tagen nach Inkraftsetzung produktiv laufen.
Erste Audit-Review dieses Dokuments: spätestens 90 Tage nach Inkraftsetzung. Danach jährlich oder bei jeder strukturellen Änderung des Storage-Layouts.
## 20. Review Items und Folgeaufgaben
Diese Sektion dokumentiert erledigte Review-Punkte und verbleibende Folgeaufgaben nach Aktivierung des Dokuments.
| Nr. | Item | Status | Verantwortung |
|-----|------|--------|---------------|
| 1 | Disk-Größen und Modelle in §3 (Disk1, Parity, externe Backup-Platte) | **ERLEDIGT 2026-05-27** — Werte aus `docs/HARDWARE_INVENTORY.md` übernommen; Disk1-Filesystem ist seit 2026-05-25 XFS | Operator + Posture-Check |
| 2 | Retention-Werte in §8.1 (Borg-Repos lokal/remote) — abhängig von tatsächlicher Storage-Kapazität | Vorschlag steht, anzupassen | Operator |
| 3 | Backup-Tooling: Backrest abschalten, Borg alleinige Backup-Technologie | **ENTSCHIEDEN 2026-05-15** | erledigt (siehe §8.0) |
| 4 | Backup-Scope für persönliche Daten: `documents`, `photos`, `finance`, `projekte` (und `media` falls behalten) **vollständig** im Pflicht-Scope | **ENTSCHIEDEN 2026-05-15** | erledigt (siehe §8.2) |
| 5 | Posture-Check-Schwellen in §11 — Vorschlag konservativ, nach erstem Monitoring kalibrieren | Vorschlag steht | Operator nach 30 Tagen |
| 6 | Alarmziel: ntfy als primärer Push-Kanal, Mail-Fallback als Folge-Erweiterung optional | **ENTSCHIEDEN 2026-05-15** | erledigt (siehe §11) |
| 7 | Optionale Stacks (Filebrowser, code-server, Speedtest, Scrutiny, Uptime-Kuma) — bleiben im Layout, sind produktiv im Scope, folgen den Standard-Konventionen aus §5 und Backup-Pflichten aus §8.2 | **ENTSCHIEDEN 2026-05-15** | erledigt |
| 8 | Verweis auf `HOMELAB_ARCHITECTURE_MASTER_V2.md` für Network-Architektur (§10) — Net-Architektur steht dort authoritativ, Verweis ist ausreichend | **ENTSCHIEDEN 2026-05-15** | erledigt (siehe §10) |
| 9 | Naming-Konvention: kebab-case durchziehen, Migration im Rahmen der Recovery-Phase | **ENTSCHIEDEN 2026-05-15** | erledigt (siehe §6); pro Stack in RESTORE_MATRIX.md zu dokumentieren |
| 10 | Pro-Stack-Klassifizierung in `RESTORE_MATRIX.md` (DB-Typ, Nutzdaten in `data/`, Dump-Verfahren, letzter Restore-Test, kebab-case-Migrationsname) — als Folge-Aufgabe aus Hard Rule §12.11 | Folge-Aufgabe | Operator + Recovery-Phase |
| 11 | Mirror-Backup für `services/gitea/git/repositories/` auf zweites Medium, ≤ 6 h Frequenz, konkrete Implementierung in `docs/SERVICES_RECOVERY.md` zu erstellen | **ERLEDIGT 2026-05-26**`ops/borg-ui/scripts/gitea-bundle-mirror.sh` erzeugt verifizierte Bundles; Host-Erstlauf mit 4 Bundles und `git fsck` erfolgreich | Operator + Folge-Doc |
Das Dokument ist mit Version 1.4 als Active geführt. Offene Punkte 2, 5 und 10 bleiben normale Folgeaufgaben und blockieren die Gültigkeit der Hard Rules nicht.
+2 -3
View File
@@ -361,7 +361,7 @@ Pflicht-Schritte vor dem Schliessen:
6. Authelia ACL-Eintrag in `security/authelia/configuration.yml` und auf dem Host bereinigen. 6. Authelia ACL-Eintrag in `security/authelia/configuration.yml` und auf dem Host bereinigen.
7. Monitoring: Blackbox-Target in `monitoring/blackbox/blackbox.yml` entfernen, Cert-Check-Liste pruefen. 7. Monitoring: Blackbox-Target in `monitoring/blackbox/blackbox.yml` entfernen, Cert-Check-Liste pruefen.
8. **Borg-UI Source-Liste**: `https://borg.kaleschke.info` -> Repository `appdata-critical` -> Source Directories -> alle `/local/appdata/<name>` und ggf. `/local/<name>`-Eintraege loeschen. Sonst kommen daily `HomelabBorgLastJobCompletedWithWarnings`-Push-Nachrichten mit `BackupFileNotFoundError` im Logfile. 8. **Borg-UI Source-Liste**: `https://borg.kaleschke.info` -> Repository `appdata-critical` -> Source Directories -> alle `/local/appdata/<name>` und ggf. `/local/<name>`-Eintraege loeschen. Sonst kommen daily `HomelabBorgLastJobCompletedWithWarnings`-Push-Nachrichten mit `BackupFileNotFoundError` im Logfile.
9. `docs/SERVICE_CATALOG.md`, `docs/REPO_MAP.md`, `HOMELAB_ARCHITECTURE_MASTER_V2.md` Sektion 7.8 (Entfernt), `docs/MIGRATION_LOG.md` nachziehen. 9. `docs/SERVICE_CATALOG.md`, `docs/REPO_MAP.md` und `HOMELAB_ARCHITECTURE_MASTER_V2.md` Sektion 7.8 (Entfernt) nachziehen.
Wenn ein Stack `webhook_enabled` in Komodo hatte, zusaetzlich pruefen, ob der zugehoerige Gitea-Hook deaktiviert oder geloescht wurde. Wenn ein Stack `webhook_enabled` in Komodo hatte, zusaetzlich pruefen, ob der zugehoerige Gitea-Hook deaktiviert oder geloescht wurde.
@@ -371,7 +371,6 @@ Wenn ein Stack `webhook_enabled` in Komodo hatte, zusaetzlich pruefen, ob der zu
Nach jeder erfolgreichen Migration oder relevanten Aenderung muessen diese Dateien geprueft werden: Nach jeder erfolgreichen Migration oder relevanten Aenderung muessen diese Dateien geprueft werden:
- `docs/MIGRATION_LOG.md`
- `docs/SECRETS_MAP.md` - `docs/SECRETS_MAP.md`
- `docs/ROLLBACK.md` - `docs/ROLLBACK.md`
- `docs/SERVICES_RECOVERY.md` falls `/mnt/user/services`, Gitea, Komodo oder Host-Automation betroffen sind - `docs/SERVICES_RECOVERY.md` falls `/mnt/user/services`, Gitea, Komodo oder Host-Automation betroffen sind
@@ -403,7 +402,7 @@ Wenn mit einer KI gearbeitet wird, gilt immer:
> 1. `HOMELAB_ARCHITECTURE_MASTER_V2.md` > 1. `HOMELAB_ARCHITECTURE_MASTER_V2.md`
> 2. `docs/WORKFLOW.md` > 2. `docs/WORKFLOW.md`
> 3. die betroffene Compose-Datei > 3. die betroffene Compose-Datei
> 4. ggf. `docs/MIGRATION_LOG.md` > 4. die relevante Betriebsdoku aus `docs/README.md`
Erst danach duerfen Aenderungen vorgeschlagen werden. Erst danach duerfen Aenderungen vorgeschlagen werden.
@@ -1,80 +0,0 @@
# AI Handoff 2026-05-06
Kompakte Quelle fuer einen neuen Chat. Ziel: nicht das ganze Repo neu auditieren, sondern mit dem bekannten Stand weiterarbeiten.
## Aktueller Stand
- Repo: `G:\Gitea_Clone\homelab-infra`
- Remote: `https://git.kaleschke.info/Micha/homelab-infra.git`
- Branch: `master`
- Letzter bekannter Commit: `e0e12f1 Document stale Komodo webhook cleanup`
- Unraid-Host: `ssh root@192.168.178.58`
- Push-Befehl, der zuverlaessig funktioniert: `git -C "G:\Gitea_Clone\homelab-infra" push origin master`
- Nicht anfassen ohne explizite Freigabe: untracked `Homelab_Audit_2026-05-05.pdf` und untracked `ops/hermes-agent/services.yaml`.
## Audit-Arbeit Erledigt
- K1: ungueltige Digests fuer Authelia, ntfy und borg-ui korrigiert und smoke-getestet.
- K2: Authelia nutzt bewusst kein Redis; Doku entsprechend korrigiert.
- K3/M1/M2 alt: Authelia Repo-Baseline geklaert, Homepage/Komodo ACL-Drift bereinigt.
- M3a/M3b: Digest-Pinning fuer stateful/Tier-1 und weitere versionierte Apps umgesetzt; Redis-Caches bewusst ohne Digest, Nextcloud bewusst offen.
- M5/N5: `.gitignore` eingefuehrt, Hermes `stack.env` zu `stack.env.example`.
- M6/M7/M8: Hermes-Domain, Grafana/Influx `user: "0"` und Tailscale-Capabilities dokumentiert.
- M9: Backup Scope / Restore Matrix erledigt.
- N-Aufraeumen: alte Compose-`version:` Felder, leere Env-Beispiele und `.keep`-Platzhalter bereinigt.
- Mail Archiver: `mail.kaleschke.info` liegt hinter `authelia@file,secure-headers@file`; Smoke-Test war 302 zu Authelia.
- Hermes: Restore/DR-Doku ergaenzt.
- Authelia SMTP: GMX SMTP eingerichtet, validiert, deployed und smoke-getestet.
- M10: `KOMODO_WEBHOOK_SECRET` ist von `KOMODO_SECRET_KEY` getrennt.
## Wichtige Runtime-Details
### Authelia SMTP
- Adresse: `submission://mail.gmx.net:587`
- Mailkonto: `michideheld@gmx.de`
- SMTP-Passwort liegt nur auf dem Host: `/mnt/user/appdata/secrets/authelia_smtp_password.txt`
- Host-Config wurde vor Umstellung gesichert: `/mnt/user/appdata/authelia/config/configuration.yml.bak-20260506-smtp`
- Authelia-Compose nutzt explizite DNS-Server, weil der SMTP-Startup-Check externe Namen aufloesen muss.
- Nach Deploy war `authelia` healthy; `auth.kaleschke.info` antwortete 200, geschuetzte Routen 302 zu Authelia.
### Komodo / M10
- Komodo-Runtime nur gemeinsam mit dem Betreiber aendern.
- `KOMODO_SECRET_KEY` wurde nicht geaendert.
- `KOMODO_WEBHOOK_SECRET` wurde geaendert und ist jetzt eigener 64-Zeichen-Wert.
- Neuer Wert liegt nur auf dem Host in `/mnt/user/services/stacks/komodo/.env`.
- Komodo Compose auf Host: `/mnt/user/services/stacks/komodo/compose.yaml`.
- Backups vom M10-Sprint:
- `/mnt/user/appdata/komodo/_m10_backup_20260506-184838`
- `/mnt/user/services/gitea/data/gitea/_m10_backup_20260506-184838/gitea.db.bak`
- `komodo-core` wurde gezielt recreated.
- `komodo-mongo` wurde nicht neu gestartet.
- `komodo-periphery` lief durch und meldete sich wieder am Core-Websocket an.
- Gitea-Komodo-Webhooks: 29 aktive Hooks, 29 zuletzt erfolgreich, 0 aktiv fehlgeschlagen.
- Ein stale Gitea-Webhook auf eine nicht mehr existierende Komodo-Stack-ID wurde deaktiviert, nicht geloescht.
- Eine Warnung `request branch does not match expected` ist ein Branch-Filter-Skip, kein Secret-/Auth-Fehler.
- Fuer neue Gitea-Webhooks im Standardfall den globalen `KOMODO_WEBHOOK_SECRET` aus der Komodo-Host-`.env` nutzen, ausser Komodo zeigt fuer den Stack explizit ein eigenes per-Stack-Secret.
## Sicherheitsregeln Fuer Weitere Arbeit
- Keine Secret-Werte im Chat oder Git ausgeben.
- Bei Host-Pruefungen nur SET/MISSING, Laengen und Pfade zeigen.
- Komodo-Compose, Komodo-Secrets und Komodo-Runtime nur bewusst und kleinschrittig aendern.
- Bei jedem Deploy pro Stack smoke-testen; nicht mehrere kritische Stacks parallel veraendern.
- Untracked Dateien nicht automatisch committen.
- Bei Authelia-Aenderungen: Host-Config sichern, `authelia validate-config` ausfuehren, dann erst neu starten.
- Bei Komodo-Aenderungen: Gitea-Webhooks und Komodo-Core-Secret-Seite zusammen betrachten.
## Naechste Sinnvolle Next-Level-Themen
- Grafana/Influx rootless betreiben statt `user: "0"`; eigener Sprint wegen Volume-Rechten.
- Restore-Test fuer Vaultwarden und Paperless dokumentiert durchfuehren.
- Komodo Periphery von Legacy-Passkey auf Public-Key-Modell haerten.
- Monitoring/Alerting reifer machen: externe Alarme, Restore-Test-Reminder, Backup-Erfolg sichtbar.
- Gitea/Komodo Webhook-Landschaft weiter aufraeumen und per-Stack-Secret-Strategie dokumentieren.
- DR-Test fuer `backend_net`/externe Docker-Netze explizit aufnehmen.
## Startprompt Fuer Neuen Chat
Bitte zuerst `docs/archive/2026-05/AI_HANDOFF_2026-05-06.md` lesen und als aktuelle Arbeitsquelle verwenden. Nicht das ganze Repo neu auditieren, ausser ich fordere es an. Beachte besonders: Komodo nur gemeinsam und kleinschrittig aendern, keine Secret-Werte ausgeben, untracked PDF und `ops/hermes-agent/services.yaml` nicht anfassen. Wir starten jetzt mit Next-Level-Hardening.
-369
View File
@@ -1,369 +0,0 @@
# Homelab Audit - 2026-05-23
Stand: 2026-05-23, repo-basiert. Erstellt nach `docs/WORKFLOW.md` und `docs/GITOPS_DRIFT_RUNBOOK.md`. Quellebasis: `origin/master` plus lokaler Clone, ohne Schreibbefehle, ohne Deploy.
Dieser Audit ist eine punktuelle Sollzustands-Bewertung, kein Live-Status. Die Live-Verifikations-Schritte stehen am Ende in Abschnitt 9; alle dortigen Outputs ersetzen Vermutungen durch Messwerte.
## 0. Executive Summary
Ampel-Bewertung pro Bereich:
| Bereich | Ampel | Kernaussage |
|---|---|---|
| GitOps-Konsistenz (lokal/Gitea) | 🟡 | Lokaler Clone ist **1 Commit voraus** auf `master` (`cd650b1`, Haertungs-Commit). Bis zum Push existiert dieser Stand nur lokal, nicht in Gitea — bei einem Clone-Verlust ist er weg. |
| GitOps-Konsistenz (Working Tree) | 🟢* | Keine echten Inhaltsaenderungen offen. Die 47 "modified files" aus `git status` im Linux-Mount sind voraussichtlich CRLF/LF-Mount-Artefakte (durch `git diff -w --stat` auf Stichprobe bestaetigt leer). Bitte am Windows-Host gegenpruefen. |
| Hardening-Sprint (Mai 2026) | 🟢 | Alle vier Post-Restore-Sprint-Items sind im Repo umgesetzt (Filebrowser-Mounts, Authelia Argon2id, Gitea Webhook-Allowlist, Backup-Dump-Konsistenz). |
| Backup/Restore-Readiness | 🟢 | `pre-backup-dumps.sh` deckt alle relevanten SQLite/PostgreSQL/Mongo-Quellen ab. Borg-UI-Scope umfasst `/mnt/user/services`, `homelab-infra`, `stacks`, `posture-check`. Live-Frische ist offen (Abschnitt 9). |
| Monitoring-Migration | 🟡 | `monitoring/` Stack im Repo komplett, aber Live-Deploy laut `docs/archive/2026-05/NEXT_SPRINT_TODO_2026-05-16.md` noch ausstehend. Alte Stacks `ops/grafana-influxdb` und `ops/loki` sollen erst nach Live-Smoke-Test gestoppt werden. |
| Doku-Drift Repo vs. Master-Doku | 🟠 | `apps/jellyfin/`, `host-services/plex/` und einige andere existieren produktiv als Compose-Stacks, sind aber in `HOMELAB_ARCHITECTURE_MASTER_V2.md`, `docs/SERVICE_CATALOG.md` und `docs/REPO_MAP.md` **nicht aufgefuehrt**. Authelia-ACL kennt `jellyfin.kaleschke.info` als bypass, im Masterdoku-Hostkatalog steht es nicht. |
| Repo-Hygiene | 🟡 | 8 leere Verzeichnisse im Working Tree (siehe 4.3). `.serena/` ist untracked und nicht in `.gitignore`. Drei `ops/windows-reinstall/*.ps1` sind untracked. |
| Bekannte Ausnahmen | 🟢 | Alle Ausnahmen aus `HOMELAB_ARCHITECTURE_MASTER_V2.md` Abschnitt 10 sind weiterhin dokumentiert und durch den Policy-Check abgedeckt (0 Critical, 4 Warnings, 9 Info alles dokumentierte Ausnahmen). |
**Kernfazit:** Das Homelab ist sehr nah an der "Endstufe". Es gibt keine kritischen Befunde. Die einzige Pflichtaktion vor dem naechsten geplanten Schritt ist der **Push des lokalen Commits `cd650b1` nach Gitea**, damit `origin/master` wieder die Quelle der Wahrheit ist. Danach sind nur noch zwei priorisierte Pakete offen: Monitoring-Stack live finalisieren und Doku auf den Stand der neuen Stacks (Jellyfin/Plex/...) nachziehen.
---
## 1. Methodik und Quellen
Diese Audit-Quellen wurden gelesen (repo-seitig):
- `HOMELAB_ARCHITECTURE_MASTER_V2.md`
- `docs/WORKFLOW.md`
- `docs/REPO_MAP.md`
- `docs/SERVICE_CATALOG.md`
- `docs/RESTORE_MATRIX.md`
- `docs/GITOPS_DRIFT_RUNBOOK.md`
- `docs/archive/2026-05/NEXT_SPRINT_TODO_2026-05-16.md`
- `ops/borg-ui/scripts/pre-backup-dumps.sh`
- `ops/borg-ui/docker-compose.yml`
- `ops/borg-ui/all-important-sources.txt`
- `ops/filebrowser/docker-compose.yml`
- `security/authelia/configuration.yml`
- `core/gitea/docker-compose.yml`
- `apps/jellyfin/docker-compose.yml`
- `host-services/plex/docker-compose.yml`
- `ops/policy-checks/last-report.md`
Schreibbefehle: keine. Deploys: keine. Containerlaufzeit, Komodo-Webhook-Status, Borg-Lauf-Frische und Host-Listener wurden bewusst nicht angetastet — dafuer steht der Live-Daten-Block in Abschnitt 9.
---
## 2. Schicht A — GitOps und Konsistenz
### 2.1 Lokaler Clone vs. `origin/master`
```
## master...origin/master [ahead 1]
HEAD = cd650b19ac057a1b74ac63503e5dba50eaf5b8ea
origin/master = af231dd4e835b19005cc0842509199d480af00d9
```
- **Befund:** Lokaler Clone ist 1 Commit voraus.
- **Commit:** `cd650b1 Close Gitea signup, dedup posture-check alerts, extend Borg scope` (Sat May 23 11:01:24 2026 +0200).
- **Inhalt** (laut Commit-Message und betroffenen Dateien):
- Gitea: `DISABLE_REGISTRATION=true`, `ENABLE_OPENID_SIGNIN=false`, `ENABLE_OPENID_SIGNUP=false`
- Repo-Pflicht-Doku ergaenzt: Komodo-Stack-Webhook-Pflicht in `CLAUDE.md`, `AI_CONTEXT.md`, `WORKFLOW.md`
- `posture-check.sh`: Disk1-NTFS-Funktion ausgelagert, Inode-Check auf NTFS uebersprungen, ntfy-Dedup via Fingerprint-State + `ALERT_REPEAT_SECONDS`
- `docker-critical-events.sh`: JSON-Parsing, `die exit=0` gefiltert, strukturierte ntfy-Message
- `borg-ui`: `/mnt/user/services` als `/local/services:ro` gemountet, `all-important-sources.txt` ergaenzt
- Unraid User Scripts dokumentiert (daily report)
- `MIGRATION_LOG.md`, `RESTORE_MATRIX.md`, `DISASTER_RECOVERY.md` aktualisiert
- **Risiko:** Bei Verlust des Windows-Clones (Reinstall, Diskcrash) ist dieser Stand verloren, weil er nicht in Gitea liegt. Komodo deployt ausserdem aus Gitea und kennt diese Aenderungen noch nicht.
- **Empfohlene Aktion (Pflicht vor weiterer Arbeit):** In GitHub Desktop `Push origin` ausfuehren. Danach Komodo-Reaktion fuer die betroffenen Stacks (`gitea`, `borg-ui`) pruefen und Smoke-Tests laufen lassen.
### 2.2 Working-Tree-Status
```
$ git status --short
M CLAUDE.md
M HOMELAB_ARCHITECTURE_MASTER_V2.md
M apps/homepage/docker-compose.yml
...
(47 Dateien als modified gemeldet, plus 4 Untracked)
```
- **Bewertung:** Die 47 "modified files" sind mit hoher Wahrscheinlichkeit **Mount-Artefakte** durch CRLF/LF zwischen Windows-Clone und Linux-Mount. Stichprobe `git diff -w --stat CLAUDE.md HOMELAB_ARCHITECTURE_MASTER_V2.md` lieferte leer — d. h. keine inhaltlichen Diffs.
- **Aktion:** Bitte am Windows-Host in GitHub Desktop `git status --short` ausfuehren. Wenn dort der Tree leer ist (nur die 4 Untracked), gibt es keinen echten Working-Tree-Drift. Wenn dort echte Diffs erscheinen, hier bitte zurueckmelden — dann ist das ein eigener Befund.
- **Optional (nicht Pflicht):** `.gitattributes` mit `* text=auto eol=lf` haerten, damit dieser Mount-Effekt fuer KI-Audits aus dem Weg geht. Das ist ein eigener kleiner Commit, kein Audit-Output.
### 2.3 Untracked Files
```
?? .serena/
?? ops/windows-reinstall/backup-delta-after-2026-05-07.ps1
?? ops/windows-reinstall/cleanup-dualboot-bcd.ps1
?? ops/windows-reinstall/repair-disk0-boot-to-new-windows.ps1
```
- `.serena/` ist das Working-Directory des Serena Code-Search-Tools. Hat eigene `.gitignore` intern, aber das `.serena/`-Verzeichnis selbst ist nicht in der Repo-`.gitignore`.
- **Aktion (klein):** `.serena/` in `.gitignore` aufnehmen, damit es nicht versehentlich committet wird.
- Die drei PowerShell-Scripts unter `ops/windows-reinstall/` sind Windows-Reinstall-Helfer. Entscheidung offen: ins Repo aufnehmen (mit Kontextkommentar warum sie dort liegen) oder lokal halten und in `.gitignore` aufnehmen. Vorschlag: aufnehmen, weil `ops/` der dokumentierte Ort fuer Ops-Skripte ist.
### 2.4 Letzte Commit-Historie (Top 10)
```
cd650b1 Close Gitea signup, dedup posture-check alerts, extend Borg scope [LOKAL, NICHT GEPUSHT]
af231dd Fix zero-count noise pattern handling
428223d Mark posture report scripts executable
b6d3ed4 Tune homelab availability alerts
9e7bebb Add daily operations report with hardened log-noise filtering
b7cbbe5 Fix Jellyfin external DNS
71ac18b Fix Jellyfin native auth routing
90f270b Fix Jellyfin config permissions
e28f8da Add Jellyfin media server stack
edfec5b Add Plex media server stack
```
- Die letzten Tage waren sichtbar: Jellyfin/Plex hinzugefuegt, Availability-Alerts feinjustiert, Posture-Check-Skripte produktiv gemacht, dann der grosse Haertungs-Commit gestern (2026-05-23 11:01).
### 2.5 Compose-Inventar vs. Doku
Repo hat folgende Compose-Stacks, die in den Doku-Quellen (`HOMELAB_ARCHITECTURE_MASTER_V2.md`, `docs/SERVICE_CATALOG.md`, `docs/REPO_MAP.md`) **nicht oder nur teilweise** aufgefuehrt sind:
| Stack | Status im Repo | Status in Master-Doku |
|---|---|---|
| `apps/jellyfin/docker-compose.yml` | produktiv vorhanden, gepinnt `jellyfin:10.11.8@sha256:...`, Traefik `jellyfin.kaleschke.info`, `secure-headers@file`, native Auth, `/mnt/user/media:ro` + `/mnt/user/photos:ro` | **fehlt** in 7.4 Apps; Authelia-ACL kennt aber bereits `jellyfin.kaleschke.info` als bypass — Doku hinkt hinterher |
| `host-services/plex/docker-compose.yml` | produktiv vorhanden, gepinnt `plexinc/pms-docker:1.43.1.10611-1e34174b1@sha256:...`, `network_mode: host`, `/mnt/user/media:ro` + `/mnt/user/photos:ro` | Master-Doku sagt explizit "Plex-Media-Server ist historischer Host-Sonderfall, nicht als Repo-Compose-Stack enthalten" — **das stimmt nicht mehr**, Plex ist jetzt ein Repo-Compose-Stack |
| `host-services/docker/` | leeres Verzeichnis | nicht erwaehnt |
| `infra/dns/` | leeres Verzeichnis | nicht erwaehnt |
| `ops/Semaphore/` | Skripten/Playbooks aber kein Compose | nicht erwaehnt |
| `ops/backrest/` | leeres Verzeichnis (Stack laut Master-Doku am 2026-05-15 entfernt) | korrekt als entfernt dokumentiert; Verzeichnis sollte leer bleiben oder weg |
| `apps/firefly/`, `apps/firefly-fints/` | leere Verzeichnisse | nicht erwaehnt |
| `apps/stirling-pdf/` | leeres Verzeichnis (durch `bentopdf` abgeloest) | korrekt als abgeloest dokumentiert |
- **Aktion (Doku-Synchronisierung):** `HOMELAB_ARCHITECTURE_MASTER_V2.md` Abschnitt 7 (Container-Zielbild), `docs/SERVICE_CATALOG.md` und `docs/REPO_MAP.md` um Jellyfin und Plex erweitern. Plex-Doku im Master umschreiben: nicht mehr "historisch ausserhalb Repo", sondern "Compose-Stack mit `network_mode: host` als VPN-Discovery-Ausnahme".
- **Aktion (Repo-Hygiene):** Die leeren Verzeichnisse `apps/firefly`, `apps/firefly-fints`, `apps/stirling-pdf`, `host-services/docker`, `infra/dns`, `ops/backrest`, `ops/grafana-influxdb/scripts`, `ops/Semaphore/playbooks`, `ops/Semaphore/Scripts` aufraeumen — Master-Doku sagt: "Leere `.keep`-Platzhalter wurden entfernt; neue Verzeichnisse sollen erst mit konkretem Inhalt ins Repo." Diese Verzeichnisse verletzen diese Regel passiv.
### 2.6 Image-Pinning
Lt. `docs/archive/2026-05/NEXT_SPRINT_TODO_2026-05-16.md` sind diese Stacks noch nicht voll versioniert gepinnt:
- `ddns-updater``latest...@sha256`
- `glances``latest-full@sha256`
- `scrutiny``latest-omnibus@sha256`
Das ist bewusst dokumentiert und kein Audit-Befund.
---
## 3. Schicht B — Hardening-Sprint 2026-05 (Sitrep)
Dies war der Sprint, der nach dem 2026-05 Restore explizit gesetzt wurde. Stand im Repo:
| Sprint-Item | Stand 2026-05-16 (Plan) | Stand 2026-05-23 (Repo) | Beleg |
|---|---|---|---|
| **(1) Backup-Konsistenz** — `dump_sqlite_container` fuer Gitea/Vaultwarden/Uptime-Kuma/Speedtest/Filebrowser + `pg_dump` Nextcloud | offen | ✅ erledigt | `ops/borg-ui/scripts/pre-backup-dumps.sh` Z. 97139 (`dump_sqlite_container`), Z. 253258 (Nextcloud `pg_dump`), Z. 261264 (alle SQLite-Container mit Host-Fallback), Z. 267 (Filebrowser BoltDB). Borg-Scope erweitert um `/mnt/user/services` (Borg-UI Compose Z. 26 + `all-important-sources.txt` Z. 2325). |
| **(2) Filebrowser entschaerfen** — `/mnt/user/appdata:/srv/appdata` weg, gezielte RW-Subpfade | offen | ✅ erledigt | `ops/filebrowser/docker-compose.yml` Z. 1116. Keine Appdata-Mounts mehr. Nur noch `/mnt/user/documents`, `/mnt/user/photos`, `/mnt/user/projekte` als Datenmounts plus eigener `/database` und `/config`. |
| **(3) Authelia Argon2id haerten** — iterations 3, memory 65536, parallelism 4 | offen | ✅ erledigt | `security/authelia/configuration.yml` Z. 1725. Exakt die geplanten Parameter sind aktiv. |
| **(4) Gitea Webhook-Allowlist** — `ALLOWED_HOST_LIST=*` einschraenken | offen | ✅ erledigt | `core/gitea/docker-compose.yml` Z. 18: `GITEA__webhook__ALLOWED_HOST_LIST=komodo-core,localhost,127.0.0.1,192.168.178.0/24`. Zusatz aus heutigem Commit: Public Registration und OpenID-Signup/Signin sind deaktiviert. |
Alle vier Items sind **im Repo abgeschlossen**. Live-Wirksamkeit haengt am Komodo-Deploy aus Gitea — und genau da haengt aktuell der ungepushte Commit `cd650b1` davor (siehe 2.1). Solange er nicht in Gitea ist, ist insbesondere die Gitea-Signup-Schliessung im Live-Stand nicht garantiert.
**Bewusst nicht angefasste Liste (Operator-Entscheidung 2026-05-16) ist weiterhin gueltig:**
- Hermes — bleibt VM-seitig offen, NAS-Stack bewusst nicht starten
- Disk1 NTFS — Phase-2-Migration nach Plan
- Komodo native Auth ohne ForwardAuth
- Grafana/Influxdb3-core `user: "0"`
- Image-Pinning-Vereinheitlichung (`:latest@sha256:`) fuer ddns-updater/glances/scrutiny
---
## 4. Schicht C — "Endstufe?"-Bewertung
### 4.1 Backup/Restore-Readiness
- **Dump-Coverage:** `pre-backup-dumps.sh` deckt 14 Quellen ab: PostgreSQL-Globals + 3 Shared-DBs + 3 dedizierte Postgres (mealie, immich, nextcloud) + 4 SQLite (gitea, vaultwarden, uptime-kuma, speedtest-tracker) + Filebrowser-BoltDB + Borg-UI + Grafana + Komodo-Mongo. Deckt 1:1 die Restore-Matrix-Eintraege ab.
- **Borg-Scope:** `all-important-sources.txt` enthaelt 27 Eintraege inkl. neuer `services/homelab-infra`, `services/stacks`, `services/posture-check` und `secrets`.
- **Restore-Validierungen:** Laut `docs/RESTORE_MATRIX.md` sind am 2026-05-07 Mini-Restores fuer `gitea`, `vaultwarden` und `paperless` validiert worden — dokumentierter Stand.
- **Live offen:** Wann lief der letzte Borg-Lauf? Sind alle Dumps unter `/mnt/user/backups/borg/dumps/latest` frischer als 24h? Siehe Live-Checkliste 9.4.
### 4.2 Monitoring-Migration
- Repo-Zielzustand `monitoring/docker-compose.yml` (337 Zeilen Compose) existiert mit Prometheus, Alertmanager, ntfy-Bridge, Blackbox-Exporter, Loki, Promtail, Grafana, node-exporter, cAdvisor, InfluxDB3 Core.
- Provisioning unter `monitoring/grafana/provisioning/` und `monitoring/prometheus/`, `monitoring/loki/`, `monitoring/promtail/`, `monitoring/alertmanager/`, `monitoring/blackbox/` vollstaendig vorhanden.
- Alte Stacks `ops/grafana-influxdb/` und `ops/loki/` bewusst noch im Repo (dokumentierter Altstand, Rollback-Referenz).
- **Live offen:** Ist `monitoring` schon als Komodo-Stack deployed? Laufen die Container? Sind die Secret-Dateien `monitoring_grafana_admin_password.txt`, `monitoring_grafana_influxdb_token.txt`, `influxdb3_admin_token.json` auf dem Host? Siehe Live-Checkliste 9.5.
### 4.3 Repo-Hygiene
| Befund | Schwere | Aktion |
|---|---|---|
| 8 leere Verzeichnisse (`apps/firefly`, `apps/firefly-fints`, `apps/stirling-pdf`, `host-services/docker`, `infra/dns`, `ops/backrest`, `ops/grafana-influxdb/scripts`, `ops/Semaphore/playbooks`, `ops/Semaphore/Scripts`) | klein | Aufraeumen, danach committen |
| `.serena/` untracked, nicht in `.gitignore` | klein | `.serena/` zu `.gitignore` hinzufuegen |
| 3 `ops/windows-reinstall/*.ps1` untracked | klein | Entscheidung treffen: ins Repo oder ignorieren |
### 4.4 Bekannte dokumentierte Ausnahmen
Aus `HOMELAB_ARCHITECTURE_MASTER_V2.md` Abschnitt 10 — alle weiterhin gueltig und durch den Policy-Check abgedeckt (`ops/policy-checks/last-report.md` 0 Critical):
- Traefik 80/443
- Tailscale Host-Netz + Capabilities
- AdGuard Port 53 + 8082 (Admin-Port LAN-only, dokumentiert; **offener Punkt im Master:** "Traefik-Absicherung ausstehend (Block F)" — bewusst spaeter)
- Plex Host-Netz (aber Master-Doku-Eintrag jetzt falsch, siehe 2.5)
- Scrutiny `privileged: true`
- Komodo Docker-Socket + keine pauschale Middleware
- glance-docker-socket-proxy Read-only Socket
- Gitea SSH 222
- ddns-updater `frontend_net`
- mail-archiver Hybrid-Netze
- `traefik/dynamic/*` manueller Host-Sync
- nextcloud native Auth
- monitoring-influxdb3-core LAN 8181 + `user: "0"`
- monitoring-promtail Docker-Socket read-only
Keine ungeplanten neuen Ausnahmen.
### 4.5 Endstufen-Definition
"Endstufe" ist erreicht, wenn alle folgenden Punkte gruen sind:
1. **Gitea = Quelle der Wahrheit** — kein lokaler Commit ohne Push 🟡 (heute: `cd650b1` ungepusht)
2. **Hardening-Sprint im Repo abgeschlossen** 🟢
3. **Backup-Konsistenz live verifiziert (Borg laeuft, Dumps frisch)** ❓ Live
4. **Monitoring-Stack live, alte Altstaende gestoppt** 🟡
5. **Doku synchron mit Repo (Jellyfin/Plex, leere Verzeichnisse, ...)** 🟠
6. **Policy-Check 0 Critical** 🟢 (4 Warnings sind dokumentierte Ausnahmen)
7. **Restore-Lab gepflegt (`mail-archiver` als naechste Uebung empfohlen)** 🟡 dokumentiert offen
Sechs der sieben Punkte sind in Reichweite ohne neue Architekturentscheidungen. Punkt 3 und 4 brauchen Live-Daten (Abschnitt 9). Punkt 1 ist 1 Push entfernt.
---
## 5. Priorisierte Restliste
| Prio | Aktion | Begruendung | Aufwand |
|---|---|---|---|
| **P0** | `cd650b1` nach Gitea pushen | GitOps-Quelle-der-Wahrheit, Voraussetzung fuer alles weitere | 30 Sekunden |
| **P0** | Live-Daten aus Abschnitt 9 einholen | Ohne Live-Frische ist Endstufen-Bewertung unvollstaendig | 5 Minuten |
| **P1** | Monitoring-Stack live finalisieren (Secrets pruefen, deployen, Smoke-Test, alte Altstaende stoppen) | Aus `docs/archive/2026-05/NEXT_SPRINT_TODO_2026-05-16.md` der naechste produktive Schritt | 12 Stunden mit Tests |
| **P2** | Doku-Drift schliessen: Jellyfin und Plex in `HOMELAB_ARCHITECTURE_MASTER_V2.md` 7.4 + 7.1, `docs/SERVICE_CATALOG.md`, `docs/REPO_MAP.md` ergaenzen; Plex-Eintrag in Abschnitt 7.7 "noch offene Sonderfaelle" entfernen (ist umgesetzt) | Doku ist Source of Truth fuer KI-Audits und Nachfolge | 30 Minuten |
| **P2** | Home Assistant -> InfluxDB final testen, HA-Dashboard in `monitoring-grafana` anlegen | aus NEXT_SPRINT_TODO | 12 Stunden |
| **P3** | Repo-Hygiene: 8 leere Verzeichnisse loeschen, `.serena/` in `.gitignore`, Entscheidung zu `ops/windows-reinstall/*.ps1` | minor, aber dokumentiert | 15 Minuten |
| **P3** | Naechster Restore-Lab-Lauf: `mail-archiver` (empfohlen in `RESTORE_MATRIX.md`) | Restore-Routine ueben, bevor sie gebraucht wird | 1 Stunde |
| **P4** | `.gitattributes` mit `* text=auto eol=lf` hinzufuegen, um CRLF/LF-Mount-Effekte bei KI-Audits zu vermeiden | klein, kosmetisch fuer kuenftige Audits | 5 Minuten |
| **bleibt** | Hermes VM-Seite, Disk1-NTFS Phase 2, AdGuard Admin-Port hinter Traefik (Block F), Image-Pinning ddns/glances/scrutiny | bewusste Operator-Entscheidung, kein Audit-Beduerfnis | nicht jetzt |
---
## 6. Was bewusst NICHT angetastet wurde (Audit-Verzicht)
Konsistent mit der bekannten Nicht-Anfassen-Liste:
- Hermes (VM-seitig offen)
- Disk1 NTFS (Phase-2-Migration nach Plan)
- Komodo native Auth ohne ForwardAuth
- Grafana / influxdb3-core `user: "0"` Uebergangsausnahme
- Image-Pinning-Vereinheitlichung fuer ddns-updater/glances/scrutiny
---
## 7. Risiken und Drift-Indikatoren
| Risiko | Wahrscheinlichkeit | Wirkung | Migitation |
|---|---|---|---|
| Lokaler Clone-Verlust (Disk, Reinstall) bevor `cd650b1` gepusht wurde | gering, aber real (Du bist im Reinstall-Kontext, siehe `ops/windows-reinstall/`!) | Verlust von Gitea-Signup-Closure und Posture-Check-Verbesserungen | **Sofort pushen** |
| Komodo deployt aus Gitea, `gitea` und `borg-ui` laufen aktuell ohne die heutigen Verbesserungen | mittel | Gitea Signup steht noch offen, Borg-Scope umfasst `/mnt/user/services` noch nicht | Push + Komodo-Reaktion pruefen |
| 47 vermeintlich modified files koennten doch echte Diffs sein, wenn der Windows-Host etwas anderes zeigt | gering | falsche Audit-Aussage | Punkt 9.1 auf dem Windows-Host pruefen |
| Doku-Drift wird groesser, wenn weitere Stacks ohne Doku-Update hinzukommen | mittel | KI-Audits und Onboarding leiden | P2-Doku-Sync nicht aufschieben |
| Monitoring-Stack-Migration unfertig, alter und neuer Stack koennten parallel werden | mittel | Doppelte Metric-/Log-Pipeline, Verwirrung bei Diagnose | Live-Status klaeren bevor Deploy |
---
## 8. Sources of Truth — Schnellzugriff
- Operative Quelle der Wahrheit: Gitea `origin/master` (https://git.kaleschke.info/Micha/homelab-infra)
- Architektur-Master: `HOMELAB_ARCHITECTURE_MASTER_V2.md`
- Workflow / GitOps-Regeln: `docs/WORKFLOW.md`
- Drift-Runbook: `docs/GITOPS_DRIFT_RUNBOOK.md`
- Restore-Quellen: `docs/RESTORE_MATRIX.md`, `docs/DISASTER_RECOVERY.md`
- Letzter Policy-Check: `ops/policy-checks/last-report.md` (0 Critical)
- Letzte Sprint-Restliste: `docs/archive/2026-05/NEXT_SPRINT_TODO_2026-05-16.md`
---
## 9. Live-Daten-Checkliste — bitte ausfuehren und zurueckspielen
Fuehre die folgenden Bloecke am Unraid-Host (per SSH oder Web-Terminal) und am Windows-Host (Git Bash / PowerShell in `G:\Gitea_Clone\homelab-infra`) aus und pastiere die Outputs zurueck. Ich integriere sie dann in diesen Report.
### 9.1 Windows-Host: Echter Working-Tree-Status
```powershell
cd G:\Gitea_Clone\homelab-infra
git status --short
git log origin/master..HEAD --oneline
```
Erwartet: Working tree leer (oder nur die 4 Untracked). `cd650b1` als einziger lokaler Commit.
### 9.2 Unraid-Host: Gitea online
```bash
curl -sI --max-time 5 https://git.kaleschke.info/ | head -5
docker exec gitea sh -lc 'gitea --version'
```
Erwartet: `HTTP/2 200` (oder ein Auth-Code, der den Erreichbarkeitstest erfuellt). Gitea-Version stimmt mit Image-Tag `1.25.4` ueberein.
### 9.3 Unraid-Host: Komodo-Webhook-Status
In Komodo UI fuer jeden produktiven Stack aus `Micha/homelab-infra` pruefen:
- `webhook_enabled: true`
- Gitea-Hook auf `http://komodo-core:9120/listener/github/stack/<stack-id>/deploy` aktiv
- `last_status` der letzten Webhook-Delivery in Gitea (Repository -> Settings -> Webhooks)
Pflicht-Stacks zum Pruefen: `traefik`, `gitea`, `authelia`, `vaultwarden`, `postgresql17`, `redis`, `paperless-ngx`, `immich`, `nextcloud`, `mealie`, `mail-archiver`, `ntfy`, `homepage`, `paperless-gpt`, `borg-ui`, `filebrowser`, `code-server`, `uptime-kuma`, `glance`, `glances`, `scrutiny`, `speedtest-tracker`, `bentopdf`, `ddns-updater`, `komodo`, `jellyfin`, `plex`, `adguard`, `tailscale`, `monitoring`, `hermes-agent` (sofern produktiv).
Bei Stacks **ohne** aktiven Webhook bitte den Grund vermerken (dokumentierte Ausnahme oder Nachholbedarf).
### 9.4 Unraid-Host: Borg-Lauf-Frische und Dump-Coverage
```bash
ls -lah /mnt/user/backups/borg/dumps/latest/
stat -c '%y %n' /mnt/user/backups/borg/dumps/latest/*.dump /mnt/user/backups/borg/dumps/latest/*.sql /mnt/user/backups/borg/dumps/latest/*.archive.gz /mnt/user/backups/borg/dumps/latest/*.sqlite 2>/dev/null | sort
docker exec borg-ui borg list --short 2>&1 | tail -10
```
Erwartet: Alle 14 Artefakte aus 4.1 sind vorhanden, mtime juenger als 24h. Borg-Archive-Liste zeigt regelmaessige Laeufe.
### 9.5 Unraid-Host: Monitoring-Stack live?
```bash
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" | grep monitoring
ls -la /mnt/user/appdata/secrets/ | grep -E 'monitoring|influxdb3'
curl -sI --max-time 5 https://monitoring.kaleschke.info/ | head -5
ss -ltnp 2>/dev/null | grep -E ':8181|:9090|:3100' | head -10
```
Erwartet: Entweder alle `monitoring-*` Container laufen (dann ist 4.2 🟢) oder gar nicht (dann ist 4.2 🟡 wie aktuell bewertet). Halb-Zustand ist ein Audit-Befund.
### 9.6 Unraid-Host: GitOps-Pflichtmatrix Spot-Check fuer einen Stack
Beispiel `gitea` (weil der heutige Commit ihn betrifft):
```bash
cd /mnt/user/services/stacks/gitea
git rev-parse --short HEAD
git status -sb
docker inspect gitea --format '{{.Image}}'
docker exec gitea sh -lc 'env | grep -E "ALLOWED_HOST_LIST|DISABLE_REGISTRATION|ENABLE_OPENID"'
```
Erwartet: Komodo-Workspace `HEAD` zeigt entweder auf `cd650b1` (wenn Push schon erfolgt + Komodo deployed) oder auf `af231dd` (vor dem Push). ENV-Vars in der Live-Runtime spiegeln den Commit, der Komodo zuletzt deployed hat.
### 9.7 Unraid-Host: Host-Listener-Spot-Check
```bash
ss -ltnp 2>/dev/null | grep -E ':80|:443|:53|:222|:8082|:8181' | sort
```
Erwartet exakt die dokumentierten Ausnahmen aus `HOMELAB_ARCHITECTURE_MASTER_V2.md` Abschnitt 10. Andere Listener = Befund.
---
## 10. Nachhalte-Vorschlag
Wenn Du moechtest, halte ich diesen Audit in zwei Schritten zu Ende:
1. Du fuehrst Abschnitt 9 aus und pastierst die Outputs zurueck.
2. Ich aktualisiere diesen Report mit den Live-Ergebnissen, ergaenze die Ampel und schliesse die offenen 🟡/🟠 Punkte oder benenne sie als echte Restliste.
Bis dahin gilt der Stand dieses Reports als Repo-Audit, nicht als Endstufen-Zertifikat.
@@ -1,22 +0,0 @@
# Homelab Audit Final - 2026-05-23
Stand: 2026-05-25 07:33 CEST. Ergebnis nach Push, Live-Messung, Doku-Sync, Repo-Hygiene und erneuter Live-Nachmessung.
| Punkt | Ampel | Beleg |
|---|---|---|
| P0 `cd650b1` nach Gitea pushen | gruen | Push `af231dd..cd650b1 master -> master`; produktiver Runtime-Stand `66ee10c` enthaelt `cd650b1`. Die abschliessenden Audit-Doku-Commits liegen in Gitea; der runtime-relevante Stack-Inhalt fuer `gitea`, `borg-ui` und `monitoring` ist seit `66ee10c` unveraendert. |
| P0 Live-Daten ablegen | gruen | `docs/archive/2026-05/AUDIT_2026-05-23_LIVE.md` angelegt, keine Secret-Werte dokumentiert. |
| P1 Monitoring live / Altstaende down | gruen | 10 `monitoring-*` Container laufen, `0` unhealthy, `0` starting, `0` stopped; alte `grafana`/`influxdb3-core`/`loki`/`alloy` Container sind nicht vorhanden. `monitoring.kaleschke.info` liefert 302 zu Authelia, Prometheus ist bereit, Loki `/ready` liefert `ready`. |
| P1 Jellyfin/Plex Doku | gruen | `HOMELAB_ARCHITECTURE_MASTER_V2.md`, `docs/SERVICE_CATALOG.md` und `docs/REPO_MAP.md` ergaenzt; Plex ist als Repo-Compose-Stack dokumentiert, nicht mehr als nicht migrierter Dockerman-Sonderfall. |
| P2 Borg-Frische | gruen | Borg-UI DB: letzter Backup-Job `completed`, Archiv `Taegliche-Sicherung-2026-05-25T05:52:44.157`, `nfiles=100221`; 15 kanonische Dump-/Archive-Artefakte von 2026-05-25 06:09/06:10 CEST und damit juenger als 24 h. |
| P3 Repo-Hygiene | gruen | `.serena/` in `.gitignore`; leere Verzeichnisse entfernt; `ops/windows-reinstall/*.ps1` bewusst ins Repo aufgenommen. |
| Policy-Check | gruen | `ops/policy-checks/check_repo.ps1`: 0 Critical, dokumentierte Warnings zu Plex Host-Netz, InfluxDB/Grafana `user: "0"` und bekannten mutable-tag-Ausnahmen. |
## Bewusst offen
- Keine offenen Punkte aus der Audit-Restliste.
- Nicht Teil des Audits: Nach Disk1 Phase 2 laeuft seit 2026-05-25 eine nicht korrigierende Parity-Pruefung (`NOCORRECT`) im Hintergrund; Zwischenstand der Nachmessung: `mdResyncAction=check P`, `mdResyncCorr=0`.
## Schlussbewertung
Das Homelab ist fuer die Audit-Restliste in der Endstufe. Es gibt keine kritischen Repo-, GitOps- oder Live-Befunde aus diesem Audit. Monitoring ist produktiv und ready, alte Altstaende sind down, Backups und Dumps sind frisch.
@@ -1,88 +0,0 @@
# Homelab Audit Live-Daten - 2026-05-23
Stand: 2026-05-23 11:27 CEST. Quelle: lokaler Windows-Clone und SSH auf `Kallilabcore`. Secret-Werte wurden nicht ausgelesen oder redaktiert; dokumentiert sind nur Status, Dateinamen, Modi, Env-Key-Namen und nicht geheime Bool-/Host-Werte.
## 9.1 Windows-Host / Git
- `git status --short` nach dem initialen Push: keine tracked Modifikationen, untracked waren `.serena/`, `docs/archive/2026-05/AUDIT_2026-05-23.md`, `docs/archive/2026-05/CODEX_ENDSTUFE_PROMPT_2026-05-23.md` und drei `ops/windows-reinstall/*.ps1`.
- `cd650b1` wurde nach `origin/master` gepusht: `af231dd..cd650b1 master -> master`.
## 9.2 Gitea online
- `curl -sI https://git.kaleschke.info/`: `HTTP/2 200`.
- `docker exec gitea gitea --version`: `1.25.4`.
- Signup-Smoke: `/user/sign_up` meldet Registration disabled.
## 9.3 Komodo / Workspace-Reaktion
| Stack | Workspace HEAD | Status | Live-Beleg |
|---|---:|---|---|
| `gitea` | `cd650b1` | `## master...origin/master` | Container neu gestartet, Env-Keys aktiv |
| `borg-ui` | `cd650b1` | `## master...origin/master` | Container healthy, `/mnt/user/services -> /local/services` gemountet |
| `monitoring` | `cd650b1` | `## master...origin/master`, untracked Host-Backup `monitoring/prometheus/alerts.yml.bak-20260520-incident` | Stack laeuft seit 4 Tagen |
Gitea Live-Env ohne Secrets:
```text
GITEA__service__DISABLE_REGISTRATION=true
GITEA__openid__ENABLE_OPENID_SIGNIN=false
GITEA__openid__ENABLE_OPENID_SIGNUP=false
GITEA__webhook__ALLOWED_HOST_LIST=komodo-core,localhost,127.0.0.1,192.168.178.0/24
```
## 9.4 Borg-Lauf-Frische und Dump-Coverage
- Borg UI DB: Repository `appdata-critical`, `last_backup=2026-05-23 02:30:12 UTC`, `archive_count=30`, letzter Job `completed`, `nfiles=100056`.
- Schedule: `Taegliche Sicherung` enabled, letzter Lauf `2026-05-23 02:30:11 UTC`, naechster Lauf `2026-05-24 02:30:00 UTC`.
- Hinweis: `docker exec borg-ui borg list --short` funktioniert ohne Repository-Parameter/Env nicht (`Invalid location format: ""`). Der Borg-UI-Status wurde deshalb ueber die lokale Borg-UI-Datenbank ohne Secret-Spalten ausgewertet.
Frische Artefakte in `/mnt/user/backups/borg/dumps/latest`:
```text
2026-05-23 04:00 postgresql17-globals.sql
2026-05-23 04:00 postgresql17-mailarchiver.dump
2026-05-23 04:00 postgresql17-paperless.dump
2026-05-23 04:01 postgresql17-authelia.dump
2026-05-23 04:01 mealie.dump
2026-05-23 04:01 gitea.sqlite.dump
2026-05-23 04:01 immich.dump
2026-05-23 04:01 nextcloud.dump
2026-05-23 04:01 uptime-kuma.sqlite.dump
2026-05-23 04:01 vaultwarden.sqlite.dump
2026-05-23 04:01 speedtest-tracker.sqlite.dump
2026-05-23 04:01 filebrowser.bolt.dump
2026-05-23 04:01 borg-ui.sqlite
2026-05-23 04:01 grafana.sqlite
2026-05-23 04:01 komodo-mongo.archive.gz
```
Bewertung: 15 aktuelle Dump-/Archive-Artefakte sind juenger als 24 h. Aeltere nackte `.sqlite`-Dateien vom 2026-05-16 liegen noch im Verzeichnis, sind aber Legacy-Kopien ohne `.dump`-Suffix und nicht Teil der aktuellen Dump-Serie.
## 9.5 Monitoring-Stack
- Aktive Container: `monitoring-grafana`, `monitoring-promtail`, `monitoring-prometheus`, `monitoring-cadvisor`, `monitoring-influxdb3-core`, `monitoring-loki`, `monitoring-blackbox-exporter`, `monitoring-alertmanager-ntfy-bridge`, `monitoring-alertmanager`, `monitoring-node-exporter`.
- Alte Altcontainer `grafana`, `influxdb3-core`, `loki`, `alloy`: nicht vorhanden in `docker ps -a`.
- Secret-Dateien vorhanden und mode `600`: `monitoring_grafana_admin_password.txt`, `monitoring_grafana_influxdb_token.txt`, `influxdb3_admin_token.json`.
- `https://monitoring.kaleschke.info/`: `HTTP/2 302` zu Authelia, wie erwartet.
- Host-Listener fuer Monitoring: nur `127.0.0.1:8181`; keine Host-Listener fuer `:9090` oder `:3100`.
- Prometheus readiness: `Prometheus Server is Ready.`
- Grafana health: `database=ok`, Version `12.4.3`.
- Loki: Container laeuft und API/metrics liefert `loki_build_info`; `/ready` liefert aktuell `503 Service Unavailable`, waehrend Query-/Metrics-Endpunkte 200-Zaehler zeigen. Kein Reparaturversuch gestartet, weil der produktive Logpfad nachweislich Daten annimmt und die Stop-Regel keine blinden Eingriffe erlaubt.
## 9.6 GitOps Spot-Check Gitea
- `/mnt/user/services/stacks/gitea`: `cd650b1`, `## master...origin/master`.
- Docker-Image: `docker.gitea.com/gitea:1.25.4`.
- Live-ENV spiegelt `cd650b1` fuer Registrierung, OpenID und Webhook-Allowlist.
## 9.7 Host-Listener
Dokumentierte Listener gefunden:
- `:80`, `:443` Traefik
- `:53` AdGuard DNS
- `:222` Gitea SSH
- `:8082` AdGuard Admin
- `127.0.0.1:8181` Monitoring InfluxDB
Zusaetzlich sichtbar: mehrere `wsdd2` Listener auf `:5355` je Interface. Das ist Host-/Unraid-Service-Discovery, kein Compose-Webdienst und kein GitOps-Stack-Port.
-895
View File
@@ -1,895 +0,0 @@
# Homelab-Audit KalliLab CORE
Stand: 2026-05-25
Methode: Repo-basierter Audit auf `master` (lokaler Clone). Keine Live-Messung gegen den Host. Querverweise auf Audit-Live-Daten aus `docs/archive/2026-05/AUDIT_2026-05-23_LIVE.md`, wo verfuegbar.
Auftrag: externer, kritischer Audit-Blick zusaetzlich zur internen `docs/archive/2026-05/STRATEGISCHE_BEWERTUNG_2026-05-23.md`.
## Wichtige Vorbemerkung
Es gibt seit dem 23.05. eine fundierte interne Bewertung (`docs/archive/2026-05/STRATEGISCHE_BEWERTUNG_2026-05-23.md`) und eine konsolidierte Hausaufgabenliste (`docs/archive/2026-05/CODEX_KONSOLIDIERUNG_2026-05-23.md`). Davon wurden seit dem 25.05. bereits umgesetzt:
- Jellyfin entfernt (MASTER 7.8)
- Homepage entfernt (MASTER 7.8)
- Uptime-Kuma entfernt (MASTER 7.8, SECRETS_MAP 29)
- Monitoring-Stack produktiv (AUDIT_FINAL 9), Altstaende-Container down
- Disk1 NTFS -> XFS Phase 2 abgeschlossen am 2026-05-25 (STORAGE_LAYOUT 3)
- Unraid-Flash-Backup live (`unraid-flash-config.tar.gz` im Borg-Lauf)
- GitHub-Push-Mirror `michaelkaleschke-spec/homelab-infra` aktiv (DR 10, MASTER 7.1)
Diese geloesten Punkte werden hier nicht wiederholt. Dieser Audit konzentriert sich auf das, was nach dem 23.05.-Sprint **noch offen ist** und auf das, was die strategische Bewertung **nicht oder nur kurz angerissen** hat.
---
# Phase 1: Repo-Inventar
## Ordnerstruktur (Ist-Zustand)
```
homelab-infra/
├── apps/ 9 Stacks (bentopdf, immich, mail-archiver, mealie, nextcloud, ntfy, paperless, paperless-gpt, unbound)
├── core/ 1 Stack (gitea)
├── docs/ 28 Markdown-Dokumente
├── env/ 2 *.example
├── host-services/ 3 Stacks (Adguard, plex, tailscale)
├── infra/ 3 Stacks (ddns-updater, postgresql17, redis)
├── monitoring/ 1 Compose mit 10 Services + Provisioning
├── ops/ 17 Verzeichnisse (Semaphore, borg-ui, code-server, filebrowser, glance, glances, grafana-influxdb [Altstand], hermes-agent, komodo, loki [Altstand], policy-checks, restore-tests, scrutiny, speedtest, uptime-kuma [Altrest], windows-reinstall)
├── security/ 2 Stacks (authelia, vaultwarden)
├── services/ 1 posture-check (Host-Skripte)
└── traefik/ 1 Compose + dynamic/ (3 Files)
```
**Inventar-Befund:**
- ~30 Compose-Dateien, 1 zentraler Compose-Multi-Service (`monitoring/`).
- 29 Composes wurden vom Policy-Checker validiert (`ops/policy-checks/last-report.md`): **0 Critical, 4 Warnings, 9 Info**.
- Doku-Dichte ist hoch (REPO_MAP, SERVICE_CATALOG, RESTORE_MATRIX, DISASTER_RECOVERY, SECRETS_MAP, WORKFLOW, STORAGE_LAYOUT, GITOPS_DRIFT_RUNBOOK, ALERTING_MAP).
- Restore-Tests sind als echte Scripts versioniert (`ops/restore-tests/`). Ueberdurchschnittlich.
## Gut dokumentierte Bereiche (Belegt)
| Bereich | Quelle |
|---|---|
| Architektur, Netze, Ausnahmen | `HOMELAB_ARCHITECTURE_MASTER_V2.md` |
| GitOps-Workflow, Drift | `docs/WORKFLOW.md`, `docs/GITOPS_DRIFT_RUNBOOK.md` |
| Backup-Scope, Restore-Wege, Tier-Modell | `docs/RESTORE_MATRIX.md`, `docs/DISASTER_RECOVERY.md`, `ops/borg-ui/BACKUP_SCOPE.md` |
| Storage / Cache-Policy / FS / Posture | `docs/STORAGE_LAYOUT.md` (zum Audit-Zeitpunkt noch `docs/STORAGE_LAYOUT.draft.md`) |
| Secret-Inventur | `docs/SECRETS_MAP.md` |
| Alert-Pfade | `docs/ALERTING_MAP.md` |
## Luecken / Unklar (vermutet bzw. Rueckfrage noetig)
| Luecke | Warum es ein Audit-Loch ist |
|---|---|
| `docs/STORAGE_LAYOUT.draft.md` ist `Draft 1.3`, nicht `Active` | Stand zum Audit-Zeitpunkt 2026-05-25: mehrere Hard Rules (12 Constitution) galten formal noch nicht. Hard Rule 11 (kein Stack ohne Restore-Pfad in RESTORE_MATRIX) wurde schon eingehalten — also nur Formal-Luecke. Folgearbeit: als `docs/STORAGE_LAYOUT.md` Active heben. |
| `docs/SERVICES_RECOVERY.md` ist als verbindlich angekuendigt (STORAGE_LAYOUT 4), aber nicht im Repo | Konkrete Mirror-Mechanik fuer `services/gitea/git/repositories/` ≤ 6 h ist nirgends spezifiziert. |
| Hardware-Inventar: kein zentrales Dokument | Keine Stelle im Repo nennt CPU-Modell, RAM-Groesse, NIC-Speed, Mainboard, Parity-Disk-Groessen — nur "Samsung 970 EVO Plus 2 TB" steht in STORAGE_LAYOUT 3. |
| USV: keine Erwaehnung | Keine Datei nennt eine USV. Unklar, ob vorhanden. |
| Familien-/User-Onboarding-Doku | Keine Doku, die deine Frau/Kinder lesen muessten ("So loggst du dich in Nextcloud ein"). Aktuell ist alles Operator-Doku. |
## Fuer den Audit besonders wichtige Dateien (verwendet)
- `HOMELAB_ARCHITECTURE_MASTER_V2.md` — komplett
- `docs/WORKFLOW.md`, `docs/REPO_MAP.md`, `docs/SERVICE_CATALOG.md` — komplett
- `docs/DISASTER_RECOVERY.md`, `docs/RESTORE_MATRIX.md`, `docs/SECRETS_MAP.md` — komplett
- `docs/STORAGE_LAYOUT.md` (zum Audit-Zeitpunkt `docs/STORAGE_LAYOUT.draft.md`), `docs/archive/2026-05/STRATEGISCHE_BEWERTUNG_2026-05-23.md` — komplett
- `docs/archive/2026-05/AUDIT_2026-05-23_LIVE.md`, `docs/archive/2026-05/AUDIT_2026-05-23_FINAL.md`
- `ops/policy-checks/last-report.md`
- `monitoring/docker-compose.yml`, `monitoring/prometheus/alerts.yml`
- `traefik/docker-compose.yml`, `traefik/dynamic/middlewares.yml`
- `security/authelia/configuration.yml`, `security/authelia/docker-compose.yml`
- `apps/paperless/docker-compose.yml`, `apps/immich/docker-compose.yml`, `apps/nextcloud/docker-compose.yml`
- `host-services/Adguard/docker-compose.yml`
---
# A. Executive Summary
**Was schon stark ist:**
- GitOps-Disziplin: Gitea als Sollzustand, Komodo als Consumer, dokumentierter Drift-Runbook, Stop-Regel ("zwei Fehlversuche -> Pflichtmatrix"). Seltene Reife.
- Backup-Architektur: Pre-Backup-Dumps + Borg + Restore-Tests mit echtem Smoke-Test-Kriterium ("Container startet ≠ Erfolg"). 15 frische Dumps < 24 h alt (`AUDIT_LIVE 9.4`).
- Architektur-Klarheit: `frontend_net` / `backend_net` / app-interne Netze, keine Sammelnetze, dokumentierte Ausnahmen.
- Image-Pinning: Tier-1-Stateful mit `<minor>@sha256:...`. Konsequent durchgezogen.
- Secrets-Hygiene: Keine Secret-Werte im Repo, `_FILE`-Mounts + Komodo Stack ENV, explizit dokumentierte Ausnahmen.
- Policy-as-Code: `check_repo.ps1` mit 0 Critical und sauber dokumentierten Exceptions.
**Was kritisch ist:**
- **AdGuard Admin-Port 8082 ohne Authelia/2FA am LAN gebunden** (`host-services/Adguard/docker-compose.yml:16`) — dokumentierte "Operator-Entscheidung" 2026-05-25. Im Heim-LAN tolerierbar, mit IoT/Gaeste-WLAN potenziell ein Pfad zur DNS-Manipulation. Niedrigster Aufwand: Bind nur auf Tailscale-Interface.
- **Authelia ACL: 2FA nur fuer `files.kaleschke.info` und `scrutiny.kaleschke.info`** (`security/authelia/configuration.yml:44-48`). Borg-UI, Code-Server, Filebrowser, Glance — alles nur `one_factor`. Bei Pwd-Kompromittierung des Operator-Accounts ist Borg-UI + Code-Server der direkteste Pfad zur Datenexfiltration.
- **Authelia-Repo-Baseline ↔ Host-Config-Drift "by design"** (`docs/REPO_MAP.md:48`, `SERVICE_CATALOG 23`). User-DB, OIDC-Clients und Secrets sind hostseitig, Manual-Merge-Pflicht. Stelle, an der Drift mit Anlauf passiert.
- **Komodo Self-Bootstrap-Problem ist nur dokumentiert, nicht geloest** (MASTER 13: Self-Stack Drift-Recovery 2026-05-04). Bei Recovery vom kalten Host musst du Komodo aus `compose.yaml` neu erzeugen — dafuer brauchst du die `.env` mit `KOMODO_*`-Secrets, die nur auf Host und ggf. Vaultwarden liegen.
- **Backup-Off-Site-Diversitaet:** BorgBase Hetzner ist Single-Provider; Borg-Passphrase analog gesichert ist als TODO markiert (`docs/DISASTER_RECOVERY.md:64,401`). Wenn Hetzner-Account verloren geht, ist das halbe DR-Versprechen weg.
**Was unnoetig komplex ist:**
- Drei dokumentierte Monitoring-/Logging-Pfade gleichzeitig im Repo: `ops/grafana-influxdb` (Altstand), `ops/loki` (Altstand), `monitoring/` (Ziel). Die Altstaende sind als Container down, aber **Verzeichnisse noch im Repo** — Doppelpflege-Risiko. Der versprochene Repo-Cleanup (`git rm`) fehlt.
- Hermes-Agent: NAS-Stack bewusst deaktiviert ("VM-seitig offen"), aber Stack-Verzeichnis und Compose mit Dashboard-Domain bleiben im Repo. Mehr "Schwebezustand" als operativer Wert.
- BentoPDF: "vorbereitet", noch nie produktiv abgenommen (`SERVICE_CATALOG 52`, `MASTER 7.5`).
- `infra/redis` ist als shared Cache deklariert, wird de facto nur von Paperless genutzt (Authelia nicht, Immich/Nextcloud/Mealie haben eigene Redis). Das "shared" stimmt im Repo nicht mit der Realitaet ueberein.
**Groesster Hebel:**
**Authelia OIDC-Provider aktivieren** — wenn Nextcloud, Immich, Grafana (und perspektivisch Mealie via OIDC-Bridge) per SSO laufen, gewinnst du gleichzeitig:
- (a) Familien-Onboarding-Komfort (ein Login),
- (b) zentrale Brute-Force-Regulation und Audit,
- (c) Voraussetzung fuer sinnvolles CrowdSec/Fail2Ban,
- (d) zentrale 2FA-Pflicht statt App-by-App.
Das ist ein Sprint, nicht ein Quartal, und macht aus deinem "Admin-Authelia" ein echtes Identity-System.
**Was ein erfahrener Homelabber sofort aendern wuerde:**
1. AdGuard-Admin-Port nur auf Tailscale-Interface binden (5 Min, Compose-Edit).
2. Borg-Passphrase auf Papier in Bankschliessfach (15 Min, off-system).
3. `scrutiny` und `ddns-updater` `no-new-privileges` Warning aufraeumen (10 Min) — kosmetisch, aber Policy-Check sollte clean sein.
4. Altstaende `ops/grafana-influxdb/` und `ops/loki/` aus Repo entfernen (Backup-Branch dann `git rm`).
5. Renovate-Bot gegen Gitea einrichten — beendet manuelle Digest-Pflicht.
---
# B. Scorecard (1 = exzellent, 10 = ungenuegend)
| Bereich | Note | Begruendung |
|---|---:|---|
| Hardware | **nicht bewertbar** | Keine Inventar-Doku im Repo. Nur Cache-NVMe genannt. Siehe Phase H. |
| Ordnerstruktur | **2** | Klare Trennung apps/infra/ops/security/core; konsistente Namenskonvention (mit Migrationsplan in STORAGE_LAYOUT 6). Kleinerer Haenger: `host-services/Adguard/` mit Grossbuchstabe. |
| Storage | **3** (Repo-Stand) / **2** (mit STORAGE_LAYOUT Active) | Cache-XFS, Disk1-XFS jetzt erreicht. Pfad-Disziplin via `/mnt/user/...`. Posture-Check etabliert. Note durch Draft-Status und fehlendes `SERVICES_RECOVERY.md` gedrueckt. |
| Docker-Architektur | **2** | Netzmodell klar, Healthchecks fehlen grossflaechig, `latest@sha256` als bewusster Kompromiss bei einigen Images dokumentiert. Keine Memory-Limits. |
| Reverse Proxy / Zugriff | **2** | DNS-Challenge, Wildcard-faehig, Authelia ForwardAuth, dynamic config sauber getrennt. Manuelle Host-Sync-Ausnahme ist pragmatisch. |
| Security | **3** | Solides Fundament (Authelia Argon2id, no-new-privileges-Standard, Webhook-Allowlist, `cloudflare_dns_api_token` als Docker Secret), aber: nur 2 Domains mit 2FA, AdGuard-Admin direkt am LAN, kein WAF/Bouncer, Authelia Regulation 5-Min-Ban ist gentil. |
| Netzwerk / DNS | **2** | AdGuard + Unbound + Tailscale-Trias ist Best-of-Class-Homelab. FritzBox als Router nicht im Repo dokumentiert, daher Note nicht 1. |
| Backup | **2** | Borg, Pre-Dumps, Tier-Modell, dokumentierter Scope. Punkt-Abzug: Single-Provider Off-Site, Passphrase nicht analog, Komodo-Mongo-Dump-Verifikation nicht im Auto-Cron. |
| Restore-Faehigkeit | **2** | RESTORE_MATRIX mit Smoke-Test je Dienst, Restore-Test-Schedule + Validierungen fuer Vaultwarden/Gitea/Paperless dokumentiert. Punkt-Abzug: Immich-Restore noch nie geuebt — groesster Datentopf. |
| GitOps | **1-2** | Webhook-Pflicht fuer neue Stacks, Source-of-Truth-Hierarchie, Drift-Runbook. Self-Bootstrap-Problem von Komodo zieht von 1 auf 2. |
| Monitoring | **3** | Stack produktiv, aber Altstaende noch im Repo, Family-View-Dashboard fehlt, Alerts (`alerts.yml`) sehr knapp (5 Regeln), keine Cert-Expiry-Alert auf Prometheus-Ebene (Cert-Token-Check laeuft separat). |
| Dokumentation | **1** | Aussergewoehnlich. SERVICE_CATALOG ist Gold. Einziger Punkt: kein End-User-/Familien-Onboarding. |
| Automatisierung | **3** | Borg-Dumps automatisiert, posture-check Host-Cron, Alert->ntfy-Pipe. Aber: keine CI gegen Repo, kein Renovate, kein automatisches Image-Update-Tracking. |
| Wartbarkeit | **2** | Doku traegt; Sprintpflege-Disziplin sichtbar (MIGRATION_LOG, AUDIT_FINAL). Risiko: Authelia-Drift, Hermes-Schwebezustand. |
| Nerd-Faktor | **2-** | Komodo + Borg-UI + ntfy-Bridge + Posture-Check + Restore-Lab + Hermes-Experiment + Push-Mirror + Digest-Pinning. Liegt zwischen "Solider Senior" und "Spielwiese halten lernen". |
**Gesamteindruck: 2 (gut).** Strukturell weit ueber durchschnittlichem Homelab; konkrete Luecken sind klar benennbar und nicht systemisch.
---
# C. Top-20 Findings
> Format: priorisiert nach Risiko-zu-Aufwand-Hebel. Jeder Eintrag hat Fundstelle, Empfehlung und Prio.
### F-01 · AdGuard-Admin am LAN ohne Auth
- **Kategorie:** Security / Zugriff
- **Fundstelle:** `host-services/Adguard/docker-compose.yml:16`, `MASTER 10`, `docs/SERVICE_CATALOG.md:14`
- **Beobachtung:** Port `8082:80` direkt auf alle Interfaces. Bewusste Operator-Entscheidung 2026-05-25.
- **Risiko:** Jedes Geraet im LAN kann DNS-Filterregeln, Upstream und Logging manipulieren. IoT-Kompromittierung oder Gast-WLAN -> DNS-Hijack moeglich.
- **Best Practice:** Admin-UIs nicht im LAN ohne Auth. Entweder hinter Traefik+Authelia mit `two_factor` oder Bind auf Tailscale-Interface (z. B. `100.x.y.z:8082:80`).
- **Empfehlung:** Schritt 1 — Bind auf Tailscale-IP (S, 5 Min). Schritt 2 — optional spaeter Traefik-Route hinter Authelia.
- **Prioritaet:** Sollte zeitnah
- **Aufwand:** S
- **Validierung:** `ss -ltnp | grep :8082` zeigt nur Tailscale-IP; LAN-Browser-Zugriff schlaegt fehl.
- **Rollback:** Compose-Diff zurueck, Komodo redeploy.
### F-02 · Borg-Passphrase nicht analog gesichert
- **Kategorie:** Backup / DR
- **Fundstelle:** `docs/DISASTER_RECOVERY.md:64,401`, `docs/SECRETS_MAP.md:48`
- **Beobachtung:** `borg_repo_passphrase.txt` liegt im Host-Filesystem unter `/mnt/user/appdata/secrets/`. Doku weist explizit darauf hin, dass eine externe analoge Sicherung Operator-Aufgabe ist.
- **Risiko:** Wenn Unraid-Host und ggf. Vaultwarden gleichzeitig defekt sind, ist das verschluesselte Borg-Repo bei Hetzner nutzlos.
- **Empfehlung:** Auf Papier ausdrucken, in Bankschliessfach oder bei vertrauter Person versiegelt. Zusaetzlich in Vaultwarden hinterlegen (aber Vaultwarden hilft nicht, wenn es selbst restauriert werden muss).
- **Prioritaet:** Muss sofort
- **Aufwand:** S
- **Validierung:** Du kannst den Wert ohne Host wiederherstellen.
### F-03 · Single-Provider Off-Site Backup
- **Kategorie:** Backup
- **Fundstelle:** `ops/borg-ui/BACKUP_SCOPE.md`, `docs/RESTORE_MATRIX.md:77-96`, `STORAGE_LAYOUT 8.1`
- **Beobachtung:** Hetzner Storage Box als alleiniges Off-Site-Borg-Ziel. STORAGE_LAYOUT 8.1 sieht zusaetzlich lokales Borg-Repo auf `/mnt/user/backups/borg/` vor (gleicher Host) und eine externe Wechselplatte (manuell rotiert).
- **Risiko:** Hetzner-Account-Verlust (Payment-Issue, Account-Hack, Provider-Outage) = halbes 3-2-1.
- **Best Practice:** Zweites Off-Site-Ziel mit unterschiedlichem Provider oder Cold-Wechselplatte mit fester Rotationskadenz.
- **Empfehlung:** (a) Wechselplatten-Rotation in fester Kadenz dokumentieren (zwei Platten, monatlicher Tausch). Oder (b) zweites Borg-Repo bei rsync.net / BorgBase EU2 / privatem 2. Standort.
- **Prioritaet:** Sollte zeitnah
- **Aufwand:** M
- **Validierung:** `borg list` gegen beide Repos, beide < 7 Tage alt.
### F-04 · Authelia 2FA-Pflicht zu schmal
- **Kategorie:** Security
- **Fundstelle:** `security/authelia/configuration.yml:44-53`
- **Beobachtung:** Nur `files.kaleschke.info` und `scrutiny.kaleschke.info` sind `two_factor`. Tier-1-Operator-UIs wie Borg-UI, Code-Server, Filebrowser (zweite Route?), Komodo (eigene Auth), Glance, Grafana laufen mit `one_factor`.
- **Risiko:** Operator-Passwort-Kompromittierung (Phishing, Keylogger, Browser-Save-Leak) gibt ohne 2FA Vollzugriff auf Code-Server (Repo + Workspace), Borg-UI (Restore-Pfade), Filebrowser (Documents/Photos).
- **Empfehlung:** ACL erweitern: `two_factor` fuer `borg.kaleschke.info`, `code.kaleschke.info`, `files.kaleschke.info` (schon), `glance.kaleschke.info` (debattierbar), `traefik.kaleschke.info`. Komodo bleibt Ausnahme.
- **Prioritaet:** Muss sofort
- **Aufwand:** S
- **Validierung:** Nach Login auf `borg.kaleschke.info` wird 2FA-Prompt erzwungen.
- **Rollback:** ACL-Block zurueck.
### F-05 · Repo-Altstaende `ops/grafana-influxdb/` und `ops/loki/` nicht entfernt
- **Kategorie:** Wartbarkeit / GitOps
- **Fundstelle:** Repo-Wurzeln `ops/grafana-influxdb/`, `ops/loki/`; `MASTER 7.6`, `SERVICE_CATALOG 68-70,80-81`, `AUDIT_FINAL 9`
- **Beobachtung:** Container down, aber Compose-Dateien + Provisioning bleiben im Repo. Doku referenziert beide gleichzeitig. Risiko: jemand (zukuenftiges Ich, KI) deployt versehentlich den Altstand.
- **Empfehlung:** `git rm` der beiden Verzeichnisse, Tag `pre-monitoring-cleanup` fuer Rollback, MIGRATION_LOG-Eintrag.
- **Prioritaet:** Sollte zeitnah
- **Aufwand:** S
- **Validierung:** `policy-checks` laeuft clean, Repo enthaelt nur noch `monitoring/`.
### F-06 · Hermes-Agent im Schwebezustand
- **Kategorie:** App-Landschaft / Wartbarkeit
- **Fundstelle:** `ops/hermes-agent/docker-compose.yml`, `MASTER 7.5`, `SERVICE_CATALOG 82-83`
- **Beobachtung:** "NAS-Stack bewusst deaktiviert" wegen offener VM-Seite. Dashboard-Domain (`hermes.kaleschke.info`) + Authelia-ACL + Secret-Pfade dokumentiert.
- **Risiko:** Schleichender Verfall — in 6 Monaten verstehst du Model-C nicht mehr ohne `ops/hermes-agent/README.md`. Bei jeder Authelia-/Compose-Aenderung musst du Hermes mitpruefen, obwohl es nichts tut.
- **Empfehlung:** Operator-Entscheidung mit 60-Tage-Deadline ehrlich treffen. Wenn nicht produktiv bis 2026-07-25: `git rm ops/hermes-agent/`, Domain aus DNS, ACL-Eintrag raus.
- **Prioritaet:** Sollte zeitnah (Entscheidung)
- **Aufwand:** S (Entfernen) / L (echte Produktiv-Aktivierung)
- **Validierung:** Entweder Smoke-Test auf `hermes.kaleschke.info` mit funktionalem Use-Case-Beleg, oder Repo-clean.
### F-07 · Monitoring-Stack ohne Digest-Pin
- **Kategorie:** Reproduzierbarkeit / GitOps
- **Fundstelle:** `monitoring/docker-compose.yml:3,28,66,84,100,118,276,296`
- **Beobachtung:** Prometheus, Alertmanager, Blackbox, Loki, Promtail, Grafana, node-exporter, cAdvisor sind alle nur per Tag gepinnt (`prom/prometheus:v3.7.3`, `grafana/grafana:12.4.3`, ...). Nur `influxdb3-core` hat `@sha256:`. Das widerspricht der Image-Versionierungs-Disziplin der Tier-1-Stateful-Dienste.
- **Risiko:** Wenn upstream einen Tag erneut pushed (Versionsdrift, Supply Chain), wird beim Rebuild ein anderer Container deployed — gerade Monitoring sollte stabil sein.
- **Empfehlung:** Beim naechsten Komodo-Redeploy aktuellen Digest auslesen und einpinnen. Vorbereitung fuer Renovate (F-12).
- **Prioritaet:** Nice to have
- **Aufwand:** S
- **Validierung:** `grep '@sha256' monitoring/docker-compose.yml` listet alle 10 Services.
### F-08 · Alert-Regeln zu duenn
- **Kategorie:** Monitoring
- **Fundstelle:** `monitoring/prometheus/alerts.yml`
- **Beobachtung:** Exakt 5 Regeln: ExternalConnectivityDown, EndpointDown, EndpointSlow, DiskAlmostFull, MemoryHighUsage, Traefik5xx. Es fehlen:
- Borg-Lauf-Frische (ueber `node_exporter` textfile collector oder Pushgateway).
- Zertifikatslaufzeit (Blackbox kann `probe_ssl_earliest_cert_expiry`, aber keine Alert-Regel dafuer).
- Container-down-Alert (cAdvisor liefert `container_last_seen`).
- PostgreSQL-Connection-Saturation.
- Loki ingestion-rate / log-volume spike.
- InfluxDB-Disk-Pressure.
- Backup-Job-Failure.
- **Risiko:** Du siehst Probleme nicht, bevor sie weh tun. Cert-Expiry und Borg-Stale sind die schmerzhaftesten Blind-Spots.
- **Empfehlung:** Mindestens zwei Regeln nachziehen: `BorgArchiveStale` (>30 h, Pushgateway oder textfile) und `TLSCertExpiryNear` (<14 Tage). Rest als Folge-Sprint.
- **Prioritaet:** Sollte zeitnah
- **Aufwand:** M
- **Validierung:** Alerts feuern in Test-Bedingung (Borg-Dump-File touch -d backwards).
### F-09 · Komodo-Self-Bootstrap-Problem
- **Kategorie:** GitOps / DR
- **Fundstelle:** `MASTER 13: Komodo Self-Stack Drift-Recovery 2026-05-04`
- **Beobachtung:** Du hattest schon einen Drift-Vorfall (Komodo-Core ran aus `/tmp/*repair.yml`, Mongo-Pfad fehlte). Recovery-ENV liegt als "temporaeres Tier-1-Secret-Material" unter `/mnt/user/appdata/secrets/_komodo_stack_env_recovery_2026-05-04.env` (Doku-Stand).
- **Risiko:** Bei Totalausfall musst du Komodo aus Compose-Datei wiederbeleben, dafuer brauchst du die Stack-ENV mit `KOMODO_SECRET_KEY`, `KOMODO_MONGO_PASSWORD`, `KOMODO_PERIPHERY_PASSKEY` etc., die nur als Komodo Stack ENV existieren. Klassisches Henne-Ei.
- **Empfehlung:** Komodo-Self-Stack aus Komodos eigener Verwaltung herausnehmen und als handgepflegten `docker compose`-Service in `services/komodo-bootstrap/` halten. Stack-ENV als versiegelte Datei unter `/mnt/user/appdata/secrets/` mit deterministischem Restore-Pfad in RESTORE_MATRIX.
- **Prioritaet:** Sollte zeitnah
- **Aufwand:** M
- **Validierung:** Komodo-Stack-Datei lebt im Repo unter `services/`, nicht in Komodos eigener Workspace-Sicht.
### F-10 · Authelia Repo↔Host Drift "by design"
- **Kategorie:** GitOps / Security
- **Fundstelle:** `docs/REPO_MAP.md:48`, `security/authelia/configuration.yml`, `SERVICE_CATALOG 23`
- **Beobachtung:** Repo enthaelt Baseline ohne Secrets, OIDC, Users-DB. Manuelles Merge auf den Host noetig. Es gibt keine automatische Konsistenz-Pruefung.
- **Risiko:** Repo-Aenderung (z. B. neue ACL-Regel) wird gepusht, aber nie auf den Host gemerged -> Drift, Authelia hinkt der Wahrheit hinterher.
- **Empfehlung:** Symmetrisch zum Traefik-Dynamic-Workflow (manueller Sync explizit als Pflicht in WORKFLOW.md). Zusaetzlich ein einfaches Diff-Script `services/authelia-diff.sh`, das `diff` zwischen Repo-Baseline und Host-Datei zeigt, und das im posture-check als Warning auftaucht, wenn die ACL-Sektion differiert.
- **Prioritaet:** Sollte zeitnah
- **Aufwand:** S
- **Validierung:** Script laeuft, posture-check kennt einen neuen Check `authelia_config_drift`.
### F-11 · Immich-Restore noch nie geuebt
- **Kategorie:** Backup / Restore
- **Fundstelle:** `docs/RESTORE_MATRIX.md:49`, Restore-Test-Schedule
- **Beobachtung:** Vaultwarden / Gitea / Paperless haben Mini-Restore-Tests (2026-05-07). Immich nicht. Immich ist der groesste Datentopf (Familien-Fotos).
- **Risiko:** Silent Corruption in Postgres-pgvecto-rs-Daten bemerkst du erst beim Restore-Versuch.
- **Empfehlung:** Eigener Sprint: Immich-Restore-Test gegen `/mnt/user/backups/restore-lab/immich/` mit Sub-Set der `immich.dump` und einem Foto-Sample. Smoke-Test = "10 Fotos im Browser sichtbar nach Restore".
- **Prioritaet:** Sollte zeitnah
- **Aufwand:** M
- **Validierung:** Report unter `/mnt/user/backups/restore-reports/immich-<datum>.json`.
### F-12 · Keine Image-Update-Automatik (Renovate o. ae.)
- **Kategorie:** Wartbarkeit
- **Fundstelle:** Repo-weit; `docs/WORKFLOW.md:282-288` (Image-Versionierung)
- **Beobachtung:** Digest-Pinning ist konsequent, aber rein manuell. Bei ~30 Images bedeutet das, du musst monatlich fuer Patch-Updates manuell Digests auslesen — oder es bleibt liegen.
- **Risiko:** CVE-Patches werden nicht eingespielt, weil "der laufende Stand ist stabil".
- **Empfehlung:** Renovate Bot (self-hosted, gegen Gitea), Gitea-Actions-Runner. Renovate oeffnet PRs fuer Patch-/Minor-Updates; Major-Updates werden mit Labels separiert.
- **Prioritaet:** Sollte zeitnah (oder Nice to have, je nach Schmerz)
- **Aufwand:** M (Initial-Setup ist substantiell)
- **Validierung:** Renovate hat erste PRs in Gitea geoeffnet, du mergst eines davon kontrolliert.
### F-13 · Keine OIDC-SSO fuer User-Apps
- **Kategorie:** Security / UX
- **Fundstelle:** `security/authelia/configuration.yml`, `docs/SECRETS_MAP.md`
- **Beobachtung:** Authelia kann OIDC, ist aber nur als ForwardAuth konfiguriert. Nextcloud, Immich, Grafana, Mealie laufen mit eigenen User-DBs.
- **Risiko:** N getrennte Passwortspeicher, N getrennte 2FA-Setups, keine zentrale Sperrung bei Account-Kompromittierung. Familie hat keinen einfachen Onboarding-Pfad.
- **Empfehlung:** OIDC-Provider in Authelia aktivieren, Nextcloud (via Plugin), Immich (nativer OIDC-Support), Grafana (nativer OIDC-Support) als Clients konfigurieren. Vaultwarden via OIDC-Bridge nur, wenn der Aufwand klar mehrwertig ist — sonst bewusst auslassen.
- **Prioritaet:** Sollte zeitnah (groesster Hebel laut Executive)
- **Aufwand:** L
- **Validierung:** Familienkonto kann sich mit einem Login bei Nextcloud + Immich + Grafana anmelden.
### F-14 · Kein WAF / Bouncer vor oeffentlichen Apps
- **Kategorie:** Security
- **Fundstelle:** `traefik/docker-compose.yml`, oeffentliche Hosts in `docs/REPO_MAP.md:127-152`
- **Beobachtung:** Sechs oeffentliche Apps mit nativer Auth (vault, paperless, mealie, ntfy, git, immich, cloud) ohne IP-Bouncer. Authelia-Regulation greift nur fuer die ForwardAuth-Pfade; Apps mit eigener Auth bekommen den vollen Traffic.
- **Risiko:** Credential-Stuffing-Bot-Wellen treffen die App selbst (Nextcloud, Immich) — Logs sind im Loki, aber kein Sperr-Mechanismus.
- **Empfehlung:** CrowdSec als Bouncer fuer Traefik (`crowdsecurity/traefik-bouncer`). Nutzt Loki/Logs fuer Erkennung, sperrt IPs auf Traefik-Ebene, bevor sie die Apps treffen.
- **Prioritaet:** Sollte zeitnah
- **Aufwand:** M
- **Validierung:** CrowdSec-Dashboard zeigt erste Sperren; Test-Brute-Force gegen `nextcloud.kaleschke.info` wird bei N Versuchen geblockt.
### F-15 · Healthchecks fehlen grossflaechig
- **Kategorie:** Docker / Operations
- **Fundstelle:** Spot-checks: `apps/paperless/docker-compose.yml`, `apps/immich/docker-compose.yml`, `security/authelia/docker-compose.yml`, `traefik/docker-compose.yml` — keiner hat `healthcheck:`-Block.
- **Beobachtung:** Restart-Policy ist ueberall `unless-stopped`, aber ohne Healthcheck kann Docker keinen Crash-Loop bei "Container laeuft, aber App tot" erkennen.
- **Risiko:** Bei Soft-Failure (Postgres-Connection-Pool tot, Authelia haengt im Storage-Connect) merkst du nichts, weil Container "running" bleibt.
- **Empfehlung:** Fuer Tier-1 (Traefik `wget /ping`, Authelia `/api/health`, PostgreSQL `pg_isready`, Komodo `wget /api/healthcheck`) Healthchecks ergaenzen. Fuer Tier-2 schrittweise.
- **Prioritaet:** Sollte zeitnah
- **Aufwand:** M (pro Stack 515 Min)
- **Validierung:** `docker ps` zeigt `(healthy)` neben den Tier-1-Containern.
### F-16 · `infra/redis` als "shared" deklariert, faktisch nur Paperless
- **Kategorie:** Architektur-Konsistenz
- **Fundstelle:** `infra/redis/docker-compose.yml`, `docs/SERVICE_CATALOG.md:31` ("shared Redis Cache")
- **Beobachtung:** Immich, Nextcloud, Mealie haben jeweils eigene Redis-Instanzen. Authelia nutzt bewusst kein Redis (MASTER 13). Paperless nutzt es laut Compose. Effektiv "Paperless-Redis im Frack des shared-Caches".
- **Risiko:** Niedrig. Aber: Wenn du `infra/redis` fuer etwas anderes wegnimmst, denkst du, es kostet Paperless was — und das waere der Fall.
- **Empfehlung:** Doku-Update: SERVICE_CATALOG 31 praezisieren ("dediziertes Redis fuer Paperless; andere Stacks haben eigene Redis-Instanzen"). Architektur bleibt, nur Etikett ehrlich machen. Alternativ: in `apps/paperless/` als App-internes Netz konsolidieren wie Mealie.
- **Prioritaet:** Nice to have
- **Aufwand:** S (Doku) / M (Architektur)
### F-17 · Plex bleibt als Host-Net-Stack
- **Kategorie:** Security / Architektur
- **Fundstelle:** `host-services/plex/docker-compose.yml`, `MASTER 7.4`
- **Beobachtung:** Plex laeuft als Host-Net wegen Discovery/GDM. Dokumentierte Ausnahme.
- **Risiko:** Plex hat hoehere Angriffsoberflaeche als Apps mit Traefik. Plex-Login wurde mehrfach Ziel von Account-Uebernahmen (Plex.tv-Auth-Issues 2024/25). Bei Plex.tv-Kompromittierung greift Authelia nicht — Plex authentifiziert gegen Plex.tv.
- **Empfehlung:** Plex bewusst beibehalten (Doku stuetzt), aber: (a) "Remote Access" in Plex-UI deaktivieren, wenn nur lokal/Tailscale genutzt. (b) Plex-Server nicht in `frontend_net` (waere schaedlich) — bleibt Host-Net, korrekt.
- **Prioritaet:** Nice to have
- **Aufwand:** S
- **Validierung:** Plex `Remote Access` UI zeigt "disabled".
### F-18 · Nextcloud ohne ForwardAuth, ohne dedizierte Brute-Force-Doku
- **Kategorie:** Security
- **Fundstelle:** `apps/nextcloud/docker-compose.yml`, `MASTER 13: Nextcloud-Entscheidung`
- **Beobachtung:** Bewusste Ausnahme (WebDAV/CardDAV). In Nextcloud selbst sind Brute-Force-Schutz, 2FA-Pflicht und App-Passwords konfigurierbar, aber nicht im Repo dokumentiert.
- **Risiko:** Familien-Konto mit schwachem Passwort + Nextcloud oeffentlich = direkter Pfad zu Dokumenten/Fotos.
- **Empfehlung:** `apps/nextcloud/POST_INSTALL.md` mit Pflicht-Checkliste: Brute-Force-Plugin aktiv, 2FA-Provider TOTP installiert, Admin-Account hat 2FA, "Enforce 2FA for admin group" gesetzt. Optional: `OCC`-Befehle als Skript in `services/nextcloud-policy/`.
- **Prioritaet:** Sollte zeitnah
- **Aufwand:** S
- **Validierung:** Test-Login ohne 2FA als Admin schlaegt fehl.
### F-19 · Keine Container-Memory-Limits
- **Kategorie:** Docker / Hardware-Schutz
- **Fundstelle:** Spot-checks aller Composes
- **Beobachtung:** Keine `mem_limit:` oder `deploy.resources.limits` Sektion in Tier-1- oder Tier-2-Stacks.
- **Risiko:** Bei Image-Bug oder Memory-Leak (z. B. Immich-ML, Paperless-OCR-Loop) kann ein Container den Host in OOM treiben. Posture-Check + Docker-Critical-Events sehen das nachher, aber praeventiver waere Container-Limit + Docker-OOM-Kill fuer den richtigen Prozess.
- **Empfehlung:** Fuer Tier-1 (Postgres, Authelia, Traefik, Komodo) sanfte Limits setzen (z. B. Postgres 2 GB, Authelia 256 MB, Traefik 256 MB). Fuer Immich-ML-Container ein hartes Limit, das Verhungern verhindert.
- **Prioritaet:** Nice to have
- **Aufwand:** M
- **Validierung:** `docker stats` zeigt `MEM USAGE / LIMIT` ungleich `unlimited`.
### F-20 · Paperless-DBPass weiter als Stack-ENV (dokumentierte Ausnahme)
- **Kategorie:** Secrets
- **Fundstelle:** `MASTER 13: Secrets in Komodo Stacks`, `docs/SECRETS_MAP.md:25`
- **Beobachtung:** Paperless unterstuetzt `_FILE` nicht fuer DB-Pass. Bewusste Ausnahme.
- **Risiko:** Stack-ENV liegt in Komodo-DB (Mongo), nicht im Repo. Bei Komodo-Mongo-Backup-Luecke fehlt das Passwort beim Restore.
- **Empfehlung:** Erweiterung der Disaster-Recovery-Doku: explizite Liste aller "Stack-ENV-only"-Secrets mit Zeiger, dass `komodo-mongo.archive.gz` fuer Restore zwingend ist, oder die ENV manuell vorgehalten werden muss (in Vaultwarden + externer Notiz).
- **Prioritaet:** Nice to have
- **Aufwand:** S
- **Validierung:** DR-Doc Abschnitt "Stack-ENV-Werte" referenziert konkrete Restore-Pfade.
---
# D. Risiko-Matrix
| Risiko | Bereich | Wahrscheinlichkeit | Auswirkung | Prioritaet | Massnahme |
|---|---|---|---|---|---|
| Borg-Passphrase weg -> Restore unmoeglich | Backup | niedrig | katastrophal | P0 | F-02 analoge Sicherung |
| Hetzner-Account-Verlust -> halbes 3-2-1 | Backup | niedrig-mittel | hoch | P0/P1 | F-03 Zweitziel |
| AdGuard-Admin-Manipulation aus LAN | Security | niedrig | hoch (DNS-Hijack) | P0 | F-01 Bind auf Tailscale |
| Operator-Pwd-Leak -> 2FA fehlt fuer Borg-UI/Code-Server | Security | mittel | hoch | P0 | F-04 2FA-ACL erweitern |
| Komodo-Self-Bootstrap-Failure nach Totalausfall | DR | niedrig | hoch | P1 | F-09 Bootstrap-Datei in `services/` |
| Authelia Repo↔Host Drift unbemerkt | GitOps/Security | mittel | mittel | P1 | F-10 Diff-Check |
| Immich Silent Corruption -> kein Restore-Test belegt | Backup | niedrig | sehr hoch (Familien-Fotos) | P1 | F-11 Restore-Test |
| Cert-Expiry unbemerkt -> Public Apps down | Operations | niedrig | mittel | P1 | F-08 Alert-Regel |
| Nextcloud Brute-Force ohne Bouncer | Security | mittel | mittel-hoch | P1 | F-14 CrowdSec / F-18 Nextcloud-Haerten |
| Image-Update-Stillstand -> CVE bleibt | Security | mittel | mittel | P1 | F-12 Renovate |
| Hermes-Wartungsschuld | Wartbarkeit | hoch | niedrig | P1 | F-06 Entscheidung |
| Repo-Altstaende ueberleben -> Doppel-Deploy | GitOps | mittel | niedrig | P1 | F-05 Cleanup |
| OOM durch unlimitierte Container | Hardware | niedrig | mittel | P2 | F-19 mem_limit |
| Healthcheck-Luecke -> Soft-Failure stumm | Operations | mittel | niedrig | P2 | F-15 Healthchecks |
| Monitoring-Stack ohne Digest-Pin | Reproduzierbarkeit | niedrig | niedrig | P2 | F-07 Digests + Renovate |
| Hardware-SPOF (kein zweiter Host) | Hardware | niedrig | sehr hoch | P3 | Cold-Standby / 2. Host |
---
# E. Zielarchitektur (realistisch fuer privates Homelab)
**Hardware**
- 1× Unraid-Host (bestehend) als Production. CPU mit AVX2/AVX-512 fuer Immich-ML. ≥ 32 GB RAM (fuer 2× Postgres + Immich-ML + Loki/Prometheus + 2 VMs).
- 2× NVMe als BTRFS-RAID1-Cache, sobald Cache-Auslastung > 70 % (STORAGE_LAYOUT 15.3).
- Parity-Disk ≥ groesste Daten-Disk.
- USV mit USB-Steuerung (NUT-faehig: APC Back-UPS RS 700+, Eaton 3S, CyberPower CP1500EPFCLCD). Direkter Shutdown bei Power-Loss.
- Optional: zweiter alter Mini-PC oder NUC als Cold-Standby mit Tailscale, der den letzten Komodo-Bootstrap + Gitea-Mirror tragen kann.
**Netzwerk**
- FritzBox (bestehend) als Router + NAT.
- VLANs nur wenn IoT-WLAN dazukommt (FritzBox-Gast-WLAN reicht fuer Anfang).
- DNS: AdGuard -> Unbound (bestehend). Admin-UI nur Tailscale.
- Tailscale (bestehend): Operator-Pfad. Subnet-Router optional fuer LAN-Devices ueber Tailscale.
**Storage**
- Cache `only` fuer `appdata`, `system`, `domains` (bestehend STORAGE_LAYOUT 4).
- Disk1 (XFS) fuer `services`, `documents`, `photos`, `backups`, `media`, `finance`, `projekte`.
- Externe Wechselplatte (XFS) fuer Cold-Off-Site mit fester monatlicher Rotation.
**Ordnerstruktur (Repo)**
- Beibehalten. Nur Cleanup von Altstaenden (F-05). Naming `kebab-case`-Migration aus STORAGE_LAYOUT 6 schrittweise.
**Docker**
- Compose-only via Komodo (bestehend).
- Digest-Pin fuer alle Images (F-07).
- Healthchecks fuer Tier-1 (F-15).
- Mem-Limits fuer Tier-1 + Immich-ML (F-19).
- App-interne Netze fuer Stack-Isolation (bestehend).
**Reverse Proxy**
- Traefik v3 (bestehend), DNS-Challenge, Wildcard.
- Dynamic Config nur fuer Middlewares, TLS, Dashboard (bestehend).
- CrowdSec-Bouncer (F-14) fuer oeffentliche Apps.
**Auth**
- Authelia als ForwardAuth **und** OIDC-Provider (F-13).
- Nextcloud, Immich, Grafana via OIDC.
- 2FA-Pflicht fuer alle Operator-Dienste (F-04).
- Komodo bewusste Ausnahme (bestehend).
**Backup**
- Borg lokal (`/mnt/user/backups/borg/`) + Borg Hetzner + Wechselplatte.
- Pre-Dump-Hooks (bestehend).
- Borg-Passphrase off-system analog (F-02).
- Restore-Tests automatisiert (F-11 Immich, dann andere via CI).
**Monitoring**
- `monitoring/`-Stack als alleinige Quelle. Altstaende raus (F-05).
- Family-View-Dashboard in Grafana (alles gruen, Backup-Frische, Cert-Tage).
- Alerts ausgebaut (F-08).
- Posture-Check + Docker-Critical-Events -> ntfy `homelab-alerts` (bestehend).
**Dokumentation**
- Aktuelle Doku-Tiefe halten.
- `SERVICES_RECOVERY.md` und `STORAGE_LAYOUT.md` (Active) finalisieren.
- Familien-/User-Onboarding-Doku als eigenes kleines Dokument.
**GitOps**
- Gitea + Komodo (bestehend).
- GitHub-Push-Mirror (umgesetzt, bestaetigt durch MASTER 7.1).
- Renovate-Bot gegen Gitea (F-12).
- Optional: Staging-Branch + zweites Komodo-Ziel in Tailscale-VM (Phase 3).
**Restore**
- RESTORE_MATRIX bleibt fuehrend.
- Restore-Lab unter `/mnt/user/backups/restore-lab/` (bestehend).
- Immich-Restore als Luecke schliessen (F-11).
- Komodo-Self-Bootstrap raus aus Komodo (F-09).
---
# F. Priorisierte Massnahmenliste
| # | Aufgabe | Warum | Kategorie | Prio | Aufwand | Risiko (bei Nicht-Tun) | Mehrwert | Abhaengigkeiten | Validierung |
|---|---|---|---|---|---|---|---|---|---|
| 1 | Borg-Passphrase analog sichern | DR-SPOF schliessen | Backup | P0 | S | katastrophal | DR-Sicherheit | — | Wert ohne Host abrufbar |
| 2 | AdGuard-Admin auf Tailscale-IP binden | LAN-Angriffsflaeche | Security | P0 | S | hoch | LAN-IoT-Haertung | — | `ss -ltnp` zeigt nur Tailscale |
| 3 | 2FA-ACL erweitern (borg, code, files, traefik) | Operator-Pwd-Leak | Security | P0 | S | hoch | 2FA-Coverage | Authelia-TOTP-Setup | Login erzwingt 2FA |
| 4 | Altstaende `ops/grafana-influxdb`+`ops/loki` `git rm` | Repo-Hygiene, kein Re-Deploy | GitOps | P0 | S | niedrig | Klarheit | Tag setzen | Policy-Check clean |
| 5 | Hermes 60-Tage-Deadline | Wartungsschuld | App | P1 | S/L | mittel | Komplexitaet raus | Operator-Entscheidung | Entweder produktiv oder weg |
| 6 | Immich-Restore-Test einrichten | Groesster Datentopf ungeprueft | Backup | P1 | M | hoch | Restore-Vertrauen | Restore-Lab-Pfad | Smoke-Test-Report |
| 7 | Renovate-Bot in Gitea | manuelle Digest-Pflege | Wartung | P1 | M | mittel | Update-Hygiene | Gitea-Runner | erste PR offen |
| 8 | Alert-Regeln (Borg-Frische, Cert-Expiry) | Blind-Spot Operations | Monitoring | P1 | M | mittel | echte Alerts | Pushgateway o. textfile | Alert in Test |
| 9 | Family-View-Dashboard Grafana | Morgens 30 s Check | Monitoring | P1 | M | niedrig | Uebersicht | Datasources stehen | Dashboard funktioniert |
| 10 | Komodo-Self-Bootstrap als `services/komodo-bootstrap/` | Henne-Ei-Problem | GitOps/DR | P1 | M | mittel | sauberer Recovery-Pfad | Komodo-Stack-Doku | Bootstrap aus Repo allein moeglich |
| 11 | Authelia-Drift-Diff-Check in posture-check | Repo↔Host Drift | GitOps | P1 | S | mittel | Drift-Detektion | posture-check-Erweiterung | neuer Check sichtbar |
| 12 | Healthchecks Tier-1 | Soft-Failure-Erkennung | Docker | P1 | M | niedrig | Self-Healing-Trigger | — | `docker ps` zeigt `healthy` |
| 13 | CrowdSec-Bouncer vor Traefik | oeffentliche Apps schuetzen | Security | P1 | M | mittel | Brute-Force-Stop | Traefik-Middleware | Test-IP wird geblockt |
| 14 | Nextcloud-Haertung dokumentieren | Public App + native Auth | Security | P1 | S | mittel | App-Haertung | Plugin-Install | 2FA-erzwingt |
| 15 | Authelia OIDC-Provider + Nextcloud/Immich/Grafana | SSO, Familien-Onboarding | Security/UX | P2 | L | niedrig | hoher Mehrwert | Authelia-OIDC-Setup | SSO-Login funktioniert |
| 16 | Immich-Smartphone-Auto-Backup fuer Familie | Killer-App fuer Familie | App | P2 | S | niedrig | hoher Mehrwert | — | Familien-Foto in Immich |
| 17 | Monitoring-Stack Digests + Renovate-Pin | Reproduzierbarkeit | GitOps | P2 | S | niedrig | konsistent | Renovate optional | `@sha256` an allen Images |
| 18 | Mem-Limits Tier-1 + Immich-ML | OOM-Schutz | Hardware/Docker | P2 | M | niedrig | Schutz | — | `docker stats` zeigt Limits |
| 19 | Off-Site-Zweitziel (rsync.net o. Wechselplatte) | Single-Provider | Backup | P2 | M | mittel | 3-2-1 echt | Borg-Config | beide Repos < 7d |
| 20 | Staging-Branch + 2. Komodo-Ziel | Risiko-Aenderung testbar | GitOps | P3 | L | niedrig | Reife | 2. VM/Host | Deploy auf staging klappt |
---
# G. Refactoring-Plan (Sprints)
## Sprint 0 — Sicherheitsnetz und Ist-Zustand sichern (1 Tag)
- **Ziel:** Du kannst danach im schlimmsten Fall alles, was du jetzt aenderst, sicher zurueckrollen.
- **Aufgaben:**
- Git-Tag `audit-2026-05-25-baseline` auf `master` setzen und nach Gitea + GitHub-Mirror pushen.
- Borg-Lauf manuell ausloesen ("freshen up"), Erfolg im Log dokumentieren.
- Aktuellen Komodo-Mongo-Dump verifizieren (`mongorestore --dry-run`).
- `docs/MIGRATION_LOG.md` Eintrag "Audit-Sprint-Start".
- **Erfolgskriterium:** Tag pushed, Borg-Lauf gruen, Mongo-Dump verifiziert.
- **Validierung:** `git fetch && git tag | grep audit-2026-05-25-baseline` und `ls /mnt/user/backups/borg/dumps/latest/` zeigt fresh.
- **Rollback:** N/A (rein additiv).
- **Risiko bei Nichtumsetzung:** Keine Notbremse fuer Sprint 1.
## Sprint 1 — Offensichtliche Risiken entschaerfen (1 Woche)
- **Ziel:** P0-Risiken weg, Repo-Hygiene wieder gruen.
- **Aufgaben (in dieser Reihenfolge):**
1. F-02 Borg-Passphrase analog sichern (off-system, kein Code-Change).
2. F-01 AdGuard-Admin-Port auf Tailscale-IP — Edit `host-services/Adguard/docker-compose.yml:16`.
3. F-04 Authelia ACL erweitern (`two_factor` fuer borg, code, files, traefik) — Edit `security/authelia/configuration.yml` + Host-Sync.
4. F-05 Altstaende `ops/grafana-influxdb/`, `ops/loki/` entfernen — `git rm`, MIGRATION_LOG.
5. Policy-Check-Warnings `SEC001` (ddns-updater, scrutiny) aufraeumen.
- **Erfolgskriterium:** Policy-Check 0 Warnings fuer SEC001, AdGuard-Admin nur via Tailscale, 2FA-Login auf borg.kaleschke.info.
- **Validierung:** Policy-Check-Report; Browser-Test mit/ohne 2FA-Cookie.
- **Rollback:** Commit-Revert pro Block.
## Sprint 2 — GitOps-Robustheit (12 Wochen)
- **Ziel:** Self-Bootstrap-Problem entschaerft, Drift-Detektion automatisiert.
- **Aufgaben:**
1. F-09 Komodo-Bootstrap-Compose nach `services/komodo-bootstrap/` extrahieren + dokumentierter Standalone-Restore-Pfad.
2. F-10 Authelia-Drift-Diff in posture-check ergaenzen.
3. F-11 Immich-Restore-Test einrichten (analog zu vaultwarden/gitea/paperless).
4. F-06 Hermes-Entscheidung mit 60-Tage-Deadline schriftlich.
- **Erfolgskriterium:** Komodo laesst sich aus Repo allein wiederherstellen. Posture-Check zeigt `authelia_config_drift: false`. Immich-Restore-Report unter `/mnt/user/backups/restore-reports/`.
- **Validierung:** Trockenversuch (Komodo-Container stoppen, `docker compose up -d` aus `services/komodo-bootstrap/`).
- **Rollback:** Bootstrap-Verzeichnis loeschen, Komodo-Self-Stack wie vorher.
## Sprint 3 — Backup & Restore belastbar machen (23 Wochen)
- **Ziel:** 3-2-1 echt, Restore-Tests breiter, Stack-ENV im DR-Pfad.
- **Aufgaben:**
1. F-03 Zweitziel: Wechselplatten-Rotation dokumentieren ODER zweites Borg-Repo (rsync.net / BorgBase EU2).
2. F-20 Stack-ENV-Liste in DR-Doc explizit machen (Restore-Reihenfolge).
3. Borg-Verifikation Cron fuer `borg check --repository-only` weekly (STORAGE_LAYOUT 8.4).
4. Quartalsweise End-to-End-Restore-Drill in Schedule aufnehmen.
- **Erfolgskriterium:** Beide Off-Site-Ziele < 7 Tage alt; DR-Doc enthaelt "ENV-Restore-Reihenfolge".
- **Validierung:** `borg list` gegen beide Repos.
## Sprint 4 — Monitoring & Alerting ausbauen (2 Wochen)
- **Ziel:** Sichtbarkeit auf das, was wirklich weh tut.
- **Aufgaben:**
1. F-08 Alert-Regeln: `BorgArchiveStale`, `TLSCertExpiryNear`, `ContainerDown`, `PostgresConnSaturation`.
2. F-15 Healthchecks fuer Traefik, Authelia, Postgres, Komodo, Gitea.
3. F-07 Digest-Pin in `monitoring/docker-compose.yml`.
4. Family-View-Dashboard in Grafana (1 Panel: Service-Up, 1 Panel: Backup-Frische, 1 Panel: Cert-Tage, 1 Panel: Disk-Fuellung).
- **Erfolgskriterium:** Family-View zeigt alles gruen; Cert-Alert feuert in Test (Datum vorgespult).
- **Validierung:** Dashboard sichtbar unter `monitoring.kaleschke.info/d/family-view`.
## Sprint 5 — Auth-Konsolidierung & Frontdoor-Haertung (34 Wochen)
- **Ziel:** SSO fuer die Familie, Brute-Force-Bouncer vor oeffentlichen Apps.
- **Aufgaben:**
1. F-13 Authelia OIDC-Provider aktivieren.
2. Nextcloud OIDC-Plugin + Test-Login.
3. Immich OIDC + Test-Login.
4. Grafana OIDC + Test-Login.
5. F-14 CrowdSec-Bouncer vor Traefik.
6. F-18 Nextcloud-Haertung-Dokument + 2FA-Pflicht.
- **Erfolgskriterium:** Familien-Konto loggt sich mit einem Login bei drei Apps ein; CrowdSec sperrt Test-IP nach N fehlerhaften Versuchen.
- **Validierung:** OIDC-Sequenz im Browser ohne Eingabe-Wiederholung; CrowdSec-Dashboard zeigt Sperre.
## Sprint 6 — Automatisierung und Nerd-Level (laufend)
- **Ziel:** Image-Update-Pipeline, optional Staging.
- **Aufgaben:**
1. F-12 Renovate-Bot gegen Gitea.
2. F-19 Mem-Limits Tier-1.
3. Restore-Test-CI via Gitea Actions (P3).
4. Optional: Staging-Branch + zweites Komodo-Ziel in Tailscale-VM (P3).
5. Optional: Firefly III / Actual Budget fuer `/mnt/user/finance`.
- **Erfolgskriterium:** Renovate-PRs erscheinen woechentlich; mindestens ein automatisches Patch-Update gemerged.
---
# H. Fehlende Informationen
> Nur, was den Audit konkret schaerfer machen wuerde.
| Frage | Warum | Bereich | Kommando / Datei |
|---|---|---|---|
| CPU-Modell, RAM-Groesse, Mainboard | Hardware-Bewertung, OOM-Risiko, Immich-ML-Eignung, AVX-Verfuegbarkeit | Hardware | `cat /proc/cpuinfo \| grep -E 'model name\|flags'`, `free -h`, `dmidecode -t baseboard \| head -20` |
| USV vorhanden? Modell? | DR-Beurteilung Power-Loss, Shutdown-Pfad | Hardware/DR | physische Sichtpruefung; `apcaccess` falls APC mit NUT |
| Stromverbrauch idle / Last | Betriebskosten, Sizing | Hardware | Smartmeter / Tibber-API |
| NIC-Speed (1 GbE? 2.5 GbE?) | Backup-Durchsatz, Plex-Streaming | Netzwerk | `ip -br link`, `ethtool eth0` |
| Disk-Inventar (Anzahl, Modelle, Alter) | Storage-Health, Replacement-Plan | Storage | `lsblk -o NAME,SIZE,MODEL,SERIAL`, Scrutiny-UI |
| Aktueller Cache-Fuellstand | Wann zweite NVMe? | Storage | `df -h /mnt/cache` |
| FritzBox-Modell + Firmware | Net-Sicherheit, VLAN-Faehigkeit | Netzwerk | FritzBox-UI / `fritzconnection` |
| Tatsaechlich genutzte Plex- vs. ungenutzte App | Konsolidierungs-Belege | App-Landschaft | Plex-Server-Logs, ggf. Glances-Container-CPU pro Stack |
| Existiert `/mnt/user/finance/`-Share schon? | Ist Firefly-Vorbereitung trivial? | Storage | `ls /mnt/user/finance/` |
| Authelia Live-User-DB-Tiefe (Anzahl User, 2FA-Status) | 2FA-Coverage-Bewertung | Security | `cat /mnt/user/appdata/authelia/config/users_database.yml` (nur Strukturansicht, keine Hashes hier zitieren) |
| Komodo-Mongo-Dump letzter Integrity-Check | F-09-Vorbereitung | Backup | `mongorestore --dry-run --archive=komodo-mongo.archive.gz --gzip` |
| Aktuelle Cert-Restlaufzeit | F-08-Test-Vorbereitung | Operations | `openssl s_client -connect git.kaleschke.info:443 -servername git.kaleschke.info < /dev/null \| openssl x509 -noout -dates` |
---
# I. Pruefkommandos (Linux / Unraid / Docker / Windows)
> Strukturiert nach Bereich. Sicher zum Ausfuehren am Host.
### Hardware
```bash
# CPU + Flags (AVX fuer Immich-ML)
cat /proc/cpuinfo | awk '/model name|flags/ {print; if(/flags/) exit}'
# RAM
free -h
dmidecode -t memory | grep -E "Size|Speed" | head -20
# Mainboard
dmidecode -t baseboard | head -20
# PCI / SATA / NVMe
lspci
nvme list
lsblk -o NAME,SIZE,MODEL,SERIAL,FSTYPE,MOUNTPOINT,VENDOR
# SMART
smartctl -a /dev/nvme0n1 | head -40
smartctl -a /dev/sdb | head -40
# Stromverbrauch (Unraid Plugin oder ipmitool falls IPMI)
sensors | head -30
```
### Filesystem / Storage / Mounts
```bash
# Filesystem-Typen (Hard Rule 12.1)
findmnt -no FSTYPE /mnt/cache /mnt/disk1 /boot
mount | grep -E "ntfs3|fuseblk" # darf leer sein
# Share-Settings
ls -la /boot/config/shares/
# Cache-Fuellstand
df -h /mnt/cache /mnt/disk1 /mnt/user
du -sh /mnt/user/appdata/* | sort -hr | head -20
```
### Docker
```bash
# Container-Inventur
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" | sort
docker ps -a --filter "status=exited" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
# Netzwerke
docker network ls
docker network inspect frontend_net | jq '.[0].Containers | keys'
docker network inspect backend_net | jq '.[0].Internal'
# Volumes ohne Container (Waisen)
docker volume ls -qf dangling=true
# Effektive Ports
ss -ltnp | sort -k4
# Healthchecks
docker ps --format "{{.Names}}\t{{.Status}}" | grep -E "healthy|unhealthy|starting"
```
### Security
```bash
# Privileged-Container und Docker-Socket-Mounts
for c in $(docker ps -q); do
docker inspect "$c" --format '{{.Name}}: priv={{.HostConfig.Privileged}}; sock={{range .HostConfig.Binds}}{{println .}}{{end}}'
done | grep -E "priv=true|docker.sock"
# Direkte Host-Ports
docker ps --format "{{.Names}}: {{.Ports}}" | grep -v "^[^:]*: $"
# Secret-Datei-Rechte
ls -la /mnt/user/appdata/secrets/
stat -c "%a %n" /mnt/user/appdata/secrets/*.txt
```
### Backup / Restore
```bash
# Borg-Frische
ls -lat /mnt/user/backups/borg/dumps/latest/ | head
find /mnt/user/backups/borg/dumps/latest -mmin +1440 -type f # aelter 24h
# Borg-Repo (Passphrase per File)
export BORG_PASSPHRASE=$(cat /mnt/user/appdata/secrets/borg_repo_passphrase.txt)
borg list ssh://... --short | tail -5
borg info ssh://... ::Taegliche-Sicherung-2026-05-25T05:52:44.157
# Posture-Check
cat /mnt/user/services/posture-check/last.json | jq '.warning_count, .critical_count'
```
### Netzwerk / DNS
```bash
# Tailscale
tailscale status
# DNS auf AdGuard testen
dig @127.0.0.1 git.kaleschke.info
dig @127.0.0.1 example.com # Unbound-Recursion
# Cert-Restlaufzeit
for h in vault git immich cloud paperless mealie ntfy; do
echo -n "$h.kaleschke.info: "
openssl s_client -connect ${h}.kaleschke.info:443 -servername ${h}.kaleschke.info </dev/null 2>/dev/null \
| openssl x509 -noout -dates 2>/dev/null
done
```
### GitOps-Konsistenz
```bash
# Komodo Stack-Workspace vs. Repo
cd /mnt/user/services/stacks/<stackname>
git rev-parse HEAD
git status --short
# Webhook-Liveness
docker logs komodo-core 2>&1 | grep -i "webhook\|deploy" | tail -20
```
### Windows (lokal)
```powershell
# Repo-Status
git status --short
git fetch origin
git log origin/master..HEAD --oneline # ungepushte Commits
git log HEAD..origin/master --oneline # ungepullte Commits
# Policy-Check
.\ops\policy-checks\check_repo.ps1
# Restore-Freshness
.\ops\restore-tests\check-restore-freshness.ps1
```
---
# J. Community- / Best-Practice-Abgleich
> Nur fuer die Architekturentscheidungen, bei denen der Markt eindeutig ist oder Gegenpositionen relevant sind.
| Entscheidung | Markt-Best-Practice | Stuetzende Quellen | Bewertung |
|---|---|---|---|
| Traefik mit Docker-Labels statt File-Provider | Standard in Selfhosted (siehe `awesome-selfhosted-docker`, Smarthome-Beginner Templates) | Traefik-Doc v3 docs.traefik.io/providers/docker | passt zu MASTER 13 (Wechsel 2026-03-28) |
| DNS-Challenge mit Cloudflare statt HTTP-01 | Standard fuer Wildcard und reduzierte Angriffsflaeche | acme.sh / lego docs | passt, korrekt |
| Authelia ForwardAuth statt Authentik | Authelia ist leichtgewichtiger, Authentik maechtiger; beide valide | r/selfhosted-Konsens 2024-25 | Authelia richtig fuer Single-Family-Setup |
| Authelia ohne Redis-Session-Backend | Markt-Standard ist mit Redis; deine Vereinfachung ist begruendet (MASTER 13 2026-05-04) | Authelia-Doc | Trade-off klar; Bewertung: vertretbar fuer Homelab |
| Komodo statt Portainer/Dockge | Komodo ist neuer (2024), Dockge etabliert, Portainer kommerziell | Selfh.st 2025 Comparison | Komodo legitim, mehr GitOps-nativ als Dockge |
| Borg statt Restic/Kopia | Borg ist klassische Wahl fuer Linux-Backup mit Deduplikation; Kopia/Restic gewinnen mit Multi-Backend | r/datahoarder, ServeTheHome 2024 | Borg passt zu Unraid-Stack; bewusste Vereinfachung |
| Glance statt Homepage als Single-Dashboard | beide auf Augenhoehe; Homepage etablierter, Glance moderner, schneller, mit Live-Widgets | github.com/glanceapp/glance vs. github.com/gethomepage/homepage | Glance legitim; Bewertung: deine Wahl ist verteidigbar |
| Immich nicht hinter Authelia ForwardAuth | offizielle Immich-Doku raet bei nativer App-Auth davon ab, weil Sync-Clients OIDC oder direkte Auth brauchen | immich.app/docs/administration/reverse-proxy | korrekt; OIDC spaeter (F-13) ist der Weg |
| Nextcloud klassisch statt AIO | NC-AIO ist offizielle Empfehlung fuer Neuaufbau, klassisch hat mehr Flexibilitaet fuer GitOps | NC-Blog 2024 | bewusste Ausnahme MASTER 13; vertretbar, da GitOps-Anbindung wichtiger |
| Single-Host + Borg statt Proxmox-Cluster + ZFS-Send | fuer Familien-Homelab ist Cluster Overkill | r/homelab, LTT-Forum | Single-Host korrekt; Cold-Standby (Sprint 6) ist die richtige naechste Stufe |
| AdGuard + Unbound statt Pi-hole | aequivalent; AdGuard hat moderne UI, Unbound recursion | gowri/networkchuck Tutorials 2024 | passt |
| Posture-Check als Skript statt Goss/InSpec | fuer Single-Host pragmatisch | Goss ist maechtiger, aber Overkill | Skript-Loesung legitim |
| Renovate gegen Gitea | mehrere Erfahrungsberichte in Gitea-Issues + Renovate-Docs | docs.renovatebot.com/modules/platform/gitea/ | Standard-Pfad |
| CrowdSec vor Traefik | starker Trend 2024-25 in Selfhosted-Community | crowdsec.net/blog, Marius-Hosting-Tutorials | sinnvolle Haertung |
**Gegenpositionen, die du kennen solltest:**
- **"Authelia OIDC ist kompliziert, lieber Authentik."** Korrekt, wenn du auch B2B-SAML brauchst. Fuer reine Familien-OIDC ist Authelia leichter wartbar.
- **"CrowdSec laedt zentrale Reputation-Listen -> Privacy-Bedenken."** Stimmt, du kannst Local-Only-Mode fahren. Fuer Homelab egal.
- **"Renovate-Bot erzeugt Laerm."** Mit Group/Schedule-Rules zaehmbar. Wert > Laerm.
- **"Komodo ist zu jung."** Gegenargument: du benutzt es seit Q1/2026 produktiv, Major-Inzidenz war beherrschbar. Der Wechsel zurueck zu Portainer/Dockge waere hoehere Kosten als der Reifegrad-Nachteil.
---
# K. Endziel — "Nerd-Level Homelab"
So sieht dein Homelab aus, wenn es wirklich auf Senior-Level ist:
**Betrieb im Alltag**
- Morgens 30 Sekunden auf `monitoring.kaleschke.info`: Family-View zeigt 7 Tier-1-Services gruen, Backup-Job in der Nacht hat 100 % Files erfasst, alle Certs > 30 Tage, Disk < 80 %.
- Push-Benachrichtigung auf dem Handy nur, wenn wirklich etwas brennt (Posture-Check critical, Borg > 30 h, Endpoint down ≥ 8 Min, Cert < 14 Tage).
- Familienmitglieder loggen sich mit einem Login bei Nextcloud, Immich, Mealie ein. 2FA per TOTP-App, kein App-by-App-Passwortzettel mehr.
**Updates**
- Renovate oeffnet woechentlich 510 Pull-Requests in Gitea fuer Patch-Versionen. Du siehst sie im Web-UI, pruefst Release Notes, klickst Merge. Komodo deployt automatisch via Webhook. Smoke-Test in der naechsten Glance-Seite.
- Major-Updates kommen separat mit Label `major`, behandelst du in einem geplanten Slot mit Restore-Snapshot davor.
**Backups**
- Borg lokal alle 6 h, Borg Hetzner taeglich, Wechselplatte monatlich. Borg-Passphrase auf Papier im Bankschliessfach. Alle drei Ziele juenger als 36 h Alert-Schwelle.
- Pre-Dump-Hooks erzeugen 15 konsistente Dump-Artefakte pro Lauf, automatische Posture-Check-Vor-Hook bricht Backup ab, wenn FS oder Mount sich veraendert haben.
**Restore**
- Monatlicher Mini-Restore-Test fuer Vaultwarden/Gitea/Paperless/Immich nach `/mnt/user/backups/restore-lab/<dienst>/` laeuft automatisiert per Gitea Actions, Erfolgs-Report landet als Datei + ntfy-Info.
- Quartalsweise End-to-End-Drill: ein kompletter Stack restauriert, App startet, Smoke-Test passt, Doku validiert. Komodo-Bootstrap-Pfad ist getestet.
- Im Ernstfall folgst du `docs/DISASTER_RECOVERY.md` Phase 05 und bist nach < 8 h wieder im Vollbetrieb. Repo-Bootstrap aus GitHub-Mirror, Stacks in Stufen 15, Verifikation pro Stufe.
**Monitoring**
- Prometheus + Loki + Grafana + Alertmanager-ntfy-Bridge. ~15 Alert-Regeln, alle in `alerts.yml` versioniert.
- Family-View, Host-Overview, Containers+Logs, Traefik-Standalone, Backup-Frische, Cert-Tage. Sechs Dashboards mehr braucht keine Familie.
- Loki sammelt 30 Tage Logs aus allen Containern via Promtail. cAdvisor + node-exporter liefern Container- und Host-Metriken. Blackbox testet oeffentliche Endpoints alle 60 s.
**Security**
- Authelia OIDC fuer Nextcloud, Immich, Grafana, Mealie. ForwardAuth fuer Operator-UIs mit 2FA-Pflicht ab Tier-2.
- CrowdSec sperrt Brute-Force-IPs auf Traefik-Ebene bevor sie die Apps treffen.
- AdGuard-Admin nur via Tailscale. Operator-Pfad ausschliesslich Tailscale.
- Authelia-Repo-Baseline und Host-Config sind per Diff-Check im posture-check abgesichert.
- Secrets-Mounts mode 600, Borg-Passphrase analog.
**Dokumentation**
- SERVICE_CATALOG, RESTORE_MATRIX, DISASTER_RECOVERY, STORAGE_LAYOUT, SECRETS_MAP, WORKFLOW, GITOPS_DRIFT_RUNBOOK, ALERTING_MAP, HOMELAB_ARCHITECTURE_MASTER bleiben aktuelle Single-Source-of-Truth.
- `services/komodo-bootstrap/` loest das Henne-Ei. `SERVICES_RECOVERY.md` ist final, nicht Draft.
- Familien-Onboarding-Doku als Markdown: "So nutzt du Nextcloud-Web", "So aktivierst du Immich-Foto-Backup auf dem Handy", "So loggst du dich neu per 2FA ein".
**Taegliche Nutzung**
- Familie scannt Briefe per ASN-Barcode in `scans_inbox/`, Paperless tagged via paperless-gpt automatisch, alles durchsuchbar.
- Immich erfasst Smartphone-Fotos aller Familienmitglieder automatisch, Familie blaettert per Web/App, Tagging per ML.
- Nextcloud traegt Kalender, Kontakte, geteilte Familienordner per WebDAV/CardDAV — kein Google/Apple-Lock-In.
- Mealie speichert Rezepte, Einkaufsliste auf dem Handy.
- Vaultwarden ist der einzige Passwort-Tresor der Familie; Familien-Organisation aktiv.
- Plex streamt Heim-Medien an alle Endgeraete.
- ntfy schickt dir Vorfaelle aufs Handy — und sonst nichts.
- Optional: Firefly III fuer die Familien-Finanzen, Ecowitt-Wetter-Dashboard, Home-Assistant-Automationen fuer Strom-Eigenverbrauch.
**Was bewusst weggelassen ist**
- Kein Kubernetes. Komodo + Compose reicht.
- Kein zweiter Medienserver neben Plex (Jellyfin-Entscheidung 2026-05-25).
- Kein zweites Dashboard neben Glance (Homepage-Entscheidung 2026-05-25).
- Kein Uptime-Kuma neben Blackbox (Entscheidung 2026-05-25).
- Kein Hermes-Agent, wenn er bis 2026-07-25 keinen klaren Alltagsnutzen liefert.
- Kein BentoPDF/paperless-gpt 24/7, wenn nicht aktiv genutzt.
- Kein Self-Stack-Komodo (durch `services/komodo-bootstrap/` ersetzt).
---
## Schlussbemerkung
Das Setup ist naeher an Senior-Reife als an Bastel-Niveau. Der groesste Hebel der naechsten drei Monate ist **Konsolidieren statt erweitern** (Hermes-Entscheidung, Altstaende raus, Auth-SSO, Off-Site-Diversitaet), kombiniert mit der einen Aktivierung, die das Setup vom Operator-Tool zum **Familien-Tool** macht: Immich-Smartphone-Backup fuer alle.
Das vorhandene 2026-05-23-Audit hat die richtigen Sprintziele bereits identifiziert. Diese externe Audit-Sicht ergaenzt:
- **2FA-Pflicht auf Tier-1-Operator-UIs** (F-04) — fehlt in der Bewertung 2026-05-23 in dieser Klarheit.
- **Healthcheck-Luecke** (F-15) und **fehlende Mem-Limits** (F-19) — operative Detail-Findings, die in der strategischen Bewertung nicht auftauchen.
- **Komodo-Self-Bootstrap als konkreter Code-Vorschlag** (F-09) statt nur als Risiko-Erwaehnung.
- **Authelia-Drift-Detection automatisieren** (F-10) statt nur "manuell merge".
- **Monitoring-Stack ohne Digest-Pin** (F-07) — Inkonsistenz mit der eigenen Image-Pinning-Disziplin.
- **`infra/redis` ist faktisch nicht shared** (F-16) — Etikett-Realitaet-Drift.
- **Alert-Regeln deutlich zu duenn** (F-08) — Sichtbarkeitsluecken bei Cert/Borg/Container-Down.
Wenn Sprint 13 in 46 Wochen sitzen, bist du auf einer 1-Note. Wenn dann Sprint 45 in weiteren 68 Wochen kommen, hat die Familie ein echtes Self-Hosting-System, kein "Container-Sammlung im Keller". Das ist der Unterschied, den der Audit-Auftrag adressiert.
-80
View File
@@ -1,80 +0,0 @@
# Audit Report - KalliLab CORE
Datum: 2026-05-16
Gepruefte Compose-Dateien: 30
## Kritische Befunde
Keine kritischen Befunde im Repo-Sollzustand gefunden.
- Keine Datenbank und kein Cache haengt in `frontend_net`.
- Keine produktive Compose-Datei ohne explizites `networks:`-Feld gefunden.
- Keine produktive `.env`- oder `stack.env`-Datei ohne `.example`-Suffix im Repository gefunden.
- Keine Klartext-Passwoerter, Tokens oder API-Keys in Compose-`environment:`-Bloecken gefunden.
## Mittlere Befunde
- Docker-Socket ausserhalb der im Audit-Prompt genannten Allowlist: `traefik` mountet `/var/run/docker.sock:/var/run/docker.sock:ro` in `traefik/docker-compose.yml:34`. Der Socket ist fuer den Docker-Provider fachlich nachvollziehbar, aber in `HOMELAB_ARCHITECTURE_MASTER_V2.md` Abschnitt 10 nicht explizit als Docker-Socket-Ausnahme aufgefuehrt.
- Docker-Socket ausserhalb der im Audit-Prompt genannten Allowlist: `alloy` mountet `/var/run/docker.sock:/var/run/docker.sock:ro` in `ops/loki/docker-compose.yml:26`. [AUSNAHME - dokumentiert] in `HOMELAB_ARCHITECTURE_MASTER_V2.md` Abschnitt 10 als `alloy` Docker-Socket read-only, aber nicht in der Prompt-Allowlist genannt.
- `komodo-periphery` haengt ohne Web-UI im `frontend_net` in `ops/komodo/docker-compose.yml:92-94`. Die Compose-Datei dokumentiert an `ops/komodo/docker-compose.yml:77-79` den Grund als Git-Zugriff auf internes Gitea und Docker-Agent-Sonderfall. [AUSNAHME - lokal in Compose dokumentiert]
- `hermes_net` ist ein app-internes Netz ohne `internal: true`: `ops/hermes-agent/docker-compose.yml:91-93`. Das ist nicht Teil der explizit geforderten `internal: true`-Liste, aber strukturell auffaellig, weil Gateway und Dashboard intern ueber dieses Netz sprechen.
- `grafana` nutzt zusaetzlich `backend_net` in `ops/grafana-influxdb/docker-compose.yml:27-30`, obwohl die Architektur das Grafana/Influx-Paar primaer als `frontend_net` + `grafana_influx_internal` beschreibt. Grund ist vermutlich Loki-Datasource-Zugriff; `docs/REPO_MAP.md` nennt `backend_net` fuer Grafana-Loki-Datasource. [AUSNAHME - dokumentiert]
- `paperless-gpt` verwendet `PAPERLESS_API_TOKEN` als Stack-ENV in `apps/paperless-gpt/docker-compose.yml:15`, taucht aber in `docs/SECRETS_MAP.md` nicht als eigener aktiver Secret-Eintrag auf.
## Hinweise
- Direkte Host-Ports sind nur bei dokumentierten Ausnahmen vorhanden:
- [AUSNAHME - dokumentiert] `traefik`: `80:80`, `443:443` in `traefik/docker-compose.yml:30-32`.
- [AUSNAHME - dokumentiert] `gitea`: `222:22` in `core/gitea/docker-compose.yml:17-18`.
- [AUSNAHME - dokumentiert] `adguard`: `53:53/tcp`, `53:53/udp`, `8082:80` in `host-services/Adguard/docker-compose.yml:13-16`.
- [AUSNAHME - dokumentiert] `influxdb3-core`: `${INFLUXDB_BIND_IP:-127.0.0.1}:8181:8181` in `ops/grafana-influxdb/docker-compose.yml:54-55`.
- `backend_net` wird in allen Compose-Dateien als `external: true` referenziert, z. B. `infra/postgresql17/docker-compose.yml:25-26`, `infra/redis/docker-compose.yml:22-23`, `traefik/docker-compose.yml:59-60`. Ob das externe Docker-Netz live wirklich `internal: true` ist, ist aus dem Repo allein nicht verifizierbar.
- `grafana_influx_lan` hat bewusst kein `internal: true`: `ops/grafana-influxdb/docker-compose.yml:88-89`. [AUSNAHME - dokumentiert]
- Mutable Tags sind mit Digests gepinnt, aber semantisch weiterhin mutable:
- `immich-server`: `release@sha256` in `apps/immich/docker-compose.yml:4`.
- `immich-machine-learning`: `release@sha256` in `apps/immich/docker-compose.yml:35`.
- `tailscale`: `stable@sha256` in `host-services/tailscale/docker-compose.yml:3`.
- `ddns-updater`: `latest@sha256` in `infra/ddns-updater/docker-compose.yml:3`.
- `komodo-core`: Major-Tag `2@sha256` in `ops/komodo/docker-compose.yml:36`.
- `komodo-periphery`: Major-Tag `2@sha256` in `ops/komodo/docker-compose.yml:82`.
- `uptime-kuma`: Major-Tag `1@sha256` in `ops/uptime-kuma/docker-compose.yml:3`.
- Reines `image: name:latest` ohne Digest wurde in produktiven Compose-Dateien nicht gefunden.
- Alle Services haben `restart: unless-stopped`.
- Alle Services haben `security_opt: no-new-privileges:true`.
- [AUSNAHME - dokumentiert] `scrutiny` nutzt `privileged: true` in `ops/scrutiny/docker-compose.yml:6`.
- [AUSNAHME - dokumentiert] `tailscale` nutzt `network_mode: host` in `host-services/tailscale/docker-compose.yml:6`.
## Dokumentations-Abweichungen
- Service-Namen in Compose vs. `docs/SERVICE_CATALOG.md` weichen bei Immich ab: Compose nutzt `immich-server`, `immich-machine-learning`, `database`, `redis` in `apps/immich/docker-compose.yml:2`, `:33`, `:44`, `:53`; der Katalog dokumentiert `immich_server`, `immich_machine_learning`, `immich_postgres`, `immich_redis`.
- Service-Name in Compose vs. `docs/SERVICE_CATALOG.md` weicht bei Paperless ab: Compose nutzt `paperless` in `apps/paperless/docker-compose.yml:2`; der Katalog dokumentiert `paperless-ngx`.
- Service-Name in Compose vs. `docs/SERVICE_CATALOG.md` weicht bei Redis ab: Compose nutzt `redis` in `infra/redis/docker-compose.yml:2`; der Katalog dokumentiert `Redis`.
- Traefik-Host bei Hermes ist im Compose variabel: `Host(`${HERMES_DASHBOARD_HOST}`)` in `ops/hermes-agent/docker-compose.yml:81`; `docs/REPO_MAP.md` dokumentiert konkret `hermes.kaleschke.info`. Das ist plausibel, aber maschinell nicht eindeutig abgleichbar.
- `docs/REPO_MAP.md` Volumes-Tabelle ist fuer mehrere Mounts nur zusammenfassend, nicht pfadgenau. Beispiele mit Compose-Pfaden, die dort nicht wortgleich auftauchen:
- `/mnt/user/appdata/unbound/config` in `apps/unbound/docker-compose.yml:7`.
- `/mnt/user/appdata/ddns-updater` in `infra/ddns-updater/docker-compose.yml:17`.
- `/mnt/user/appdata/filebrowser/database` und `/mnt/user/appdata/filebrowser/config` in `ops/filebrowser/docker-compose.yml:15-16`.
- `/mnt/user/appdata/borg-ui/restore` in `ops/borg-ui/docker-compose.yml:27`.
- Netzwerke in Compose sind alle in `docs/REPO_MAP.md` aufgefuehrt.
- Traefik-Hosts aus Compose sind in `docs/REPO_MAP.md` aufgefuehrt; einzige Besonderheit ist der variable Hermes-Host.
- `.example`-Dateien haben passende Gegenspieler in `.gitignore`: `.env`, `*.env`, `!*.env.example`, `**/stack.env`, `!**/stack.env.example`.
- Keine `.keep`-Platzhalter-Dateien gefunden.
## Offene Architektur-Punkte: aktueller Stand
- `immich_redis`: weiterhin kein named volume. Compose-Service `redis` in `apps/immich/docker-compose.yml:44-50` hat aktuell gar keinen `volumes:`-Block. Architekturpunkt bleibt offen.
- `AdGuard Home`: Admin-Port ist weiterhin direkt veroeffentlicht: `8082:80` in `host-services/Adguard/docker-compose.yml:15`; keine Traefik-Labels vorhanden. Block F bleibt offen.
- `filebrowser`: Appdata-Breitmount ist entfernt; aktuelle Mounts sind `/mnt/user/documents`, `/mnt/user/photos`, `/mnt/user/projekte` plus eigene DB/Config in `ops/filebrowser/docker-compose.yml:12-16`. Langfristiges Hardening bleibt moeglich, aber der groesste alte Breitmount ist erledigt.
- `bentopdf`: Compose ist vorhanden und Traefik-abgesichert in `apps/bentopdf/docker-compose.yml:2-27`. Runtime-Deploy und fachliche Abnahme koennen aus dem Repo allein nicht bestaetigt werden; Architekturpunkt bleibt als Live-Pruefung offen.
- `grafana` und `influxdb3-core`: laufen weiterhin als `user: "0"` in `ops/grafana-influxdb/docker-compose.yml:6` und `ops/grafana-influxdb/docker-compose.yml:53`. [AUSNAHME - dokumentiert]
- `plex-media-server`: keine Compose-Datei im Repo gefunden; bleibt Dockerman-/Host-Sonderfall laut Architektur.
## Bestanden
- 30 produktive `docker-compose.yml` wurden geprueft.
- Keine Datenbanken oder Caches im `frontend_net`: `postgresql17` nur `backend_net` in `infra/postgresql17/docker-compose.yml:18`; shared `redis` nur `backend_net` in `infra/redis/docker-compose.yml:15`; `mealie-postgres` nur `mealie_internal` in `apps/mealie/docker-compose.yml:56`; Immich `database` und `redis` nur `immich_default` in `apps/immich/docker-compose.yml:48` und `:64`; Nextcloud DB/Redis nur `nextcloud_internal` in `apps/nextcloud/docker-compose.yml:61` und `:73`; `komodo-mongo` nur `komodo_net` in `ops/komodo/docker-compose.yml:16`.
- App-interne Pflichtnetze sind korrekt `internal: true`: `immich_default` in `apps/immich/docker-compose.yml:73-75`, `mealie_internal` in `apps/mealie/docker-compose.yml:66-68`, `nextcloud_internal` in `apps/nextcloud/docker-compose.yml:82-84`, `grafana_influx_internal` in `ops/grafana-influxdb/docker-compose.yml:90-91`.
- `frontend_net` und `backend_net` werden in Compose-Dateien als `external: true` referenziert.
- Alle Services mit `traefik.enable=true` setzen explizit `traefik.docker.network=frontend_net`, `entrypoints=websecure`, `tls=true` und `tls.certresolver=le`.
- Kein Traefik-Label mit `yourdomain.tld` gefunden.
- Admin-/Ops-Router haben Middleware `authelia@file,secure-headers@file`, u. a. `homepage` in `apps/homepage/docker-compose.yml:31`, `filebrowser` in `ops/filebrowser/docker-compose.yml:27`, `scrutiny` in `ops/scrutiny/docker-compose.yml:32`, `grafana` in `ops/grafana-influxdb/docker-compose.yml:46`. [AUSNAHME - dokumentiert] `komodo-core` hat keine ForwardAuth-Middleware in `ops/komodo/docker-compose.yml:64-71`; [AUSNAHME - dokumentiert] `nextcloud` nutzt native Auth und nur Redirect-Middleware in `apps/nextcloud/docker-compose.yml:42-46`.
- Web-UIs ohne Traefik-Labels wurden nur als dokumentierte Sonderfaelle gefunden: `adguard` mit LAN-Admin-Port in `host-services/Adguard/docker-compose.yml:13-16`; `ddns-updater` hat keine Web-UI und braucht Internet in `infra/ddns-updater/docker-compose.yml:8`; `komodo-periphery` ist Agent ohne Traefik-Route in `ops/komodo/docker-compose.yml:81-103`.
@@ -1,32 +0,0 @@
# Codex-Prompt: KalliLab Endstufe
Du hast Vollzugriff auf `G:\Gitea_Clone\homelab-infra`, Gitea-Push, Komodo, und SSH auf Unraid `Kallilabcore`.
## Lies zuerst
1. `CLAUDE.md`
2. `docs/archive/2026-05/AUDIT_2026-05-23.md` — dort steht die komplette Restliste
## Auftrag
Den Audit von oben verifizieren und die offenen Punkte abarbeiten, bis das Homelab in der Endstufe ist. Reihenfolge:
1. **P0** — Lokalen Commit `cd650b1` nach Gitea pushen, danach Komodo-Reaktion fuer `gitea` und `borg-ui` pruefen.
2. **P0** — Live-Daten aus Audit-Abschnitt 9 messen und in `docs/archive/2026-05/AUDIT_2026-05-23_LIVE.md` ablegen (Secrets redacten).
3. **P1** — Monitoring-Stack (`monitoring/`) live deployen, alte `ops/grafana-influxdb` und `ops/loki` `down` (nicht loeschen).
4. **P1** — Jellyfin und Plex in `HOMELAB_ARCHITECTURE_MASTER_V2.md`, `docs/SERVICE_CATALOG.md`, `docs/REPO_MAP.md` nachtragen. Plex-Eintrag "nicht als Repo-Stack enthalten" korrigieren.
5. **P2** — Borg-Lauf-Frische pruefen, ggf. neuen Lauf ausloesen, alle 14 Dump-Artefakte juenger als 24 h.
6. **P3** — Repo-Hygiene: 8 leere Verzeichnisse weg, `.serena/` in `.gitignore`, Entscheidung zu `ops/windows-reinstall/*.ps1`.
## Regeln (aus CLAUDE.md, nicht verhandelbar)
- Secrets nie im Klartext ausgeben.
- Keine Aenderungen direkt in Komodo, nur ueber Git → Push → Komodo.
- Kein `push --force`, kein blindes Loeschen von `/mnt/user/{appdata,documents,photos,services,backups}`.
- Working-Tree-Status nur aus `git status --short` ableiten, nie aus `git diff` ueber Linux-Mount.
- Traefik dynamic config wird nicht von Komodo deployed — Aenderungen dort manuell auf `/mnt/user/appdata/traefik/dynamic/` syncen.
- Nicht anfassen: Hermes, Disk1 NTFS Phase 2, Komodo-Auth, Grafana/InfluxDB `user: "0"`, Image-Pinning ddns/glances/scrutiny.
- Wenn zwei Reparaturversuche scheitern: stoppen, Drift-Runbook Pflichtmatrix, Operator fragen.
## Arbeitsmodus pro Block
Lesen → minimal aendern → `ops/policy-checks/check_repo.ps1` lokal → Commit → Push → Komodo-Reaktion + Smoke-Test → eine Zeile in `docs/MIGRATION_LOG.md`.
## Fertig
Wenn alles abgearbeitet ist (oder ein Punkt bewusst offen bleibt): `docs/archive/2026-05/AUDIT_2026-05-23_FINAL.md` schreiben mit Ampel + konkretem Beleg pro Punkt, committen, pushen, kurz an mich melden.
@@ -1,202 +0,0 @@
# Codex-Prompt: Jellyfin entfernen, Plex bleibt
> **Status (Stand 2026-05-30):** Auftrag ausgefuehrt. Jellyfin wurde 2026-05-25 aus Repo, Komodo, Traefik-Routing, Authelia-ACL und Appdata-Live-Pfad entfernt (Service-Removal-Checkliste in `docs/WORKFLOW.md`, MIGRATION_LOG-Eintrag dort). Datei bleibt im Repo als **Codex-Removal-Pattern** fuer kuenftige Stack-Removals (z.B. Hermes nach Review-Deadline 2026-07-25 oder bei BentoPDF/paperless-gpt-Folgeentscheidung). Inhaltlich nicht mehr aendern — als Vorlage referenzieren und pro Anwendung neu instanzieren.
Stand: 2026-05-23
Ausloeser: Operator-Entscheidung "Plex bleibt, Jellyfin weg".
Bezug: `docs/archive/2026-05/STRATEGISCHE_BEWERTUNG_2026-05-23.md` Block 9 Quick Wins.
Du hast Vollzugriff auf `G:\Gitea_Clone\homelab-infra`, Gitea-Push, Komodo, und SSH auf Unraid `Kallilabcore`.
## Lies zuerst
1. `CLAUDE.md`
2. `HOMELAB_ARCHITECTURE_MASTER_V2.md` Abschnitt 7.4 und 7.8
3. `docs/WORKFLOW.md`
4. `docs/ROLLBACK.md`
5. `apps/jellyfin/docker-compose.yml`
6. `security/authelia/configuration.yml` (Access-Control-Block)
7. `docs/SERVICE_CATALOG.md`, `docs/REPO_MAP.md`, `docs/MIGRATION_LOG.md`
## Ziel
Jellyfin ist vollstaendig aus dem Repo, Komodo, Traefik-Routing und Authelia-ACL entfernt. Plex laeuft unveraendert weiter. Medien-/Foto-Mounts unter `/mnt/user/media` und `/mnt/user/photos` bleiben unberuehrt. Domain `jellyfin.kaleschke.info` antwortet nicht mehr ueber Traefik.
## Wichtige Vorabinformation
- `/mnt/user/appdata/jellyfin/{config,cache}` ist **nicht** im Borg-Scope (`ops/borg-ui/all-important-sources.txt`). Watch-History, User-Settings und Metadaten-Cache sind danach weg. Das ist akzeptabel, weil Plex die Nutzungsdaten ohnehin separat fuehrt.
- Plex teilt sich `/mnt/user/media:ro` und `/mnt/user/photos:ro` mit Jellyfin — Datenpfad bleibt unangetastet.
- Authelia-Eintrag `jellyfin.kaleschke.info` steht unter `policy: bypass` in `security/authelia/configuration.yml` Zeile 42. Die `configuration.yml` ist Repo-Baseline und muss laut `docs/AI_CONTEXT.md` manuell auf den Host gemerged werden.
- Jellyfin hat **keinen** Eintrag in `monitoring/blackbox/blackbox.yml`, `ops/glance/config/glance.yml`, `apps/homepage/docker-compose.yml`. Die Homepage-Service-Cards liegen hostseitig unter `/mnt/user/appdata/homepage/config/services.yaml` — dort moeglicherweise ein Eintrag, vor Ort pruefen.
## Reihenfolge
### P0 — Plex-Tauglichkeit verifizieren (vor jeglichem Loeschen)
Smoke-Test auf dem Host:
```bash
curl -fsS -o /dev/null -w "%{http_code}\n" http://192.168.178.58:32400/identity
docker exec plex test -d /data/Filme || echo "Filme nicht erreichbar"
docker exec plex test -d /photos || echo "Photos nicht erreichbar"
```
Akzeptanzkriterium: Plex antwortet mit `200`, beide Medien-Pfade sind im Plex-Container sichtbar, Plex zeigt in seiner Web-UI alle erwarteten Bibliotheken. **Wenn nicht erfuellt: abbrechen und Operator fragen.**
### P1 — Jellyfin Stack in Komodo stoppen (nicht loeschen)
In Komodo Web-UI Stack `jellyfin` `Stop` ausfuehren (nicht `Destroy`). Damit ist der Container weg, Stack-Definition und Workspace bleiben — Rollback per `Start` moeglich.
Akzeptanzkriterium:
```bash
docker ps -a --filter name=jellyfin --format "{{.Names}}\t{{.Status}}"
```
zeigt entweder keine Zeile oder `Exited`.
### P2 — Authelia ACL um Jellyfin-Bypass bereinigen
Datei: `security/authelia/configuration.yml`
```diff
- domain:
- immich.kaleschke.info
- paperless.kaleschke.info
- mealie.kaleschke.info
- vault.kaleschke.info
- ntfy.kaleschke.info
- git.kaleschke.info
- - jellyfin.kaleschke.info
policy: bypass
```
Kein anderer Authelia-Eintrag referenziert Jellyfin. Wildcard `*.kaleschke.info` mit `policy: one_factor` greift fuer geloeschte Domains nicht, weil Traefik die Route nicht mehr kennt.
**Manueller Host-Sync danach Pflicht** (`docs/AI_CONTEXT.md`, Workflow): die geaenderte `configuration.yml` muss auf `/mnt/user/appdata/authelia/config/configuration.yml` gemerged werden (OIDC-/Secret-Block hostseitig erhalten). Danach `docker exec authelia authelia validate-config -c /config/configuration.yml` und Stack-Restart.
### P3 — Compose-Stack aus Repo entfernen
```bash
git rm -r apps/jellyfin/
```
Akzeptanzkriterium: `apps/jellyfin/` existiert nicht mehr; `git status --short` zeigt `D apps/jellyfin/docker-compose.yml`.
### P4 — Doku synchronisieren
**`HOMELAB_ARCHITECTURE_MASTER_V2.md`:**
- Abschnitt 3.2 Diagramm: `jellyfin` aus oeffentliche-Apps-Zeile entfernen.
- Abschnitt 4.1 "Oeffentlich ueber Traefik": Zeile `- jellyfin — jellyfin.kaleschke.info` entfernen.
- Abschnitt 7.4 Tabelle "Produktive Apps": Zeile `jellyfin` entfernen.
- Abschnitt 7.8 "Entfernte Container": neue Zeile
```text
| `jellyfin` | 2026-05-23 | doppelter Medienserver neben Plex; Plex bleibt einziger Medienserver |
```
**`docs/SERVICE_CATALOG.md`:**
- Block "Public / User Apps": Jellyfin-Zeile entfernen.
**`docs/REPO_MAP.md`:**
- Abschnitt "Apps"-Tabelle: Jellyfin-Zeile entfernen.
- Abschnitt "Traefik Hosts": `jellyfin.kaleschke.info`-Zeile entfernen.
- Abschnitt "Volumes und Datenpfade": Jellyfin-Zeile entfernen.
**`docs/MIGRATION_LOG.md`:** neuen Eintrag anhaengen
```text
### Jellyfin entfernt (2026-05-23)
- Operator-Entscheidung: Plex bleibt einziger Medienserver.
- Compose-Stack `apps/jellyfin/` aus Repo entfernt.
- Authelia-ACL bereinigt (`jellyfin.kaleschke.info` aus `bypass`-Liste raus), Host-Config gemerged.
- Komodo-Stack `jellyfin` gestoppt; Stack-Eintrag und Webhook bei Abschluss von Schritt P7 entfernt.
- Appdata unter `/mnt/user/appdata/jellyfin/` nach `/mnt/user/appdata/_archive/jellyfin-removed-2026-05-23/` verschoben (siehe Schritt P5).
- DNS-Eintrag `jellyfin.kaleschke.info` in Cloudflare belassen, kann beim naechsten DNS-Cleanup mit entfernt werden.
```
### P5 — Appdata archivieren statt loeschen
Auf dem Host:
```bash
mkdir -p /mnt/user/appdata/_archive
mv /mnt/user/appdata/jellyfin /mnt/user/appdata/_archive/jellyfin-removed-2026-05-23
```
Akzeptanzkriterium: `/mnt/user/appdata/jellyfin` existiert nicht mehr, `/mnt/user/appdata/_archive/jellyfin-removed-2026-05-23` enthaelt `config/` und `cache/`. **Nicht endgueltig loeschen** vor mindestens 14 Tagen Plex-Stabilitaet.
### P6 — Policy-Check + Commit
```powershell
pwsh ops/policy-checks/check_repo.ps1
```
Akzeptanzkriterium: 0 Critical, keine neuen Warnings (vorher: 4 dokumentierte Warnings, soll danach gleich bleiben).
Commit:
```bash
git add -A
git commit -m "Remove Jellyfin stack; Plex remains sole media server"
git push origin master
```
### P7 — Komodo-Stack-Eintrag und Webhook entfernen
Nach erfolgreichem Push und gruener Komodo-Reaktion auf restliche Stacks:
- In Komodo: Stack `jellyfin` `Destroy` (Workspace `/mnt/user/services/stacks/jellyfin/` entfernen).
- In Gitea: Webhook-Eintrag fuer Komodo-Stack-ID `jellyfin` entfernen.
Akzeptanzkriterium: `ls /mnt/user/services/stacks/ | grep jellyfin` ist leer; Gitea-Webhook-Liste fuer `Micha/homelab-infra` enthaelt keinen Jellyfin-Eintrag mehr.
### P8 — Smoke-Test final
```bash
curl -fsS -o /dev/null -w "%{http_code}\n" https://jellyfin.kaleschke.info/
docker ps --filter name=jellyfin --format "{{.Names}}"
ss -ltnp | grep 8096 || echo "Port 8096 frei"
```
Erwartung:
- `https://jellyfin.kaleschke.info/` antwortet `404` (Traefik kennt die Route nicht mehr) oder Cert-Fehler je nach Cache.
- Kein laufender `jellyfin`-Container.
- Port `8096` nicht belegt (Jellyfin nutzte nur Container-Port, sollte sowieso frei sein).
- Authelia-Login fuer Admin-Domains (`uptime`, `files`, `scrutiny`) funktioniert weiterhin — Bypass-Liste-Aenderung darf 2FA-Domains nicht angreifen.
## Rollback (bis Schritt P5 einschliesslich)
- Git: `git revert <commit-sha>` und push. Komodo deployt Jellyfin neu, sobald Stack in Komodo wieder existiert.
- Appdata: `mv /mnt/user/appdata/_archive/jellyfin-removed-2026-05-23 /mnt/user/appdata/jellyfin`.
- Authelia-ACL: Bypass-Eintrag wieder rein, Host-Sync, Authelia-Restart.
Ab Schritt P7 (Komodo Destroy + Webhook weg) ist Rollback nur per Neuanlegen des Komodo-Stacks moeglich.
## Regeln (aus CLAUDE.md, nicht verhandelbar)
- Secrets nie im Klartext ausgeben.
- Keine Aenderungen direkt in Komodo, nur ueber Git → Push → Komodo. **Ausnahme:** Schritt P1 (`Stop`) und P7 (`Destroy`) sind explizit Komodo-Aktionen nach erfolgreichem Repo-Stand.
- Kein `push --force`, kein blindes Loeschen unter `/mnt/user/{appdata,documents,photos,services,backups}` — Appdata wird in `_archive/` verschoben, nicht entfernt.
- Working-Tree-Status nur aus `git status --short` ableiten, nie aus `git diff` ueber Linux-Mount.
- Traefik dynamic config wird nicht von Komodo deployed — fuer diesen Auftrag nicht relevant, weil Jellyfin nur per Docker-Labels gerouteted war.
- Nicht anfassen: Plex-Stack, Medien-/Foto-Mounts, alle anderen Apps.
- Wenn zwei Reparaturversuche scheitern: stoppen, `docs/GITOPS_DRIFT_RUNBOOK.md` Pflichtmatrix, Operator fragen.
## Arbeitsmodus pro Schritt
Lesen → minimal aendern → `ops/policy-checks/check_repo.ps1` lokal (nur P6) → Commit → Push → Komodo-Reaktion + Smoke-Test → Eintrag in `docs/MIGRATION_LOG.md`.
## Fertig
Wenn alle 8 Schritte abgehakt sind: kurze Erfolgsmeldung an Operator mit:
- Commit-SHA des Removal-Commits
- Bestaetigung Plex weiterhin gruen
- Bestaetigung Authelia validate-config gruen
- Bestaetigung Komodo-Stack und Webhook entfernt
- Pfad zum Appdata-Archiv und Erinnerung, dass `_archive/jellyfin-removed-2026-05-23/` nach 14 Tagen Plex-Stabilitaet entfernt werden darf
@@ -1,123 +0,0 @@
# Codex-Prompt: Komodo 5xx-Spam Root-Cause
Stand: 2026-05-31
Auftraggeber: Operator
Vorarbeit: Claude (auto-mode), siehe Ermittlungsstand unten.
## Auftrag
`HomelabTraefik5xx` feuert dauerhaft fuer `service="komodo@docker"`. Quelle
finden, fixen, dokumentieren. Bitte einmal **bis zum Ende** durchziehen, nicht
nur eine Hypothese pruefen.
## Vor Arbeitsbeginn lesen
- `CLAUDE.md`
- `docs/WORKFLOW.md`
- `monitoring/prometheus/alerts.yml`
- `docs/ALERT_RULES.md`
- `ops/komodo/docker-compose.yml`
- `traefik/docker-compose.yml`
- `monitoring/prometheus/prometheus.yml` (Blackbox-Targets)
- `monitoring/blackbox/blackbox.yml`
- `ops/glance/config/glance.yml` (5 Komodo-URL-Stellen, **NICHT** die Quelle — siehe Ermittlung)
## Ermittlungsstand (bereits geklaert)
### Was gemessen wurde
- Traefik-Access-Log: Source-IP ist **eure WAN-IP `217.249.121.39`** (Hairpin
aus dem Heimnetz). User-Agent leer (`"-"`).
- Muster: `GET /` 200 **alle 15s** + `GET /user` **500** alle 30s, plus
gelegentlich `POST /auth/login/GetLoginOptions` 200 und
`POST /read/GetCoreInfo` 500.
- Prometheus `sum by (code) (increase(traefik_service_requests_total{service="komodo@docker"}[5m]))`:
`200`=22, `500`=14 (Werte vom 2026-05-31 08:11 UTC).
- `docker logs komodo-core` ist still — keine internen Errors, nur normale
Execute-Requests. Komodo wirft den 500 also vermutlich auf Auth-Pfad
(`/user` ohne gueltige Session sollte `401` sein, nicht `500`). Das ist ein
Komodo-Bug-on-Top, **aber nicht die Frage**.
### Ausgeschlossene Kandidaten (durch Test)
- **Browser-Tabs** — User hat alle Komodo-Tabs zugemacht, Polling laeuft
weiter.
- **PWA auf Handy** — User hat keine.
- **Uptime-Kuma** — Container existiert nicht mehr.
- **Homepage** — entfernt.
- **Glance** — Test 2026-05-31 ~08:35 UTC: 130s gestoppt, 5xx-Rate
unveraendert (2/60s Baseline → 4/130s waehrend Stop). Trotz 5 Komodo-URL-
Eintraegen in `ops/glance/config/glance.yml` (search-shortcut Zeile 40,
bookmark Zeilen 131/768, monitor-Widget Zeile 237 mit `check-url:
http://komodo-core:9120`, docker-containers-Widget Zeile 725). Glance ist
raus.
### Noch nicht getestete Kandidaten
- **Posture-Check / cert-token-check.sh** (`services/posture-check/`) — koennte
periodisch Komodo-HTTPS pingen. 15s-/30s-Kadenz waere ungewoehnlich fuer
einen Cron-Job, aber pruefen.
- **Blackbox-Exporter** — pollt laut `monitoring/prometheus/prometheus.yml`
`https://komodo.kaleschke.info` alle 15s. Das erklaert den `GET / 200`-
Anteil sauber. Erklaert aber NICHT den `GET /user 500` 30s-Takt.
- **Komodo Periphery** — auf `komodo_net` und `frontend_net`. Sollte mit
Core via internes Netz reden, koennte aber per Misconfig die Public-URL
treffen. Logs noch nicht eingesehen.
- **Komodo Core selbst** mit `KOMODO_HOST=https://komodo.kaleschke.info`
evtl. Self-Check via Public-URL.
- **Ein Gerat im LAN**, das wir noch nicht auf dem Schirm haben (zweiter
Rechner mit altem Tab, Smart-TV, etc.).
### Was nicht geht
- `tcpdump` fehlt auf dem Host.
- `conntrack` zeigt die Hairpin-Pakete nicht (NAT-Pre-Routing).
## Naechste Schritte (Vorschlag)
1. **Blackbox-Exporter ausschliessen**: Targets in `prometheus.yml` zeigen,
dass Blackbox NUR `https://komodo.kaleschke.info` pollt (also `/`, kein
`/user`). Bestaetigen.
2. **Posture-Check pruefen**: `services/posture-check/cert-token-check.sh`
lesen, Kadenz und Endpunkte protokollieren. Falls dort `/user` oder ein
30s-Loop drin ist → Treffer.
3. **Periphery isolieren**: Periphery 2 min stoppen, Traefik-Log gegen-
checken. `docker stop komodo-periphery; sleep 130; <log-check>; docker
start komodo-periphery`. Vorsicht: Periphery-Down heisst Komodo-Deploy
funktioniert nicht — also nur kurz, kein Deploy in dem Fenster.
4. **Komodo-Core isolieren**: Wenn 1-3 nichts ergeben, Komodo-Core selbst 2 min
stoppen. Wenn Polling weiterlaeuft, ist der Client ausserhalb der Komodo-
Stack (LAN-Geraet). Wenn es aufhoert, polled Komodo Core sich selbst.
5. **LAN-Aufnahme via Komodo-Container**: Falls Container-Stack ausgeschlossen,
im komodo-core-Container per `ss -tnp state syn-recv` waehrend einer
typischen Polling-Sekunde mitschauen. Source-IP/Port der eingehenden
Connection liefert den Hairpin-Origin am genauesten.
## Fix-Erwartung
Sobald Quelle bekannt:
- **Wenn Container im Stack**: Config so anpassen, dass die Anfrage intern
laeuft (kein Public-Hostname), inkl. Doku.
- **Wenn LAN-Geraet**: User informieren, was es ist; wenn moeglich Geraet
reparieren (Tab schliessen, App deinstallieren). Kein Repo-Change noetig.
- **Wenn nicht abstellbar**: separate Frage, ob `HomelabTraefik5xx` fuer
`service="komodo@docker"` mit einem Exclude versehen werden soll — aber nur
als letzter Ausweg. Default ist: Quelle fixen.
## Doku am Ende
- Eintrag in `docs/MIGRATION_LOG.md`: Datum, Symptom, Root-Cause, Fix,
Smoke-Test.
- Falls eine Glance-/Periphery-/sonstige Config-Aenderung noetig wird:
Standard-Loop (Commit → Push → Komodo-Deploy → Smoke), Co-Authored-By-Tag
mitgeben.
## Regeln (nicht verhandelbar)
- Git → Push → Komodo. Keine direkten Komodo-Edits.
- Stop/Start-Tests sind okay, aber nur kurz (≤ 3 min) und mit
Wiederanlauf-Schritt im selben Block.
- Secrets nicht ausgeben.
- Bei zwei gescheiterten Versuchen: stop, Pflichtmatrix aus
`docs/GITOPS_DRIFT_RUNBOOK.md`, Operator fragen.
@@ -1,83 +0,0 @@
# Codex-Prompt: KalliLab Konsolidierung (Bewertungs-Followup)
> **Status (Stand 2026-05-30):** Erstprompt fuer den Audit-Zyklus 2026-05-25, Stand weitgehend abgearbeitet. Verbleibende Punkte sind in `docs/AUDIT_2026-05-25_TODO.md` weiter gefuehrt (offen, geparkt, bewusst nicht umgesetzt). Datei bleibt im Repo als **Codex-Prompt-Vorlage** fuer kuenftige Konsolidierungs-Sweeps; inhaltlich nicht mehr aendern.
Stand: 2026-05-23
Auftraggeber: Operator
Quelle: `docs/archive/2026-05/STRATEGISCHE_BEWERTUNG_2026-05-23.md`
## Schritt 0 — Reviewe die Bewertung kritisch
Lies `docs/archive/2026-05/STRATEGISCHE_BEWERTUNG_2026-05-23.md` komplett. Bevor du irgendetwas anfasst, sag dem Operator ehrlich:
- Wo ist Claudes Befund richtig?
- Wo liegt Claude daneben oder hat etwas Wichtiges uebersehen?
- Welche Hausaufgaben unten wuerdest du anders priorisieren oder weglassen?
Erst nach Operator-Freigabe weitermachen.
## Lies vor jedem Block
`CLAUDE.md`, `docs/WORKFLOW.md`, betroffene Compose-Datei. Bei DR/Backup zusaetzlich `docs/DISASTER_RECOVERY.md` und `docs/RESTORE_MATRIX.md`.
## Hausaufgaben
### P0 — Quick Wins (≤ 1 Woche, hoher Nutzen)
1. **Externer Repo-Mirror** einrichten (GitHub privat oder zweites Gitea); Push-Mirror in Gitea aktivieren. Schliesst das groesste DR-Loch.
2. **Borg-Passphrase analog sichern** (Schliessfach oder Familienmitglied).
3. **Jellyfin entfernen, Plex bleibt.** Detail-Schritte in `docs/archive/2026-05/CODEX_JELLYFIN_REMOVAL_2026-05-23.md`. Kurzfassung: Plex-Smoke-Test → Komodo-Stop → Authelia-Bypass raus + Host-Sync → `git rm apps/jellyfin/` → Doku (MASTER 3.2/4.1/7.4/7.8, SERVICE_CATALOG, REPO_MAP, MIGRATION_LOG) → Appdata nach `_archive/` → Policy-Check → Push → Komodo-Destroy + Webhook weg.
4. **Glance oder Homepage** als einziges Dashboard waehlen, das andere stoppen und aus Repo entfernen.
5. **AdGuard Admin-Port 8082** hinter Authelia oder nur via Tailscale (Block F aus MASTER 10).
6. **Authelia 2FA-Pflicht** fuer alle aktiven User verifizieren bzw. aktivieren.
7. **Disk1 NTFS → XFS Phase 2** abschliessen, anschliessend `ALLOW_DISK1_NTFS=0` in posture-check.
### P1 — Stabilitaet und Ordnung (24 Wochen)
8. **Monitoring-Migration abschliessen**: `monitoring/` produktiv, `ops/grafana-influxdb` + `ops/loki` `down` + aus Repo entfernen.
9. **Uptime-Kuma abloesen** durch Blackbox + Grafana-Alerts (nach 7 Tagen Parallelbetrieb mit Paritaet, wie in SERVICE_CATALOG vorgesehen).
10. **Hermes-Agent Entscheidung**: produktiv mit klarem Alltagsnutzen oder vollstaendig entfernen. Kein weiteres Quartal "halb da".
11. **paperless-gpt und BentoPDF**: gleiche Frage. Produktiv im Workflow oder weg.
12. **Unraid USB-Flash-Backup** einrichten (eingebauter Mechanismus).
13. **Family-View-Dashboard** in Grafana: alles-gruen-Uebersicht fuer den Morgen-Check.
### P2 — Automatisierung und Transparenz (412 Wochen)
14. **Authelia OIDC-Provider** aktivieren; Nextcloud + Immich + Grafana als OIDC-Clients.
15. **Renovate Bot gegen Gitea** fuer kontrollierte Image-Update-PRs (loest die manuelle Digest-Pflege ab).
16. **Restore-Test fuer Immich** als eigener Sprint einplanen (groesster Datentopf ohne Mini-Restore).
17. **Immich Smartphone-Auto-Backup** fuer alle Familien-Geraete aktivieren — der eigentliche Familien-Nutzen.
18. **CrowdSec vor Traefik** als Bouncer fuer oeffentlich erreichbare Apps.
### P3 — Advanced (36 Monate)
19. Staging-Branch + zweites Komodo-Ziel in Tailscale-VM.
20. Restore-Test-Automatisierung als CI (Gitea Actions oder Drone).
21. Off-Site-Backup zu zweitem Ziel (zweites BorgBase-Repo oder Hetzner Storage Box).
22. Cold-Standby-Konzept dokumentieren.
23. Komodo-Self-Stack aus Komodo-Management herausnehmen, als handgepflegter `docker compose`-Service in `services/`.
### P4 — Nice-to-have / Spielwiese
24. Firefly III oder Actual Budget fuer `/mnt/user/finance`.
25. Wandtablet im Flur mit Family-Dashboard.
26. Home Assistant tiefer in ntfy-Workflows verzahnen (Frostwarnung, PV-Ueberschuss, Briefkasten).
27. Ecowitt-Wetter-Dashboard, sobald HA→InfluxDB-Pipeline aus `docs/HOME_ASSISTANT_INFLUXDB_ECOWITT.md` laeuft.
## Regeln (aus CLAUDE.md, nicht verhandelbar)
- Git → Push → Komodo. Keine direkten Komodo-Edits, kein `push --force`.
- Secrets nie ins Repo, nie loggen.
- Appdata-Pfade nicht blind loeschen — vor Removal nach `/mnt/user/appdata/_archive/<ding>-removed-<datum>/` verschieben, 14 Tage warten.
- Traefik dynamic config manueller Host-Sync.
- Working-Tree-Status nur aus `git status --short`, nie aus `git diff` ueber Linux-Mount.
- Nicht anfassen: Komodo native Auth (dokumentierte Ausnahme), Grafana/InfluxDB `user: "0"`, Image-Pinning ddns/glances/scrutiny.
- Bei zwei gescheiterten Versuchen: stop, `docs/GITOPS_DRIFT_RUNBOOK.md` Pflichtmatrix, Operator fragen.
## Arbeitsmodus pro Block
Lesen → minimal aendern → `ops/policy-checks/check_repo.ps1` → Commit → Push → Komodo + Smoke-Test → eine Zeile in `docs/MIGRATION_LOG.md`.
## Fertig pro Block
Kurz an Operator: Commit-SHA, Smoke-Test-Beleg, ggf. neuer Watchpoint.
@@ -1,77 +0,0 @@
# Disk1 Phase 2 - NTFS to XFS Migration
Stand: 2026-05-25 06:15 CEST. Ziel erreicht: `/mnt/disk1` wurde von `ntfs3` auf XFS migriert, ohne produktive Compose-Pfade zu aendern. Container nutzen weiter `/mnt/user/...`.
## Preflight
| Check | Ergebnis |
|---|---|
| Disk1 Mount | `/dev/md1p1` auf `/mnt/disk1`, `ntfs3`, 5.5T, 1.7T genutzt, 3.8T frei |
| Cache Mount | `/dev/nvme0n1p1` auf `/mnt/cache`, `xfs`, 1.9T, 100G genutzt |
| H: Backup-Ziel | `H:\`, Label `Externe HDD`, NTFS, 8T, 5.96T frei, healthy |
| Compose-Binds | Keine Treffer fuer direkte `/mnt/disk1`, `/mnt/cache`, `/mnt/disks`, `/mnt/remotes` Binds |
| SMB-Zugriff | `\\Kallilabcore\services`, `documents`, `photos`, `media` erreichbar |
## Zu sichernde Shares
| Share | Groesse laut Preflight |
|---|---:|
| `services` | 451M |
| `documents` | 196M |
| `photos` | 23G |
| `backups` | 2.2G |
| `media` | 1.7T |
| `finance` | 0 |
| `projekte` | 92K |
Zusaetzliche Disk1-Top-Level-Pfade: `scripts` 3.3M, `isos` 0, `System Volume Information` 0.
## Backup-Strategie
- Zielroot: `H:\kallilab-recovery\disk1-phase2-2026-05-23`.
- Kritischer Linux-/GitOps-Pfad `services` wird zusaetzlich als Tar-Archiv ueber SSH gesichert, damit Linux-Metadaten erhalten bleiben.
- User-Shares werden per SMB/Robocopy kopiert, mit Logs und anschliessender Zaehler-/Groessenverifikation.
- Keine produktiven Datenpfade werden geloescht.
## Gates
1. Backup komplett und verifiziert.
2. Dienste-Freeze vorbereitet und letzte Dumps frisch.
3. Direkt vor Format/Array-Prozedur: Operator-Bestaetigung im konkreten Moment.
## Backup-Ergebnis
Stand: 2026-05-24 13:07 CEST.
| Bereich | Sicherungsart | Ergebnis |
|---|---|---|
| `media` | Robocopy/SMB nach `H:\kallilab-recovery\disk1-phase2-2026-05-23\shares\media` | 2722 Dateien, 1677.11 GiB, Manifestvergleich: 0 missing, 0 size mismatch, 0 extra |
| `services` | Host-Tar auf Unraid Cache, danach binaer per `scp` nach H: | `services.tar`, 0.441 GiB, `tar -tf` gueltig |
| `documents` | Host-Tar auf Unraid Cache, danach binaer per `scp` nach H: | `documents.tar`, 0.192 GiB, `tar -tf` gueltig |
| `photos` | Host-Tar auf Unraid Cache, danach binaer per `scp` nach H: | `photos.tar`, 22.876 GiB, `tar -tf` gueltig |
| `backups` | Host-Tar auf Unraid Cache, danach binaer per `scp` nach H: | `backups.tar`, 2.099 GiB, `tar -tf` gueltig |
| `finance` | Host-Tar auf Unraid Cache, danach binaer per `scp` nach H: | `finance.tar`, leerer Share, `tar -tf` gueltig |
| `projekte` | Host-Tar auf Unraid Cache, danach binaer per `scp` nach H: | `projekte.tar`, klein, `tar -tf` gueltig |
| Disk1-Extras `scripts`, `isos` | Host-Tar auf Unraid Cache, danach binaer per `scp` nach H: | `disk1-extra.tar`, 0.003 GiB, `tar -tf` gueltig |
Hinweis: Erste Tar-Versuche per PowerShell-Redirect wurden verworfen, weil PowerShell den binaeren SSH-Stream als UTF-16 geschrieben hatte. Die ungueltige `media.tar` und unvollstaendige SMB-Teilkopien fuer `services`/`documents` wurden vom Backup-Ziel entfernt, damit nur verwertbare Sicherungen uebrig bleiben.
## Abschluss
Stand: 2026-05-25 06:15 CEST.
| Check | Ergebnis |
|---|---|
| Freeze-Dumps | `pre-backup-dumps.sh` vor Format ausgefuehrt; nach Wiederanlauf erneut erfolgreich, 15 kanonische Dump-Artefakte juenger als 24 h |
| Disk1 Filesystem | `/dev/md1p1` auf `/mnt/disk1`, `xfs`, 5.5T, 1.8T genutzt, 3.7T frei |
| Restore `media` | 2722 Dateien, 1,800,782,188,226 Bytes; finaler Manifestvergleich: 0 missing, 0 size mismatch, 0 extra |
| Restore Tar-Shares | `services`, `documents`, `photos`, `backups`, `finance`, `projekte` und Disk1-Extras aus H:-Freeze-Archiven nach `/mnt/disk1` extrahiert |
| Docker/Services | 49 Container laufend, 0 stopped, 0 unhealthy, 0 starting |
| Smoke-Tests | `git.kaleschke.info` 200, `komodo.kaleschke.info` 200, `borg.kaleschke.info` 302, `jellyfin.kaleschke.info` 302, `monitoring.kaleschke.info` 302 |
| Service-Mounts | Gitea SSH `:222` offen; Jellyfin und Plex sehen `/media`; Prometheus readiness ok |
| Backup-Lauf | Borg-UI Repository `appdata-critical`, letzter Job `completed`, Archiv `Taegliche-Sicherung-2026-05-25T05:52:44.157`, `nfiles=100221` |
| Temp-Cleanup | `/mnt/cache/disk1-phase2-tmp/*.tar` nach H:-Verifikation geloescht; Cache weiter XFS mit ca. 1.7T frei |
Hinweis zum Docker-Wiederanlauf: Nach dem manuellen Docker-Start liefen die Container, aber Healthcheck-Execs scheiterten wegen `dockerd` mit `XDG_RUNTIME_DIR=/run/user/0`. Ein gezielter Docker-Neustart mit unsetztem `XDG_RUNTIME_DIR` behob den Runtime-Fehler; danach wurden alle Healthchecks gruen. `monitoring-prometheus` war durch den geplanten Docker-Stop sauber beendet und wurde als bestehender Container wieder gestartet.
Offener Nachlauf: Die Array-Parity-Anzeige zeigte nach Abschluss weiter `mdNumDisabled=1`, `mdNumInvalid=1` und `mdResyncAction=check P`, waehrend beide beteiligten Devices `rdevStatus=DISK_OK` und `rdevNumErrors=0` melden. Parity-Zustand separat in Unraid pruefen und keinen Parity-/Disk-Slot ohne Operator-Entscheid aendern.
@@ -1,104 +0,0 @@
# FRITZ!Box Portfreigaben - Korrektur-Vorbereitung
Status: **umgesetzt 2026-05-28**, Doku bleibt als Historie und Begruendungs-Anker.
Audit-Bezug: `docs/AUDIT_2026-05-25_TODO.md` Sprint 4 ("FRITZ!Box-Portfreigaben gegen Repo-Soll abgleichen") und `docs/NETWORK_INVENTORY.md`.
## Umsetzungs-Befund 2026-05-28
| Punkt | Entscheidung | Validierung |
|---|---|---|
| 1. `80/tcp` entfernen | **umgesetzt** | Mobilfunk-Test: `http://vault.kaleschke.info` Timeout, `https://...` weiter erreichbar. Lokal via LAN: HTTP->HTTPS-Redirect funktioniert weiter durch Traefik. |
| 2. `222/tcp` ergaenzen | **bewusst NICHT umgesetzt** | Tailscale bleibt Operator-Pfad; GitHub-Mirror deckt DR-Bootstrap ab; SSH-Brute-Force-Vektor vermieden. Repo-Soll in `NETWORK_INVENTORY.md` und `MASTER_V2.md` Sektion 10 entsprechend angepasst. |
| 3. UPnP-Selbstfreigabe `PC-192-168-178-71` | **umgesetzt** | "Selbstständige Portfreigaben fuer dieses Geraet erlauben" deaktiviert. Identifiziert als VONETS-WiFi-Bridge (Hostname `VONETS.COM`, MAC `00:17:13:2F:61:96`) — vermutlich Bridge zum SolarEdge-Wechselrichter. SolarEdge-Cloud-Sync ist outbound und benoetigt keine UPnP. |
Aktiver Endstand: ausschliesslich `443/tcp -> 192.168.178.58:443` (Traefik HTTPS) ist von WAN aus erreichbar.
## 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 |
|---|---|---|
| erledigt 2026-05-28 | `80/tcp` entfernen | umgesetzt, Mobilfunk-validiert |
| erledigt 2026-05-28 (bewusst nicht) | `222/tcp` Entscheidung | bleibt Tailscale-only; Repo-Soll entsprechend angepasst |
| erledigt 2026-05-28 | UPnP-Freigabe `PC-192-168-178-71` | UPnP-Selbstfreigabe-Recht fuer VONETS-Bridge deaktiviert |
| offen | FRITZ!OS 8.21 Update | Service-Fenster, separat geplant |
| offen | IPv6-Exposure pruefen | bei naechstem WAN-Touch mit erfassen |
@@ -1,41 +0,0 @@
# Aktuelle Restliste - KalliLab CORE
Stand: 2026-05-17
Diese Datei ersetzt die alte Sprint-Liste vom 2026-05-16. Die damaligen Backup-, Posture-, Logging- und Hardening-Bloecke sind weitgehend erledigt oder dokumentiert. Sie bleibt nur als kurze Restliste fuer die naechsten bewussten Arbeitspakete bestehen.
## Erledigt / nicht mehr offen
- Filebrowser-Hardening: breiter Appdata-Mount ist entfernt; Filebrowser mountet nur noch Documents, Photos, Projekte und eigenen App-State.
- Authelia Argon2id-Haertung: `iterations: 3`, `memory: 65536`, `parallelism: 4`, `key_length: 32`, `salt_length: 16` sind gesetzt.
- Gitea Webhook-Allowlist: `GITEA__webhook__ALLOWED_HOST_LIST` ist auf `komodo-core,localhost,127.0.0.1,192.168.178.0/24` eingeschraenkt.
- Backup-Konsistenz: SQLite-/Nextcloud-Dumps, Borg-Scope fuer Nextcloud-Daten, Restore-Matrix und Freshness-Checks sind umgesetzt.
- Posture-/Cert-/Drift-Checks: Skripte und Unraid User Scripts sind vorhanden und geplant.
- Monitoring-Zielstack: `monitoring/` buendelt Prometheus, Loki, Promtail, Grafana, node-exporter, cAdvisor und InfluxDB 3 Core im Repo-Zielzustand.
- Docker-Log-Rotation: Unraid-native Rotation ist dokumentiert; keine separate `/etc/docker/daemon.json` setzen.
- Disk1-NTFS-Migration Phase 2: am 2026-05-25 abgeschlossen; Disk1 ist XFS, `posture-check` akzeptiert NTFS nicht mehr als Standard.
## Morgen / bewusst spaeter
- Monitoring live finalisieren:
- Secrets `monitoring_grafana_admin_password.txt`, `monitoring_grafana_influxdb_token.txt`, `influxdb3_admin_token.json` auf dem Host pruefen/anlegen
- `monitoring` in Komodo deployen
- alte Stacks `ops/loki` und `ops/grafana-influxdb` nach erfolgreichem Smoke-Test stoppen
- `https://monitoring.kaleschke.info`, Prometheus Targets, Loki-Logs und InfluxDB-Datasource pruefen
- Home Assistant -> InfluxDB finalisieren:
- HA-Token/Writer final pruefen
- erste Messwerte in InfluxDB verifizieren
- Grafana-HA-/Wetter-Dashboard in `monitoring-grafana` aufbauen
- Hermes VM-Seite:
- Runner-VM, echte `.env`, SSH-Key und Dashboard/Gateway final zusammenfuehren
- NAS-Stack erst starten, wenn VM-Seite bereit ist
## Verbleibende bekannte Warnings
- `ddns-updater`, `glances`, `scrutiny`: nutzen noch `latest...@sha256`; spaeter durch konkrete Versionstags ersetzen, sofern upstream sinnvoll versioniert.
- `ops/grafana-influxdb` und `ops/loki`: bleiben nur noch als Rollback-/Migrationsreferenz im Repo, nach Live-Migration nicht parallel betreiben.
- `scrutiny`: bleibt `privileged: true`; dokumentierte SMART-Ausnahme, spaeter erneut pruefen.
## Regel
Neue Arbeit erst starten, wenn klar ist, ob sie eines der drei Morgen-Themen betrifft oder eine der bekannten Warnings bewusst abbaut.
@@ -1,89 +0,0 @@
# Recovery Handoff - KalliLab CORE - 2026-05-15
Zweck: Startpunkt fuer einen neuen Chat, ohne das komplette Repo erneut zu lesen.
## Kontext
- Incident: NTFS-Cache-Vorfall ab 2026-05-11.
- Host: Unraid `Kallilabcore`, SSH `root@192.168.178.58`.
- Root Cause: Cache war NTFS/ntfs3; Disk1 ist noch NTFS/ntfs3 und wird spaeter separat migriert.
- Recovery-Prinzip: `docs/STORAGE_LAYOUT.md` ist bindend. Zum Zeitpunkt dieses Handoffs hiess die Datei noch `docs/STORAGE_LAYOUT.draft.md`.
- Keine Stacks starten, wenn ein Pfad/Setting gegen Storage Layout, Restore Matrix oder Architecture Master verstoesst.
## Host-Zustand
- Cache wurde erfolgreich von NTFS auf XFS neu formatiert.
- Verifiziert: `/mnt/cache` ist XFS auf `/dev/nvme0n1p1`.
- Disk1 bleibt vorerst NTFS auf `/mnt/disk1`; Migration ist Phase 2 nach stabilem Cache-Betrieb.
- Docker und Libvirt wurden nach dem Format wieder gestoppt.
- `/mnt/user/appdata` ist leer bzw. nur Basisverzeichnis; produktive Appdaten sind noch nicht restored.
- Share-Settings wurden nach Storage Layout korrigiert:
- `appdata`, `system`, `domains`: cache `only`
- `services`, `documents`, `photos`, `backups`, `media`, `finance`, `projekte`: cache `no`, include `disk1`
- `isos`: cache `yes`
- Backup alter Share-Configs: `/boot/config/shares.bak-20260515-pre-storage-layout`
## Image und Backups
- Full NVMe image liegt auf Windows `H:\kallilab-recovery\2026-05-14\nvme0n1-full-20260514.img`.
- `dd` exit code war `0`; Image-Groesse/Padding geprueft; Source-Raw-Hash war fertig.
- Image-Data-Hash wurde aus Zeitgruenden bewusst abgebrochen. Risiko wurde als ca. 1-3 Prozent eingeschaetzt.
- Hetzner-Borg-Archiv `Taegliche-Sicherung-2026-05-10T04:30:52.050` wurde als lesbare Recovery-Quelle verifiziert.
- Verifiziert wurden u. a. Vaultwarden SQLite, Gitea SQLite, Postgres-Dumps und Komodo Mongo-Archiv-Header.
- Lokaler Verify-Auszug liegt unter `H:\kallilab-recovery\2026-05-14\borg-verify-may10`.
## Entscheidungen seit dem Cache-Rebuild
- WD MyBookLive Duo wird komplett aus dem Setup entfernt.
- Backrest wird komplett aus dem aktiven Setup entfernt.
- Borg ist alleinige Backup-Technologie.
- Appdata Backup Plugin bleibt deaktiviert; WD-Ziele wurden aus aktiver Host-Konfiguration geleert.
- Unassigned Devices SMB-Remote fuer `//MYBOOKLIVEDUO/Public` wurde aus aktiver Host-Konfiguration entfernt.
- Backrest User Script `check_backrest_hetzner` wurde aus Schedule/Cron entfernt.
- Host-Konfig-Backup fuer diese Bereinigung: `/boot/config/cleanup-backup-20260515-remove-wd-backrest`
## Repo-Aenderungen im aktuellen Arbeitsbaum
Backrest wurde aus dem aktiven Zielbild entfernt:
- `ops/backrest/docker-compose.yml` geloescht
- `HOMELAB_ARCHITECTURE_MASTER_V2.md` aktualisiert
- `docs/REPO_MAP.md` aktualisiert
- `docs/SERVICE_CATALOG.md` aktualisiert
- `docs/RESTORE_MATRIX.md` aktualisiert
- `docs/AI_CONTEXT.md` aktualisiert
- `docs/DISASTER_RECOVERY.md` aktualisiert
- `ops/borg-ui/BACKUP_SCOPE.md` aktualisiert
- `ops/hermes-agent/services.json` aktualisiert
- `ops/hermes-agent/services.yaml` aktualisiert
- `ops/policy-checks/last-report.md` aktualisiert
Verifikation:
- `rg "/mnt/(cache|disk1|disks|remotes)" -g docker-compose.yml -g compose.yaml -g *.yml -g *.yaml` findet keine aktiven Compose/YAML-Treffer.
- `rg "ops/backrest|backrest.kaleschke|/mnt/user/appdata/backrest|192.168.178.86|MYBOOKLIVEDUO|WD-DUO"` findet nur historische/gewollte Hinweise.
- `python -m json.tool ops/hermes-agent/services.json` ok.
- `ops/hermes-agent/services.yaml` YAML ok.
- `ops/policy-checks/check_repo.ps1` ok: 29 Compose-Dateien, 0 Critical, 4 Warnings.
## Wichtigste Stop-Regeln
- Keine Container starten, solange Core-Pfade oder Share-Settings nicht gegen Storage Layout geprueft sind.
- Keine Backrest-/WD-Referenzen reaktivieren.
- Keine Bind-Mounts auf `/mnt/cache`, `/mnt/disk1`, `/mnt/disks`, `/mnt/remotes`.
- Keine Schreibaktionen auf Disk1 ausser bewusst noetig; Disk1 ist noch NTFS.
- Komodo nur gemeinsam und explizit anfassen.
- Erst Daten/Secrets restoren, dann Stacks einzeln starten und smoke-testen.
## Naechster sinnvoller Schritt
1. Repo-Aenderungen kurz reviewen und committen/pushen, bevor Komodo wieder produktiv wird.
2. DNS-Basis wiederherstellen:
- AdGuard: `/mnt/user/appdata/adguard/conf` aus Borg oder Image restoren; `work` kann frisch sein.
- Unbound: `/mnt/user/appdata/unbound/config` aus Borg oder Image restoren.
- Danach nur AdGuard + Unbound starten und DNS testen.
3. Danach Traefik + Authelia + Gitea/Vaultwarden in kleinen Schritten.
## Startprompt fuer neuen Chat
Lies zuerst `docs/archive/2026-05/RECOVERY_HANDOFF_2026-05-15.md`, dann `docs/STORAGE_LAYOUT.md`, `docs/RESTORE_MATRIX.md` und nur die Compose-Dateien des naechsten betroffenen Stacks. Fuehre den KalliLab-CORE-Restore token-sparend fort. Nichts erfinden, keine Container starten, wenn etwas gegen Storage Layout verstoesst. Backrest und WD MyBookLive Duo sind entfernt und duerfen nicht wieder ins Setup.
@@ -1,522 +0,0 @@
# Strategische Bewertung KalliLab CORE
> **Status (Stand 2026-05-30): Historischer Snapshot vom 2026-05-23, inhaltlich grossteils ueberholt.**
>
> Dieses Dokument bleibt im Repo als Audit-Anker und als "wo standen wir am 2026-05-23". Die konkreten Befunde, Top-5-Listen und Mehrwert-Fahrplaene sind durch den Audit-Zyklus 2026-05-25 zu einem grossen Teil **abgearbeitet, bewusst nicht umgesetzt oder explizit geparkt**.
>
> - **Nicht als TODO-Liste lesen.** Aktuelle Arbeitsliste: `docs/AUDIT_2026-05-25_TODO.md`.
> - **Originaltext nicht aendern.** Statt Inline-Annotationen steht der pro-Punkt-Status in einer Tabelle am Ende des Dokuments (Abschnitt "Status-Anhang 2026-05-30").
> - **Schulnote 2- gilt nicht mehr.** Mit den Konsolidierungen seit 2026-05-25 sind die meisten Notenabzieher behoben; eine neue Note wuerde hier eher bei 1- bis 2 landen, ist aber kein Selbstzweck.
Stand: 2026-05-23
Bewertet von: externer Blick auf den Repo-Sollzustand
> Diese Bewertung ist bewusst kein Sicherheits- oder Konfigurations-Audit, sondern eine ganzheitliche Einordnung: was das Setup heute leistet, wo es stark ist, wo es zu komplex ist, und wo der nächste echte Mehrwert liegt.
---
## Vorbemerkung und Methode
Bewertet wurde der Repo-Stand auf `master`, nicht der Live-Zustand auf dem Host. Grundlage waren `HOMELAB_ARCHITECTURE_MASTER_V2.md`, `docs/WORKFLOW.md`, `docs/SERVICE_CATALOG.md`, `docs/REPO_MAP.md`, `docs/DISASTER_RECOVERY.md`, `docs/RESTORE_MATRIX.md`, `docs/SECRETS_MAP.md`, `docs/AI_CONTEXT.md`, `docs/GITOPS_DRIFT_RUNBOOK.md`, `docs/HOME_ASSISTANT_INFLUXDB_ECOWITT.md`, `docs/ALERTING_MAP.md`, `ops/borg-ui/BACKUP_SCOPE.md`, `ops/policy-checks/last-report.md`, `ops/restore-tests/schedule.md`, repräsentative Compose-Dateien (Paperless, Monitoring) sowie die Memory-Notiz zum Post-Restore-Sprint.
Ehrliche Einschätzung in einem Satz: Das ist kein Bastel-Homelab mehr. Das ist eine kleine private Plattform mit dokumentationsbasierter Disziplin, die ein paar Lasten mitschleppt, die man jetzt bewusst loswerden sollte, bevor noch mehr dazukommt.
---
## 1. Architektur und Grundidee
Der Aufbau ist nicht gewachsen, er ist gestaltet. Das sieht man sofort an drei Stellen:
Erstens trennt das Repo Verantwortlichkeiten konsequent über die Top-Level-Ordner `core/`, `security/`, `infra/`, `apps/`, `ops/`, `host-services/`, `monitoring/`, `traefik/` und `services/`. Das ist keine willkürliche Kosmetik, das spiegelt die Tier-Hierarchie aus DR und Restore wider. Ein neuer Dienst weiß durch das Einordnungsschema in `HOMELAB_ARCHITECTURE_MASTER_V2.md` Abschnitt 6 sofort, wo er hin gehört und in welche Netze er kommt. Das ist überdurchschnittlich.
Zweitens ist das Netzmodell schlicht und gleichzeitig diszipliniert: `frontend_net` für alles mit Web-UI oder Internetbedarf, `backend_net` `internal: true` für DB/Redis, und app-interne Netze (`mealie_internal`, `immich_default`, `nextcloud_internal`, `monitoring_net`) für Stack-Isolation. Es gibt keine Kunstnetze wie `admin_net` oder `media_net` aus reiner Symmetriesucht. Das ist genau die Linie, die viele Homelabs verfehlen, weil sie entweder alles in ein Bridge-Netz werfen oder fünfzehn semantische Netze erfinden, ohne dass sie was tun.
Drittens ist die Source-of-Truth-Hierarchie explizit (Gitea Online → lokaler Clone → Komodo → Docker → Host) und es gibt ein Drift-Runbook (`docs/GITOPS_DRIFT_RUNBOOK.md`) mit echter Messreihenfolge. Das schlägt 95% der "Selfhoster mit Portainer und Glück".
**Wo es trotzdem hakt:** Die Trennung Monitoring/Spielerei ist noch nicht sauber. Es gibt `ops/grafana-influxdb` (Altstand), `ops/loki` (Altstand) und `monitoring/` (Zielstack) gleichzeitig im Repo. Solange die Migration nicht abgeschlossen und die Altstände nicht entfernt sind, ist das echte Doppelpflege-Risiko — und genau so entstehen die Bugs, die man nachher zwei Wochen sucht. Die Doku sagt klar "nicht parallel betreiben", aber das Repo macht es trotzdem möglich. Das ist eine offene Baustelle, kein Architekturproblem.
**Note für diesen Block: 1-2.**
---
## 2. Nutzen und Mehrwert
Hier wird es ehrlicher: Das Setup hat sehr viel Substanz, aber auch klare Spielereien.
Echter Alltagsnutzen, der den Aufwand rechtfertigt:
- **Vaultwarden** als Passwort-Tresor — der Klassiker, der wirklich täglich genutzt wird.
- **Paperless-ngx** mit Scan-Inbox, Barcode/ASN-Workflow und Tika aus — das ist klassische Familien-Dokumentenverwaltung mit echtem Wert, sobald man Briefe digitalisiert. Die Barcode-Konfiguration (`PAPERLESS_CONSUMER_BARCODE_DPI=600`, `PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE=1`) zeigt, dass der Workflow durchdacht ist.
- **Immich** für Fotos, mit `family_archive`-Mount — das ersetzt sinnvoll Google Photos für eine Familie.
- **Nextcloud** für Dateifreigabe und WebDAV/CardDAV — wenn das wirklich genutzt wird, ist es ein echtes Google-Drive/iCloud-Replacement.
- **Mealie** für Rezepte — nett, aber das ist ein Lebensmittel-Ding, nicht Infrastruktur.
- **Mail-Archiver** — sehr persönlicher Mehrwert, wenn IMAP-Archive aus GMX/Gmail langfristig durchsuchbar bleiben sollen.
- **ntfy** als Push-Backbone für `homelab-alerts` und `homelab-info` — operativ unverzichtbar.
- **Gitea** — primär als GitOps-Quelle. Für andere Projekte ein Bonus.
- **AdGuard Home + Unbound** — ja, das hat Alltagsnutzen für alle Geräte im LAN.
- **Tailscale** — Remote-Zugang ohne Port-Freigabe nach außen, klar wertvoll.
Spielerei oder Overengineering, das ehrlich auf den Prüfstand gehört:
- **Plex zusätzlich zu Jellyfin**: Beide sind Medienserver mit derselben Bibliothek (`/mnt/user/media`, `/mnt/user/photos`). Plex bringt zwar Remote-Streaming und bessere Clients, Jellyfin bringt native Open-Source-Auth und keine Lizenz. Einen davon kann man weglassen — die ehrlichste Antwort ist, das nach 30 Tagen Nutzungs-Tracking zu entscheiden.
- **Glance + Homepage + Komodo-UI als drei parallele Dashboards**: Homepage als Startseite, Glance als zweites Dashboard mit Widgets, Komodo als Stack-Sicht. Hier ist mindestens eines redundant. Glance ist erst seit kurzem live und wirkt eher wie "weil cool" als "weil nötig".
- **paperless-gpt**: Coole Idee (LLM für Tagging), aber wenn das nicht aktiv genutzt wird, ist es nur ein Container, der idle Ressourcen frisst. Frage an dich: Wann hast du das letzte Mal eine GPT-Vorschlags-Tag-Liste angenommen?
- **BentoPDF**: Ist als "vorbereitet" markiert, Fachabnahme offen. Wenn du in zwei Monaten noch keine PDFs verarbeitet hast: weglassen.
- **Hermes-Agent**: Das ist die eindeutigste Spielerei. Ein LLM-Agent über SSH-Runner zu einer separaten VM, mit eigenem Dashboard, dessen NAS-Seite bewusst deaktiviert ist, weil die VM-Seite "offen" ist. Komplexes Modell C, abhängig von einer dedizierten Linux-VM, mit Provider-Keys und Dashboard-Domain. Das ist klassisches Nerd-Lieblingsprojekt-ohne-klaren-Alltagsnutzen-Symptom. Solange du nicht ehrlich beschreiben kannst, was Hermes für dich täglich tut, ist es Reifegrad "Experiment".
- **Speedtest-Tracker**: Nett für Monitoring der ISP-Qualität, aber ein einziger Speedtest-Container für eine private Leitung ist eher "ich messe gerne" als "ich brauche das wirklich".
- **code-server**: Web-IDE im Browser. Sinnvoll, wenn du wirklich vom iPad aus arbeitest. Sonst: VSCode lokal reicht.
Use Cases, die echten Mehrwert hätten und fehlen:
- **Finanzen**: Im DR-Doc steht `/mnt/user/finance` als Share, aber kein App-Stack. Firefly III oder Actual Budget würden hier sofort spürbaren Alltagsnutzen liefern — Konten konsolidieren, Budgets verfolgen, Steuer-Vorbereitung.
- **Familien-SSO**: Du hast Authelia, aber Authelia ist primär für Admin-UIs konfiguriert. Wenn deine Familie Nextcloud, Immich, Mealie und Vaultwarden mit einem Login nutzen könnte (Authelia OIDC-Provider), wäre das ein echter Mehrwert für andere als dich.
- **Smartphone-Foto-Auto-Backup**: Immich kann das nativ. Wenn das nicht eingerichtet ist und alle Familien-Smartphones automatisch in `immich` landen würden, wäre das die Killer-App für deine Frau und Kinder, nicht für dich.
- **Tagliche Familien-Übersicht auf einem Wandtablet**: Homepage oder Glance auf einem alten Tablet im Flur, mit Kalender (Nextcloud), Wetter (Ecowitt), und ntfy-Notifications.
- **Kalender/Aufgaben/CardDAV-Nutzung**: Nextcloud kann das, aber ich sehe in der Doku keinen Hinweis, dass die Familie das tatsächlich nutzt. Wenn nicht: Migration weg von Google Calendar/iCloud wäre ein echter Souveränitäts-Gewinn.
**Note für diesen Block: 2-3.** Die infrastrukturelle Substanz ist top, aber der Anteil "Container läuft, weil ich ihn ausprobieren wollte" ist höher als nötig.
---
## 3. Best Practices
Was richtig gut ist gemessen an dem, was professionelle Setups machen würden:
- **Image-Pinning mit Tag und Digest** für Stateful Services (Postgres 17.9, Redis 7.4-alpine, Mongo 7.0.32, alle mit `@sha256:...`). Das machen die wenigsten Homelabs. Echt vorbildlich.
- **Secrets via Docker `_FILE`-Mounts oder Komodo Stack ENV, niemals im Git** — und das ist konsequent durchgezogen, inklusive `.gitignore` für `.env`-Dateien und expliziter Doku in `docs/SECRETS_MAP.md`.
- **`no-new-privileges:true`** als Standard, mit dokumentierten Ausnahmen für Scrutiny (SMART) und Glances (Host-Observability) statt versteckter Lockerungen.
- **Policy-as-Code light** über `ops/policy-checks/check_repo.ps1` — der letzte Report zeigt 0 Critical und 4 dokumentierte Warnings. Das ist Tooling-Disziplin, die viele Firmen nicht haben.
- **Restore-Tests mit Schedule** (`ops/restore-tests/schedule.md`): wöchentliche Freshness-Checks, monatliche Mini-Restores für Vaultwarden und Gitea, alle zwei Monate Paperless. Erfolg ist explizit als "Smoke-Test passt", nicht "Container startet" definiert. Das ist seltene Reife.
- **Pre-Backup-Dumps statt rohe Live-DB-Verzeichnisse als primärer Restore-Pfad** — das ist die Lehre, die viele erst nach dem ersten kaputten Restore lernen.
- **Posture-Check + Docker-Critical-Events → ntfy** als Live-Alarmierung, bereits mit Wiederholungsschutz (`ALERT_REPEAT_SECONDS=86400`) und Dedup. Das ist Operations-Reife.
- **Cloudflare DNS Challenge für ACME** statt HTTP-01, ermöglicht Wildcard-Zertifikate und keine Port-80-Abhängigkeit für Erneuerung.
- **GitOps mit Webhook-Pflicht für neue Stacks** (`docs/WORKFLOW.md`, Abschnitt "Pflicht bei neuen Komodo-Stacks"). Das verhindert "deployed-once-then-forgotten"-Stacks.
Was Standard wäre und du sinnvoll davon abweichst:
- **Komodo bewusst ohne pauschale ForwardAuth-Middleware** — richtig, weil Webhooks, API und Periphery sonst brechen. Die meisten würden hier blind Authelia davor schalten und dann zwei Tage debuggen.
- **Authelia ohne Redis-Session-Backend** — bewusste Vereinfachung. Du bezahlst dafür mit Re-Login nach Authelia-Restart, gewinnst dafür weniger Tier-1-Abhängigkeiten. Vertretbarer Trade-off.
- **Traefik dynamic config als manuelle Host-Sync-Ausnahme** — pragmatisch dokumentiert, statt eines komplexen Auto-Sync-Workarounds.
Wo du gefährlich von Best Practice abweichst:
- **Externer Repo-Mirror als DR-Voraussetzung ist offenes TODO** (`docs/DISASTER_RECOVERY.md`, Abschnitt 11). Wenn Gitea ausfällt — und Gitea hängt auch noch an Traefik und PostgreSQL — kannst du Komodo nicht aus Git deployen, kannst die Repo-Doku nicht lesen, und je nach Schaden hast du den lokalen Clone als einzigen Pfad. Das ist ein echter Single Point of Failure. Ein Push-Mirror nach GitHub/GitLab (privat) oder zumindest ein versionierter Sync nach BorgBase würde das in 30 Minuten lösen.
- **Unraid USB-Flash-Backup ist offenes TODO**. Wenn der USB-Stick stirbt, ist das nicht das Ende der Welt (Daten leben), aber es kostet einen vollen Wiederaufsetzungs-Tag. Unraid hat dafür einen eingebauten Backup-Mechanismus.
- **Komodo Self-Stack Drift Mai 2026**: Du hattest schon einen Vorfall, wo Komodo selbst nicht mehr sauber managebar war ("Recovery-ENV als Tier-1-Secret-Material"). Das Bootstrap-Problem — Komodo verwaltet Komodo — ist nicht gelöst, nur dokumentiert. Eine echte Lösung wäre: Komodo-Self-Stack explizit aus Komodo herauslassen und nur als `docker compose`-Script in `services/` halten.
- **Kein Fail2Ban / CrowdSec vor Traefik**. Vaultwarden und Nextcloud sind im Internet erreichbar mit eigener Auth. Die meisten Anti-Brute-Force-Maßnahmen liegen in den Apps selbst, nicht auf Layer 7. Bei einer ernsten Bot-Welle würde Authelia die Last tragen, ohne IP-Bans auszusprechen. CrowdSec als Bouncer für Traefik wäre eine sinnvolle Härtung mit überschaubarem Aufwand.
**Note für diesen Block: 2.**
---
## 4. Nerd-Level / Advanced Homelab
Was sehr erfahrene Selfhoster mit so einem Repo zusätzlich machen würden:
- **Renovate Bot oder ein vergleichbares Image-Update-Tracking**: Du pinnst Digests, was richtig ist, aber damit hast du dich auch in die manuelle Update-Pflicht begeben. Renovate gegen Gitea würde wöchentliche PRs für neue Patch-Versionen auf master öffnen, die du mergen oder ignorieren kannst. Das ist deutlich besser als "irgendwann manuell den Digest aktualisieren".
- **Staging-Path**: Aktuell hast du master und das ist gleichzeitig produktiv. Ein zweiter Branch (`staging`) der gegen einen zweiten Komodo-Server (in einer Tailscale-VM oder einem zweiten Unraid-Share) deployed, würde Risiko-Aenderungen testbar machen. Das ist viel Aufwand für ein Homelab, aber wenn dich die Stabilität ernsthaft kümmert, ist es der nächste Reifegrad.
- **OIDC-Provider statt nur ForwardAuth**: Authelia kann OIDC. Wenn Nextcloud, Immich, Grafana, Komodo (theoretisch), Vaultwarden (via OIDC-Bridge) per SSO laufen, ist das die "echte" Konsolidierung. Heute hast du ForwardAuth für Admin-Dienste, aber Apps mit eigener Auth (Nextcloud, Immich, Jellyfin) sind Eigeninseln.
- **Restore-Tests automatisiert in CI**: Du hast Skripts und Cron-Slots, aber kein CI gegen das Repo, das die Restore-Test-Skripte syntaktisch und semantisch prüft. Ein Gitea Actions oder Drone-Setup auf dem Host könnte das gegen jeden Commit laufen lassen.
- **Backup-Test-Härtung: Restore in eine echte Test-Domain mit Traefik-Route hinter Authelia** (heute bewusst ohne Domain — siehe `docs/RESTORE_MATRIX.md`). Das ist eine bewusste Entscheidung, würde aber einen "End-to-End restore drill" möglich machen, der einmal pro Quartal komplett durchläuft.
- **Disk1 NTFS → XFS Phase 2**: Im Repo dokumentiert, im posture-check temporär toleriert mit `ALLOW_DISK1_NTFS=1`. Das ist die offensichtlichste offene Hardening-Baustelle.
- **Loki-Retention und Log-Volume mal anschauen**: 30 Tage Retention ist gut, aber im aktuellen Stand wirst du irgendwann Storage-Probleme bekommen, wenn du nicht weißt, wie viel der Stack pro Tag produziert.
Was sie bewusst weglassen würden:
- **Hermes-Agent**: Genau dieses "ich baue mir einen Agenten der über SSH meine VM bedient und ein Dashboard hat" ist das, wovor erfahrene Leute nach dem dritten Homelab warnen. Es bringt Komplexität, Wartungslast und keine messbare Reduktion deiner manuellen Arbeit. Wenn Hermes nicht in den nächsten 60 Tagen produktiv und unverzichtbar wird: entfernen.
- **Drei Dashboard-Tools**: Sie würden eines wählen (vermutlich Homepage), die anderen rauswerfen.
- **Zwei Medienserver**: Plex und Jellyfin parallel ist Tool-Sammlerei.
- **Eigenes paperless-gpt-Container** wenn nicht aktiv im Workflow: lieber das LLM ein-zwei mal manuell auf eine PDF werfen als einen Container 24/7 idle laufen lassen.
**Note für diesen Block: 2-3.** Sehr nahe am nächsten Reifegrad, aber an drei, vier Stellen würde erfahrene Hand jetzt entrümpeln statt erweitern.
---
## 5. Betrieb und Wartbarkeit
Hier ist die Bewertung am eindeutigsten positiv. Dieses Setup ist langfristig wartbar.
Die Dokumentation ist auf einem Niveau, das ich selten sehe. `docs/SERVICE_CATALOG.md` ist ein vollständiger Dienst-Katalog mit Restore-Quelle, Smoke-Test und Besonderheiten pro Dienst. `docs/REPO_MAP.md` ist eine technische Landkarte. `docs/RESTORE_MATRIX.md` ist nicht nur "wo ist das Backup", sondern "was ist die führende Quelle", "welche Dumps", "welche Secrets müssen vor Start da sein", "was ist der Smoke-Test". Das ist Doku, die in sechs Monaten noch funktioniert.
Der Workflow ist klar (`docs/WORKFLOW.md`): Fetch → Pull → ändern → Commit → Push → Komodo. Es gibt eine explizite Stop-Regel ("wenn zwei Reparaturversuche nicht funktionieren, Pflichtmatrix ausfüllen"), die viele Selfhoster nicht haben und stattdessen in Mut-Spiralen rutschen.
Drei Stellen, wo Wartbarkeit gefährdet ist:
- **Hermes-Agent**: Spätestens nach sechs Monaten ohne aktive Pflege verstehst du die Model-C-Architektur nicht mehr ohne `ops/hermes-agent/README.md` zu lesen — und dann ist die Frage, warum überhaupt.
- **Doppelter Monitoring-Stack (`ops/grafana-influxdb`, `ops/loki`, `monitoring/`)**: Solange beide Welten im Repo sind, vergisst du in einem halben Jahr, welche live ist. Die Migration muss abgeschlossen und die Altstände müssen entfernt werden.
- **Authelia Repo-Baseline vs. Host-Config**: Du dokumentierst selbst, dass die Repo-`configuration.yml` "manuell auf den Host gemerged" werden muss, mit OIDC und Secrets hostseitig. Das ist Drift-Risiko per Design. Ein zweiter Mechanismus (z. B. das manuelle Pendant zum Traefik-dynamic-Sync) oder mindestens ein expliziter Diff-Check vor jeder Auth-Änderung wäre Pflicht.
**Note für diesen Block: 1-2.**
---
## 6. Sicherheit und Zugriff
Du hast eine durchdachte Schichtung:
- Authelia für Admin-UIs (`uptime`, `borg`, `files`, `code`, `grafana`, `monitoring`, `pdf`, `glance`, `glances`, `scrutiny`, `paperless-gpt`, `speedtest`, `hermes`, `traefik`-Dashboard, `homepage`).
- Native App-Auth für User-Apps (`vaultwarden`, `nextcloud`, `immich`, `jellyfin`, `paperless`).
- Komodo mit eigener Auth ohne ForwardAuth (bewusste Ausnahme).
- Tailscale für Admin-Zugriff von außen.
Was wirklich gut ist:
- Authelia mit Argon2id, `iterations=3`, `memory=65536`, `parallelism=4`, `key_length=32`, `salt_length=16` — das ist solide Konfiguration, nicht Default-Müll.
- Secrets durchgängig per File-Mount oder Komodo Stack ENV, nie im Compose im Klartext.
- Gitea Webhook-Allowlist (`GITEA__webhook__ALLOWED_HOST_LIST=komodo-core,localhost,127.0.0.1,192.168.178.0/24`) und Registrierung deaktiviert — das schließt Webhook-SSRF-Vektoren.
- `cloudflare_dns_api_token` als Docker Secret, nicht als ENV.
Wo du härter trennen solltest:
- **AdGuard Admin-Port 8082 ist direkt am LAN gebunden ohne Authelia**. Das ist im Architekturdokument als offenes TODO ("Block F") markiert. Im Home-LAN ist das verschmerzbar, aber wenn du eines Tages einen Gast im WLAN hast oder ein IoT-Gerät kompromittiert wird, ist das ein direkter Pfad in die DNS-Konfiguration.
- **Nextcloud läuft ohne ForwardAuth** (bewusst wegen WebDAV/CardDAV). Wenn deine Familie schwache Passwörter setzt, ist Nextcloud im Internet das primäre Angriffsziel. Nextcloud-eigene Maßnahmen (Brute-Force-Protection, 2FA-Pflicht für Admin) sollten dokumentiert aktiv sein.
- **2FA-Pflicht in Authelia**: In der Doku nicht klar erwähnt. Wenn 2FA nur "optional" ist, läuft die Härtung ins Leere.
Wo Komfort wichtiger ist und das sinnvoll so bleibt:
- Komodo ohne ForwardAuth — richtig.
- Authelia ohne Redis-Session-Backend — vertretbar.
- Plex/Jellyfin mit nativer Auth — sinnvoll, weil die Clients eigene Auth machen.
**Note für diesen Block: 2.**
---
## 7. Backup und Disaster Recovery
Das ist eine der stärksten Säulen.
Was du richtig machst:
- Borg statt Backrest — dokumentierte Entscheidung, eine Backup-Technologie statt zwei.
- **Pre-Backup-Dumps als kanonische Restore-Quelle**, nicht rohe Live-DB-Verzeichnisse. Explizit dokumentiert in `ops/borg-ui/BACKUP_SCOPE.md` ("Do not back up raw live database storage directories as the primary recovery artifact").
- **Restore-Tests mit Schedule** und expliziter Erfolgsregel ("Container läuft reicht nicht — Smoke-Test muss greifen").
- Dump-Skript für SQLite-Container (Gitea, Vaultwarden, Uptime-Kuma, Speedtest-Tracker), BoltDB-Snapshot für Filebrowser, `pg_dump` für die einzelnen Postgres-Datenbanken, `mongodump` für Komodo-Mongo.
- Borg-Scope erweitert um `/mnt/user/services` für GitOps-Recovery (Repo, Stack-Workspaces, Posture-Check-State).
- Tier-Modell in `docs/RESTORE_MATRIX.md` mit klarer Reihenfolge.
- Dokumentierte Restore-Lab-Praxis: Testpfad `/mnt/user/backups/restore-lab/<dienst>`, Reports unter `/mnt/user/backups/restore-reports`, ohne Traefik-Route — keine Vermischung von Test und Produktion.
Was wirklich offen ist:
- **Externer Backup-Mirror oder Off-Site-Ziel**: BorgBase ist erwähnt (`ops/borg-ui` als Borg-UI auf BorgBase-Repo), aber die Frage "was passiert, wenn der Unraid-Host und BorgBase gleichzeitig down sind" hat keine dokumentierte Antwort. Zwei Repos (z. B. BorgBase + ein zweites lokales NAS oder ein Hetzner Storage Box) wären die Standardlösung.
- **Externer Repo-Mirror als DR-Voraussetzung** — in DR.md als TODO markiert. Wenn Gitea nicht aufsteht, ist das Repo nur über deinen lokalen Clone erreichbar.
- **Unraid USB-Flash-Backup** — in DR.md als TODO markiert.
- **Borg-Passphrase extern sicher hinterlegt** — als TODO markiert. Das ist die typische "wenn das Haus brennt"-Frage. Vaultwarden hilft dir nicht, wenn Vaultwarden gerade restauriert werden soll. Eine zweite Kopie der Passphrase (verschlüsselt auf einem USB-Stick im Bankschließfach, oder bei einem Familienmitglied) ist Standard.
- **Komodo-Mongo Dump nach Major-Upgrades verifizieren** — als Watchpoint dokumentiert, aber nicht im automatischen Restore-Test-Cron.
Restore-Tests sind monatlich für Vaultwarden und Gitea, alle zwei Monate für Paperless, "später" für Immich. Das ist gut, aber Immich-Restore-Tests sind die kritischsten, weil dort die größten Datenmengen liegen und ein silent corruption am schmerzhaftesten wäre.
**Note für diesen Block: 1-2.** Wenn die offenen DR-Vorbereitungs-TODOs abgehakt wären, klare 1.
---
## 8. Monitoring und Transparenz
Das ist der Bereich mit dem größten Übergang. Du hast viele Tools, mit Überschneidungen, und der zentrale Monitoring-Stack ist im Aufbau.
Was du heute hast:
- **Uptime-Kuma**: HTTP/TCP-Uptime-Checks mit Web-UI.
- **Glances**: Live-System-Sicht (CPU/RAM/Disk pro Host).
- **Scrutiny**: SMART-Monitoring für Laufwerke.
- **Speedtest-Tracker**: Periodische Speedtests gegen den ISP.
- **Glance**: Status-Dashboard mit Widgets (Immich, AdGuard, Speedtest, Docker-Container).
- **Homepage**: Start-Dashboard mit Service-Cards.
- **Posture-Check**: Host-Filesystem, NVMe-SMART, Mover, Füllstand → ntfy.
- **Docker-Critical-Events**: `die`/`oom`/`kill` → ntfy.
- **`monitoring/`-Zielstack**: Prometheus, Alertmanager, ntfy-Bridge, Blackbox, Loki, Promtail, Grafana, node-exporter, cAdvisor, InfluxDB 3 Core.
Was richtig gut ist:
- **Ein zentraler Alert-Pfad**: alle problemrelevanten Meldungen landen auf `homelab-alerts` per ntfy. Das ist die wichtigste Disziplin und du hast sie. `docs/ALERTING_MAP.md` listet alle Sender.
- Prometheus-Stack mit Alertmanager + Bridge zu ntfy, also nicht "Grafana sendet Email" sondern "Alertmanager-Pflicht-Pfad".
- Blackbox-Exporter ersetzt mittelfristig Uptime-Kuma (richtige Strategie).
- 30 Tage Retention für Prometheus und Loki — sinnvoll für Diagnose-Daten, kein Backup-Surrogat.
- node-exporter + cAdvisor + Traefik-Metrics → wirklich vollständige Infrastruktur-Telemetrie.
Was fehlt oder zu viel ist:
- **Doppelte Beobachtungs-Tools nebeneinander**: Uptime-Kuma vs. Blackbox, Glances vs. node-exporter+cAdvisor, Glance vs. Homepage. Du weißt das, die Migration ist im Gang. Bis sie fertig ist, gibt es Verwirrung darüber, welches die "Wahrheit" ist.
- **Smoke-Test-Dashboards**: In `monitoring/grafana/dashboards/` sind ein paar Dashboards, aber die "Family-View" — "alles grün, alles erreichbar, Backup heute Nacht durchgelaufen" — fehlt als explizites Dashboard. Das wäre der Wert für dich selbst: morgens kurz draufschauen und wissen, ob etwas die Aufmerksamkeit braucht.
- **Alert-Regeln explizit listen**: `monitoring/prometheus/alerts.yml` existiert, aber eine kurze Doku, welche Regeln wann feuern (Disk > 90%, Borg älter 36h, Cert läuft in 14 Tagen ab, etc.), würde die Operations-Reife komplettieren.
- **Cert-Token-Check** läuft (laut ALERTING_MAP) — gut, das ist die einzige sinnvolle Methode, "TLS-Cert läuft ab" früh genug zu sehen.
**Note für diesen Block: 3.** Bricht ab, sobald die Monitoring-Migration sauber abgeschlossen ist und die Altstände entfernt sind — dann eine 2.
---
## 9. Konkreter Mehrwert-Fahrplan
### Quick Wins (≤ eine Woche, hoher Nutzen)
- **Externer Push-Mirror für das Repo nach GitHub privat** einrichten. Das ist ein Webhook in Gitea + ein leerer GitHub-Privat-Repo. 30 Minuten. Löst das wichtigste DR-Risiko.
- **Borg-Passphrase auf einen USB-Stick im Bankschließfach** oder in eine versiegelte Umschlag-Box. Eine analoge Sicherung gegen das digitale Worst-Case-Szenario.
- **Plex oder Jellyfin entscheiden**: einen davon weg. 14 Tage Nutzungs-Tracking via Server-Logs oder einfach beobachten, wer welchen Client öffnet. Dann den ungenutzten Stack archivieren.
- **Glance ODER Homepage** als einziges Dashboard wählen. Heute laufen beide. Es gibt keinen technischen Grund für zwei.
- **Authelia 2FA-Pflicht für alle aktiven User** verifizieren — wenn nicht gesetzt, jetzt setzen.
- **Disk1 NTFS → XFS Phase 2 abschließen** — das ist im Repo dokumentiert und im posture-check als Übergangsausnahme markiert. Loswerden.
### Phase 1 (zwei bis vier Wochen, Stabilität und Ordnung)
- **Monitoring-Migration abschließen und Altstände entfernen**: `monitoring/` produktiv, dann `ops/grafana-influxdb` und `ops/loki` aus dem Repo löschen (mit Backup-Branch fürs Gewissen).
- **Uptime-Kuma ablösen durch Blackbox + Grafana-Alerts**: nach den sieben Tagen Parallelbetrieb, die in `docs/SERVICE_CATALOG.md` als Pflicht stehen.
- **Hermes-Agent ehrliche Entscheidung**: produktiv machen mit klarem Alltagsnutzen, oder entfernen. Kein "halb da, halb deaktiviert"-Zustand für ein weiteres Quartal.
- **paperless-gpt und BentoPDF Status**: gleiche Frage. Produktiv oder weg.
- **Unraid USB-Flash-Backup** einrichten (Unraid hat einen eingebauten Mechanismus).
- **Ein Family-View-Dashboard in Grafana** bauen: alles-grün-Übersicht für den Morgen-Check.
### Phase 2 (vier bis zwölf Wochen, Automatisierung und Transparenz)
- **Authelia OIDC-Provider aktivieren** und Nextcloud, Immich, Grafana als OIDC-Clients konfigurieren. Echtes SSO für die Familie.
- **Renovate Bot gegen Gitea** für kontrollierte Image-Updates (PRs statt manuelle Digest-Pflege).
- **Restore-Test für Immich** als eigener Sprint einplanen — der größte Datentopf und der einzige Tier-2-Dienst ohne Mini-Restore-Test.
- **Familie onboarden**: Smartphone-Auto-Backup zu Immich für alle Familien-Geräte. Das ist der Schritt vom "ich betreibe Container" zum "meine Familie benutzt aktiv was Selbstgebautes".
- **CrowdSec vor Traefik** als Bouncer für die öffentlich erreichbaren Apps (Vaultwarden, Nextcloud, Immich, Gitea).
### Phase 3 (drei bis sechs Monate, Advanced Nerd-Level)
- **Staging-Branch + zweites Komodo-Ziel** in einer Tailscale-VM, für Risiko-Änderungen.
- **Restore-Test-Automatisierung als CI** (Gitea Actions oder Drone).
- **Off-Site-Backup zu einem zweiten Ziel** (zweites BorgBase-Repo, Hetzner Storage Box, oder zweites NAS bei einem Familienmitglied).
- **Cold-Standby-Konzept** dokumentieren: was passiert, wenn der Unraid-Host stirbt und du erst in zwei Wochen Ersatz hast?
- **Komodo-Self-Stack rausnehmen** und als handgepflegten `docker compose`-Service in `services/` halten — löst das Bootstrap-Problem.
### Phase 4 (Spielwiese, nice-to-have)
- Firefly III oder Actual Budget für Finanz-Übersicht.
- Wandtablet-Setup im Flur mit Family-Dashboard.
- Smart-Home-Automatisierungen über Home Assistant tiefer mit ntfy verzahnen (Frost-Warnung, PV-Überschuss-Hinweis, Briefkasten-Sensor).
- Ein eigenes kleines Dashboard für Ecowitt-Wetterdaten (sobald die Pipeline aus `docs/HOME_ASSISTANT_INFLUXDB_ECOWITT.md` läuft).
---
## Schulnote, Top-5-Listen, klare Empfehlung
### Schulnote
**2- (gut bis befriedigend, eher 2).**
Auf der Skala "durchschnittliches Homelab" wäre das eine 1. Die strukturelle Disziplin, GitOps-Konsequenz, Doku-Qualität, Backup-Reife und Architektur-Klarheit liegen weit über dem, was die meisten Selfhoster jemals erreichen. Was die Note von einer 1 runterzieht: drei bis fünf offene Baustellen, die nicht trivial sind (externer Repo-Mirror, Monitoring-Migration unfertig, Hermes-Agent im Schwebezustand, zwei Medienserver parallel, AdGuard Admin-Port ohne ForwardAuth). Wenn du Phase 1 abschließt, bist du klar bei einer 1.
### Top 5 sofort verbessern
1. **Externer Repo-Mirror** (GitHub privat, BorgBase, oder zweites Gitea). 30 Minuten Aufwand, schließt das wichtigste DR-Loch.
2. **Borg-Passphrase analog außerhalb des Systems sichern** (Bankschließfach, Familienmitglied, Tresor).
3. **Plex oder Jellyfin entscheiden**, einen davon entfernen.
4. **Glance oder Homepage** als einziges Dashboard wählen.
5. **AdGuard Admin-Port hinter Authelia** oder mindestens nur via Tailscale erreichbar (heute LAN-direkt).
### Top 5 mit dem größten zusätzlichen Mehrwert
1. **Smartphone-Auto-Backup zu Immich für die ganze Familie**: macht aus deinem Foto-Server eine echte Killer-App für andere als dich.
2. **Authelia OIDC-Provider aktivieren** und Nextcloud + Immich + Grafana per SSO: ein Login für alle wichtigen Apps.
3. **Renovate Bot gegen Gitea**: automatisierte Update-PRs für deine Digest-pinnten Images.
4. **Family-View-Dashboard in Grafana**: morgens 30 Sekunden draufschauen statt Tools-Tour.
5. **Finanz-App (Firefly III oder Actual Budget)**: füllt den `/mnt/user/finance`-Share mit echtem Alltagsnutzen.
### Top 5 lieber NICHT machen
1. **Hermes-Agent ausbauen statt loswerden**. Wenn du in 60 Tagen nicht ehrlich sagen kannst, was Hermes dir täglich abnimmt: weg damit. Komplexität ohne Gegenwert ist das größte Anti-Pattern in jedem Homelab.
2. **Noch mehr Dashboards einbauen**. Du hast bereits Homepage, Glance, Komodo-UI und kommst gleich noch mit Grafana-Family-View. Mehr wäre Sammlerei.
3. **Pauschale Authelia-ForwardAuth vor Komodo** schalten. Dokumentierte Ausnahme aus gutem Grund. Webhooks, API und Periphery würden brechen.
4. **Backend_net auf `external: true` statt `internal: true`** umstellen, weil "ist ja einfacher". Genau das ist die Mauer, die viele Apps vor öffentlichem Zugriff schützt.
5. **Komodo Self-Stack komplett über Komodo managen lassen**. Du hattest schon einen Drift-Vorfall. Komodo verwaltet Komodo ist ein Bootstrap-Problem ohne befriedigende Lösung.
### Klare Empfehlung
**Vereinfachen und konsolidieren, NICHT weiter ausbauen.**
Du bist an einem Punkt, an dem das Setup mehr Substanz hat, als aktiv genutzt wird. Die nächsten sechs Monate sollten weniger neue Dienste sehen und mehr Entrümpelung (Plex vs. Jellyfin, Glance vs. Homepage, Hermes-Entscheidung, Monitoring-Altstände raus, paperless-gpt/BentoPDF-Entscheidung), mehr Familien-Aktivierung (Immich-Smartphone-Backup, OIDC-SSO, Family-Dashboard), und mehr DR-Resilienz (externer Mirror, Off-Site-Backup-Ziel, Borg-Passphrase analog gesichert).
Wenn du das in den nächsten drei Monaten machst, hast du eine private Plattform mit echtem Alltagsnutzen, klarer Wartbarkeit, und einer Wiederherstellbarkeit, die seriöser ist als das, was viele Mittelstandsfirmen für ihre Office-IT haben. Das ist das Ziel, nicht "noch ein Container".
Wenn du dann weiterausbauen willst, bist du in der Position, das aus einer Stärke heraus zu tun, nicht aus dem "ich muss noch das hier probieren"-Reflex.
---
## Status-Anhang 2026-05-30
Dieser Anhang ist nicht Teil der Originalbewertung vom 2026-05-23. Er ordnet jedem konkret handelbaren Befund den tatsaechlichen Stand nach den Audit-Sprints zu, damit das Dokument selbststaendig lesbar bleibt.
### Block 1 - Architektur
| Originalbefund | Stand 2026-05-30 |
|---|---|
| ops/grafana-influxdb + ops/loki + monitoring/ parallel im Repo | **erledigt** 2026-05-26: Altstaende entfernt, monitoring/ einziger aktiver Observability-Stack |
### Block 2 - Nutzen und Mehrwert
| Originalbefund | Stand 2026-05-30 |
|---|---|
| Plex zusaetzlich zu Jellyfin | **erledigt** 2026-05-25: Jellyfin entfernt, Plex bleibt einziger Medienserver |
| Glance + Homepage + Komodo-UI als drei parallele Dashboards | **erledigt** 2026-05-25: Homepage entfernt, Glance bleibt einziges Dashboard |
| paperless-gpt — produktiv oder weg? | **entschieden** 2026-05-28: behalten bis Paperless-NGX 3.0 native KI-Features, dann neu bewerten |
| BentoPDF — produktiv oder weg? | **entschieden** 2026-05-28: behalten als situatives Tool (~4 MB RAM-Footprint) |
| Hermes-Agent als Spielerei, Review-Deadline gesetzt | **geparkt** mit Review-Deadline 2026-07-25 |
| Speedtest-Tracker als "ich messe gerne" | unveraendert, keine Operator-Entscheidung getroffen |
| code-server — sinnvoll oder weg? | unveraendert, keine Operator-Entscheidung getroffen |
| Finanz-App (Firefly III / Actual Budget) als fehlender Mehrwert | offen, nice-to-have ohne aktiven Termin |
| Familien-SSO ueber Authelia OIDC | **geparkt** im Auth-Block (F-13) |
| Smartphone-Auto-Backup zu Immich | offen, Anwendungsentscheidung pro Familienmitglied |
| Wandtablet im Flur mit Family-Dashboard | offen, Spielwiese |
| Kalender/Aufgaben/CardDAV-Nutzung dokumentieren | offen, Operator-Frage |
### Block 3 - Best Practices
| Originalbefund | Stand 2026-05-30 |
|---|---|
| Externer Repo-Mirror als DR-Voraussetzung offen | **erledigt**: GitHub-Push-Mirror `michaelkaleschke-spec/homelab-infra` aktiv |
| Unraid USB-Flash-Backup offen | **erledigt** 2026-05-25: `unraid-flash-config.tar.gz` im Borg-Scope |
| Komodo Self-Stack Drift Mai 2026 nur dokumentiert, nicht geloest | **teilweise erledigt** 2026-05-29/30: Trockenlauf-Skript + erfolgreicher Erstlauf belegen `ops/komodo/docker-compose.yml` als Recovery-Anker; Self-Stack-Entkopplung selbst bleibt offen |
| Kein Fail2Ban/CrowdSec vor Traefik | **geparkt** im Auth-Block (F-14) |
| Renovate Bot fehlt | **erledigt** 2026-05-29: live, erster Lauf erfolgreich, 5 PRs in Gitea |
### Block 4 - Nerd-Level
| Originalbefund | Stand 2026-05-30 |
|---|---|
| Renovate Bot oder vergleichbares Update-Tracking | **erledigt** 2026-05-29 |
| Staging-Path mit zweitem Komodo-Ziel | offen, Phase 3 nice-to-have |
| OIDC-Provider statt nur ForwardAuth | **geparkt** im Auth-Block (F-13) |
| Restore-Tests automatisiert in CI (Gitea Actions / Drone) | offen, Phase 3 |
| End-to-End restore drill mit Test-Domain hinter Traefik | offen, bewusst nicht (Test-Lab bleibt ohne Domain) |
| Disk1 NTFS -> XFS Phase 2 | **erledigt** (`ALLOW_DISK1_NTFS=0` als Default im posture-check, XFS-Erwartung aktiv) |
| Loki-Retention und Log-Volume bewerten | offen, Detail-Sweep |
| Hermes-Agent loswerden | **geparkt** mit Review 2026-07-25 |
| Drei Dashboard-Tools auf eines reduzieren | **erledigt**: Glance bleibt als einziges |
| Zwei Medienserver | **erledigt**: Jellyfin entfernt |
### Block 5 - Betrieb und Wartbarkeit
| Originalbefund | Stand 2026-05-30 |
|---|---|
| Doppelter Monitoring-Stack als Wartbarkeits-Risiko | **erledigt** 2026-05-26 |
| Authelia Repo-Baseline vs. Host-Config Drift "by design" | **erledigt** 2026-05-30 (F-10): `services/authelia-diff.sh` + Posture-Check ueberwacht ACL-Drift automatisch, WORKFLOW.md hat eigene Pflicht-Sektion |
| Hermes-Agent verstaendnis-kritisch nach 6 Monaten | **geparkt** mit Review 2026-07-25 |
### Block 6 - Sicherheit und Zugriff
| Originalbefund | Stand 2026-05-30 |
|---|---|
| AdGuard Admin-Port 8082 LAN-direkt ohne Authelia | **erledigt** 2026-05-26: auf Tailscale-IP `100.80.98.33:8082` gebunden, LAN-Zugriff blockiert |
| Nextcloud ohne ForwardAuth, Brute-Force-Doku offen | **geparkt** im Auth-Block (F-18) |
| Authelia 2FA-Pflicht nicht klar dokumentiert | **geparkt** im Auth-Block (F-04) |
### Block 7 - Backup und Disaster Recovery
| Originalbefund | Stand 2026-05-30 |
|---|---|
| Externer Backup-Mirror / zweites Off-Site-Ziel | **entschieden** 2026-05-28: kein zweites Off-Site; 3-2-1 mit Live + lokalem Borg + Hetzner + H:/-Nearline erfuellt; Hetzner-Haertungen als Folge-TODOs |
| Externer Repo-Mirror | **erledigt**: GitHub-Push-Mirror aktiv |
| Unraid USB-Flash-Backup | **erledigt** 2026-05-25 |
| Borg-Passphrase analog gesichert | **erledigt** 2026-05-26: Operator bestaetigt, offline gesichert |
| Komodo-Mongo Dump-Verifikation nach Major-Upgrades | offen, Watchpoint dokumentiert, nicht im automatischen Cron |
| Restore-Tests Immich (groesster Datentopf ohne Mini-Restore) | **erledigt** 2026-05-27: erster Host-Lauf erfolgreich (`SUCCESS`, 11977 Assets) |
### Block 8 - Monitoring und Transparenz
| Originalbefund | Stand 2026-05-30 |
|---|---|
| Doppelte Tools (Uptime-Kuma vs. Blackbox, Glance vs. Homepage) | **erledigt** 2026-05-25: Uptime-Kuma und Homepage entfernt |
| Family-View Dashboard fehlt als Morgens-Check | **Spec da, JSON offen**: `docs/FAMILY_VIEW_DASHBOARD.md` definiert Layout/Queries/Thresholds; JSON wird gebaut, sobald Metriken 7+ Tage stabil sind |
| Alert-Regeln explizit listen | **teilweise**: `monitoring/prometheus/alerts.yml` enthaelt Regeln (Borg-Stale, Cert-Expiry, Container-Down), `docs/ALERTING_MAP.md` mappt Sender — eine Doku-Zusammenfassung "welche Regel feuert wann" ist noch nicht zentralisiert |
### Block 9 - Konkreter Mehrwert-Fahrplan
#### Quick Wins (≤ 1 Woche)
| Original-Punkt | Stand 2026-05-30 |
|---|---|
| Externer Push-Mirror GitHub privat | **erledigt** |
| Borg-Passphrase analog sichern | **erledigt** 2026-05-26 |
| Plex oder Jellyfin entscheiden | **erledigt** 2026-05-25: Jellyfin weg |
| Glance oder Homepage waehlen | **erledigt** 2026-05-25: Homepage weg |
| Authelia 2FA-Pflicht aktivieren | **geparkt** (F-04) |
| Disk1 NTFS -> XFS Phase 2 | **erledigt** |
| AdGuard Admin Tailscale-only | **erledigt** 2026-05-26 |
#### Phase 1 (2-4 Wochen)
| Original-Punkt | Stand 2026-05-30 |
|---|---|
| Monitoring-Migration abschliessen, Altstaende entfernen | **erledigt** 2026-05-26 |
| Uptime-Kuma abloesen durch Blackbox + Grafana | **erledigt** 2026-05-25 |
| Hermes-Agent Entscheidung | **geparkt** mit Review 2026-07-25 |
| paperless-gpt / BentoPDF Entscheidung | **entschieden** 2026-05-28: beide behalten mit Begruendung |
| Unraid USB-Flash-Backup | **erledigt** 2026-05-25 |
| Family-View-Dashboard | Spec da, JSON wartet |
#### Phase 2 (4-12 Wochen)
| Original-Punkt | Stand 2026-05-30 |
|---|---|
| Authelia OIDC fuer Nextcloud/Immich/Grafana | **geparkt** (F-13) |
| Renovate Bot gegen Gitea | **erledigt** 2026-05-29 |
| Restore-Test fuer Immich | **erledigt** 2026-05-27 |
| Familien-Smartphone-Auto-Backup zu Immich | offen, Operator-Anwendungsentscheidung |
| CrowdSec vor Traefik | **geparkt** (F-14) |
#### Phase 3 (3-6 Monate)
| Original-Punkt | Stand 2026-05-30 |
|---|---|
| Staging-Branch + zweites Komodo-Ziel | offen |
| Restore-Test-Automatisierung als CI | offen |
| Off-Site-Backup zu zweitem Ziel | **entschieden** 2026-05-28: bewusst nicht |
| Cold-Standby-Konzept dokumentieren | offen |
| Komodo-Self-Stack rausnehmen | teilweise erledigt: Bootstrap-Anker und Trockenlauf-Skript da, Entkopplung selbst noch nicht |
#### Phase 4 (Spielwiese)
| Original-Punkt | Stand 2026-05-30 |
|---|---|
| Firefly III / Actual Budget | offen |
| Wandtablet mit Family-Dashboard | offen |
| Home Assistant + ntfy enger verzahnen | offen |
| Ecowitt-Wetter-Dashboard | offen |
### Top-5-Listen vom 2026-05-23
#### Top 5 sofort verbessern
| Original-Top-5 | Stand 2026-05-30 |
|---|---|
| 1. Externer Repo-Mirror | **erledigt** |
| 2. Borg-Passphrase analog sichern | **erledigt** |
| 3. Plex oder Jellyfin entscheiden | **erledigt** |
| 4. Glance oder Homepage waehlen | **erledigt** |
| 5. AdGuard Admin-Port haerten | **erledigt** |
**Alle 5 erledigt.**
#### Top 5 mit groesstem zusaetzlichen Mehrwert
| Original-Top-5 | Stand 2026-05-30 |
|---|---|
| 1. Smartphone-Auto-Backup zu Immich | offen, Anwendungsentscheidung |
| 2. Authelia OIDC fuer SSO | **geparkt** |
| 3. Renovate Bot gegen Gitea | **erledigt** |
| 4. Family-View-Dashboard | Spec da, JSON wartet |
| 5. Finanz-App | offen |
#### Top 5 lieber NICHT machen
| Original-Anti-Top-5 | Stand 2026-05-30 |
|---|---|
| 1. Hermes-Agent ausbauen statt loswerden | gehalten — Agent geparkt mit Review, nicht ausgebaut |
| 2. Noch mehr Dashboards einbauen | gehalten — Homepage entfernt, Glance bleibt einziges |
| 3. Pauschale Authelia vor Komodo | gehalten — Komodo bleibt ohne ForwardAuth |
| 4. backend_net auf external statt internal | gehalten — backend_net bleibt internal |
| 5. Komodo Self-Stack komplett via Komodo | teilweise gehalten — Trockenlauf-Skript als Gegenmaszahme, vollstaendige Entkopplung offen |
### Zusammenfassung des Status-Anhangs
- **Top 5 sofort**: 5/5 erledigt.
- **Quick Wins (7)**: 6 erledigt, 1 geparkt.
- **Phase 1 (6)**: 4 erledigt, 1 geparkt, 1 wartend.
- **Phase 2 (5)**: 2 erledigt, 2 geparkt, 1 offen.
- **Phase 3 (5)**: 1 entschieden (nicht umgesetzt), 1 teilweise, 3 offen.
- **Phase 4 (Spielwiese)**: alle offen, bewusst niedrige Prioritaet.
- **Auth-Block (F-04/13/14/18)**: vollstaendig geparkt nach Operator-Entscheidung 2026-05-26, gebuendelte Bearbeitung ausserhalb des aktuellen Zyklus.
Wer hier weiterarbeiten will, schaut auf `docs/AUDIT_2026-05-25_TODO.md` — dort ist der operative Stand gepflegt.
-15
View File
@@ -1,15 +0,0 @@
# Documentation Archive
Dieses Archiv enthaelt historische Dokumente, die nicht mehr als aktive Betriebsquelle dienen.
Regel:
- Datierte Audits, Chat-Handoffs, Codex-Prompts und erledigte Aktionsplaene werden unter `YYYY-MM/` abgelegt.
- Aktuelle Runbooks, Inventare, Restore-Pfade und offene Arbeitslisten bleiben in `docs/`.
- Archivierte Dateien duerfen weiterhin verlinkt werden, sollen aber nicht als Startpunkt fuer neue Arbeit dienen.
Aktueller Archivbereich:
| Pfad | Inhalt |
|---|---|
| `2026-05/` | Mai-2026-Audits, Zwischenstaende, Prompt-Vorlagen und abgeschlossene Plaene |
+3 -3
View File
@@ -2,11 +2,11 @@
Status: **Vorbereitung**. Echte `mem_limit`-Werte werden erst gesetzt, wenn mindestens 7 Tage realer Peak-Werte vorliegen. Status: **Vorbereitung**. Echte `mem_limit`-Werte werden erst gesetzt, wenn mindestens 7 Tage realer Peak-Werte vorliegen.
Bezug: `docs/archive/2026-05/AUDIT_2026-05-25.md` F-19 "Keine Container-Memory-Limits". Bezug: Mai-2026-Audit F-19 "Keine Container-Memory-Limits".
## Warum nicht heute ## Warum nicht heute
Audit-TODO 2026-05-30: F-19 ist nicht akut. Im `docs/MIGRATION_LOG.md` ist **kein einziger** OOM-/Memory-Vorfall dokumentiert. `services/posture-check/docker-critical-events.sh` ueberwacht `die`/`oom`/`kill`-Events und alarmiert via ntfy der Detektions-Pfad ist da, der Daten-Befund fehlt. Limits ohne Peak-Daten zu setzen bedeutet entweder zu eng (Flapping) oder so weit weg vom Realwert, dass die Schutzwirkung gegen Null geht. Audit-TODO 2026-05-30: F-19 ist nicht akut. Es gibt keinen dokumentierten OOM-/Memory-Vorfall. `services/posture-check/docker-critical-events.sh` ueberwacht `die`/`oom`/`kill`-Events und alarmiert via ntfy; der Detektions-Pfad ist da, der Daten-Befund fehlt. Limits ohne Peak-Daten zu setzen bedeutet entweder zu eng (Flapping) oder so weit weg vom Realwert, dass die Schutzwirkung gegen Null geht.
Familien-Einladung verschiebt die Risiko-Bilanz nach oben: Ein OOM in Authelia/Postgres bei Familien-Nutzung kostet Vertrauen, nicht nur Operator-Zeit. Sobald die Einladung raus ist, wird F-19 ein "should" statt "nice". Familien-Einladung verschiebt die Risiko-Bilanz nach oben: Ein OOM in Authelia/Postgres bei Familien-Nutzung kostet Vertrauen, nicht nur Operator-Zeit. Sobald die Einladung raus ist, wird F-19 ein "should" statt "nice".
@@ -90,7 +90,7 @@ Tier-2 (Immich, Nextcloud, Paperless, Mealie, Mail-Archiver) bewusst spaeter, nu
## Stop-Regel ## Stop-Regel
Falls in Phase 3 ein Container nach Limit-Setzung haeufiger restartet als vor dem Limit: Limit raus, kein zweiter Versuch ohne dazwischenliegende Peak-Reanalyse. Doku-Eintrag in `docs/MIGRATION_LOG.md`, F-19 weiter offen. Falls in Phase 3 ein Container nach Limit-Setzung haeufiger restartet als vor dem Limit: Limit raus, kein zweiter Versuch ohne dazwischenliegende Peak-Reanalyse. F-19 bleibt dann offen.
## Was nicht ins Skript gehoert ## Was nicht ins Skript gehoert
+1 -1
View File
@@ -2,7 +2,7 @@
## Status ## Status
Skript und Test-Compose sind vorbereitet. **Erstlauf 2026-05-27 erfolgreich** (`SUCCESS`, HTTP `200`, `11977` Assets im Test-DB-Check). Report: `/mnt/user/backups/restore-reports/immich-2026-05-27.md`. Folgelaeufe je Quartal gemaess `docs/RESTORE_DRILL_ROUTINE.md` (Q2 = Immich). Skript und Test-Compose sind vorbereitet. **Erstlauf 2026-05-27 erfolgreich** (`SUCCESS`, HTTP `200`, `11977` Assets im Test-DB-Check). Report: `/mnt/user/backups/restore-reports/immich-2026-05-27.md`. Folgelaeufe je Quartal gemaess `ops/restore-tests/schedule.md` (Q2 = Immich).
Vor dem ersten Lauf muss Operator entscheiden: Vor dem ersten Lauf muss Operator entscheiden:
+1 -1
View File
@@ -84,5 +84,5 @@ Optional spaeter:
## Folgeschritte ## Folgeschritte
- Quartals-Belegung: Komodo-Bootstrap passt zum DR-Sanity-Check (`docs/RESTORE_DRILL_ROUTINE.md` Q2/Q4) und kann ohne Borg-Archiv jederzeit wiederholt werden. - Quartals-Belegung: Komodo-Bootstrap passt zum DR-Sanity-Check (`ops/restore-tests/schedule.md` Q2/Q4) und kann ohne Borg-Archiv jederzeit wiederholt werden.
- Optional fuer kuenftige Laeufe: echtes Restore aus `komodo-mongo.archive.gz` in die Test-Mongo, danach Schreiben einer Wegwerf-Resource ueber die API. - Optional fuer kuenftige Laeufe: echtes Restore aus `komodo-mongo.archive.gz` in die Test-Mongo, danach Schreiben einer Wegwerf-Resource ueber die API.
@@ -2,7 +2,7 @@
## Status ## Status
Skript und Test-Compose sind vorbereitet. **Erstlauf 2026-05-30 erfolgreich** (`SUCCESS`, alle 5 Checks gruen, Komodo Core HTTP `200`). Report: `/mnt/user/backups/restore-reports/komodo-bootstrap-2026-05-30.md`. Folgelaeufe quartalsweise empfohlen als Teil des DR-Sanity-Checks (`docs/RESTORE_DRILL_ROUTINE.md`). Skript und Test-Compose sind vorbereitet. **Erstlauf 2026-05-30 erfolgreich** (`SUCCESS`, alle 5 Checks gruen, Komodo Core HTTP `200`). Report: `/mnt/user/backups/restore-reports/komodo-bootstrap-2026-05-30.md`. Folgelaeufe quartalsweise empfohlen als Teil des DR-Sanity-Checks (`ops/restore-tests/schedule.md`).
## Vorbedingungen ## Vorbedingungen
@@ -84,7 +84,7 @@ Produktive Komodo-Container, Mongo-Datadir und `KOMODO_*`-Secrets werden niemals
## Schedule ## Schedule
Aktuell nicht im automatischen Schedule. Empfohlen als Teil des quartalsweisen DR-Sanity-Check (`docs/RESTORE_DRILL_ROUTINE.md`). Aktuell nicht im automatischen Schedule. Empfohlen als Teil des quartalsweisen DR-Sanity-Check (`ops/restore-tests/schedule.md`).
## Festgelegte Entscheidungen ## Festgelegte Entscheidungen
+25 -2
View File
@@ -26,7 +26,7 @@ Alle 2 Monate:
Quartalsweise: Quartalsweise:
- Restore-/DR-Sanity-Check gemaess `docs/RESTORE_DRILL_ROUTINE.md` - Restore-/DR-Sanity-Check
- `immich` Restore-Smoke-Test (DB + UI, ohne produktive Foto-Mounts; Erstlauf 2026-05-27 erfolgreich) - `immich` Restore-Smoke-Test (DB + UI, ohne produktive Foto-Mounts; Erstlauf 2026-05-27 erfolgreich)
- pruefen: - pruefen:
- Restore-Lab-Struktur - Restore-Lab-Struktur
@@ -34,7 +34,17 @@ Quartalsweise:
- Skripte und Pfade - Skripte und Pfade
- Doku noch passend - Doku noch passend
Die Quartals-Belegung (welcher Dienst, welcher Sanity-Fokus) steht in `docs/RESTORE_DRILL_ROUTINE.md` Tabelle "Quartals-Kadenz". Quartals-Belegung:
| Quartal | Mini-Restore | Sanity-Fokus |
|---|---|---|
| Q1 | `paperless` | Tier-1-Reihenfolge, Posture-Check, Borg-Frische |
| Q2 | `immich` | Komodo-Bootstrap, Gitea-Bundles, Secrets-Inventur |
| Q3 | `mealie` oder `nextcloud` | DNS-Pfad und Cert-Expiry-Sicht |
| Q4 | `vaultwarden` oder `gitea` | Externe Abhaengigkeiten, Hetzner, GitHub-Mirror |
Bestaetigte Mini-Restores: Vaultwarden, Gitea und Paperless am 2026-05-07;
Immich am 2026-05-27; Paperless erneut am 2026-05-31.
## Konkreter Kalender ## Konkreter Kalender
@@ -100,6 +110,19 @@ Manuell:
- Restore-Lab: `/mnt/user/backups/restore-lab` - Restore-Lab: `/mnt/user/backups/restore-lab`
- Reports: `/mnt/user/backups/restore-reports` - Reports: `/mnt/user/backups/restore-reports`
## Quartals-Sanity
Kurz pruefen:
- `docs/DISASTER_RECOVERY.md` Phase 1-5 passt noch zum Repo.
- `docs/RESTORE_MATRIX.md` Tier-Klassifizierung und letzte Restore-Erfolge stimmen.
- `docs/SECRETS_MAP.md` enthaelt die noetigen Secret-Pfade ohne Werte.
- Gitea-Bundles sind frisch und klonbar.
- GitHub-Mirror ist erreichbar und aktuell.
- ntfy-Testnachricht an `homelab-info` kommt an.
- Offline-Kopie der Borg-Passphrase ist auffindbar.
- Capacity-Stand passt zu `docs/CAPACITY_AND_LIFECYCLE.md`.
## Erfolgsregel ## Erfolgsregel
Ein Test gilt erst dann als erfolgreich, wenn: Ein Test gilt erst dann als erfolgreich, wenn:
+1 -1
View File
@@ -5,6 +5,6 @@ Diese Skripte sind bewusst versionierte Operator-Hilfen fuer den Windows-Neuaufs
- `backup-delta-after-2026-05-07.ps1` kopiert nach einem definierten Cutoff lokale Nutzdaten in ein Backup-Ziel. - `backup-delta-after-2026-05-07.ps1` kopiert nach einem definierten Cutoff lokale Nutzdaten in ein Backup-Ziel.
- `repair-disk0-boot-to-new-windows.ps1` repariert EFI/Bootdateien fuer das neue Windows auf der Intel-SSD und verlangt Adminrechte. - `repair-disk0-boot-to-new-windows.ps1` repariert EFI/Bootdateien fuer das neue Windows auf der Intel-SSD und verlangt Adminrechte.
- `cleanup-dualboot-bcd.ps1` bereinigt BCD-Bootmenueeintraege und verlangt eine explizite Textbestaetigung. - `cleanup-dualboot-bcd.ps1` bereinigt BCD-Bootmenueeintraege und verlangt eine explizite Textbestaetigung.
- `docs/windows-neuaufsetzen-masterplan.md` und `docs/postinstall-erstes-ziel-codex.md` enthalten die zugehoerigen Operator-Notizen. - `ops/windows-reinstall/docs/windows-neuaufsetzen-masterplan.md` und `ops/windows-reinstall/docs/postinstall-erstes-ziel-codex.md` enthalten die zugehoerigen Operator-Notizen.
Die Skripte enthalten keine Secrets, arbeiten aber mit lokalen Windows-Datentraegern und duerfen nur interaktiv und mit vorheriger Sichtpruefung ausgefuehrt werden. Die Skripte enthalten keine Secrets, arbeiten aber mit lokalen Windows-Datentraegern und duerfen nur interaktiv und mit vorheriger Sichtpruefung ausgefuehrt werden.