From 31e30b04d15b05fcff139f35d9796a6901377e9f Mon Sep 17 00:00:00 2001 From: oskar Date: Tue, 14 Jul 2026 19:25:51 +0200 Subject: [PATCH] =?UTF-8?q?docs(infra):=20monitoring=20coverage=20recon=20?= =?UTF-8?q?=E2=80=94=20co=20biega=20vs=20co=20monitorowane=20+=20plan=20do?= =?UTF-8?q?mkni=C4=99cia?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- docs/infra/monitoring-coverage-2026-07-14.md | 479 +++++++++++++++++++ 1 file changed, 479 insertions(+) create mode 100644 docs/infra/monitoring-coverage-2026-07-14.md diff --git a/docs/infra/monitoring-coverage-2026-07-14.md b/docs/infra/monitoring-coverage-2026-07-14.md new file mode 100644 index 0000000..71e7d27 --- /dev/null +++ b/docs/infra/monitoring-coverage-2026-07-14.md @@ -0,0 +1,479 @@ +# Monitoring coverage — co biega vs co jest monitorowane (recon 2026-07-14) + +**Pytanie:** czy wszystkie serwisy floty są monitorowane? +**Odpowiedź krótka:** NIE. Biega **76 kontenerów** na 5 osiągalnych węzłach, alertowanych jest **15** (~20%). +**61 kontenerów** działa bez alertowania — w tym krytyczne: vaultwarden (hasła), forgejo (git = źródło +prawdy GitOps), immich (zdjęcia), homeassistant5 (dom), obie instancje NPM (ingress), agent-system-telegram-bot +(sam kanał alertów). + +--- + +## Metadane zbierania danych + +| | | +|---|---| +| **Data zebrania** | 2026-07-14, 17:16–17:17 UTC | +| **Metoda** | `docker ps --format '{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'` | +| **PIHA** | ssh `piha` (100.108.208.3, user oskar) — 17:16:37Z | +| **VPS** | ssh `vps` (100.95.58.48, user oskar) — 17:16:39Z | +| **SOLARIA** | lokalnie (sesja biegła na solarii) — 17:16:41Z | +| **LUSTRO** | ssh `pi@100.99.85.73` (uwaga: user **pi**, nie oskar) — 17:17:11Z | +| **SATURN** | ssh `oskar@100.121.168.72` — 17:17:21Z | +| **CHELSTY-INFRA / CHELSTY-HA** | OFFLINE — `tailscale status`: last seen 42 dni temu. **Do weryfikacji po powrocie online.** | +| **Kod** | branch `task/monitoring-coverage-recon` @ parent `d5139c9` (master) | + +Sekcje A–C to **STAN NA DZIEŃ ZBIERANIA** (zmienny — przy odświeżaniu przewalidować w całości). +Sekcja E (klasyfikacja) i F (plan) to **DECYZJE** (trwałe — przy odświeżaniu tylko dopisać nowe kontenery). + +--- + +## Jak działa monitoring w tym systemie (zweryfikowane w kodzie, stan na d5139c9) + +Warstwy, od detekcji do alertu: + +1. **node-agent** (`services/node-agent/src/node_agent.py`, `check_containers()` ~linia 305): + czyta docker socket i emituje eventy dla **WSZYSTKICH kontenerów z restart policy** + (`unless-stopped`/`always`/`on-failure`) — NIE filtruje po services.yaml. + Emituje: `containers_not_running` (exited/dead), `healthcheck_failed` (running+unhealthy), + `service_healthy` (running). Kontenery w stanie `created` pomija. **Uwaga (gap):** kontener + w stanie `restarting` (crash-loop) nie pasuje ani do `exited/dead` ani do `running` — + **crash-loop nie generuje żadnego eventu** (zaobserwowane na lustro/watchtower, patrz niżej). + Nazwa kanoniczna = label `com.docker.compose.service` (fallback: nazwa kontenera). + Zdeployowany na: piha, vps, solaria, lustro (na saturnie NIE). +2. **stability-agent** (`services/stability-agent/src/stability_agent.py`): również raportuje + wszystkie kontenery (bez filtra) — eventy `containers_not_running`, `mqtt_unreachable`, + `disk_usage_high` + publikacja do Redis. Biega na piha, vps, solaria (poza services.yaml!). +3. **Observer** (`scripts/observer/observer.py`, uruchamiany w control-plane@VPS): syntetyzuje + eventy do `/opt/homelab/world/{services,nodes,incidents}.json`. Rejestruje **wszystko** + (na dziś 116 wpisów w services.json, w tym wszystkie shadow kontenery). Widoczność ≠ alert. +4. **Supervisor** (`services/control-plane/src/supervisor.py`, `reconcile()` linia 288): + generuje akcje (`redeploy`/`container_restart` → pending → Telegram → operator) **WYŁĄCZNIE + dla serwisów zadeklarowanych w `hosts//services.yaml`** (`_load_desired_state()`, + linie 161–195). Flaga `monitor: false` (linie 182–186) wyklucza serwis z desired-state — + serwis jest udokumentowany, ale supervisor go pomija. **To jest jedyny filtr: detekcja jest + flotowa, alertowanie tylko dla zadeklarowanych.** Dodatkowo supervisor routuje niezależnie + od desired-state: eventy `ha_*` (z ha-diag-agent), `node_offline/stale/online` (liveness + węzłów z heartbeatów node-agenta) i `disk_pressure`. +5. **fleet-prometheus** (`services/fleet-prometheus/`, VPS): scrape node_exporterów = + **liveness HOSTA, nie serwisów**. Scrape'uje: vps, piha, solaria, lustro. Świadomie NIE: + saturn (workstation, często off), chelsty (LTE, exporter DOWN — do zbadania osobno). + Alert `NodeDown` (rules/liveness.yml) tylko dla `vps|piha` (solaria/lustro bywają planowo + wyłączane). Bez Alertmanagera — alerty odbiera brain-watchdog. +6. **brain-watchdog** (`services/brain-watchdog/`, PIHA): pilnuje świeżości control-plane + (/summary) + polluje `PROMETHEUS_URL/api/v1/alerts` i forwarduje do Telegrama. +7. **ha-diag-agent** (piha → HA "ken" localhost:8123; chelsty-infra → chelsty-ha): diagnostyka + HA (websocket, integracje, encje) → supervisor routuje na alert/restart **niezależnie od + services.yaml** (dziś wisi `container-restart-piha-homeassistant` mimo braku wpisu HA + w hosts/piha/services.yaml). + +**Definicja "monitorowany" w tym raporcie** = supervisor wygeneruje akcję/alert gdy serwis +padnie = wpis w `hosts//services.yaml` bez `monitor: false`, z nazwą zgodną z nazwą +compose-service kontenera. + +--- + +## A. Stan faktyczny — pełne listy kontenerów (STAN NA 2026-07-14) + +### PIHA — 42 kontenery + +| Kontener | Obraz | Status | Porty | +|---|---|---|---| +| node-agent | node-agent-node-agent | Up 22h (healthy) | — | +| paperless | ghcr.io/paperless-ngx/paperless-ngx:2.14 | Up 47h (healthy) | 192.168.31.5:8210→8000 | +| vikunja | vikunja/vikunja:latest | Up 4d | 0.0.0.0:3456→3456 | +| vikunja-db | postgres:16-alpine | Up 4d (healthy) | 5432 (internal) | +| brain-watchdog | brain-watchdog-brain-watchdog | Up 4d (healthy) | — | +| paperless-db | postgres:16-alpine | Up 4d (healthy) | 192.168.31.5:5434→5432 | +| paperless-broker | redis:7-alpine | Up 4d (healthy) | 192.168.31.5:6380→6379 | +| llm-gateway | llm-gateway-llm-gateway | Up 11d (healthy) | 100.108.208.3:8080→8080 | +| kb-postgres | pgvector/pgvector:pg16 | Up 2w (healthy) | 0.0.0.0:5433→5432 | +| agent-system-webui | agent-system-webui | Up 2w | 0.0.0.0:18180→8080 | +| agent-system-runtime-materializer | agent-system-runtime-materializer | Up 2w | — | +| agent-system-telegram-bot | agent-system-telegram-bot | Up 2w | — | +| ha-diag-agent | ha-diag-agent-ha-diag-agent | Up 2w (healthy) | — | +| stability-agent | stability-agent-stability-agent | Up 2w (healthy) | — | +| agent-system-redis | redis:7 | Up 2w | 0.0.0.0:6379→6379 | +| nginxproxymanager-app-1 | jc21/nginx-proxy-manager:latest | Up 2w | 0.0.0.0:80-81→80-81, 443→443 | +| homeassistant5 | ghcr.io/home-assistant/home-assistant:stable | Up 2w | (host network, :8123) | +| immich_server | ghcr.io/immich-app/immich-server:release | Up 2w (healthy) | 0.0.0.0:2283→2283, 8081-8082 | +| immich_postgres | tensorchord/pgvecto-rs:pg14-v0.2.0 | Up 2w (healthy) | 5432 (internal) | +| immich_machine_learning | ghcr.io/immich-app/immich-machine-learning:release | Up 2w (healthy) | — | +| immich_redis | redis:6.2-alpine | Up 2w (healthy) | 6379 (internal) | +| forgejo_dind | docker:dind | Up 2w | 2375-2376 (internal) | +| forgejo | codeberg.org/forgejo/forgejo:7 | Up 12d | 0.0.0.0:3000→3000, 222→22 | +| forgejo-db-1 | postgres:14 | Up 2w | 5432 (internal) | +| zigbee2mqtt | koenkk/zigbee2mqtt | Up 3d | 0.0.0.0:8087→8080 | +| audiobookshelf | ghcr.io/advplyr/audiobookshelf:latest | Up 2w | 0.0.0.0:13378→80 | +| grafana | grafana/grafana-enterprise:latest | Up 2w | 0.0.0.0:9003→3000 | +| wikijs-db-1 | postgres:15-alpine | Up 2w | 5432 (internal) | +| code-server | lscr.io/linuxserver/code-server:latest | Up 2w | 0.0.0.0:8443→8443 | +| portainer | portainer/portainer-ce:latest | Up 2w | 0.0.0.0:8008→8000, 9009→9000 | +| prom | prom/prometheus:latest | Up 2w | 0.0.0.0:9090→9090 | +| homepage | ghcr.io/gethomepage/homepage:latest | Up 2w (healthy) | 0.0.0.0:3033→3000 | +| actual-server | actualbudget/actual-server:latest-alpine | Up 2w | 0.0.0.0:5006→5006 | +| mqtt-exporter-mqtt-exporter-1 | kpetrem/mqtt-exporter:latest | Up 2w | 0.0.0.0:9000→9000 | +| node-exporter | quay.io/prometheus/node-exporter:latest | Up 2w | — | +| wikijs-wiki-1 | ghcr.io/requarks/wiki:2 | Up 2w | 0.0.0.0:3300→3000 | +| owntracks-recorder | owntracks/recorder:latest | Up 2w | 0.0.0.0:8083→8083 | +| vaultwarden | vaultwarden/server:latest | Up 2w (healthy) | 0.0.0.0:3012→80 | +| pihole-exporter | ekofr/pihole-exporter:latest | Up 2w | 0.0.0.0:9617→9617 | +| fail2ban-prometheus-exporter-exporter-1 | registry.gitlab.com/hctrdev/fail2ban-prometheus-exporter:latest | Up 2w **(unhealthy)** | 0.0.0.0:9191→9191 | +| owntracks-prometheus-exporter-prometheus-owntracks-exporter-1 | linusgroh/prometheus-owntracks-exporter | Up 2w | 0.0.0.0:8780→80 | +| own-tracks-frontend-owntracks-frontend-1 | owntracks/frontend | Up 2w | 0.0.0.0:8084→80 | + +Zmiany vs audyt 2026-06-30 (`docs/infra/inventory-2026-06-30.md`): **przybyły** paperless, +paperless-db, paperless-broker (Deploy 1, 2026-07-10); **zniknęły** diskover i elasticsearch +(w audycie 06-30 były w 33 shadow; dziś nie biegają). 06-30: 40 kontenerów → dziś: 42. + +### VPS — 24 kontenery + +| Kontener | Obraz | Status | Porty | +|---|---|---|---| +| control-plane-executor | control-plane-executor | Up 46m (healthy) | — | +| control-plane-observer | control-plane-observer | Up 46m (healthy) | — | +| control-plane-supervisor | control-plane-supervisor | Up 46m (healthy) | — | +| control-plane-ui | control-plane-operator-ui | Up 46m (healthy) | 0.0.0.0:18180→8080 | +| fleet-prometheus | prom/prometheus:v3.5.0 | Up 2w (healthy) | 100.95.58.48:9090→9090 | +| node-agent | node-agent-node-agent | Up 2w (healthy) | — | +| humanai-mailer | humanai-mailer | Up 2w | — | +| humanai-landing | humanai-landing | Up 2w | 80 (internal) | +| umami | ghcr.io/umami-software/umami:postgresql-latest | Up 2w (healthy) | 3000 (internal) | +| umami-db | postgres:16-alpine | Up 2w (healthy) | 5432 (internal) | +| node_exporter | quay.io/prometheus/node-exporter:latest | Up 5w | (host network :9100) | +| stability-agent | stability-agent-stability-agent | Up 5w (healthy) | — | +| outline-outline-1 | outlinewiki/outline:1.6.1 | Up 5w (healthy) | 0.0.0.0:3000→3000 | +| outline-postgres-1 | 4e6e670bb069 (anonimowy image ID!) | Up 5w (healthy) | 5432 (internal) | +| outline-redis-1 | redis:7-alpine | Up 5w (healthy) | 6379 (internal) | +| ai-cluster-service-ops-worker-1 | ai-cluster-service-ops-worker | Up 5w | — | +| ai-cluster-codex-worker-1 | ai-cluster-codex-worker | Up 5w | — | +| ai-cluster-openclaw-1 | ai-cluster-openclaw | Up 5w (healthy) | 0.0.0.0:8000→8000 | +| ai-cluster-planner-worker-1 | ai-cluster-planner-worker | Up 5w | — | +| ai-cluster-redis-1 | redis:7-alpine | Up 5w | 6379 (internal) | +| mosquitto | eclipse-mosquitto:2 | Up 5w | 100.95.58.48:1883→1883 | +| joplin-server | joplin/server:latest | Up 5w | 127.0.0.1:22300→22300 | +| npm | jc21/nginx-proxy-manager:latest | Up 5w | 0.0.0.0:80-81→80-81, 443→443 | +| joplin-db | postgres:18 (pre-release tag!) | Up 5w (healthy) | 5432 (internal) | + +**gokapi NIE biega** mimo deklaracji w hosts/vps/services.yaml — supervisor poprawnie to wykrył: +w `/opt/homelab/actions/pending/` wisi `redeploy-vps-gokapi.json`. To dowód, że monitoring +desired-state DZIAŁA dla zadeklarowanych serwisów. + +### SOLARIA — 5 kontenerów + +| Kontener | Obraz | Status | Porty | +|---|---|---|---| +| paperless-worker | ghcr.io/paperless-ngx/paperless-ngx:2.14 | Up 5h (healthy) | 8000 (internal) | +| planner-agent | planner-agent-planner-agent | Up 5h (healthy) | — | +| node-agent | node-agent-node-agent | Up 5h (healthy) | — | +| stability-agent | stability-agent-stability-agent | Up 5h (healthy) | — | +| node_exporter | quay.io/prometheus/node-exporter:latest | Up 5h | (host network :9100) | + +### LUSTRO — 4 kontenery + +| Kontener | Obraz | Status | Porty | +|---|---|---|---| +| node-agent | node-agent-node-agent | Up 20h (healthy) | — | +| node-exporter | quay.io/prometheus/node-exporter:latest | Up 20h | (host network :9100) | +| piper-tts | piper-tts-rpi5 | Up 20h | 0.0.0.0:5000→5000, 10200→10200 | +| pi-watchtower-1 | containrrr/watchtower | **Restarting (1) — crash-loop!** | — | + +**pi-watchtower-1 jest w crash-loopie** i przez lukę w node-agencie (stan `restarting` nie +generuje eventu, patrz sekcja o mechanizmie) — **nikt tego nie widzi**; world-state pokazuje +`lustro/watchtower healthy` (stary event `service_healthy`). + +### SATURN — 1 kontener + +| Kontener | Obraz | Status | Porty | +|---|---|---|---| +| agent-system-webui | agent-system-webui | Up 9h | 0.0.0.0:8080→8080 | + +Stack control-plane z audytu 06-30 (executor/observer/supervisor/ui) już NIE biega na saturnie +— posprzątane. Saturn nie ma node-agenta, nie ma services.yaml, nie jest scrape'owany przez +fleet-prometheus → **węzeł całkowicie poza monitoringiem** (świadomie: workstation, często off). + +### CHELSTY-INFRA / CHELSTY-HA — OFFLINE (42 dni) + +Niedostępne przez Tailscale (last seen ~2026-06-02). Deklaracje: chelsty-infra = ha-diag-agent, +node-agent, mosquitto, zigbee2mqtt, frigate; chelsty-ha = homeassistant (`monitor: false`). +`world/services.json` nadal pokazuje serwisy chelsty-infra jako "healthy" — to STĘCHŁY stan +(status serwisów nie wygasa; liveness wygasa tylko na poziomie węzła). **Do weryfikacji po +powrocie online**, w tym: czy alerty `node_offline` dla chelsty odpaliły 42 dni temu. + +--- + +## B. Stan deklarowany — hosts//services.yaml (STAN NA 2026-07-14) + +| Węzeł | Zadeklarowane serwisy | monitor: false? | +|---|---|---| +| piha | ha-diag-agent, node-agent, brain-watchdog, vikunja, llm-gateway, kb-postgres | — | +| vps | node-agent, control-plane, node_exporter, fleet-prometheus, gokapi | — | +| solaria | node-agent | — | +| lustro | node-agent | — | +| saturn | **BRAK PLIKU services.yaml** | — | +| chelsty-infra | ha-diag-agent, node-agent, mosquitto, zigbee2mqtt, frigate | — | +| chelsty-ha | homeassistant | **TAK** (node-agent tam nie zdeployowany; HA kryty pośrednio przez MQTT) | + +Razem: **18 deklaracji alertowalnych** (w tym 5 na offline'owym chelsty-infra) + 1 z monitor:false. + +Uwaga: `stability-agent` ma katalogi w `hosts/{piha,solaria,vps}/runtime/`, ale NIE ma wpisów +w żadnym services.yaml — sam strażnik nie jest pilnowany. + +--- + +## C. Luka — biega, ale poza alertowaniem (STAN NA 2026-07-14) + +Pokrycie per węzeł (kontenery alertowane / biegające): + +| Węzeł | Biega | Alertowane | Luka | +|---|---|---|---| +| PIHA | 42 | 6 (node-agent, ha-diag-agent, brain-watchdog, vikunja, llm-gateway, kb-postgres) | **36** | +| VPS | 24 | 7 (node-agent, control-plane ×4¹, node_exporter, fleet-prometheus) | **17** | +| SOLARIA | 5 | 1 (node-agent) | **4** | +| LUSTRO | 4 | 1 (node-agent) | **3** | +| SATURN | 1 | 0 | **1** | +| **RAZEM** | **76** | **15** | **61 (~80%)** | + +¹ control-plane to 1 deklaracja pokrywająca 4 kontenery — node-agent na VPS probe'uje endpoint +HTTP :18180/summary i emituje `service_healthy`/`service_unhealthy` dla logicznego serwisu +"control-plane" (`_check_control_plane_health()`). + +Częściowe pokrycie (nie liczone jako "alertowane" powyżej): +- **homeassistant5@piha** — ha-diag-agent pilnuje websocketu HA i supervisor generuje + `container_restart`/`alert_only` niezależnie od services.yaml. Padnięcie HA zostanie + zauważone. Brak jednak wpisu desired-state (drift "kontener zniknął" nie będzie wykryty). +- **node-exporter@piha** — jego padnięcie = `up==0` w fleet-prometheus = alert NodeDown + (piha) przez brain-watchdog → Telegram. Pośrednio alertowany. +- **node_exporter@solaria, node-exporter@lustro** — scrape'owane, ale świadomie wyłączone + z reguły NodeDown (węzły planowo wyłączane) → brak alertu. +- **gokapi@vps** — zadeklarowany, NIE biega; drift poprawnie wykryty (pending + `redeploy-vps-gokapi.json`). Luka deploymentu, nie monitoringu. + +--- + +## D. Nowe serwisy KB — weryfikacja wpisu z backlogu (f135365) + +Backlog ("Nowe serwisy KB nie sa w monitoringu", 2026-07-12) **potwierdzony w połowie** — +paperless i paperless-worker faktycznie poza monitoringiem; gokapi/llm-gateway/kb-postgres są OK: + +| Serwis | services// w repo? | hosts/*/services.yaml? | Biega? | Alertowany? | +|---|---|---|---|---| +| paperless (+db, +broker) | ✓ (+ hosts/piha/runtime/paperless) | ✗ | ✓ piha (healthy) | **✗ LUKA** | +| paperless-worker | ✓ (owner_node: solaria) | ✗ | ✓ solaria (healthy) | **✗ LUKA** | +| nextcloud | ✓ (owner_node: piha, commit 7e5577c) | ✗ | ✗ nigdzie (nie zdeployowany) | ✗ (nie dotyczy — dodać wpis RAZEM z deployem) | +| gokapi | ✓ (owner_node: vps) | ✓ hosts/vps | **✗ nie biega** | ✓ — drift wykryty, wisi `redeploy-vps-gokapi` | +| llm-gateway | ✓ (owner_node: piha) | ✓ hosts/piha | ✓ piha (healthy) | ✓ | +| kb-postgres | ✓ (owner_node: piha) | ✓ hosts/piha | ✓ piha (healthy) | ✓ | + +--- + +## E. Klasyfikacja niemonitorowanych kontenerów (DECYZJA — trwała) + +Kategorie: +- **A (pełny GitOps):** krytyczny — docelowo compose w `services/`, service.yaml, deploy przez + deploy.sh. Każde A dostaje NAJPIERW wpis monitoringowy (krok B) — tanio, od razu. +- **B (tylko monitoring):** deployment zostaje jak jest; dodać do `hosts//services.yaml` + → supervisor alarmuje gdy padnie. +- **C (świadomie poza):** padnięcie nie boli / narzędzie jednorazowe. Uzasadnienie obowiązkowe. + +Kontenery pomocnicze stacka (db/redis/broker) dziedziczą kategorię stacka — w services.yaml +deklaruje się serwis główny (padnięcie db zwykle wywala healthcheck głównego kontenera). + +### PIHA (36 luk) + +| Kontener | Co robi | Kategoria | Uzasadnienie | +|---|---|---|---| +| vaultwarden | menedżer haseł | **A** (najpierw B) | Hasła całej rodziny — padnięcie/utrata danych boli maksymalnie; musi być w GitOps z backupem i alertem | +| forgejo, forgejo-db-1, forgejo_dind | git hosting + CI | **A** (najpierw B) | Źródło prawdy GitOps + OIDC dla vikunji; bez forgejo nie ma deployów; już zidentyfikowane w audycie 06-30 (owner_node błędnie=saturn) | +| homeassistant5 | Home Assistant "ken" (dom) | **A** (najpierw B) | Automatyka domu; częściowo kryty przez ha-diag-agent (websocket), ale bez wpisu desired-state; wpis `homeassistant` w services.yaml domknie lukę | +| immich_server, immich_postgres, immich_machine_learning, immich_redis | zdjęcia rodzinne | **A** (najpierw B) | Dane niereprodukowalne (zdjęcia); padnięcie = brak backupu telefonów w tle, zauważalne późno | +| nginxproxymanager-app-1 | ingress LAN (*.kapala.org: HA, immich, paperless, cloud…) | **A** (najpierw B) | Pojedynczy punkt wejścia do wszystkich usług domowych; padnięcie = "wszystko nie działa" | +| paperless, paperless-db, paperless-broker | KB: dokumenty (OCR pipeline) | **B** | Już w GitOps (services/paperless + runtime override) — brakuje TYLKO wpisu w services.yaml; przypadek z backlogu f135365 | +| stability-agent | watchdog węzła | **B** | Repo-managed (runtime override jest); strażnik musi być pilnowany; dotyczy też vps i solarii | +| zigbee2mqtt | bridge Zigbee→MQTT (ken) | **B** | services/zigbee2mqtt istnieje (owner=piha); sensory domowe przestają raportować gdy padnie | +| agent-system-telegram-bot | kanał alertów Telegram | **B** | Jak padnie, ŻADEN alert nie dojdzie — a nikt się nie dowie; pilnować w pierwszej kolejności | +| agent-system-webui | operator UI (approve akcji) | **B** | Bez niego nie da się zatwierdzać akcji z przeglądarki; część pętli human-in-the-loop | +| agent-system-redis | redis dla agent-system | **B** | Zależność telegram-bota i webui; tani wpis | +| agent-system-runtime-materializer | materializacja runtime agentów | **B** | Część stacka agent-system; padnięcie cichaczem psuje odświeżanie stanu | +| wikijs-wiki-1, wikijs-db-1 | wiki | **B** | Notatki/dokumentacja domowa — padnięcie boli umiarkowanie, wpis jest tani | +| actual-server | budżet domowy | **B** | Dane finansowe; używany regularnie, padnięcie zauważalne przy wpisywaniu wydatków | +| prom | domowy Prometheus (LAN) | **B** | Źródło metryk domowych (HA, piha OS); padnięcie = dziura w historii; UWAGA: ma plaintext token HAOS (anti-pattern, patrz services/fleet-prometheus/prometheus.yml komentarz) | +| owntracks-recorder | lokalizacja rodziny (backend) | **C** | Hobby/eksperyment; padnięcie = luka w śladzie lokalizacji, nie boli operacyjnie | +| own-tracks-frontend-… | frontend owntracks | **C** | Czysta wizualizacja | +| owntracks-prometheus-exporter-… | exporter owntracks | **C** | Pomocniczy; objaw padnięcia = brak metryk, widoczny w grafanie | +| mqtt-exporter-mqtt-exporter-1 | exporter MQTT→prom | **C** | Jak wyżej | +| pihole-exporter | exporter pihole | **C** | Jak wyżej | +| fail2ban-prometheus-exporter-… | exporter fail2ban | **C** | Jak wyżej; DZIŚ unhealthy — naprawić lub usunąć (follow-up) | +| node-exporter | metryki hosta dla fleet-prometheus | **C** | Pośrednio alertowany: padnięcie = up==0 = NodeDown(piha) przez brain-watchdog | +| grafana | dashboardy | **C** | Wizualizacja; padnięcie zauważalne przy wejściu, zero utraty danych (provisioning/db na dysku) | +| homepage | strona startowa | **C** | Kosmetyka | +| audiobookshelf | audiobooki | **C** | Media; restart ręczny wystarczy | +| code-server | IDE w przeglądarce | **C** | Narzędzie dev, używane ad-hoc | +| portainer | GUI dockera | **C** | Narzędzie administracyjne ad-hoc; wręcz kandydat do usunięcia (GitOps ma być źródłem prawdy) | + +### VPS (17 luk) + +| Kontener | Co robi | Kategoria | Uzasadnienie | +|---|---|---|---| +| npm | publiczny ingress okit.pl | **A** (najpierw B) | Cała publiczna powierzchnia (share.okit.pl, vikunja.okit.pl…); manifesty A już istnieją na NIEZMERGOWANEJ gałęzi `feat/vps-service-migration` (commit 862c04a, cutover niewykonany) | +| mosquitto | broker MQTT ai-cluster | **B** | services/mosquitto istnieje ale owner_node=piha (błąd — biega na VPS od tygodni, audyt 06-30 poz. 3); szyna komunikacji ai-cluster | +| stability-agent | watchdog węzła | **B** | Jak na piha | +| outline-outline-1, outline-postgres-1, outline-redis-1 | wiki zespołowa | **B** | Notatki; manifesty na gałęzi feat/vps-service-migration; przy okazji naprawić anonimowy image ID postgresa (audyt 06-30 poz. 19) | +| joplin-server, joplin-db | sync notatek | **B** | Notatki osobiste; przy okazji zejść z postgres:18 pre-release (audyt 06-30 poz. 20) | +| ai-cluster-codex-worker-1, ai-cluster-planner-worker-1, ai-cluster-service-ops-worker-1, ai-cluster-openclaw-1, ai-cluster-redis-1 | workery agentowe | **B** | Padnięcie = agenci przestają mielić kolejki (objawia się późno); docelowo migracja compute na SOLARIA (CLAUDE.md), monitoring dodać już teraz | +| humanai-landing, humanai-mailer | strona publiczna + mailer | **B** | Publiczna wizytówka — padnięcie boli reputacyjnie i cicho (nikt nie patrzy); brak w repo (audyt 06-30 poz. 21) | +| umami, umami-db | analityka www | **C** | Statystyki odwiedzin; padnięcie = luka w danych analitycznych, nie boli operacyjnie | + +### SOLARIA (4 luki) + +| Kontener | Co robi | Kategoria | Uzasadnienie | +|---|---|---|---| +| paperless-worker | OCR/konsument kolejki KB | **B** | Przypadek wprost z backlogu f135365: "jesli worker padnie (…) dowiesz sie po tym, ze kolejka nie jest przetwarzana"; repo+runtime są, brakuje wpisu | +| planner-agent | agent planowania | **B** | services/planner-agent istnieje (owner=solaria ✓); audyt 06-30 poz. 14 | +| stability-agent | watchdog węzła | **B** | Jak na piha/vps | +| node_exporter | metryki hosta | **C** | Scrape'owany, ale solaria świadomie poza NodeDown (planowe wyłączenia); alerting wróci z anomaly-detection (backlog) | + +### LUSTRO (3 luki) + +| Kontener | Co robi | Kategoria | Uzasadnienie | +|---|---|---|---| +| piper-tts | TTS dla magic mirror | **C** | Padnięcie = lustro nie mówi; kosmetyka. Jeśli tani wpis — można podnieść do B przy okazji | +| node-exporter | metryki hosta | **C** | Jak solaria — świadomie poza NodeDown | +| pi-watchtower-1 | auto-update obrazów | **C** + **follow-up PILNY** | DZIŚ w crash-loopie, niewykrywalnym przez node-agent (luka na stan `restarting`). Decyzja operatora: naprawić albo USUNĄĆ (watchtower na edge = niekontrolowane pulle, sprzeczne z GitOps) | + +### SATURN (1 luka) + +| Kontener | Co robi | Kategoria | Uzasadnienie | +|---|---|---|---| +| agent-system-webui | operator UI (kopia dev?) | **C** | Saturn = workstation, często wyłączony; świadomie poza monitoringiem (jak w fleet-prometheus). Wyjaśnić czemu biega duplikat UI z pihy (follow-up) | + +--- + +## F. Plan domknięcia — paczki na jedną sesję każda (DECYZJA — trwała) + +**Zasada procesowa (z backlogu, do utrwalenia):** wpis w `hosts//services.yaml` + +`inventory/topology.yaml` to CZĘŚĆ deployu każdego nowego serwisu, nie osobny krok "kiedyś". + +**⚠ Gotcha techniczna dla WSZYSTKICH paczek (sprawdzić przed każdym wpisem):** supervisor +matchuje klucz `node/nazwa-serwisu` z nazwą kanoniczną kontenera = label +`com.docker.compose.service` (node_agent.py `_canonical_container_name()`). Dla stacków +compose'owych to NIE jest nazwa kontenera: np. `nginxproxymanager-app-1` ma compose-service +**"app"**, `wikijs-wiki-1` → "wiki", stąd w world/services.json wiszą kolizyjne klucze typu +`piha/app`, `piha/db`, `piha/redis`, `piha/database`. Przed dodaniem wpisu sprawdzić na hoście: +`docker inspect --format '{{ index .Config.Labels "com.docker.compose.service" }}'` +— i jeśli label koliduje/jest ogólnikowy, najpierw nadać `container_name` + porządny compose +project name albo poprawić observer (patrz follow-upy). Bez tego wpis będzie wiecznym +`missing_service` i zaspamuje kolejkę akcji. + +### Paczka 1 — KB + już-zGitOps'owane serwisy (najtańsza-najważniejsza; czysta kategoria B) + +Serwisy, które JUŻ są w repo (services/ + runtime overrides) — brakuje tylko rejestracji: + +1. `hosts/piha/services.yaml`: + paperless, + stability-agent, + zigbee2mqtt +2. `hosts/solaria/services.yaml`: + paperless-worker, + planner-agent, + stability-agent +3. `hosts/vps/services.yaml`: + stability-agent, + npm, + mosquitto (i naprawić + `services/mosquitto/service.yaml` owner_node: piha→vps oraz `services/npm` — potwierdzić + że nazwa compose-service = "npm") +4. `inventory/topology.yaml`: dopisać ww. do sekcji węzłów +5. Weryfikacja: po 1–2 cyklach supervisora `world/services.json` ma klucze o zgodnych nazwach, + kolejka pending NIE zawiera fałszywych `missing_service`; test driftu: `docker stop + paperless-worker` na chwilę → pojawia się akcja → `docker start` → akcja auto-cancelled + (test wymaga zgody operatora; alternatywnie tylko obserwacja biernie) +6. Domknięcie wiszącego driftu: zdeployować gokapi na VPS (akcja `redeploy-vps-gokapi` już + czeka na approve) — UWAGA: brak `hosts/vps/runtime/gokapi/docker-compose.override.yml` + z mem_limit (reguła VPS 4GB) — dodać przed deployem. + +### Paczka 2 — krytyczne shadow na PIHA (kategoria B dla przyszłych A) + +Wpisy monitoringowe (bez ruszania deploymentu!) dla: vaultwarden, forgejo, homeassistant +(kontener homeassistant5), immich, nginxproxymanager (compose-service prawdopodobnie "app" — +patrz gotcha!), agent-system (telegram-bot, webui, redis, runtime-materializer), wikijs, +actual-server, prom. Każdy wpis poprzedzony sprawdzeniem compose-label na hoście. +Jeśli labels kolidują (db/app/redis) — najpierw follow-up "canonical naming" (niżej) albo +wpis odroczony z adnotacją. + +### Paczka 3 — VPS shadow (kategoria B + przygotowanie cutover) + +1. Wpisy: outline, joplin, ai-cluster, humanai-landing, humanai-mailer (+ umami jeśli tanio) +2. Przejrzeć gałąź `feat/vps-service-migration` (commit 862c04a: manifesty npm/outline/joplin/ + ai-cluster; cutover NIE wykonany) — zdecydować: merge + cutover per checklista z CLAUDE.md, + czy przepisać od nowa. UWAGA: CLAUDE.md już dziś twierdzi "All VPS services are now + GitOps-managed" — to NIEPRAWDA na masterze; poprawić doc albo zmergować gałąź. + +### Paczka 4 — pełny GitOps dla krytycznych (kategoria A; JEDEN serwis = JEDNA sesja) + +Kolejność wg bólu: 1) vaultwarden, 2) forgejo, 3) npm@piha + npm@vps (cutover), 4) immich, +5) homeassistant5. Dla każdego: compose do `services//`, service.yaml, env.example, +healthcheck.sh, runtime override w `hosts/piha/runtime/`, deploy przez deploy.sh. +**Reguła migracji danych: ścieżki danych zostają NA MIEJSCU** (CLAUDE.md) — zero przenoszenia +wolumenów bez osobnego planu. + +### Paczka 5 — porządki i decyzje C (follow-upy) + +- **lustro/pi-watchtower-1**: naprawić albo usunąć (crash-loop TERAZ; watchtower vs GitOps) +- **piha/fail2ban-exporter**: unhealthy od ≥2 tyg. — naprawić albo usunąć +- **saturn/agent-system-webui**: wyjaśnić czemu biega; usunąć jeśli pozostałość dev +- **nextcloud**: przy deployu (osobna sesja per plan KB) wpis do services.yaml OD RAZU +- **chelsty**: po powrocie online powtórzyć inwentaryzację (sekcja "Jak odświeżyć") +- **saturn**: decyzja czy dostaje node-agenta + services.yaml (dziś świadomie poza) + +### Follow-upy techniczne w kodzie monitoringu (poza zakresem tego reconu, do osobnych tasków) + +1. **node-agent nie widzi stanu `restarting`** (crash-loop = cisza) — dodać gałąź dla + `status == "restarting"` w `check_containers()` → event `containers_not_running` lub nowy + typ `container_crash_loop`. Dowód: lustro/pi-watchtower-1. +2. **Kolizje nazw kanonicznych** w observerze/node-agencie: label `com.docker.compose.service` + bez project-name daje klucze `piha/db`, `piha/app`, `piha/redis` (dziś w world/services.json + są duplikaty typu `piha/immich_server` I `piha/immich-server`). Propozycja: klucz + `_` albo preferować container_name gdy label jest ogólnikowy. +3. **Stęchły world-state dla offline węzłów**: serwisy chelsty-infra "healthy" 42 dni po + zniknięciu węzła — status serwisu powinien degradować się razem z liveness węzła. +4. **Alerting solaria/lustro**: świadomie wyłączone z NodeDown; docelowo anomaly detection + (istniejący backlog "Plan: Monitoring floty — Prometheus jako źródło prawdy"). + +--- + +## TL;DR + +- **Biega:** 76 kontenerów (42 piha, 24 vps, 5 solaria, 4 lustro, 1 saturn); chelsty-infra/ha + offline od ~42 dni — niezweryfikowane. +- **Alertowane:** 15 kontenerów przez 13 deklaracji desired-state (+ HA częściowo przez + ha-diag-agent, + node-exporter@piha pośrednio przez NodeDown, + gokapi: zadeklarowany, + niezdeployowany, drift POPRAWNIE wykryty i wisi w pending). +- **Luka:** 61 kontenerów (~80%) bez alertowania. Detekcja (node-agent/observer) widzi + wszystko; filtr jest w supervisorze — alertuje TYLKO to, co wpisane w + `hosts//services.yaml` (bez `monitor: false`). +- **Najboleśniejsze luki:** vaultwarden (hasła), forgejo (git), immich (zdjęcia), + homeassistant5 (dom, częściowo kryty), npm ×2 (cały ingress), agent-system-telegram-bot + (kanał alertów pilnujący wszystkiego innego, sam niepilnowany). +- **Znaleziska przy okazji:** lustro/watchtower w crash-loopie niewykrywalnym przez node-agent + (luka na stan `restarting`); fail2ban-exporter@piha unhealthy; gokapi czeka na deploy; + CLAUDE.md błędnie twierdzi że VPS jest w pełni GitOps (manifesty na niezmergowanej gałęzi). +- **Co najpierw:** Paczka 1 (wpisy dla paperless, paperless-worker, stability-agent ×3, + zigbee2mqtt, npm, mosquitto, planner-agent — wszystko już w repo, tylko rejestracja) + → Paczka 2 (krytyczne shadow PIHA) → Paczka 3 (VPS) → Paczka 4 (pełny GitOps po kolei). + +--- + +## Jak odświeżyć ten dokument + +1. Stan faktyczny (sekcja A) — jedna pętla (uwaga na userów ssh: lustro=pi, reszta=oskar; + solaria/saturn wg tego skąd odpalasz): + + ```bash + for target in oskar@100.108.208.3 oskar@100.95.58.48 oskar@100.100.231.104 \ + pi@100.99.85.73 oskar@100.121.168.72 oskar@100.98.91.98; do + echo "===== $target =====" + ssh -o ConnectTimeout=10 -o BatchMode=yes "$target" \ + "date -u +%Y-%m-%dT%H:%M:%SZ; docker ps --format '{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'" + done + ``` + (kolejno: piha, vps, solaria, lustro, saturn, chelsty-infra; chelsty-infra pomiń jeśli offline) + +2. Stan deklarowany (sekcja B): `grep -A2 "^services:" hosts/*/services.yaml` + ręcznie + sprawdzić flagi `monitor: false`. +3. Luka (sekcja C): porównać 1 z 2 per węzeł; pamiętać że 1 deklaracja może pokrywać stack + wielokontenerowy (control-plane, vikunja) i że match idzie po labelu + `com.docker.compose.service`, nie po nazwie kontenera. +4. Wiszące akcje/drifty: `ssh vps ls /opt/homelab/actions/pending/`. +5. Sekcje E–F: NIE odtwarzać — tylko dopisać nowe kontenery i odhaczyć wykonane paczki. + Zmienić klasyfikację wolno tylko świadomą decyzją (to sekcje DECYZYJNE).