diff --git a/.claude/skills/node-onboarding/SKILL.md b/.claude/skills/node-onboarding/SKILL.md index b054b14..d18d632 100644 --- a/.claude/skills/node-onboarding/SKILL.md +++ b/.claude/skills/node-onboarding/SKILL.md @@ -5,7 +5,7 @@ description: > repo manifest, Tailscale mesh, node-agent, monitoring, and UI registration. Keywords: "nowy node", "dodaj node", "onboarding", "onboard node". living_doc: true -maturity: partial # PROVEN: 00-access; SCAFFOLD: base → verify. Update after each step lands on a real node. +maturity: partial # PROVEN: 00-access, 20-base, 30-node-agent; WRITTEN: 40-register, 50-verify (live pending). Update after each step lands on a real node. --- > **Living document** — sections marked **SCAFFOLD** are stubs waiting for battle-testing on a real node. @@ -22,10 +22,10 @@ User asks to onboard / add a new node. Load this skill before touching any onboa ``` preflight (read-only) └─ 00-access [PROVEN] - └─ base [SCAFFOLD] - └─ node-agent [SCAFFOLD] - └─ register [SCAFFOLD] - └─ verify(50) [SCAFFOLD] + └─ 20-base [PROVEN] + └─ 30-node-agent [PROVEN] + └─ 40-register [WRITTEN — live pending] + └─ 50-verify [WRITTEN — live pending] ``` Never skip ahead. Each step must exit 0 before the next begins. @@ -57,9 +57,11 @@ scripts/onboard/onboard.sh --node --dry-run | `00-preflight` | `steps/00-preflight.sh` | SCAFFOLD | Read-only: arch, RAM, docker, swap, MM runtime → YAML snippet for node.yaml | | `00-access` | `steps/00-access.sh` | **PROVEN** | SSH key → `first_contact`, install Tailscale, `tailscale up` (interactive URL), verify over mesh | | `10-bootstrap-runtime` | `steps/10-bootstrap-runtime.sh` | SCAFFOLD | Create `/opt/homelab/` layout, `chown ` | +| `20-base` | `steps/20-base.sh` | **PROVEN** | swap→zram, `/opt/homelab/` layout, event dir `/opt/homelab/events//` | | `20-install-docker` | `steps/20-install-docker.sh` | SCAFFOLD | Install Docker Engine if `docker_present=false`; skip if already installed | -| `40-deploy-node-agent` | `steps/40-deploy-node-agent.sh` | SCAFFOLD | Deploy node-agent container; user 1000:1000; `mem_limit` from node.yaml | -| `50-verify` | `steps/50-verify.sh` | SCAFFOLD | End-to-end smoke: event reaches control plane, visible in UI, Telegram alert path | +| `30-node-agent` | `steps/30-node-agent.sh` | **PROVEN** | rsync base compose + override, `docker compose up -d --build`, verify container + events | +| `40-register` | `steps/40-register.sh` | WRITTEN | Dopisuje node do `inventory/topology.yaml` + tworzy `hosts//services.yaml`, commit na branchu (bez push) | +| `50-verify` | `steps/50-verify.sh` | WRITTEN | SSH node: container+events; SSH VPS: restart observer + heartbeat poll + world/nodes.json | --- @@ -122,6 +124,9 @@ Always run `--dry-run` first; dry-run must print real commands (`run()` propagat | `yaml_get` drops value prefix after `:` | Non-greedy colon: `s/^[[:space:]]*[^:]*:[[:space:]]*//'` — handles `systemd:unit` correctly | | `yaml_get` keeps inline YAML comments | Strip with `s/[[:space:]]\+#.*$//` after extraction (requires ≥1 space before `#`) | | dry-run stops at orchestrator level | `run()` wrapper + `export DRY_RUN=1` propagated to all step scripts; probes execute for real | +| rsync push Permission denied to VPS events/ | ssh-user must be in the **group that owns `/opt/homelab/events/`** (aerbot/1000 on VPS). Symptom: silent WARNING in node-agent log, 292k files backlog, panel stale. Fix: `usermod -aG 1000 ` on VPS + re-login | +| observer not seeing new node after topology.yaml edit | `_load_inventory()` runs once at `__init__`. After `git pull` on VPS (bind-mount is live), **`docker restart control-plane-observer`** is required — no redeploy needed | +| worktree on wrong branch | Always check `git branch --show-current` on entry. One task = one worktree (`agent.sh new`). Never manually `git checkout` between task branches in the same worktree | --- diff --git a/docs/backlog.md b/docs/backlog.md new file mode 100644 index 0000000..736ad94 --- /dev/null +++ b/docs/backlog.md @@ -0,0 +1,57 @@ +# Tech-debt backlog + +Centralny tracker tech-długu i znanych usterek. Wpisy ze sesji — dodawaj z datą i kontekstem. + +--- + +## Aktywne + +### 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. + +--- + +### 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. + +--- + +## Zamknięte + +### 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)* diff --git a/docs/sessions/2026-06-09-flota-recovery-lustro-register.md b/docs/sessions/2026-06-09-flota-recovery-lustro-register.md new file mode 100644 index 0000000..cda1c89 --- /dev/null +++ b/docs/sessions/2026-06-09-flota-recovery-lustro-register.md @@ -0,0 +1,124 @@ +# Sesja 2026-06-09 — flota recovery + LUSTRO register + +## Cel + +Diagnoza cichej awarii reportingu floty; dokończenie kroku REGISTER dla LUSTRO +(40-register.sh + 50-verify.sh); update skilla node-onboarding. + +--- + +## GŁÓWNE: 8-dniowa cicha awaria reportingu floty — ROZWIĄZANA + +### Root cause + +`oskar` (uid 1002) **spoza grupy aerbot (1000)** na VPS. +`/opt/homelab/events/*` = `aerbot:aerbot 775` → `oskar` w "other" (r-x). +`rsync` push z każdego node'a (jako `oskar` przez SSH) = **Permission denied** przy +zapisie → `--remove-source-files` nie czyścił backlogu → **292 000 plików** nagromadzonych +w staging cache node-agentów. + +### Fix + +```bash +usermod -aG 1000 oskar # na VPS; ssh re-login wymagany +``` + +### Weryfikacja + +- VPS `events/piha` 3443 pliki (rośnie) +- `piha` lokalnie: 2 pliki (staging wyczyszczony) +- Panel agents.okit.pl: vps / piha / solaria — Last Seen świeże + +### Diagnoza — 5 warstw, 4 obalone hipotezy + +Verify-before-fix obalił kolejno: +1. `authorized_keys` missing — klucz był, SSH działał (piha→VPS ręcznie OK) +2. Remote agent down — procesy `rsync` widoczne w `ps`, logi bez crash +3. VPS IP zmiana — Tailscale IP niezmieniony 100.95.58.48 +4. Bridge/relay cutoff — ping VPS→piha OK przez mesh + +5 warstwa (błąd uprawnienia) odkryta przez ręczny `rsync` jako `oskar` na VPS → +`Permission denied (13)` → `stat /opt/homelab/events/` → `aerbot:aerbot 775`. + +### Dlaczego awaria była CICHA (3 warstwy maskujące) + +| Warstwa | Mechanizm | +|---------|-----------| +| (a) shipping fail | Logowany jako `WARNING`, nie crash — node-agent nie failował, milczał | +| (b) observer staleness | Stale node pokazywany NOMINAL — brak heartbeat TTL, observer trzyma ostatni znany stan | +| (c) brain-watchdog | Ślepy na per-node freshness — nie monitoruje świeżości eventów per-node | + +### Pozostały drobny błąd + +`rsync` exit code 23: `set-times` na katalogu = `EPERM` (oskar nie jest właścicielem +`/opt/homelab/events/` — `aerbot` jest). Kosmetyka — rsync działa poprawnie. +**Fix**: dodać `--omit-dir-times` do wywołania rsync w node-agent (wpisane do backlogu). + +--- + +## LUSTRO register: stan po sesji + +### Dokonane + +- `40-register.sh` — napisany i zcommitowany na `task/node-onboarding` + - Idempotentny: grep topology, `[[ -f services.yaml ]]`, `git diff --quiet` + - Commituje tylko `inventory/topology.yaml` + `hosts/lustro/services.yaml` na bieżącym branchu + - BEZ `git push` (merge należy do operatora) +- `50-verify.sh` — napisany i zcommitowany + - 4 checki: node-agent running, eventy, observer restart + heartbeat poll, world/nodes.json + - Tabela pass/fail; exit 1 on failure +- `40-deploy-node-agent.sh` — scaffold usunięty (deploy w 30-node-agent.sh) +- Dry-run `40-register.sh --dry-run` przeszedł czysto + +### Mechanizm aktywacji observera (zbadany) + +Observer bind-mountuje repo root jako `/repo:ro` z `services/control-plane/docker-compose.yml` +(`../..:/repo:ro` → `/home/oskar/homelab-codex-ws` na VPS). `_load_inventory()` wywoływane +raz przy starcie. **Aktywacja po merge**: `git pull` VPS + `docker restart control-plane-observer` +— bez redeploy. + +### Wpis lustro w topology.yaml (minimalistyczny, 1:1 z piha) + +```yaml + lustro: + roles: + - edge + services: + - node-agent +``` + +### PENDING (jutro) + +1. Commit B: `onboard.sh --node lustro --step 40-register` live → commit na branchu +2. `agent.sh merge task/node-onboarding` → master +3. `git pull` na VPS + `docker restart control-plane-observer` +4. `onboard.sh --node lustro --step 50-verify` → lustro widoczny w agents.okit.pl + +--- + +## fix-event-bloat (task/fix-event-bloat) + +Commit `d483274` na branchu: batch rsync, backlog trim, timeout 120s, backlog warn. +**PENDING**: review + deploy na flotę. + +--- + +## OOM ai-cluster (obserwacja live) + +Zaobserwowany na VPS podczas sesji: cgroup OOM restart-loop, python workery ~195 MB, +0 swap. **PENDING**: migracja `ai-cluster` → SOLARIA + dodanie swap na VPS. + +--- + +## Gotcha sesji + +**Worktree branch confusion**: `~/homelab-codex-ws-node-onboarding` był przełączony +ręcznie na `task/fix-event-bloat` (jeden worktree, dwa branche ręcznie switchwane). +Anty-wzorzec: zawsze sprawdzać `git branch --show-current` na wejściu do worktree. +Docelowo: osobny worktree per task. + +--- + +## Tech-debt złapany w sesji + +→ wpisany do `docs/backlog.md`