Rozstrzygniecie 1. Monolit mieszal cztery typy OKF. Rozbity PER TYP po
granicy sekcji `##`:
10 x decision — pozycje backlogu (w tym backlog-aktywne 28 KB
i backlog-zamkniete 12 KB, ktore zostaja calosciami)
7 x incident — bugi/awarie dotad wtopione w backlog: cutover HA ken,
checkpoint observera, ha-diag-agent node=unknown,
deploy-local ghost-kontenery, paperless-worker config,
deploy-node nie przebudowuje obrazu, ollama bez sterownika
2 x phase — HA configs-as-code, monitoring floty Prometheus
kb/phases/backlog.md zostaje jako cienki indeks (type: phase, status: active):
oryginalna preambula + wygenerowany spis linkow do wszystkich 19 elementow.
23 przychodzace odwolania zostaja przepiete na ta sciezke w grupie 7.
NIE rozbijano po `###` (38 pozycji w "Aktywne" + 13 w "Zamkniete" = 51
plikow). Rozstrzygniecie mowi "rozbij per typ", a nie per pozycja;
rozdrobnienie do 51 plikow rozerwaloby czytelnosc backlogu.
Kontrola: preambula + 19 sekcji == oryginal z HEAD (multizbior niepustych
linii). Tresc pozycji nietknieta.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
626 lines
32 KiB
Markdown
626 lines
32 KiB
Markdown
---
|
||
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 `services/narty27/README.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/<domain>/config/<id>`) —
|
||
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 <instance> --delete <plik...>` (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/<domain>/config/<id>` 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**: `services/home-assistant/docs/audyt-automatyzacji-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**: `services/home-assistant/docs/audyt-automatyzacji-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**: `services/home-assistant/docs/audyt-automatyzacji-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**: `services/home-assistant/docs/audyt-automatyzacji-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/<node>/` 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
|
||
`docs/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 `<tailscale_ip>: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/<service>/.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/<id>.conf się NIE generuje → "unrecognized name" mimo
|
||
dobrego certu. Objaw mylący (wygląda jak problem certu/DNS).
|
||
Diagnoza: `strings /data/database.sqlite | grep <domena>` → 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 <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.
|
||
|
||
---
|
||
|