6.8 KiB
Clusev — Session Handoff (2026-06-14)
Self-contained ramp-up for the next session. Read rules.md (R1–R17) + CLAUDE.md first;
this file = current state, open items, and the exact work recipes. handoff.md is the
original foundational decisions doc (do not overwrite it).
1. Current state
- Branch:
feat/v1-foundation(never commit tomain). Remote: Giteagit.bave.dev/boban/clusev.git. - Shipped tags (all pushed):
v0.3.0— full bilingual UI (DE default + EN), user-selectable; rule R16.v0.4.0— change the panel domain from the dashboard (DB snapshot → applied on restart; Caddy on-demand TLS; request-derived Reverb endpoint; anti-lockout middleware).v0.4.1— fixed a garbled<header>on every signed-in page (Blade raw-block bug → rule R17); made broadcast channels private (metrics no longer public).
- Dev stack runs on this VM via
docker compose(app container IP 172.18.0.6, host10.10.90.136:80). Managed fleet host = 10.10.90.162 (Debian / apt / ufw; the only live-verified target — dnf/zypper/firewalld command strings are built but not live-tested).
2. RESOLVED — fail2ban & UFW falsely shown "nicht installiert" (fixed 221e49f)
Was: the Hardening checklist showed fail2ban and Firewall (UFW) as OFFEN ·
nicht installiert, while the fail2ban-Status panel below showed aktiv · 14 gesperrt with
real SSH-scanner bans. Contradiction.
Root cause — two different detection methods (same host):
HardeningService::state(checklist): installed =dpkg -l <pkg> | grep -q '^ii'/rpm -q; active =systemctl is-active. → asked "is the apt/rpm package recorded?"Fail2banService::status+FirewallTool(panels): installed =command -v <client>; active =systemctl is-active. → asked "is the binary present + service running?"- The host is Debian/apt and fail2ban/ufw is genuinely running, but dpkg does not list it as
^ii(source install, or dpkgrcconfig-only state) → checklist false-negative.
Fix (committed on feat/v1-foundation, NOT yet tagged/pushed): the checklist's installed
probe in app/Services/HardeningService.php::state now ORs three conditions —
package-manager test (dpkg ^ii / rpm -q) OR client binary on PATH (command -v)
OR service active (systemctl is-active) — via the local $anyOf() helper, for both
fail2ban and the firewall (ufw/firewalld). Consistent with Fail2banService::status. Since a
component can never be active without being installed, this only removes FALSE negatives,
never adds a false-positive. PackageManager/FirewallTool were left unchanged (the OR is
composed in HardeningService, not in their primitives).
Verified: new unit test tests/Feature/HardeningServiceTest.php (Debian/apt source-install
fallback) passes; Pint clean; Codex review clean (no actionable regression).
Still pending (deferred this session): (a) live acceptance check against fleet host 10.10.90.162 — load Server-Details and confirm fail2ban/ufw now read "installiert"; (b) no version bump / CHANGELOG / tag / push — the fix lives on the branch only. The prior session's "Fix fail2ban/UFW false negative" task chip is now stale (work is done).
3. Other open items / follow-ups
resources/js/app.jsdualChartis dead code — the dashboard chart is a server-rendered SVG, nothing subscribes via Echo. Consider removing it (itsEcho.private('metrics')is the only metrics subscriber and it never runs).- Caddy on-demand TLS / real ACME issuance is config-validated only (no Caddy in dev, no
public DNS). App-side logic (
/_caddy/ask, snapshot, redirects, Reverb tunnel) is browser-verified; the actual cert issuance needs a real prod + DNS test. - "Install-with-domain → clear-to-bare-IP" is the rough edge: re-run
install.shfor a clean bare-IP realtime (REVERB_* in.envotherwise stays domain-shaped). - firewalld is read-only (rule add/delete is ufw-only) — needs a RHEL/Fedora/openSUSE host to finish.
4. Plugins installed this session (need a SESSION RESTART to load)
Installed via the server's claude CLI (~/.claude/remote/ccd-cli/<ver>), scope user,
into the local plugin system (separate from the cloud-synced set):
caveman@caveman— ultra-compressed output (token saver); SessionStart hook auto-activates.superpowers@superpowers-dev(v5.1.0) — workflow skills (TDD, systematic-debugging, …).ui-ux-pro-max@ui-ux-pro-max-skill(v2.5.0).
Marketplaces persisted in ~/.claude/plugins/known_marketplaces.json. They are not in the
current session's skill registry until the session is restarted (that's why /caveman:caveman
still fails this session). After restart: /caveman:*, /superpowers:*, ui-ux-pro-max work.
5. Work recipes (how to actually verify & ship)
- Everything in containers (R8). Pint:
docker compose exec -u "${HOST_UID:-1002}:${HOST_GID:-1002}" -T app ./vendor/bin/pint --dirty. Build: same prefix +npm run build. - R12 — verify in a real browser, BOTH locales: every touched route → HTTP 200 + 0 console
errors AND inspect the rendered DOM for leaked
@/{{ }}/$var/group.keytext (R17 — this is how the v0.3.0/v0.4.0<header>bug slipped: 200 + clean console but garbage markup). Recipe: headless Puppeteer imageghcr.io/puppeteer/puppeteer:lateston docker networkclusev_clusev, targethttp://172.18.0.6,NODE_PATH=/home/pptruser/node_modules. Auth via a TEMP routeRoute::get('/__peek', fn (Request $r) => [AuthFacade::loginUsingId(1), redirect($r->query('to','/'))][1])— remove before commit (route:clear after). Locale is per-user (user->localewins over session), so to test EN the peek must also persistuser->locale. Dashboard route is/(not/dashboard). - R15 — Codex:
export PATH="$HOME/.npm-global/bin:$PATH"; codex review --uncommitteduntil clean (filter node_modules noise). - Localization (R16): every visible string in
lang/{de,en}/<group>.php, keys identical in both. Verify: scan all__('group.key')usages resolve viaLang::has($k,'de'/'en')and that eachlang/de/*.phpgroup has the same keys aslang/en/*.php. - Push: token in
/home/nexxo/.env.gitea(GIT_ACCESS_TOKEN) — read ONLY at push time, never echo/commit; sanitize push output (sed -E 's#https://[^@]*@#https://<redacted>@#g; s/[0-9a-f]{40}/<redacted>/g'). - Fleet host safety: SSH password is in the encrypted vault (never in code/logs). Never ban/lock-out the control plane (10.10.90.x).
**Done = ** Pint clean → npm build → R12 (200 + 0 console + DOM scan, DE+EN) → Codex clean →
version bump + CHANGELOG → commit on feat/v1-foundation → tag → push (sanitized) → temp
/__peek removed.