From e59eb12da38a37c32a60d1478d1b47d31621c870 Mon Sep 17 00:00:00 2001 From: Oskar Kapala Date: Mon, 8 Jun 2026 22:31:03 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20session=20log=202026-06-08=20=E2=80=94?= =?UTF-8?q?=20LUSTRO=20onboarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Records the onboarding session for LUSTRO (RPi4, KEN site): node facts from preflight, key decisions (user pi/uid-1000, IP over mDNS, zram target), 00-access status, tool bugs fixed (dry-run propagation, yaml_get greedy-colon + inline comment, ssh known-hosts in verify), open items for next session (worktree hygiene first, bootstrap-runtime, node-agent, register, verify, mm-watch). Co-Authored-By: Claude Sonnet 4.6 --- docs/sessions/2026-06-08-lustro-onboarding.md | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 docs/sessions/2026-06-08-lustro-onboarding.md diff --git a/docs/sessions/2026-06-08-lustro-onboarding.md b/docs/sessions/2026-06-08-lustro-onboarding.md new file mode 100644 index 0000000..e19fc73 --- /dev/null +++ b/docs/sessions/2026-06-08-lustro-onboarding.md @@ -0,0 +1,90 @@ +# Sesja 2026-06-08 — onboarding LUSTRO (RPi4 / Magic Mirror / KEN) + +## Cel + +Budowa reużywalnego narzędzia onboardingu nodów `scripts/onboard/` (bash idempotentny, +NIE Ansible — świadoma decyzja), napędzanego deklaratywnym manifestem +`hosts//node.yaml`. Pierwszy realny node: LUSTRO. + +## Node LUSTRO (fakty z preflight) + +- RPi4, aarch64, Debian bookworm, hostname pimirror2, sieć KEN 192.168.31.x +- RAM 4 GB (MM zjada ~1.7 Gi — ten sam profil co VPS z OOM 2026-06-01 → `mem_limit` obowiązkowy) +- dysk 58 G / 48% (luz) +- docker 29.5.3 już zainstalowany (krok `20-install-docker` zbędny dla tego node'a) +- user `pi`: uid=1000, passwordless sudo (potwierdzone `sudo -n true`=0), grupy docker+ollama +- Magic Mirror = systemd unit `magicmirror.service` (Electron jako pi) — **NIETKNIĘTY** przez całą sesję +- swap = 200 M plik `/var/swap` na SD → do migracji na zram (wear karty) +- Tailscale: zainstalowany w tej sesji, Running, IP 100.99.85.73 + +## Decyzje + +- **user = istniejący `pi`** (NIE tworzymy `oskar` — `pi` już zajmuje uid 1000, jest + właścicielem MM, ma docker+sudo; node-agent docker `1000:1000` pasuje out-of-box). + Świadome odstępstwo od konwencji "oskar wszędzie". +- runtime node-agent = docker +- `first_contact` = LAN IP `pi@192.168.31.19` (mDNS `.local` okazał się zawodny — + transient resolve fail); po `tailscale up` kontakt przejmuje mesh (`pi@lustro`) +- Tailscale auth = login interaktywny (URL), bez authkey +- swap target = zram + +## Stan: 00-access ZAMKNIĘTY + +Idempotentny, przeszedł na ostro + re-run czysty. Lustro w mesh, kanał SATURN→lustro +przez Tailscale działa bezhasłowo. Verify czysty (arch=aarch64). + +## Bugi narzędzia naprawione w tej sesji + +1. **dry-run był płytki** (tylko orchestrator) → `run()` helper + propagacja `DRY_RUN=1` + do steps (`lib/common.sh`, `onboard.sh`, `remote.sh`, `00-access.sh`) +2. **`yaml_get` fallback** (bez `yq`): + - inline-comment stripping — `[[:space:]]+#.*$` po wartości + - PRE-EXISTING greedy-colon bug — `.*:` ucinał ostatni dwukropek, gubił prefix + w `systemd:magicmirror.service`; fix: `^[[:space:]]*[^:]*:[[:space:]]*` +3. **`00-access` verify** — ssh known-hosts warning wpadał do parsowanego `arch` + (`WARN "Unexpected arch 'Warning:Permanently…'"`); fix: `-o LogLevel=ERROR` + + czysty stdout (bez `2>&1`) + +## Branch / commity + +`feat/node-onboarding` (6 commitów): + +| Hash | Opis | +|------|------| +| `adb8407` | scaffold — onboard.sh, lib/, steps/00-preflight, hosts/lustro/node.yaml draft | +| `9012a36` | 00-access.sh + node.yaml ssh_user/first_contact/hardware | +| `931fd46` | dry-run propagacja — run() helper, DRY_RUN=0/1 | +| `eed0ad0` | yaml_get fix — inline-comment + greedy-colon | +| `1bed855` | first_contact: IP zamiast mDNS .local | +| `471ba09` | verify fix — LogLevel=ERROR, czysty stdout | + +## OTWARTE — do następnej sesji (kolejność) + +1. **WORKTREE HYGIENE** (pierwsza rzecz): cała sesja jechała w MAIN checkout wbrew + zasadzie "main = deploy-only". Decyzja nierozstrzygnięta: + - (A) rename `feat/` → `task/node-onboarding` + worktree + main→master + (pełna zgodność z `agent.sh`; merge=FF) + - (B) zostać `feat/` + ręczny `git merge --ff-only` + + `agent.sh new` tworzy `task/` od `master` i NIE bierze istniejącego brancha. + `git worktree list` jeszcze nieodczytany (potrzebny wzorzec ścieżki). + +2. **base step**: migracja swap 200 M-plik → zram; `/opt/homelab` + `chown pi` + (uid 1000 już pasuje); event dir `/opt/homelab/events/lustro/` +3. **node-agent step**: docker override, user 1000:1000 (pi=1000), `mem_limit: 256m` +4. **register step**: observer/supervisor inventory + redis sub + UI panel agents.okit.pl +5. **verify step (50)**: smoke end-to-end (event dotarł do control plane, widać w UI, + realny alert path Telegram) +6. **mm-watch**: health check `systemctl is-active magicmirror.service` +7. **drobiazgi**: baner URL w 00-access ma defekt wyrównania; `locale pl_PL` + niewygenerowane na lustrze (niegroźne) + +## Learnings + +(odzwierciedlone też w `scripts/onboard/README.md`) + +- mDNS `.local` zawodny do automatyzacji → `first_contact` przez IP lub tailscale, nie `.local` +- istniejący node z userem uid=1000: użyj go zamiast tworzyć `oskar` (kolizja uid) +- swap na SD = wear → zram +- dry-run MUSI propagować do step-skryptów (`run()` wrapper), inaczej bezużyteczny +- yaml fallback bez `yq` musi strippować inline komentarze i nie być greedy na `:`