- 🔴 deploy.sh vps↔control-plane → Zamknięte (fix3b71707, guard deploy-local.sh) - krok 2 planu floty (targety 100.x) → ZROBIONE (7d4014e) - NOWE: deploy-node.sh nie reloaduje config-driven serwisów (cicha rozbieżność deploy↔config) - NOWE: zbadać chelsty/chelsty-infra node_exporter DOWN z VPS - Problem B (ghost kontenery) pozostaje otwarty osobno Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
15 KiB
Tech-debt backlog
Centralny tracker tech-długu i znanych usterek. Wpisy ze sesji — dodawaj z datą i kontekstem.
Plan: Monitoring floty — Prometheus jako źródło prawdy
Data: 2026-06-22
Źródło: sesja 2026-06-22 (docs/sessions/2026-06-22.md)
Decyzja: Prometheus (pull, up{}) zastępuje warstwę WYKRYWANIA liveness
(node-agent shipper + rsync/ssh + event-store + observer prune/checkpoint + ręczne TTL)
— przyczynę nawracających awarii (uid≠1000, ślepy ssh-mount, bloat ~242k eventów,
race prune↔checkpoint, NOMINAL-bez-TTL). Osobny fleet-Prometheus pod GitOps, nie
adopcja domowego instance PIHA. BEZ Alertmanagera — alert przez brain-watchdog.
Placement: VPS. Granica: zostają supervisor (remediacja), observer/panel, ha-diag-agent,
historia incydentów, out-of-band watchdog.
Zastępuje wcześniejszy szkic (blackbox + Alertmanager) z sesji 2026-06-17.
Kroki (priorytetowo):
- Scaffold serwisu
fleet-prometheuspod GitOps (worktreetask/fleet-prometheus, wzorzecservices/vikunja/): compose +env.example+service.yaml+ README +healthcheck.sh; rejestracja whosts/vps/services.yaml+inventory/topology.yaml; exposuretailscale-internal; pusty scrape na start (self + lokalnynode_exporterVPS). - ✅ ZROBIONE (2026-06-26, commit
7d4014e) — Inwentaryzacja nodów floty100.xdo scrape. Dodane: piha/solaria/lustro (node:label), vps zachowany. Saturn pominięty (workstation), chelsty/chelsty-infra pominięte (node_exporter down z VPS → osobny wpis). - Container-layer exporter (cAdvisor lub lekki docker-state) —
node_exporternie widzi kontenerów. - Reguły liveness (
up==0 for: 5m) w Prometheusie. - brain-watchdog: drugie wejście — odpyt Prometheus
/api/v1/alerts(firing) → Telegram, bez ruszania działającego torutoken → chat_id. - Rotacja tokenu HAOS w domowym prom (plaintext).
- Przepięcie observer / panel
agents.okit.plna Prometheus jako źródło — największy znak zapytania przy cutoverze. - Parallel-run obok rury eventowej; cutover dopiero gdy Prometheus-truth się udowodni.
Aktywne
deploy-node.sh nie reloaduje config-driven serwisów (cicha rozbieżność deploy↔config)
Data: 2026-06-26
Źródło: sesja 2026-06-26 (docs/sessions/2026-06-26.md)
Problem: po zmianie prometheus.yml (bez zmiany obrazu) deploy-node.sh NIE
recreate'uje kontenera. Compose widzi "kontener działa, obraz ten sam" → zostawia
Running, NIE podmienia configu → Prometheus trzyma stary config w pamięci. Deploy
raportuje green, a zmiana configu nie wchodzi w życie. Dziś wymagało ręcznego
docker compose ... up -d --force-recreate. Dotyczy KAŻDEGO serwisu config-driven bez
zmiany obrazu (nie tylko Prometheus).
Fix: po zmianie configu serwisu albo --force-recreate, albo POST /-/reload dla
serwisów z lifecycle API (fleet-prometheus ma --web.enable-lifecycle). Rozważyć
wykrywanie zmiany plików config w deploy i wymuszanie recreate.
Zbadać: chelsty + chelsty-infra node_exporter DOWN z VPS
Data: 2026-06-26
Źródło: sesja 2026-06-26 (docs/sessions/2026-06-26.md)
Problem: przy inwentaryzacji targetów floty do fleet-prometheusa, node_exporter
na chelsty i chelsty-infra był nieosiągalny z VPS — pominięte w scrape. To LTE edge
(intermittent uplink), więc DOWN może być normą, ale wymaga rozróżnienia: brak
node_exportera vs odcięty uplink vs zablokowany port.
Fix: ustalić, czy node_exporter w ogóle działa na obu chelsty (compose/proces),
czy jest osiągalny po Tailscale z VPS, i czy ma sens go scrape'ować mimo LTE
(prawdopodobnie tak — up==0 na LTE = sygnał dla anomaly detection, nie fałszywy alarm).
Ghost kontenery w panelu (Problem B — pozostałość po project-name divergence)
Data: 2026-06-24 (wykryte), pozostaje otwarte po fixie Problemu A (2026-06-26)
Źródło: sesja 2026-06-24 + 2026-06-25 + 2026-06-26
Problem: martwe kontenery ze starych project-name'ów
(8547b46c0317_control-plane-supervisor, 12bd3059a70f_control-plane-observer itp.)
są raportowane przez observera jako error → System Status ERROR w panelu mimo
zdrowego realnego mózgu.
Status: destrukcyjny deploy (Problem A) NAPRAWIONY (3b71707, patrz Zamknięte), ale
ghosty z poprzednich rozjazdów wciąż wiszą.
Fix: docker rm ghostów na VPS; rozważyć czyszczenie kontenerów z obcym
project-name przy deployu.
Supervisor nie enqueue'uje akcji remediacji przy error-state
Data: 2026-06-25 (powtórka sygnału z 2026-06-19)
Źródło: sesja 2026-06-25 (docs/sessions/2026-06-25.md)
Problem: Action Queue pusta mimo System Status ERROR widocznego w panelu.
Supervisor nie generuje container_restart / redeploy dla serwisów w stanie error.
Objaw zaobserwowany co najmniej dwukrotnie — wymaga izolowanego dochodzenia.
Podejrzane: supervisor może nie reagować na error-state jeśli źródłem są ghost kontenery
(błędne project-name), nie realne health-check failures.
Fix: zbadać osobno — sprawdzić, czy supervisor otrzymuje właściwe eventy od observera,
czy ma własną logikę de-duplifikacji blokującą enqueue.
Rozjazd world-state observera: panel pokazuje serwis NOMINAL przed jego istnieniem
Data: 2026-06-25
Źródło: sesja 2026-06-25 (docs/sessions/2026-06-25.md)
Problem: panel wykazał fleet-prometheus jako nominal na SOLARII zanim kontener
w ogóle istniał — observer world_state rozjechany z dockerem. Artefakt rejestracji
w manifeście bez realnego kontenera. Podobna klasa błędu jak ghost kontenery.
Fix: observer powinien weryfikować faktyczny stan kontenera przy budowaniu world_state
zamiast opierać się wyłącznie na zarejestrowanych serwisach.
🔴 BLOKUJĄCE — FLOTA-BOMBA: node-agent SSH mount ślepy po recreate
Data: 2026-06-11
Źródło: sesja lustro ssh shipping fix
Problem: solaria/piha/chelsty to stare root kontenery node-agenta (piha Created
2026-05-27, uid 0) — sprzed dodania user: "1000:1000" do bazowego compose. Ich override
montuje klucz SSH w /root/.ssh, co działa tylko dla uid 0. Pierwszy --force-recreate /
reboot hosta / update obrazu przełączy kontener na uid 1000 (homelab, HOME=/home/homelab)
i shipping eventów na VPS padnie z "Permission denied" — dokładnie jak na lustrze
(naprawione a5a1352). ssh w _ship_events_to_vps() nie ma -i i szuka klucza
w $HOME/.ssh.
⚠️ NIE RECREATE node-agenta na solaria/piha/chelsty przed fixem.
Fix: ujednolicić mount → /home/homelab/.ssh we wszystkich
hosts/*/runtime/node-agent/docker-compose.override.yml (wzór: hosts/lustro/)
ALBO dodać -i $HOME/.ssh/id_rsa w _ship_events_to_vps().
ha-diag-agent deploy ZABLOKOWANY (placeholder token)
Data: 2026-06-11
Źródło: sesja — deploy config merged (5e9db5c), .env na piha utworzony
(/opt/homelab/config/ha-diag-agent/.env, chmod 600) ale token = PLACEHOLDER.
Blokada: chelsty-ha offline → brak tokenu i połączenia.
Do decyzji: cel HA — chelsty-ha vs HA Ken (homeassistant5 na piha; z kontenera
NIE localhost).
Przed shadow_mode=false: target restartu w supervisorze = nazwa kontenera
homeassistant5; curl endpointu HA z tokenem = HTTP 200.
observer-poison-quarantine — review brancha (78c9e4a)
Data: 2026-06-11
Źródło: sesja — patch Codexa zachowany na task/observer-poison-quarantine, NIE w master.
Do zrobienia: zweryfikować, czy observer realnie wiesza się na malformed evencie
(poison NIE był przyczyną awarii lustra — hipoteza niezweryfikowana, obalona przez
verify-before-fix). Realny bug → merge; inaczej → drop brancha i worktree.
node_agent.py — drobne sprzątanie shippingu
Data: 2026-06-11 Źródło: sesja lustro ssh shipping fix
- Stale komentarz
node_agent.py:546-548— twierdzi, że kontener "runs as root"; nieaktualne oduser: "1000:1000". - Sukces shippingu na
logger.debug→ podnieść doinfolub dodać licznik — działający shipping jest niewidoczny w logach przy INFO, co utrudniało diagnozę (cicha awaria wyglądała identycznie jak ciche działanie).
event-bloat: wyczyścić spłynięty backlog lustro na VPS
Data: 2026-06-11
Źródło: sesja — po fixie shippingu 7600+ plików backlogu spłynęło do
/opt/homelab/events/lustro/ na VPS.
Fix: wyczyścić stare pliki (observer już je przetworzył); docelowo polityka retencji
w event-store.
rsync --omit-dir-times (node-agent)
Data: 2026-06-09
Źródło: flota recovery session
Objaw: rsync exit code 23 po każdym push — set-times na katalogu /opt/homelab/events/
zwraca EPERM (oskar nie jest właścicielem katalogu; aerbot jest). Pliki są kopiowane poprawnie,
ale exit 23 zaśmieca logi i może maskować prawdziwe błędy.
Fix: dodać --omit-dir-times do wywołania rsync w node-agent.py.
Lokalizacja: services/node-agent/src/node_agent.py — wywołanie rsync w pętli push.
Update 2026-06-11: potwierdzone flotowo — każdy node loguje fałszywe
"Event shipping failed" (rsync code 23) co cykl, mimo że pliki przechodzą; katalogi
/opt/homelab/events/* na VPS należą do aerbot, klient nie ustawi na nich czasów.
Deklaratywny zapis oskar ∈ aerbot w manifeście VPS
Data: 2026-06-09
Źródło: flota recovery — root cause: oskar spoza grupy aerbot(1000) → rsync Permission denied
Problem: przynależność do grupy jest zarządzana ręcznie (usermod -aG 1000 oskar ad-hoc).
Brak gwarancji po przeinstalowaniu VPS lub zmianie usera.
Fix: dodać do hosts/vps/host.yaml lub hosts/vps/capabilities.yaml sekcję
users: oskar: groups: [aerbot] — i wyegzekwować w deploy/bootstrap skrypcie VPS.
Alternatywa: zmienić właściciela /opt/homelab/events/ na oskar:oskar i zaktualizować
node-agent deploy skrypty.
Rozdzielenie worktree per task (agent.sh)
Data: 2026-06-09
Źródło: sesja — homelab-codex-ws-node-onboarding używany raz dla task/node-onboarding,
raz dla task/fix-event-bloat przez ręczne git checkout.
Problem: jeden worktree współdzielony przez dwa branche = anty-wzorzec. git branch
mogło wskazywać zły branch; + w listingu = pozornie "w innym worktree" ale nieprawda.
Prowadzi do commitowania na złej gałęzi.
Fix: egzekwować — jeden task = jeden worktree (agent.sh new <task-name>). Przy wejściu
do worktree zawsze git branch --show-current i weryfikacja .agent-task.
Długoterminowo: agent.sh new powinien odmawiać jeśli żądana gałąź jest już sprawdzona.
Zamknięte
🔴 KRYTYCZNY — deploy.sh vps niszczył control-plane — NAPRAWIONE (commit 3b71707)
Data: 2026-06-24 (wykryte), 2026-06-25 (incydent w produkcji), 2026-06-26 (naprawione)
Źródło: sesja 2026-06-24 + 2026-06-25 + 2026-06-26 (docs/sessions/2026-06-26.md)
Było (Problem A): control-plane w hosts/vps/services.yaml jako zwykły serwis
pętli deploy-node.sh. Pętla używała innego COMPOSE_PROJECT_NAME niż deploy-local.sh
(cwd=services/control-plane). Niezgodność → Recreate → No such container: <hash>_control-plane-observer → set -e przerywa pętlę → observer/supervisor/executor/ui
znikają. Każdy deploy.sh vps rozkładał mózg (potwierdzone w produkcji 2026-06-25).
Naprawione: guard w deploy-node.sh pomijający serwisy z własnym
services/<svc>/deploy-local.sh. control-plane ZOSTAJE w services.yaml (gate
pytest+build nadal go testuje), pomijana jest tylko destrukcyjna pętla deployu.
Potwierdzone w boju: deploy.sh vps wypisał Skipping control-plane: ma własną ścieżkę deployu, mózg Up 25h healthy, nietknięty.
Pozostało osobno: ghosty z poprzednich rozjazdów (Problem B — patrz Aktywne).
Flaky testy control-plane — state-leak w pytest — NAPRAWIONE (commit 992ff7c)
Data: 2026-06-24 (zgłoszone), 2026-06-25 (naprawione)
Źródło: sesja 2026-06-24 + sesja 2026-06-25 (docs/sessions/2026-06-25.md)
Było: test_incident_lifecycle.py flaky przez state-leak — OBSERVER_STATE_FILE
wyprowadzany przy imporcie, helpery patchowały STATE_DIR ale nie OBSERVER_STATE_FILE →
checkpointy pisane na realny dysk /opt/homelab/state/ z ścieżkami otagowanymi numerem
przebiegu pytest. Gate deploy.sh czerwony przy zdrowym kodzie.
Naprawione: autouse monkeypatch fixture redirectujący WSZYSTKIE ścieżki stanu w tym
OBSERVER_STATE_FILE; usunięto buggy _make_observer; posprzątano zatruty realny checkpoint.
Weryfikacja: 6/6 przebiegów → 28 passed. Gate rzetelny.
deploy-node.sh — brak --env-file (bind 0.0.0.0) — NAPRAWIONE (commit 686aca7)
Data: 2026-06-25
Źródło: sesja 2026-06-25 (docs/sessions/2026-06-25.md)
Było: deploy-node.sh nie przekazywał --env-file do compose up → zmienne .env
(jak TAILSCALE_BIND_IP) interpolowały się do pustego stringa → bind 0.0.0.0 zamiast
Tailscale IP. Potencjalna dziura na publicznym VPS.
Naprawione: guard if [ -f "services/<svc>/.env" ] + --env-file per-serwis przed
docker compose up. Worktree task/deploy-envfile-fix, merge ff-only.
Swap 2–4 GB na VPS — ZROBIONE
Data: 2026-06-22 (zgłoszone PENDING w sesji 2026-06-09 flota-recovery)
Źródło: sesja 2026-06-22 (docs/sessions/2026-06-22.md)
Było: VPS (3.7 GB RAM) z swap=0 → OOM 2026-06-01.
Zrobione: /swapfile 4 GB aktywny i trwały (wpis w /etc/fstab); vm.swappiness=10
na żywo i w /etc/sysctl.conf. Weryfikacja: free -h → Swap: 4.0Gi (0B used),
/proc/swaps zawiera /swapfile.
Uwaga: host-level one-off (nie czysto GitOps), udokumentowany jako celowy host-state
w hosts/vps/host.yaml. Brak commitu kodu — zmiana host-side gotowcem.
Observer staleness — martwy node pokazywany NOMINAL
Data: 2026-06-08 (złapane), status: OTWARTY w sensie implementacji
Problem: observer/supervisor trzyma ostatni znany stan; brak heartbeat TTL.
Chelsty-infra milczy, ale status NOMINAL podważa zaufanie do panelu.
Fix: heartbeat TTL → po przekroczeniu oznacz status stale lub down.
Powiązane: brain-watchdog ślepy na per-node freshness.
(Otwarty jako TODO implementacyjny — przeniesiony z sesji 2026-06-08)
Anomaly detection liveness — mózg uczy się wzorca dobowego per node (pomysł 2026-06-26)
Idea: zamiast statycznych okien czasowych w regułach alertowych (np. "lustro 7-23"),
mózg (supervisor/observer) czyta historię metryk z Prometheus (range queries / Grafana)
i SAM wykrywa wzorzec dobowy każdego węzła. up==0 zgodne z nauczonym wzorcem offline
(lustro zwykle off nocą, solaria nieregularnie) = NIE anomalia, nie alarmuj. up==0
odbiegające od wzorca = realna awaria → alert. Inteligencja w mózgu + dane jako źródło
wzorca, nie sztywne godziny wpisywane ręcznie.
Warunek: wymaga TYGODNI historii metryk. fleet-prometheus postawiony 2026-06-25 → realne dopiero za ~2-4 tygodnie, gdy uzbiera się wzorzec dobowy.
Pułapka: uczący się system może przeoczyć realną awarię pokrywającą się z typowym oknem offline (statyczna reguła jest głupia, ale przewidywalna). Uwzględnić przy projektowaniu.
Na teraz: targety scrape'owane BEZ polityki alertowej, label tylko node:. Prometheus
gromadzi historię. Anomaly detection = osobny świadomy projekt później (CC, z testami).