homelab-codex-ws/docs/sessions/2026-06-08-lustro-onboarding.md

5.7 KiB

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>/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 oskarpi 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/<name> 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 :

Update — worktree hygiene

  • feat/node-onboarding → task/node-onboarding. Main checkout (~/homelab-codex-ws) wrócił na master (deploy-only). Praca onboardingu w ~/homelab-codex-ws-node-onboarding.
  • Origin: task/ pushnięty+tracking, feat/ usunięty.
  • DROBIAZG: worktree utworzony ręcznie (git worktree add) → agent.sh list pokazuje "(no marker)"/parent=?. Działa; przy finałowym agent.sh merge node-onboarding zweryfikować, czy brak markera nie przeszkadza — inaczej dorobić marker (wzór: ha-piha) lub ręczny git merge --ff-only.
  • NASTĘPNE: base step (zram, /opt/homelab, event dir /opt/homelab/events/lustro/) — z worktree node-onboarding.
  • Osobny przyszły projekt: parent-layout refaktor (bare + worktree pod jednym katalogiem) — wymaga przepisania agent.sh + zabezpieczenia dirty ha-piha.

Tech-debt złapany w sesji

  • OBSERVER STALENESS: martwy node (chelsty-infra) świeci NOMINAL w agents.okit.pl — observer/supervisor trzyma ostatni znany stan i nie degraduje przy braku heartbeatu (eventy: tylko VPS raportuje świeżo, chelsty milczy a status NOMINAL). FIX (zdalny, software): heartbeat TTL → po przekroczeniu oznacz stale/down. Ważne: false-NOMINAL podważa zaufanie do statusu wszystkich nodów. Przenieść do głównego tech-debt backlogu, jeśli istnieje osobny.