--- okf: "0.1" type: decision visibility: private status: active updated: 2026-08-03 links: - ../phases/backlog.md --- ## Aktywne ### Bug upstreamu `GoogleCloudPlatform/knowledge-catalog`: generator grafu pomija linki od `/` **Data**: 2026-07-31 **Źródło**: pilot fazy 5 — narty27 (`docs/sessions/2026-07-31-kb-f4-final-narty27.md`, sekcja „Wnioski do przeniesienia na fazę 5", pkt 3) **Problem**: generator grafu z `knowledge-catalog` pomija linki zaczynające się od `/` (ścieżki absolutne), wbrew §5.1 **własnej spec** OKF, która je dopuszcza. Efekt: część krawędzi grafu po prostu nie powstaje — cicho, bez ostrzeżenia. Rozbieżność implementacja↔spec po stronie upstreamu, nie naszej konfiguracji. **Obejście (zastosowane)**: własny generator grafu (cytoscape) w pilocie narty27 — nie używamy generatora upstreamu. **Fix**: zgłosić issue/PR do `GoogleCloudPlatform/knowledge-catalog` (minimalny repro: dokument z linkiem `/foo` → brak krawędzi w wyjściu grafu, mimo §5.1). Jeśli upstream naprawi — rozważyć powrót z własnego generatora przy fazie 5 (wiki-kompilat), żeby nie utrzymywać własnego kodu bez potrzeby. --- ### `hosts/solaria/runtime/ollama/docker-compose.override.yml` — brak w repo (rozjazd repo↔runtime) **Data**: 2026-07-31 **Źródło**: sesja 2026-07-31 (`docs/sessions/2026-07-31-kb-f4-final-narty27.md`, sekcja „Otwarte po sesji" pkt 2) **Problem**: `ollama` jest zadeklarowana w `hosts/solaria/services.yaml` (rola `llm-inference`, exposure `private` = bind na `TAILSCALE_BIND_IP` + loopback), ale `hosts/solaria/runtime/` zawiera wyłącznie `node-agent` i `stability-agent` — override dla `ollama` **nie istnieje w repo**. Host-specific konfiguracja żywego kontenera (rezerwacja GPU, bind, env) nie jest zatem wersjonowana: repo nie opisuje tego, co faktycznie biega na SOLARII. Ta sama klasa rozjazdu repo↔runtime co przy węzłach repo-less — dowolny redeploy z repo może wystawić serwis inaczej, niż działa dziś. Wzorzec docelowy istnieje obok: `hosts/piha/runtime/ollama-piha/docker-compose.override.yml`. **Fix**: zrekonstruować override z żywego kontenera na SOLARII (`docker inspect` → bind/porty/GPU/env/limity) i zacommitować jako `hosts/solaria/runtime/ollama/docker-compose.override.yml`; zweryfikować, że deploy z repo daje kontener identyczny z obecnym **zanim** ktokolwiek zrobi redeploy. --- ### `services/narty27`: `exposure: private` w `service.yaml` + README vs faktyczna publiczna ekspozycja (NPM VPS + LE) **Data**: 2026-07-31 **Źródło**: pilot fazy 5 — narty27 (`docs/sessions/2026-07-31-kb-f4-final-narty27.md`); znalezione przy porządkowaniu backlogu po tej sesji **Problem**: kontrakt serwisu deklaruje `private` — `services/narty27/service.yaml:5` (`exposure: private # LAN/Tailscale only; no npm vhost, no public ingress`) i to samo w `kb/runbooks/narty27-deploy.md` — podczas gdy `narty27.kapala.org` jest **publiczne** (NPM na VPS + cert Let's Encrypt). Pole `exposure` steruje traktowaniem ekspozycji przez agentów (patrz „Discovery Entry Points for Agents" w CLAUDE.md — `service.yaml` jest kontraktem operacyjnym, z którego agent czyta, jak zarządzać serwisem), więc rozjazd kontrakt↔rzeczywistość jest tu groźniejszy niż zwykła nieaktualna dokumentacja: agent podejmie decyzję na podstawie pola, które kłamie. **Fix (osobny task)**: (1) **najpierw** zweryfikować, co realnie konsumuje pole `exposure` (observer / supervisor / ścieżka deployu) — dopiero to pokaże, czy poza dokumentacją zmiana coś przestawia; (2) potem poprawić `service.yaml` + README na `public`, z komentarzem wskazującym NPM VPS host **#14** i cert LE **#38**. --- ### `scripts/ha/deploy.sh --delete`: brak wspólnego toru kasacji automatyzacji **Data**: 2026-07-27 **Źródło**: sesja porządków po audycie (`task/ha-porzadki`); kontekst bezpośredni: kasacja pkt 10 (`36b43e5`, para Tymka/powitanie-test/notify-router/para-prototyp) zrobiona pętlą ręcznych `curl DELETE` po API zamiast przez repo tooling. **Problem**: `deploy.sh` ma tylko WRITE (`POST /api/config//config/`) — zgodnie z DESIGN.md ("Deploy path") i istniejącym wpisem w tym backlogu (sekcja "Cutover HA ken", krok 4) DELETE obiektów usuniętych z repo jest poza zakresem, drift-check tylko ostrzega (`warnings`), nigdy nie kasuje. Efekt: jedyna droga usunięcia automatyzacji z żywej instancji to ręczne wywołanie API, bez drift-check/`check_config`/verify, czyli bez żadnej z gwarancji, które deploy.sh daje dla write. **Fix**: `deploy.sh --delete ` (albo `--delete` jako tryb pracy na plikach usuniętych z repo, wykrytych przez `drift_warnings`) z tym samym rytmem co write: dry-run domyślny, `--dry-run`/LIVE jak dziś, `DELETE /api/config//config/` per obiekt, verify (GET → oczekiwane 404) zamiast porównania treści. Scope jak przy write: automations/scripts/scenes. --- ### HA ken: `input_number.klima_salon_tolerancja` nie ma triggera — zmiana nie przelicza progu **Data**: 2026-07-27 **Źródło**: sesja porządków po audycie (`task/ha-porzadki`), przy okazji przeglądu `1784804667795` dla konwencji automatyzacji (pkt 17) **Problem**: `"Klima salon: włącz chłodzenie i synchronizuj cel"` (`1784804667795`) ma trigger `id: sync` na `input_number.klima_salon_temp_docelowa` (zmiana celu od razu przelicza próg), ale brak odpowiednika dla `input_number.klima_salon_tolerancja` — to ta sama klasa buga co "trigger brzegowy bez lustrzanego warunku" z audytu (sekcja 3, wzorzec `klima_salon` z fixu `faa2e2a`), tylko po stronie triggerów, nie warunków: zmiana tolerancji na dashboardzie nic nie przelicza do najbliższej naturalnej zmiany `sensor.thsalon_temperature`. **Fix**: dodać trigger `state` na `input_number.klima_salon_tolerancja` do gałęzi `sync` (albo do analogicznej gałęzi w automatyzacji OFF, jeśli tolerancja wpływa też na próg wyłączenia) w `1784804667795`, tak samo jak istniejący trigger na `_temp_docelowa`. --- ### HA ken: guard TRV kalibracji przed sezonem grzewczym (~2026-09) **Data**: 2026-07-23 **Źródło**: `kb/audits/ha-automatyzacje-2026-07-23.md` sekcja 1.4 (pkt 2 checklisty operatora) **Problem**: kalibracje TRV sypialnia (`1764751049013`) i Tymek (`1765817937658`) liczą `room_temp` jako średnią z czujnika zhimi, który jest `unavailable` od 2026-07-17 → `float(0)` w formule zaniża temperaturę o połowę → kalibracja dojechała do −5.0 (potwierdzone w fixtures). Latem (TRV `off`) nieszkodliwe; w sezonie grzewczym = trwałe przegrzewanie obu pokoi. Dodatkowo wszystkie 6 automatyzacji TRV pollują co 40–50s (baterie 20–38%), a SalonPrawy ma clamp `diff` ±5 zamiast ±1.5 jak reszta. **Fix (przed sezonem)**: (1) guard `is not unavailable` na czujnikach zhimi zamiast ślepego uśredniania; (2) ręczny reset kalibracji sypialnia/Tymek po naprawie; (3) zwolnić pętle do ≥5 min lub trigger na zmianę stanu; (4) ujednolicić clamp SalonPrawy; (5) reanimować albo wyłączyć TRV łazienki (`number.*_calibration` unavailable — urządzenie zniknęło z sieci). --- ### HA ken: przycisk graceful shutdown klimy salonowej na kartę dashboardu **Data**: 2026-07-23 **Źródło**: `kb/audits/ha-automatyzacje-2026-07-23.md` sekcja 3.1, fix-pack 1 (`task/ha-fix-pack-1`, DESIGN.md „Decyzje operatora po audycie 2026-07-23") **Kontekst**: po fix-packu 1 „Klima salon: wyłącz…" (`1784804668795`) respektuje `klima_salon_auto = on` dla gałęzi sunset/balkon; suszenie parownika przy ręcznym zgaszeniu `klima_salon_auto` działa już tylko jako efekt uboczny przełącznika trybu auto (osobna gałąź `choose` na trigger `auto_off`). Brakuje wygodnego jednego przycisku „wyłącz klimę i osusz teraz" niezależnego od przełącznika auto. **Fix**: dodać na dashboard przycisk/skrypt wywołujący `script.klima_salon_dry_off` bezpośrednio (bez przełączania `klima_salon_auto`), żeby ręczne graceful shutdown nie wymagało znajomości wewnętrznej logiki automatyzacji. --- ### HA ken: diagnoza wspólnej awarii sprzętowej 2026-07-17 (czujniki ruchu, pilot 4button, xiaomi_miot) **Data**: 2026-07-23 **Źródło**: `kb/audits/ha-automatyzacje-2026-07-23.md` sekcje 1.2, 1.6, 4.5 (pkt 1, 3, 15 checklisty operatora) **Problem**: restart HA / update Supervisora 2026-07-17 15:07 zbiega się z `unavailable` na: klaster czujników ruchu/obecności (mdwejscie, mdsypialnia, mdheli — przynajmniej od restartu; occusalon/mdtymka/mdubikacja gasną w kolejnych dniach — obraz siadających baterii), całej integracji xiaomi_miot (fan.zhimi_mb3/mc2, czujniki temperatury zhimi używane w kalibracjach TRV) i `sensor.4button_battery` (mimo że pilot 4button strzelał jeszcze 2026-07-12 — 8 automatyzacji salonu na tym urządzeniu). Nie jest jasne, czy to jedna awaria (Zigbee coordinator/integracja) czy zbieg kilku. **Fix**: (1) sprawdzić fizycznie baterie/re-pairing 6 czujników ruchu + czujnik wycieku WC (pkt 1 checklisty); (2) zdiagnozować integrację xiaomi_miot po stronie HA zamiast łatać pojedyncze automatyzacje (pkt 3); (3) sprawdzić fizycznie baterię pilota 4button (pkt 15). Reanimacja czujników ruchu odblokuje też ~15 cicho martwych automatyzacji (alerty on-leave, nocne gaszenie, `Poranek start`) — patrz audyt sekcja 1.2 dla pełnej listy skutków. --- ### HA ken: projekt „architektura night_mode" (konsolidacja sleep/night mode) **Data**: 2026-07-23 **Źródło**: `kb/audits/ha-automatyzacje-2026-07-23.md` sekcje 2.2, 2.4, 6 (pkt 7, 9 checklisty operatora — świadomie NIE załatane w fix-packu 1, patrz DESIGN.md „Decyzje operatora po audycie 2026-07-23") **Problem**: cztery flagi trybu (`sleep_mode`, `night_mode`, `passive_mode`, `movie_mode`) o częściowo pokrywającej się semantyce, sprawdzane niespójnie (raz jedna flaga, raz druga, raz OR obu); `automation.turn_off_sleep_mode_at_sunrise` steruje w rzeczywistości `night_mode`. Cztery nakładające się automatyzacje gaszą ten sam zestaw świateł nocą (`1702844937214` martwa, `1763384250145` martwa, `1768946230585` enforcer co ~10 min całą noc, `1700832676138` o 3:00) — po reanimacji martwych czujników (patrz wpis wyżej) trzy z nich ożyją naraz i zaczną się ścigać. `1768946230585` ma dodatkowo trigger `time_pattern /5` obok krawędziowego `off→on`, więc mimo aliasu „5 minut po aktywacji" gasi światła cyklicznie całą noc — do decyzji, czy to zamierzone. **Fix (projekt, nie one-liner)**: skonsolidować do jednego `input_select.tryb_domu` + jednej automatyzacji „nocne domknięcie" z jasnym priorytetem trybów i enforcerem (cykliczny dozorca vs. jednorazowe zadziałanie po aktywacji — decyzja pkt 7), plus finalny/ostateczny wyłącznik nocny zastępujący dzisiejsze cztery. Zakres większy niż fix-pack — osobny task. --- ### Executor: zepsuty JSON w `approved/` retry'owany w nieskończoność co 10s zamiast trafić do `failed/`/`rejected/` **Data**: 2026-07-23 **Źródło**: sesja 2026-07-22/23 (`docs/sessions/2026-07-23-control-plane-remediation-e2e.md`) **Problem**: podczas testów E2E remediacji bez SSH zepsuty JSON akcji w `approved/` (przyczyna: podstawienie zmiennej lokalnej w cudzysłowie w zagnieżdżonym heredoc przez ssh) powodował, że executor próbował go sparsować co cykl (10s), logując "Failed to move ... to running: Expecting value" bez końca — plik nigdy nie trafiał do `failed/`/`rejected/`. **Fix**: executor powinien przenosić nieparsowalny JSON do `failed/` (albo dedykowanego `malformed/`) po pierwszej próbie, nie zostawiać go do nieskończonego retry w `approved/`. --- ### Uprawnienia `actions/` naprawione TYLKO na PIHA (ręcznie) — SOLARIA i lustro niezweryfikowane **Data**: 2026-07-23 **Źródło**: sesja 2026-07-22/23 (`docs/sessions/2026-07-23-control-plane-remediation-e2e.md`) **Problem**: fix uprawnień `/opt/homelab/actions` (grupa `pi` + setgid, patrz Zamknięte) zastosowany ręcznie tylko na PIHA. SOLARIA (oskar tam podobno uid 1000 — do sprawdzenia czy problem w ogóle występuje) i lustro (repo-less, patrz osobny wpis niżej) niezweryfikowane — remediacja na tych węzłach może paść identycznie przy pierwszej próbie. **Fix docelowy**: node-agent powinien sam tworzyć `dispatch//` z właściwymi prawami (grupa współdzielona + setgid) przy starcie, żeby to nie wracało za każdym razem jako ręczna interwencja — patrz też „Tech-debt: globalny porządek uid/gid" niżej. --- ### `deploy-local.sh` (control-plane): brak twardego checka, że `.env` istnieje i `TAILSCALE_BIND_IP` jest ustawione **Data**: 2026-07-22 **Źródło**: sesja 2026-07-22/23 (`docs/sessions/2026-07-23-control-plane-remediation-e2e.md`) **Problem**: przy fixie bindu `operator_ui.py:18180` odkryto footgun: bez `.env`, `docker compose` tylko OSTRZEGA i po cichu wraca do bindu `0.0.0.0` zamiast failować — dokładnie ten sam wzorzec błędu jak historyczny bug fleet-prometheus/deploy-node.sh (brak `--env-file`, patrz Zamknięte, commit `686aca7`), tym razem w `deploy-local.sh`/control-plane. **Fix**: dodać twardy check w `deploy-local.sh` — `.env` musi istnieć i `TAILSCALE_BIND_IP` musi być ustawiony, inaczej abort przed `docker compose up`. --- ### `operator_ui.py` nie ma ŻADNEJ autoryzacji **Data**: 2026-07-22 **Źródło**: sesja 2026-07-22/23 (`docs/sessions/2026-07-23-control-plane-remediation-e2e.md`) **Problem**: po zamknięciu publicznego bindu 18180 (patrz Zamknięte, commit `9a5c160`) `operator_ui.py` nadal przyjmuje `/action/mutate` (w tym przejście do `approved`) bez żadnego uwierzytelnienia — chroni tylko granica Tailscale mesh, nie autoryzacja per operator. **Fix**: osobny temat — do zaprojektowania (token/basic auth/mTLS w mesh), poza zakresem fixu bindu. --- ### `alert_only` zapycha approval queue **Data**: 2026-07-22 **Źródło**: sesja 2026-07-22/23 (`docs/sessions/2026-07-23-control-plane-remediation-e2e.md`) **Problem**: przy reconie stanu wyjściowego 16 z 18 pending actions to były liveness-transitions typu `alert_only`, nie realne decyzje do klikania — operator musi przewijać szum, żeby znaleźć akcje, które faktycznie wymagają Approve/Reject. **Fix**: rozważyć osobny widok/filtr dla `alert_only` w operator UI, albo auto-acknowledge bez wejścia do tej samej kolejki co `container_restart`/`redeploy`. --- ### Crash-loop nie generuje `container_restart` — gap detekcja→akcja **Data**: 2026-07-22 **Źródło**: sesja 2026-07-22/23 (`docs/sessions/2026-07-23-control-plane-remediation-e2e.md`) **Problem**: `node_exporter` na PIHA miał 1048 restartów i event `[high]`, ale supervisor nie wygenerował żadnej akcji `container_restart` — crash-loop widoczny w evencie nie przekłada się na akcję remediacyjną. **Fix**: zbadać routing supervisora dla crash-loop sygnałów (`disk_pressure` i `containers_not_running` mają jasne mapowanie na akcje w CLAUDE.md — crash-loop najwyraźniej nie). --- ### Telegram yes/no dla pending actions brakuje **Data**: 2026-07-22 **Źródło**: sesja 2026-07-22/23 (`docs/sessions/2026-07-23-control-plane-remediation-e2e.md`) **Problem**: brain-watchdog ma `send_telegram` (używane dla alertów Prometheus), ale nic nie łączy go z `actions/pending` — operator musi wejść do operator UI, żeby zatwierdzić/odrzucić akcję, zamiast dostać yes/no bezpośrednio na Telegramie. **Fix**: rozszerzyć brain-watchdog (albo osobny konsument) o powiadomienie z przyciskami approve/reject per pending action. --- ### `homeassistant5` na PIHA: `Exited (0)`, nie podnosi się mimo `restart: unless-stopped` **Data**: 2026-07-22 **Źródło**: sesja 2026-07-22/23 (`docs/sessions/2026-07-23-control-plane-remediation-e2e.md`) **Problem**: kontener `homeassistant5` (legacy HA "ken", patrz cutover `kb/phases/backlog.md` sekcja "Cutover HA ken") zaobserwowany w stanie `Exited (0)` — exit code 0 = czyste zatrzymanie, więc Docker `restart: unless-stopped` świadomie go nie podnosi (to nie crash). Do zweryfikowania, czy to zamierzone wygaszenie z cutoveru czy coś zatrzymało kontener niezamierzenie. **Fix**: sprawdzić czy to spójne z planem wygaszenia `homeassistant5` (patrz cutover HA ken, krok 2 „Wygaszenie homeassistant5") — jeśli tak, brak akcji; jeśli nie, zbadać czemu wyszedł z kodem 0. --- ### lustro: `node_agent.py` wymaga ręcznego wypchnięcia do `/opt/homelab/deploy/node-agent` (repo-less) **Data**: 2026-07-22 **Źródło**: sesja 2026-07-22/23 (`docs/sessions/2026-07-23-control-plane-remediation-e2e.md`) **Problem**: węzeł lustro nie ma repo/gita — `node_agent.py` (z fixami remediacji bez SSH, commit `2dac154`) musi być ręcznie skopiowany do `/opt/homelab/deploy/node-agent`, żeby dotrzeć na ten węzeł. Ryzyko rozjazdu repo↔runtime identyczne jak przy innych repo-less węzłach. **Fix**: ustalić mechanizm dystrybucji kodu node-agenta na węzły repo-less (rsync przy deployu / paczka artefaktu / inny kanał) zamiast ręcznego kopiowania. --- ### 🔴 npm@VPS panel admina :81 publicznie osiągalny z internetu **Data**: 2026-07-10 **Źródło**: sesja `scripts/npm/npm_api.py` (skrypt do zarządzania NPM przez REST API) **Problem**: `services/npm/docker-compose.yml` mapuje `81:81` bez ograniczenia do interfejsu — Docker bindem domyślnym wystawia to na `0.0.0.0`, czyli panel admina NPM@VPS jest osiągalny z publicznego IP (`135.181.153.108:81`), nie tylko przez Tailscale mesh (`100.95.58.48:81`). Panel admina (login+hasło, bez 2FA wymuszonego) nie powinien być publiczny. Brak `hosts/vps/runtime/npm/docker-compose.override.yml` ograniczającego bind. **Fix**: dodać override z bindem `127.0.0.1:81:81` (dostęp tylko przez Tailscale/SSH tunnel) albo `:81:81`, zachowując `80`/`443` publiczne (to jest ich rola). Zweryfikować po zmianie, że `npm_api.py --npm vps` nadal łączy się przez `100.95.58.48:81`. --- ### Ghosty B WRÓCIŁY — hash-prefixed control-plane na VPS nieposprzątane **Data**: 2026-07-06 **Źródło**: sesja 2026-07-06 (`docs/sessions/2026-07-06.md`) — snapshot panelu agents.okit.pl **Problem**: hash-prefixed kontenery control-plane znów widoczne na VPS — wpis „ZNIKNĘŁY (potwierdzone reconem 2026-07-02)" w Zamkniętych zdezaktualizowany. **Fix**: ręczny `docker rm` hash-prefixed kontenerów control-plane na VPS; przy okazji sprawdzić, skąd wróciły (fix A `3b71707` miał blokować źródło divergence). **Update 2026-07-16** (sesja `docs/sessions/2026-07-16.md`): po odetkaniu supervisora (event flood + pętla zamrożona, oba naprawione dziś) potwierdzone, że wpisy `error` w topologii panelu to dokładnie te ghosty — hash-prefixed w world_state observera, NIE żywe kontenery (`docker ps -a exited=0` na VPS). Observer nie prune'uje wpisów po zniknięciu kontenerów spod tych nazw. To trzyma `System Status: ERROR` fałszywie. **Fix (do zrobienia)**: observer powinien weryfikować faktyczny stan kontenera przy budowaniu world_state i prune'ować wpisy dla kontenerów, których już nie ma (ta sama klasa błędu co „Rozjazd world-state observera: NOMINAL przed istnieniem" niżej). --- ### shadow_mode → remediacja: decyzja o auto-restart **Data**: 2026-07-16 **Źródło**: sesja 2026-07-16 (`docs/sessions/2026-07-16.md`) — wątek control-plane **Problem**: po odetkaniu supervisora (event flood + pętla zamrożona, oba naprawione dziś) kolejka akcji nadal pusta częściowo dlatego, że `shadow_mode=True` downgrade'uje HA `container_restart` do `alert_only` — supervisor widzi problem, ale świadomie nie enqueue'uje akcji restartu. **Do decyzji**: czy i kiedy włączyć auto-restart padłych kontenerów — wymaga architektury (guardraile, cooldowny, blast radius per serwis) zanim `shadow_mode=false`. Patrz też istniejący wpis „ha-diag-agent deploy ZABLOKOWANY" niżej — przed `shadow_mode=false` tam wymieniony konkretny target (`homeassistant5`). --- ### gokapi: deploy-node VPS rzuca błąd — brakujący `.env` **Data**: 2026-07-16 **Źródło**: sesja 2026-07-16 (`docs/sessions/2026-07-16.md`) — wątek control-plane **Problem**: `deploy-node.sh` na VPS rzuca błąd na serwisie gokapi z powodu brakującego `.env` (`/opt/homelab/config/gokapi/.env` nieutworzony lub niepełny — wzorzec z sesji 2026-07-09 `docs/sessions/2026-07-09-kb-configi-gokapi.md`). **Fix**: sprawdzić `services/gokapi/env.example`, utworzyć/uzupełnić `.env` na VPS wg konwencji `env.example` → `/opt/homelab/config//.env`. --- ### elasticsearch + diskover w stanie error na PIHA — observer zna usunięte serwisy **Data**: 2026-07-06 **Źródło**: sesja 2026-07-06 (`docs/sessions/2026-07-06.md`) — snapshot panelu agents.okit.pl **Problem**: elasticsearch i diskover usunięte z PIHA w module 0 (2026-07-02), ale observer wciąż je zna i raportuje `error` w panelu. World-state nie zapomina serwisów, które przestały istnieć — ta sama klasa błędu co „NOMINAL przed istnieniem" (patrz wpis z 2026-06-25). **Fix**: wyczyścić martwe wpisy z world_state / dodać wygaszanie serwisów nieobecnych w desired state i w dockerze. --- ### Gotchas (z 2026-06-30 — migracja kapala.org → Cloudflare/wildcard) **Data**: 2026-06-30 **Źródło**: sesja 2026-06-30 (`docs/sessions/2026-06-30-kapala-cloudflare-wildcard-mesh.md`) - **NPM custom WS config + Websockets Support:** NIE wklejać `proxy_http_version 1.1;` do Advanced gdy Websockets Support = ON. NPM dodaje tę dyrektywę sam → duplikat → nginx -t failuje → plik proxy_host/.conf się NIE generuje → "unrecognized name" mimo dobrego certu. Objaw mylący (wygląda jak problem certu/DNS). Diagnoza: `strings /data/database.sqlite | grep ` → pole nginx_err. - **Cloudflare auto-proxy na import:** CF proxuje A/CNAME przy dodaniu strefy. DKIM CNAME (fm1/2/3._domainkey) proxied = zepsuty podpis maila. Zawsze przełączyć na DNS only (szara chmurka) przed aktywacją. Reserved/CGNAT IP (Tailscale 100.x) CF wymusza DNS only automatycznie. --- ### Migracja okit.pl → Cloudflare (większy projekt, firmowa domena) **Data**: 2026-06-30 **Źródło**: sesja 2026-06-30 (`docs/sessions/2026-06-30-kapala-cloudflare-wildcard-mesh.md`) Naprawi wszystkie wygasłe certy okit.pl naraz (HTTP-01 failuje przy mesh DNS): ap, audiobooks, code-server, dysk, forgejo, ha-embed, hagc, ngpm, node-red, okit.pl, pihole, ha.okit.pl. Wzorzec jak kapala.org: NS na CF, wildcard *.okit.pl przez DNS-01, przepiąć hosty. UWAGA: okit.pl ma usługi publiczne (foty) — rozdzielić mesh-only od publicznych. Ostrożnie — firmowa domena. --- ### foty.kapala.org renew failuje (#47, expired 2026-06-19, HTTP-01) **Data**: 2026-06-30 **Źródło**: sesja 2026-06-30 (`docs/sessions/2026-06-30-kapala-cloudflare-wildcard-mesh.md`) Foty są PUBLICZNE celowo (zewn. dostęp za hasłem NPM) — port 80 powinien być dostępny, więc HTTP-01 powinno działać. Sprawdzić czemu failuje (DNS foty wskazuje na zły IP? port 80 zablokowany?). NIE przenosić na mesh. --- ### Cleanup po błędnej ścieżce HA-Tailscale-addon (z 2026-06-29) **Data**: 2026-06-30 **Źródło**: sesja 2026-06-30 (`docs/sessions/2026-06-30-kapala-cloudflare-wildcard-mesh.md`) - ha-ken: Stop + Uninstall add-on Tailscale - Tailscale admin: usunąć node ha-ken (100.98.128.40) - 42.pl/okit.pl: usunąć rekord ha-ken → 87.205.110.38 (jeśli jest) --- ### Stary ha.okit.pl (cert wygasł 6/28, teraz "Not Used") **Data**: 2026-06-30 **Źródło**: sesja 2026-06-30 (`docs/sessions/2026-06-30-kapala-cloudflare-wildcard-mesh.md`) Zostawić lub usunąć proxy host + DNS. Niepilne (martwy, nie szkodzi). --- ### Stopniowa migracja usług domowych okit.pl → kapala.org (mesh-only) **Data**: 2026-06-30 **Źródło**: sesja 2026-06-30 (`docs/sessions/2026-06-30-kapala-cloudflare-wildcard-mesh.md`) dysk, audiobooks, budget, code-server, home, ha-embed, node-red... wg wzorca migracji usługi na kapala.org (patrz sesja: CF rekord A → 100.108.208.3 DNS only, NPM proxy host z wildcard *.kapala.org, Advanced PUSTE, weryfikacja grep+curl). --- ### `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). --- ### 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. **Update 2026-07-02**: ghost kontenery (bug B) zniknęły z VPS — jeśli objaw wróci, hipoteza "źródłem są ghosty" jest już nieaktualna. UWAGA: ślepy supervisor na SATURN (brak mountu repo, patrz sesja 2026-07-02) to INNY przypadek — nie mylić z tym bugiem. **Update 2026-07-16** (sesja `docs/sessions/2026-07-16.md`, druga połowa dnia): dwie głębsze przyczyny pustej kolejki znalezione i naprawione (petla supervisora zamrożona ~24h — patrz „Supervisor: pętla zamrożona…" w Zamkniętych; event flood 358k plików paraliżujący reconcile — patrz „Event flood…" w Zamkniętych). Po obu fixach supervisor tika i reconcile się kończy, ALE objaw z tego wpisu (brak `redeploy` mimo widocznego `error` — elasticsearch/diskover na piha, ollama solaria) **nadal aktualny** — drift→action nie domyka się mimo odetkanego mózgu. Zostaje otwarte jako osobne dochodzenie. --- ### 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 1. **Stale komentarz** `node_agent.py:546-548` — twierdzi, że kontener "runs as root"; nieaktualne od `user: "1000:1000"`. 2. **Sukces shippingu na `logger.debug`** → podnieść do `info` lub 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 `). 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. ---