Finalize homelab audit end state

This commit is contained in:
2026-05-23 11:29:08 +02:00
parent cd650b19ac
commit 8e400fb3c3
13 changed files with 924 additions and 7 deletions
+1
View File
@@ -27,3 +27,4 @@
Thumbs.db
*.tmp
*.log
.serena/
+6 -3
View File
@@ -3,7 +3,7 @@
> **Single Source of Truth** für Docker-Netzwerkarchitektur, Sicherheitsregeln, Zielbild und Migration des Kallilabcore-Homelabs.
> **Arbeitsregel für KI-Assistenten:** Dieses Dokument immer zuerst lesen, bevor Fragen zu Containern, Netzwerken, Traefik, Tailscale, Migration oder Security beantwortet werden.
**Stand:** 2026-05-16 | **Aktueller Schwerpunkt:** GitOps / Doku-Synchronisierung / Reproduzierbare Deployments
**Stand:** 2026-05-23 | **Aktueller Schwerpunkt:** GitOps / Doku-Synchronisierung / Reproduzierbare Deployments
---
@@ -105,7 +105,7 @@ Internet
traefik (80/443)
└── frontend_net
├── öffentliche Apps (vaultwarden, mealie, paperless, immich, gitea, ntfy, mail-archiver, nextcloud)
├── öffentliche Apps (vaultwarden, mealie, paperless, immich, gitea, ntfy, mail-archiver, nextcloud, jellyfin)
├── geschützte UIs mit Middleware (homepage, glance, paperless-gpt, uptime-kuma, filebrowser, scrutiny, code-server, borg-ui, glances, speedtest, bentopdf, grafana)
├── Admin-UI mit nativer Auth (komodo)
└── Dienste mit Internetbedarf ohne öffentliche UI (ddns-updater)
@@ -149,6 +149,7 @@ Diese Dienste sind über echte `*.kaleschke.info`-Domains erreichbar:
- `gitea` (Web) — git.kaleschke.info
- `immich_server` — immich.kaleschke.info
- `nextcloud` — cloud.kaleschke.info
- `jellyfin` — jellyfin.kaleschke.info
### 4.2 Nicht öffentlich / nur Tailscale oder Traefik + Middleware
Diese Dienste sind **keine Public Apps**:
@@ -278,6 +279,8 @@ Legende Status:
| `immich_server` | ✅ | `immich_default`, `frontend_net` | Traefik | aktiv via `immich.kaleschke.info` | — |
| `immich_machine_learning` | ✅ | `immich_default` | intern | bleibt intern | — |
| `nextcloud` | ✅ | `frontend_net`, `nextcloud_internal` | Traefik | aktiv via `cloud.kaleschke.info`, nativer Nextcloud-Login, WebDAV/CardDAV faehig | CalDAV/CardDAV-Redirect via Traefik-Labels |
| `jellyfin` | ✅ | `frontend_net` | Traefik | aktiv via `jellyfin.kaleschke.info`, native Jellyfin-Auth, Media-/Photos-Mounts read-only | — |
| `plex` | ✅ | `host` | Plex native / Host-Netz | Compose-Stack unter `host-services/plex/`; Host-Netz bleibt fuer Discovery / Plex GDM dokumentierte Ausnahme | — |
### 7.5 Admin / Operations
@@ -311,7 +314,7 @@ Legende Status:
| Container | Status | Ziel |
|---|---|---|
| `Plex-Media-Server` | ⏳ Dockerman | Compose-Migration, `host`-Netz bleibt (Discovery) |
| — | — | Plex ist nicht mehr offen: der Dienst ist als Repo-Compose-Stack unter `host-services/plex/` dokumentiert; `host`-Netz bleibt als Discovery-Ausnahme. |
### 7.8 Entfernte Container
+369
View File
@@ -0,0 +1,369 @@
# 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/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/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/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/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/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.
+22
View File
@@ -0,0 +1,22 @@
# Homelab Audit Final - 2026-05-23
Stand: 2026-05-23 11:30 CEST. Ergebnis nach Push, Live-Messung, Doku-Sync und Repo-Hygiene.
| Punkt | Ampel | Beleg |
|---|---|---|
| P0 `cd650b1` nach Gitea pushen | gruen | Push `af231dd..cd650b1 master -> master`; `gitea` und `borg-ui` Workspaces auf `cd650b1`, beide `## master...origin/master`. |
| P0 Live-Daten ablegen | gruen | `docs/AUDIT_2026-05-23_LIVE.md` angelegt, keine Secret-Werte dokumentiert. |
| P1 Monitoring live / Altstaende down | gelb-gruen | `monitoring-*` Container laufen, alte `grafana`/`influxdb3-core`/`loki`/`alloy` Container sind nicht vorhanden. Grafana, Prometheus und Traefik-Route sind ok; Loki nimmt Daten an, aber `/ready` liefert 503. |
| 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 am 2026-05-23 02:30 UTC; 15 aktuelle Dump-/Archive-Artefakte von 04:00/04:01 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
- `monitoring-loki` `/ready` meldet trotz laufendem Container und aktiven Metrics-/Query-Zaehlungen `503`. Da keine Altcontainer mehr laufen und Loki Logs annimmt, wurde kein blinder Reparaturversuch gestartet. Naechster sauberer Schritt waere ein eigener Loki-Readiness-Check gegen Konfiguration, Ring/Single-Binary-Modus und Grafana-Datasource, nicht Teil dieses Endstufen-Commits.
- Host-Workspace `monitoring` enthaelt untracked `monitoring/prometheus/alerts.yml.bak-20260520-incident`. Nicht angefasst, weil es Host-/Komodo-Workspace-Drift ist und nicht im lokalen Repo liegt.
## Schlussbewertung
Das Homelab ist fuer die Audit-Restliste in der Endstufe. Es gibt keine kritischen Repo- oder GitOps-Befunde. Einzige gelbe Restkante ist Loki-Readiness als separater Monitoring-Hardening-Punkt; der produktive Stack ist deployed, alte Altstaende sind down, Backups und Dumps sind frisch.
+88
View File
@@ -0,0 +1,88 @@
# 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/AUDIT_2026-05-23.md`, `docs/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.
+32
View File
@@ -0,0 +1,32 @@
# 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/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/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/AUDIT_2026-05-23_FINAL.md` schreiben mit Ampel + konkretem Beleg pro Punkt, committen, pushen, kurz an mich melden.
+7
View File
@@ -16,6 +16,13 @@ Dieses Dokument ist nur noch ein historischer Verlauf. Der aktuelle operative Ab
## Historische Meilensteine
### 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/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.
+8 -3
View File
@@ -1,6 +1,6 @@
# Repository Map
Stand: 2026-05-04
Stand: 2026-05-23
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.
@@ -75,6 +75,7 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
| BentoPDF | `apps/bentopdf/docker-compose.yml` | `bentopdf` -> `bentopdfteam/bentopdf:2.8.4` | `pdf.kaleschke.info` | `frontend_net` | keine | Traefik + Authelia; COOP/COEP Middleware |
| Homepage | `apps/homepage/docker-compose.yml` | `homepage` -> `ghcr.io/gethomepage/homepage:v1.12.3@sha256:...` | `home.kaleschke.info` | `frontend_net` | keine | Docker-Socket read-only fuer Widgets; viele `HOMEPAGE_VAR_*` Env-Keys |
| 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` |
| Jellyfin | `apps/jellyfin/docker-compose.yml` | `jellyfin` -> `jellyfin/jellyfin:10.11.8@sha256:...` | `jellyfin.kaleschke.info` | `frontend_net` | keine | native Jellyfin-Auth; `/mnt/user/media` und `/mnt/user/photos` read-only |
| 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 |
@@ -99,6 +100,7 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
| 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`, `8082:80/tcp` | Unbound in `dns_net`; direkte Ports sind dokumentierte Ausnahme |
| 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
@@ -141,6 +143,7 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
| `hermes.kaleschke.info` | Hermes Dashboard | Traefik + Authelia |
| `home.kaleschke.info` | Homepage | Traefik + Authelia; faellt in Authelia unter die 1FA-Wildcard-Regel |
| `immich.kaleschke.info` | Immich | Traefik, native App-Auth |
| `jellyfin.kaleschke.info` | Jellyfin | 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 |
@@ -172,7 +175,7 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
| `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 historisch ausserhalb Repo |
| `host` | Host-Netz | Tailscale; Plex als Repo-Compose-Stack unter `host-services/plex/` |
## Volumes und Datenpfade
@@ -189,6 +192,8 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
| Mealie | `/mnt/user/appdata/mealie/data`, `/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/postgres`, `/mnt/user/appdata/nextcloud/redis` |
| Jellyfin | `/mnt/user/appdata/jellyfin/config`, `/mnt/user/appdata/jellyfin/cache`, `/mnt/user/media`, `/mnt/user/photos` |
| Plex | `/mnt/user/appdata/plex/config`, `/mnt/user/appdata/plex/transcode`, `/mnt/user/media`, `/mnt/user/photos` |
| Homepage | `/mnt/user/appdata/homepage`, `/mnt/user/appdata/homepage/images`, Docker socket read-only |
| ntfy | `/mnt/user/appdata/ntfy` |
| Paperless-GPT | `/mnt/user/appdata/paperless-gpt/data`, `/mnt/user/appdata/paperless-gpt/prompts` |
@@ -252,5 +257,5 @@ Das Skript liest Secret-Dateien auf dem Host und schreibt Dump-Artefakte. Bei An
- `tailscale` nutzt Host-Netz, `NET_ADMIN`, `NET_RAW` und `/dev/net/tun` als dokumentierte VPN-Ausnahme.
- `grafana`, `influxdb3-core` und `monitoring-influxdb3-core` laufen 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-Media-Server` ist als historischer Host-Sonderfall dokumentiert, aber nicht als Repo-Compose-Stack enthalten.
- `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`.
+3 -1
View File
@@ -1,6 +1,6 @@
# Service Catalog
Stand: 2026-05-04
Stand: 2026-05-23
Dieser Katalog beschreibt produktive und repo-vorbereitete Dienste aus Sicht von Betrieb, Restore und KI-Kontext. Er basiert auf dem Repo-Sollzustand. Vor produktiven Eingriffen immer den Live-Zustand in Komodo/Docker pruefen.
@@ -47,6 +47,8 @@ Secret-Werte sind nicht enthalten. Es werden nur Secret-Namen, Env-Key-Namen und
| `nextcloud` | Datei-/Cloud-Dienst | `apps/nextcloud/docker-compose.yml` | `https://cloud.kaleschke.info` | eigene PostgreSQL, eigene Redis, Traefik | `/mnt/user/appdata/nextcloud/html`, `/mnt/user/documents/nextcloud-data` | Tier 2, `nextcloud.dump` + Share | ja | native App-Auth ohne zentrale ForwardAuth; WebDAV/CardDAV beachten |
| `nextcloud-postgres` | Nextcloud-Datenbank | `apps/nextcloud/docker-compose.yml` | intern | `nextcloud_internal` | `/mnt/user/appdata/nextcloud/postgres`, `nextcloud_postgres_password.txt` | `nextcloud.dump`, raw DB nicht primaerer Restore-Weg | nein | interne DB |
| `nextcloud-redis` | Nextcloud Cache/Locking | `apps/nextcloud/docker-compose.yml` | intern | `nextcloud_internal` | `/mnt/user/appdata/nextcloud/redis` | Teil von Nextcloud-Restore | nein | interne Redis |
| `jellyfin` | Medienserver | `apps/jellyfin/docker-compose.yml` | `https://jellyfin.kaleschke.info` | Traefik, `frontend_net` | `/mnt/user/appdata/jellyfin/config`, `/mnt/user/appdata/jellyfin/cache`, `/mnt/user/media`, `/mnt/user/photos` | Tier 2, Appdata + Medienpfade im Borg-/Share-Scope | ja | native Jellyfin-Auth; Medien- und Fotomounts read-only; keine direkten Host-Ports |
| `plex` | Medienserver mit LAN-/Client-Discovery | `host-services/plex/docker-compose.yml` | Plex native / LAN / Remote je Plex-Konfiguration | Host-Netz | `/mnt/user/appdata/plex/config`, `/mnt/user/appdata/plex/transcode`, `/mnt/user/media`, `/mnt/user/photos` | Tier 2, Appdata + Medienpfade im Borg-/Share-Scope | nein | Repo-Compose-Stack; `network_mode: host` bleibt dokumentierte Discovery-Ausnahme, kein Traefik-Stack |
| `ntfy` | Push-Benachrichtigungen | `apps/ntfy/docker-compose.yml` | `https://ntfy.kaleschke.info` | Traefik, upstream mobile push | `/mnt/user/appdata/ntfy` | Tier 2 | ja | `NTFY_BEHIND_PROXY=true`; Problem-Alerts gehen gebuendelt an `homelab-alerts`, optionale Erfolgsmeldungen an `homelab-info` |
| `bentopdf` | PDF-Tooling / Ersatz fuer Stirling-PDF | `apps/bentopdf/docker-compose.yml` | `https://pdf.kaleschke.info` | Traefik + Authelia | keine kritische Persistenz im Compose | Tier 3, rebuildbar | ja + Authelia | COOP/COEP per Middleware; fachliche Abnahme/Live-Status pruefen |
+9
View File
@@ -0,0 +1,9 @@
# Windows Reinstall Helpers
Diese Skripte sind bewusst versionierte Operator-Hilfen fuer den Windows-Neuaufsetzen-/Dual-Boot-Kontext vom Mai 2026.
- `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.
- `cleanup-dualboot-bcd.ps1` bereinigt BCD-Bootmenueeintraege und verlangt eine explizite Textbestaetigung.
Die Skripte enthalten keine Secrets, arbeiten aber mit lokalen Windows-Datentraegern und duerfen nur interaktiv und mit vorheriger Sichtpruefung ausgefuehrt werden.
@@ -0,0 +1,118 @@
param(
[datetime]$Cutoff = "2026-05-07T15:30:00",
[string]$BackupRoot = "H:\Windows-Neuaufsetzen-Backup",
[string]$UserProfilePath = "C:\Users\michi"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
function Invoke-RobocopyDelta {
param(
[string]$Source,
[string]$Destination,
[string]$LogName
)
if (-not (Test-Path $Source)) {
Write-Host "SKIP missing source: $Source"
return
}
New-Item -ItemType Directory -Force -Path $Destination | Out-Null
$LogPath = Join-Path $DeltaLogDir $LogName
Write-Host "DELTA COPY $Source"
Write-Host " -> $Destination"
robocopy $Source $Destination /E /COPY:DAT /DCOPY:DAT /R:2 /W:2 /XJ /MAXAGE:$CutoffString /TEE /LOG:$LogPath
$ExitCode = $LASTEXITCODE
if ($ExitCode -gt 7) {
throw "Robocopy failed with exit code $ExitCode for source: $Source"
}
}
if (-not (Test-Path $BackupRoot)) {
throw "BackupRoot does not exist: $BackupRoot"
}
if (-not (Test-Path $UserProfilePath)) {
throw "UserProfilePath does not exist: $UserProfilePath"
}
$DeltaRoot = Join-Path $BackupRoot "_Delta_2026-05-19"
$DeltaLogDir = Join-Path $DeltaRoot "00_Logs"
New-Item -ItemType Directory -Force -Path $DeltaLogDir | Out-Null
$CutoffString = $Cutoff.ToString("yyyyMMdd")
$Manifest = [ordered]@{
CreatedAt = (Get-Date).ToString("s")
Cutoff = $Cutoff.ToString("s")
CutoffNote = "Robocopy /MAXAGE uses date granularity, so files changed on or after the cutoff day are included."
RunningSystemDrive = $env:SystemDrive
RunningWinDir = $env:windir
BackupRoot = $BackupRoot
DeltaRoot = $DeltaRoot
}
$Manifest.GetEnumerator() |
ForEach-Object { "$($_.Key)=$($_.Value)" } |
Out-File (Join-Path $DeltaRoot "delta_manifest.txt") -Encoding UTF8
$Jobs = @(
@{ Source = Join-Path $UserProfilePath "Desktop"; Destination = Join-Path $DeltaRoot "01_Desktop"; Log = "delta_current_desktop.log" },
@{ Source = Join-Path $UserProfilePath "Documents"; Destination = Join-Path $DeltaRoot "02_Dokumente"; Log = "delta_current_documents.log" },
@{ Source = Join-Path $UserProfilePath "Pictures"; Destination = Join-Path $DeltaRoot "03_Bilder"; Log = "delta_current_pictures.log" },
@{ Source = Join-Path $UserProfilePath "Videos"; Destination = Join-Path $DeltaRoot "04_Videos"; Log = "delta_current_videos.log" },
@{ Source = Join-Path $UserProfilePath "Downloads"; Destination = Join-Path $DeltaRoot "05_Downloads"; Log = "delta_current_downloads.log" },
@{ Source = Join-Path $UserProfilePath ".ssh"; Destination = Join-Path $DeltaRoot "09_Programme_Settings_Lizenzen\ssh"; Log = "delta_current_ssh.log" },
@{ Source = Join-Path $UserProfilePath "AppData\Local\Subsembly"; Destination = Join-Path $DeltaRoot "09_Programme_Settings_Lizenzen\Subsembly_Local"; Log = "delta_subsembly_local.log" },
@{ Source = Join-Path $UserProfilePath "AppData\Local\Buhl"; Destination = Join-Path $DeltaRoot "09_Programme_Settings_Lizenzen\Buhl_Local"; Log = "delta_buhl_local.log" },
@{ Source = Join-Path $UserProfilePath "AppData\Local\Buhl Data Service GmbH"; Destination = Join-Path $DeltaRoot "09_Programme_Settings_Lizenzen\Buhl_Data_Service_Local"; Log = "delta_buhl_data_service_local.log" },
@{ Source = "C:\ProgramData\Buhl Data Service GmbH"; Destination = Join-Path $DeltaRoot "09_Programme_Settings_Lizenzen\Buhl_Data_Service_ProgramData"; Log = "delta_buhl_data_service_programdata.log" },
@{ Source = "G:\Gitea_Clone"; Destination = Join-Path $DeltaRoot "06_Projekte\Gitea_Clone"; Log = "delta_g_gitea_clone.log" },
@{ Source = "G:\open-webui"; Destination = Join-Path $DeltaRoot "09_Programme_Settings_Lizenzen\open-webui"; Log = "delta_g_open_webui.log" },
@{ Source = "G:\Treiber"; Destination = Join-Path $DeltaRoot "13_Treiber_Windows\G_Treiber"; Log = "delta_g_treiber.log" },
@{ Source = "F:\BMW Leasing"; Destination = Join-Path $DeltaRoot "07_Banking_Finanzen\BMW Leasing"; Log = "delta_f_bmw_leasing.log" },
@{ Source = "F:\Marina Handy 2025"; Destination = Join-Path $DeltaRoot "03_Bilder\Marina Handy 2025"; Log = "delta_f_marina_handy_2025.log" }
)
foreach ($Job in $Jobs) {
Invoke-RobocopyDelta -Source $Job.Source -Destination $Job.Destination -LogName $Job.Log
}
$ExplicitBankingDir = Join-Path $DeltaRoot "07_Banking_Finanzen\Banking4_Datentresor_explizit"
New-Item -ItemType Directory -Force -Path $ExplicitBankingDir | Out-Null
$BankingFiles = @(
(Join-Path $UserProfilePath "Documents\Mein Datentresor.sub"),
(Join-Path $UserProfilePath "Documents\.Mein Datentresor.sub.att")
)
foreach ($File in $BankingFiles) {
if (Test-Path $File) {
Copy-Item -LiteralPath $File -Destination (Join-Path $ExplicitBankingDir (Split-Path $File -Leaf)) -Force
}
}
$SummaryRoots = Get-ChildItem $DeltaRoot -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -ne "00_Logs" }
$Summary = foreach ($Root in $SummaryRoots) {
$Files = @(Get-ChildItem $Root.FullName -Recurse -Force -File -ErrorAction SilentlyContinue)
$Measure = $Files | Measure-Object Length -Sum
[pscustomobject]@{
Path = $Root.FullName
Files = $Files.Count
SizeGB = [math]::Round(($Measure.Sum / 1GB), 4)
Newest = ($Files | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty LastWriteTime)
}
}
$Summary | Export-Csv (Join-Path $DeltaLogDir "delta_summary.csv") -NoTypeInformation -Encoding UTF8
Write-Host ""
Write-Host "Delta backup finished:"
Write-Host " $DeltaRoot"
Write-Host ""
Write-Host "Summary:"
$Summary | Format-Table -AutoSize
@@ -0,0 +1,141 @@
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$BackupDir = "H:\Windows-Neuaufsetzen-Backup\12_Exportierte_Listen"
$BeforeFile = Join-Path $BackupDir "bcdedit_enum_before_dualboot_cleanup.txt"
$AfterFile = Join-Path $BackupDir "bcdedit_enum_after_dualboot_cleanup.txt"
function Assert-Admin {
$Identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$Principal = [Security.Principal.WindowsPrincipal]::new($Identity)
if (-not $Principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
throw "Dieses Skript muss in PowerShell als Administrator ausgefuehrt werden."
}
}
function Get-BcdOsLoaderEntries {
$Text = & bcdedit /enum osloader /v
if ($LASTEXITCODE -ne 0) {
throw "bcdedit /enum osloader /v ist fehlgeschlagen."
}
$Entries = @()
$Current = $null
foreach ($Line in $Text) {
if ($Line -match "^-{5,}$") {
if ($Current -and $Current.Identifier) {
$Entries += [pscustomobject]$Current
}
$Current = @{
Identifier = $null
Device = $null
OsDevice = $null
Path = $null
Description = $null
}
continue
}
if (-not $Current) {
continue
}
if ($Line -match "^\s*(identifier|Bezeichner)\s+(.+)$") { $Current.Identifier = $Matches[2].Trim(); continue }
if ($Line -match "^\s*device\s+(.+)$") { $Current.Device = $Matches[1].Trim(); continue }
if ($Line -match "^\s*osdevice\s+(.+)$") { $Current.OsDevice = $Matches[1].Trim(); continue }
if ($Line -match "^\s*path\s+(.+)$") { $Current.Path = $Matches[1].Trim(); continue }
if ($Line -match "^\s*description\s+(.+)$") { $Current.Description = $Matches[1].Trim(); continue }
}
if ($Current -and $Current.Identifier) {
$Entries += [pscustomobject]$Current
}
return $Entries
}
Assert-Admin
New-Item -ItemType Directory -Force -Path $BackupDir | Out-Null
& bcdedit /enum all /v | Out-File $BeforeFile -Encoding UTF8
$Entries = Get-BcdOsLoaderEntries
Write-Host "Gefundene Windows-Boot-Eintraege:"
$Entries | Select-Object Identifier, Description, Device, OsDevice, Path | Format-Table -AutoSize
$NewWindows = $Entries | Where-Object {
$_.OsDevice -eq "partition=D:" -and
$_.Path -match "winload\.efi$" -and
(Test-Path "D:\Windows\System32\winload.efi")
} | Select-Object -First 1
$OldWindows = $Entries | Where-Object {
$_.OsDevice -eq "partition=C:" -and
$_.Path -match "winload\.efi$" -and
(Test-Path "C:\Windows\System32\winload.efi")
} | Select-Object -First 1
if (-not $NewWindows) {
throw "Abbruch: Neuer Windows-Eintrag auf D:\Windows wurde nicht eindeutig gefunden."
}
if (-not $OldWindows) {
throw "Abbruch: Alter Windows-Eintrag auf C:\Windows wurde nicht eindeutig gefunden."
}
$KeepIds = @($NewWindows.Identifier, $OldWindows.Identifier)
$DeleteEntries = $Entries | Where-Object { $KeepIds -notcontains $_.Identifier }
Write-Host ""
Write-Host "Bleibt erhalten:"
Write-Host " NEU: $($NewWindows.Identifier) -> $($NewWindows.OsDevice) -> D:\Windows"
Write-Host " ALT: $($OldWindows.Identifier) -> $($OldWindows.OsDevice) -> C:\Windows"
Write-Host ""
Write-Host "Wird aus dem Bootmenue entfernt:"
if ($DeleteEntries) {
$DeleteEntries | Select-Object Identifier, Description, Device, OsDevice, Path | Format-Table -AutoSize
} else {
Write-Host " Keine ueberfluessigen Eintraege gefunden."
}
Write-Host ""
Write-Host "Es werden nur Bootmenue-Eintraege geloescht, keine Partitionen und keine Windows-Ordner."
$Confirmation = Read-Host "Tippe exakt BOOTCLEAN um fortzufahren"
if ($Confirmation -ne "BOOTCLEAN") {
throw "Abbruch: Bestaetigung wurde nicht eingegeben."
}
& bcdedit /set $NewWindows.Identifier description "Windows 11 Neu"
if ($LASTEXITCODE -ne 0) { throw "Konnte neuen Windows-Eintrag nicht umbenennen." }
& bcdedit /set $OldWindows.Identifier description "Windows 11 Alt"
if ($LASTEXITCODE -ne 0) { throw "Konnte alten Windows-Eintrag nicht umbenennen." }
& bcdedit /default $NewWindows.Identifier
if ($LASTEXITCODE -ne 0) { throw "Konnte Standard-Boot-Eintrag nicht setzen." }
& bcdedit /timeout 5
if ($LASTEXITCODE -ne 0) { throw "Konnte Timeout nicht setzen." }
foreach ($Entry in $DeleteEntries) {
Write-Host "Loesche Boot-Eintrag $($Entry.Identifier) ($($Entry.Description))"
& bcdedit /delete $Entry.Identifier /f
if ($LASTEXITCODE -ne 0) {
throw "Konnte Boot-Eintrag $($Entry.Identifier) nicht loeschen."
}
}
& bcdedit /enum all /v | Out-File $AfterFile -Encoding UTF8
Write-Host ""
Write-Host "Bootmenue bereinigt."
Write-Host "Vorher gesichert: $BeforeFile"
Write-Host "Nachher gesichert: $AfterFile"
Write-Host ""
Write-Host "Bitte neu starten. Es sollten nur noch zwei Eintraege erscheinen:"
Write-Host " Windows 11 Neu"
Write-Host " Windows 11 Alt"
@@ -0,0 +1,120 @@
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$TargetDiskNumber = 0
$TargetWindowsDrive = "D"
$TargetDiskFriendlyName = "INTEL SSDSC2BW180A3L"
$TargetDiskSerialNumber = "CVCV3105053K180EGN"
$EfiSizeMB = 260
$EfiLabel = "SYSTEM"
function Assert-Admin {
$Identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$Principal = [Security.Principal.WindowsPrincipal]::new($Identity)
if (-not $Principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
throw "Dieses Skript muss in PowerShell als Administrator ausgefuehrt werden."
}
}
function Get-FreeDriveLetter {
$Used = (Get-Volume | Where-Object DriveLetter | Select-Object -ExpandProperty DriveLetter)
foreach ($Letter in "S","T","U","V","W","X","Y","Z") {
if ($Used -notcontains $Letter) {
return $Letter
}
}
throw "Kein freier Laufwerksbuchstabe fuer die EFI-Partition gefunden."
}
Assert-Admin
$Disk = Get-Disk -Number $TargetDiskNumber
$TargetWindowsPath = "$TargetWindowsDrive`:\Windows"
$TargetWinload = "$TargetWindowsDrive`:\Windows\System32\winload.efi"
Write-Host "Aktuell gestartetes Windows:"
Write-Host " SystemDrive: $env:SystemDrive"
Write-Host " windir: $env:windir"
Write-Host ""
Write-Host "Zieldatentraeger:"
$Disk | Select-Object Number, FriendlyName, SerialNumber, HealthStatus, OperationalStatus, @{Name="SizeGB";Expression={[math]::Round($_.Size/1GB,2)}}, PartitionStyle | Format-Table -AutoSize
Write-Host "Partitionen auf Datentraeger ${TargetDiskNumber}:"
Get-Partition -DiskNumber $TargetDiskNumber | Sort-Object PartitionNumber | Select-Object DiskNumber, PartitionNumber, DriveLetter, Type, @{Name="SizeGB";Expression={[math]::Round($_.Size/1GB,3)}}, GptType | Format-Table -AutoSize
if ($Disk.FriendlyName -ne $TargetDiskFriendlyName) {
throw "Abbruch: Datentraeger 0 ist nicht die erwartete Intel-SSD."
}
if ($Disk.SerialNumber -ne $TargetDiskSerialNumber) {
throw "Abbruch: Seriennummer von Datentraeger 0 passt nicht."
}
if (-not (Test-Path $TargetWinload)) {
throw "Abbruch: Ziel-Windows wurde nicht gefunden: $TargetWinload"
}
if ($env:SystemDrive -eq "$TargetWindowsDrive`:") {
throw "Abbruch: Du bist bereits im Ziel-Windows. Dieses Skript ist fuer den Zustand gedacht, in dem neues Windows als D: sichtbar ist."
}
$ExistingEfiOnDisk0 = Get-Partition -DiskNumber $TargetDiskNumber | Where-Object {
$_.GptType -eq "{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}"
}
if ($ExistingEfiOnDisk0) {
Write-Host "EFI-Systempartition auf Datentraeger 0 existiert bereits."
$EfiPartition = $ExistingEfiOnDisk0 | Select-Object -First 1
} else {
Write-Host ""
Write-Host "Auf Datentraeger 0 existiert keine EFI-Systempartition."
Write-Host "Das Skript verkleinert $TargetWindowsDrive`: um $EfiSizeMB MB und erstellt daraus eine neue EFI-Systempartition."
Write-Host ""
$Confirmation = Read-Host "Tippe exakt EFI um fortzufahren"
if ($Confirmation -ne "EFI") {
throw "Abbruch: Bestaetigung wurde nicht eingegeben."
}
$TargetPartition = Get-Partition -DiskNumber $TargetDiskNumber | Where-Object DriveLetter -eq $TargetWindowsDrive
if (-not $TargetPartition) {
throw "Abbruch: Windows-Partition $TargetWindowsDrive`: wurde auf Datentraeger 0 nicht gefunden."
}
$Supported = Get-PartitionSupportedSize -DiskNumber $TargetDiskNumber -PartitionNumber $TargetPartition.PartitionNumber
$NewSize = $TargetPartition.Size - ($EfiSizeMB * 1MB)
if ($NewSize -lt $Supported.SizeMin) {
throw "Abbruch: Partition kann nicht um $EfiSizeMB MB verkleinert werden."
}
Resize-Partition -DiskNumber $TargetDiskNumber -PartitionNumber $TargetPartition.PartitionNumber -Size $NewSize
$EfiPartition = New-Partition -DiskNumber $TargetDiskNumber -Size ($EfiSizeMB * 1MB) -GptType "{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}"
Format-Volume -Partition $EfiPartition -FileSystem FAT32 -NewFileSystemLabel $EfiLabel -Confirm:$false | Out-Null
}
$EfiLetter = Get-FreeDriveLetter
Set-Partition -DiskNumber $TargetDiskNumber -PartitionNumber $EfiPartition.PartitionNumber -NewDriveLetter $EfiLetter
$EfiDrive = "$EfiLetter`:"
Write-Host ""
Write-Host "Schreibe Bootdateien fuer $TargetWindowsPath nach $EfiDrive ..."
bcdboot "$TargetWindowsPath" /s $EfiDrive /f UEFI
if ($LASTEXITCODE -ne 0) {
throw "bcdboot ist fehlgeschlagen."
}
Write-Host ""
Write-Host "Setze Bootmenue-Timeout auf 3 Sekunden."
bcdedit /timeout 3
Write-Host ""
Write-Host "Aktuelle BCD-Eintraege:"
bcdedit /enum
Write-Host ""
Write-Host "Fertig. Naechster Schritt:"
Write-Host "1. Neustarten."
Write-Host "2. Im UEFI/BIOS Windows Boot Manager der Intel-SSD/Datentraeger 0 als erste Bootoption waehlen, falls angeboten."
Write-Host "3. Im Bootmenue den neuen Windows-11-Eintrag starten."
Write-Host "4. Wenn das neue Windows laeuft, sollte SystemDrive C: sein und der alte Windows-Datentraeger einen anderen Buchstaben haben."