homelab-codex-ws/docs/sessions/2026-08-07.md
oskar 8031396f04 docs: session 2026-08-06 — deploy fixu dispatch 0o775 + zdjecie M1 na SOLARII i VPS
Wdrozenie do runtime dwoch zmergowanych commitow (52eca1c, 1bab321), zero zmian
w kodzie. Deploy przez deploy-service.sh --build-if-needed, nie przez
deploy.sh <target>: ten drugi to dyspozytor Saturn-side po SSH deployujacy caly
node (na VPS ruszylby npm/outline/joplin/ai-cluster), a sesja toczyla sie z
SOLARII, gdzie `ssh solaria` to polaczenie do samego siebie.

Pierwszy cykl prune po zdjeciu M1 poszedl zgodnie z ostrzezeniem z 1bab321 —
markera last-docker-cleanup nie bylo na zadnym z nodow, wiec cleanup odpalil
~0,5 s po starcie. Zero ubytkow kontenerow (SOLARIA 9/9, VPS 24/24), humanai-*
nietkniete. Dwie prognozy wymagaly korekty, obie bo `docker images` pokazuje
rozmiar pozorny z warstwami wspoldzielonymi: na SOLARII 4 dangling zniknely, ale
SpaceReclaimed=0 (warstwy dzielone z control-plane i kb-query), a na VPS jedyny
dangling okazal sie zywym obrazem outline-postgres-1 (flaga U) i prune go nie
ruszyl — slusznie.

Test dispatch end-to-end: wszystkie 6 kryteriow spelnione. dispatch/lustro
755 -> 775 w momencie zapisu przez executora, plik akcji zniknal ze zrodla i nie
wrocil, oba zalegle pliki z 10:39 i 11:08 zdrenowaly sie przy okazji, a spam
"already processed — skipping" co 60 s ustal. Poszlo przez approved/, nie przez
approval operatora: supervisor auto-anulowal pending po 7 s jako
drift_resolved_auto, zanim operator zdazyl kliknac.

Klasyfikacja rc=23 pozostaje niezweryfikowana na zywo — LUSTRO ma nadal stary
node-agent (fee079e8), ktory fizycznie nie umie tego zalogowac (follow-up #3).

Refs docs/sessions/2026-08-06.md (follow-up #1, #5)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 15:22:49 +02:00

215 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
okf: "0.1"
type: session-log
visibility: private
status: active
updated: 2026-08-06
links: []
---
# Session log 2026-08-07
> **Uwaga do datowania.** Praca wykonana **2026-08-06, 14:1015:25 CEST**. Plik nosi datę
> następnego dnia na wyraźne życzenie operatora, bo `docs/sessions/2026-08-06.md` jest już
> zajęty przez sesję fazy mailowej z tego samego okna. Wszystkie znaczniki czasu w treści
> są rzeczywiste (2026-08-06).
## Session 15:25
Wdrożenie do runtime dwóch fixów zmergowanych na `master` (supervised, checkpointy
zatwierdzane przez operatora): dispatch `0o775` + rc=23 w executorze/node-agencie
(`52eca1c`) oraz zdjęcie mitygacji M1 na SOLARII i VPS (`1bab321`).
### Commits
Ta sesja **nie wprowadziła żadnych commitów** poza niniejszym logiem — deploy runtime,
zero zmian w kodzie. Wdrożone commity powstały wcześniej, na branchu
`task/dispatch-perms-m1`:
```
1bab321 revert(m1): zdjecie NODE_TYPE=lte_node na SOLARII i VPS po wdrozeniu R1
52eca1c fix(dispatch): inbox 0o775 + rsync rc=23 przestaje byc cichy
```
W trakcie sesji main checkout przesunął się o `75d9695 docs(recon): przyrostowka IMAP
gmail + fastmail` (druga sesja operatora, docs-only). Bez wpływu: `node_agent.py` ma to
samo `sha256 aec6cb03` w obu drzewach, więc SOLARIA — zdeployowana jeszcze z `1bab321`
nie rozjechała się z VPS-em deployowanym z `75d9695`.
### Files changed
Brak — drzewo robocze czyste przez całą sesję, poza tym plikiem.
### Deploys
| Node | Serwis | Przed | Po | Wynik |
|---|---|---|---|---|
| SOLARIA | node-agent | `node_agent.py` `fee079e8`, obraz `438111e2` | `aec6cb03` == repo HEAD, obraz `bc28a30a` | OK |
| VPS | node-agent | `node_agent.py` `c21967d3`, obraz `27be875d` | `aec6cb03` == repo HEAD | OK |
| VPS | control-plane | `executor.py` `5ca0490e`, 4 obrazy z 2026-08-05 | `1c3b569f` == repo HEAD, 4 obrazy przebudowane | OK |
Mechanizm: `scripts/deploy/deploy-service.sh --build-if-needed` dla node-agenta (ta sama
ścieżka co 2026-08-05). **Nie** użyto `scripts/deploy/deploy.sh <target>`: jest to
dyspozytor Saturn-side po SSH, deployujący *cały* node — na VPS ruszyłby npm, outline,
joplin i ai-cluster, czyli daleko poza zakres, a sesja toczyła się z SOLARII (`ssh solaria`
to połączenie do samego siebie).
`NODE_TYPE` po zdjęciu M1: SOLARIA `lte_node`**`ai_node`** (jawnie w override),
VPS `lte_node`**linia usunięta**, `NODE_TYPE=""` z base compose → `_resolve_node_type()`
zwraca `standard`. Log startowy potwierdza jedno i drugie (`type=ai_node`, `type=standard`),
czyli przewidywanie z commita `1bab321` co do pustego stringa było trafne.
Gate testowy: **pytest niedostępny w main checkoucie** (`.venv` bez pytest,
`~/.local/bin/pytest` ma zepsuty `_pytest`). Oparto się na wyniku sprzed merge'a
(node-agent 70 passed, control-plane 173 passed) plus `docker build` obu stacków przy
deployu. Follow-up #4.
### Pierwszy cykl cleanup po zdjęciu M1
Na obu nodach marker `/opt/homelab/state/last-docker-cleanup` **nie istniał** (M1 blokował
zapis od 2026-08-04), więc `_cleanup_rate_ok()` zwrócił `True` i prune poszedł w pierwszym
cyklu, ~0,5 s po starcie — zgodnie z ostrzeżeniem w `1bab321`. Dlatego kolejność w każdym
kroku była: **najpierw tagi rollback, potem deploy.**
SOLARIA:
```
INFO - node-agent starting: node=solaria type=ai_node
INFO - Pruned dangling images (0 MB reclaimed)
INFO - No disposable stopped containers (kept 0 managed)
INFO - Pruned build cache (91 MB reclaimed)
```
VPS:
```
INFO - node-agent starting: node=vps type=standard
INFO - Pruned dangling images (0 MB reclaimed)
INFO - No disposable stopped containers (kept 0 managed)
INFO - Pruned build cache (239 MB reclaimed)
```
**Zero ubytków kontenerów na obu nodach**`docker ps -a` przed vs po, diff nazw pusty
(SOLARIA 9/9, VPS 24/24). `humanai-mailer` i `humanai-landing` (bez definicji w repo)
nietknięte. Tagi `rollback-*` przeżyły prune.
Dwie prognozy przedwdrożeniowe wymagały korekty — obie z tego samego powodu, że
`docker images` pokazuje **rozmiar pozorny z warstwami współdzielonymi**, a nie realny
odzysk:
* **SOLARIA, „4 dangling ≈ 553 MB":** cztery obrazy faktycznie zniknęły (35 → 32, przy
+1 nowym buildzie), ale `SpaceReclaimed` = **0 MB**. Ich warstwy są współdzielone z
control-plane i kb-query. Realny odzysk obrazów ≈ 70 MB (17,89 → 17,82 GB); z build
cache (606,1 → 510,5 MB) łącznie ≈ 165 MB.
* **VPS, „1 dangling 395 MB":** ten obraz to **żywy `outline-postgres-1`** — untagged, ale
oznaczony `U` (in use). Docker odmawia usunięcia obrazu używanego przez kontener, więc
prune go nie ruszył i **nie miał prawa ruszyć**. Realny odzysk to wyłącznie build cache
239 MB (z 250,8 MB reclaimable).
Kontener `control-plane-ui` na SOLARII stoi w stanie `created` — poza zasięgiem prune'a
podwójnie: `_prune_stopped_containers()` listuje wyłącznie `status=exited`, a kontener ma
i tak label compose oraz `restart=unless-stopped`.
### Weryfikacja fixu dispatch end-to-end
Stan wejściowy zdjęty **przed** deployem control-plane (patrz follow-up #1 — inaczej
`deploy-local.sh` by go zatarł):
| ścieżka | mode | |
|---|---|---|
| `actions/dispatch/piha` | 775 | historycznie działał |
| `actions/dispatch/lustro` | **755** | wyciek |
| `actions/deploy` | 755 | inbox deploy-runnera, bez podkatalogów |
Mechanizm potwierdzony co do joty: inbox `aerbot:aerbot 755`, a ciągnie z niego
`VPS_EVENTS_USER=oskar` — uid **1002**, tylko *członek* grupy `aerbot` (gid 1000). Grupa ma
`r-x` bez `w`, więc `--remove-source-files` nie może zrobić unlinku. Dwa pliki akcji z
10:39 i 11:08 leżały w źródle, a LUSTRO re-pullowało je **co 60 s**, odbijając od bramki
idempotencji:
```
13:15:13 INFO - Action container-restart-lustro-node-exporter already processed — skipping
13:15:13 INFO - Action container-restart-lustro-watchtower already processed — skipping
13:16:16 ... (to samo) 13:17:19 ... (to samo)
```
**Test HITL.** Cel zmieniony z `watchtower` na `node-exporter`: watchtower na LUSTRO jest
w crash-loopie (137 restartów, `restart=always`, nie potrafi pullnąć lokalnego obrazu
`piper-tts-rpi5` — 401 z Docker Huba), więc nie dałby czystego sygnału. Nowe `action_id`
(`container-restart-lustro-node-exporter-dispatchfix`), bo recykling starego odbiłby się od
bramki idempotencji na LUSTRO i zawiesiłby akcję w `running` do timeoutu.
**Ścieżka: przez `approved/`, nie przez approval operatora.** Plik `pending` zapisany
13:16:56 UTC został auto-anulowany przez supervisora po **7 sekundach**
(`drift_resolved_auto` — `node-exporter` był zdrowy), zanim operator zdążył kliknąć. Zgodnie
z ustaleniem przed testem druga kopia trafiła prosto do `approved/`; cel testu to mechanika
dispatchu, nie ścieżka approvalu. Pole `status` w tej kopii zostało `cancelled` — bez
znaczenia, executor kieruje się katalogiem, nie polem.
Wynik — wszystkie sześć kryteriów spełnione:
1. **755 → 775** dokładnie w momencie zapisu przez executora (13:17:23) ✅
2. plik akcji **zniknął** ze źródła po pobraniu i **nie wrócił** w kolejnych cyklach ✅
3. **oba zaległe pliki zdrenowane przy okazji**`dispatch/lustro/` całkowicie pusty ✅
4. spam `already processed — skipping` **ustał**: dwa pełne cykle (13:19:25, 13:20:28) z
zerem trafień ✅
5. akcja `completed` przez `action_result` po 70 s, `node-exporter` z nowym uptime ✅
6. zero ERROR/WARNING w executorze, observerze, supervisorze i node-agentach — poza znanym,
niezwiązanym crash-loopem watchtowera na LUSTRO ✅
`13:18:23 INFO - Restarted container 'node-exporter' for action
container-restart-lustro-node-exporter-dispatchfix`
**Czego ten test NIE dowiódł.** Druga połowa fixu — klasyfikacja rc=23 z WARNING-iem — na
LUSTRO nie pojechała: node-agent tam ma nadal `fee079e8` (ten sam sha, który SOLARIA miała
przed dzisiejszym deployem). `grep -c "rc=23"` = 0 w logach LUSTRO dlatego, że stary kod
fizycznie nie umie tego zalogować, a nie dlatego, że jest dobrze. Follow-up #3.
### Stan tagów rollback
| Node | Tag | Image ID |
|---|---|---|
| SOLARIA | `node-agent:rollback-pre-dispatchfix` | `438111e2` |
| SOLARIA | `node-agent:rollback-pre-r1` (z 2026-08-05) | `c80a711f` |
| VPS | `node-agent:rollback-pre-dispatchfix` | `27be875d` |
| VPS | `control-plane-executor:rollback-pre-dispatchfix` | `b878d835` |
| VPS | `control-plane-observer:rollback-pre-dispatchfix` | `4b79511d` |
| VPS | `control-plane-supervisor:rollback-pre-dispatchfix` | `357b70d1` |
| VPS | `control-plane-operator-ui:rollback-pre-dispatchfix` | `e522bbaa` |
Pierwsze tagi `rollback-*` na VPS w ogóle. Otagowano wszystkie cztery obrazy control-plane,
nie tylko executora — `deploy-local.sh` przebudowuje cały stack, więc każdy potrzebuje celu
rollbacku.
### Follow-upy
1. **`deploy-local.sh` robi rekurencyjny `chown` + `chmod` na całym `/opt/homelab`** — do
przeglądu, czy to w ogóle pożądane. Oba branche by dziś zadziałały: `sudo chown -R
1000:1000` (wyzwalacz: `events/solaria/evt-solaria-1785946768-node_health-node.json`) i
`sudo chmod -R 775` (wyzwalacze: `actions/dispatch/lustro`, `actions/deploy`). Ten drugi
ustawiłby `dispatch/lustro` na 775 z zupełnie innego powodu niż fix w executorze,
zacierając stan wejściowy testu, i nadałby 775 *plikom* w całym drzewie runtime (stąd
`-rwxrwxr-x` na heartbeatach i JSON-ach akcji). **Świadoma decyzja operatora: nie
naprawiać ręcznie.** Control-plane zdeployowano samym krokiem compose (`up -d --build
--force-recreate`) — bez sudo, więc rozjazdy chown/chmod zostały nietknięte i czekają na
przegląd. Dodatkowo `sudo` na VPS wymaga hasła, więc kanoniczna ścieżka i tak kończyłaby
się handoffem (exit 5).
2. **Lokalny control-plane na SOLARII** (executor/observer/supervisor/ui, `dispatch/solaria/`
z 2026-07-22) ma nadal executor z bugiem `0o755`. Nie jest dyspozytorem floty — log to
same `Starting executor loop` — ale to drugi, niezdeployowany egzemplarz tego samego
kodu. Osobna sesja.
3. **LUSTRO i PIHA bez dzisiejszego node-agenta.** LUSTRO: `fee079e8`. Bez R-fixu rc=23
nadal *połykają* nieudany unlink — dziś to nie boli, bo executor już nie tworzy złych
inboxów, ale każdy przyszły wyciec o innej przyczynie znowu będzie niewidoczny.
4. **Brak pytest w main checkoucie na SOLARII**`deploy.sh <target>` przewróciłby się na
gate (exit 2) bez `--no-gate`. Do naprawy, zanim ktoś sięgnie po kanoniczną ścieżkę
deployu z tej maszyny.
5. **`watchtower` na LUSTRO w crash-loopie** — 137 restartów, `restart=always`, próbuje
pullnąć lokalnie zbudowany `piper-tts-rpi5:latest` z Docker Huba i dostaje 401.
Niezwiązane z tym deployem, ale generuje WARNING co 60 s w logach node-agenta.
6. **Duplikat akcji testowej**`container-restart-lustro-node-exporter-dispatchfix.json`
leży jednocześnie w `completed/` (właściwy przebieg) i w `cancelled/` (kopia z
auto-anulowania sprzed approvalu). Pozostawione bez zmian — kosmetyka, żaden zapis
produkcyjny nie był potrzebny.
### Narrative
> _user-provided summary_