151 lines
9.5 KiB
Markdown
151 lines
9.5 KiB
Markdown
# 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 to `main`). Remote: Gitea
|
||
`git.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).
|
||
- `v0.4.2` — Server-Details UX + hardening polish (see §2b): uniform `danger-soft` buttons,
|
||
installed-only firewall/fail2ban grid, auto-updates as a neutral preference, SSH-verified
|
||
server creation + `pending` status, SSH key-only hint, and the fail2ban/UFW detection fix.
|
||
- `v0.4.3` — responsive audit (R7, all views @ 375/768): hardened mobile overflow from long
|
||
panel domains (`break-all`/`min-w-0`) and gebannte IPv6 in der fail2ban-Liste. Live pages were
|
||
already overflow-free; only data-dependent edge cases fixed.
|
||
- `v0.4.4` — stop iOS zoom-on-focus: one unlayered `@media (pointer: coarse)` rule in `app.css`
|
||
forces 16px on `input`/`textarea`/`select` on touch devices (phone + tablet any size), desktop
|
||
sizing unchanged.
|
||
- **Dev stack** runs on this VM via `docker compose` (app container IP **172.18.0.6**, host
|
||
`10.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 dpkg `rc` config-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).
|
||
|
||
---
|
||
|
||
## 2b. DONE — Server-Details UX + hardening polish (2026-06-14)
|
||
|
||
Seven reported issues fixed on `feat/v1-foundation`, **released as `v0.4.2`** (tagged + pushed). Spec:
|
||
`docs/superpowers/specs/2026-06-14-server-details-hardening-polish-design.md`; plan:
|
||
`docs/superpowers/plans/2026-06-14-server-details-hardening-polish.md`.
|
||
|
||
1. **Buttons unified** — `x-btn`: retired borderless `ghost`/`ghost-danger`, added `danger-soft`
|
||
(red-tinted border+bg, always). Löschen/trash now consistent, no hover-only red. All 10 usages
|
||
migrated (servers/settings/files).
|
||
2. **Firewall + fail2ban panels** — render only when the tool is installed **or** the read failed
|
||
(`readError`); 2-col `lg` grid when both, full width when one, hidden when absent. Removed the
|
||
redundant "nicht installiert" boxes.
|
||
3. **Auto-updates** — reframed as an operator preference: `secure` always true, muted
|
||
aktiv/inaktiv chip (never SICHER/OFFEN). `HardeningService` `neutral` row flag.
|
||
4. **Create-server** — verifies the SSH login inside the create transaction via
|
||
`FleetService::testConnection` (echoes a marker, requires it in output — proves exec, tolerates
|
||
missing exit-status). Failure rolls back + shows the SSH reason. New server starts `pending`.
|
||
5. **`pending` status** = "Initialisierung" (cyan, never red) across status-pill/dot, server-item,
|
||
fleet index, and the details hero. Promotes on first contact.
|
||
6. **SSH key-only hint** on the SSH-Passwort-Login checklist row while password auth is on.
|
||
|
||
Verified: 16 tests pass (`ButtonComponentTest`, `StatusComponentTest`, `HardeningServiceTest`,
|
||
`FleetTestConnectionTest`, `CreateServerTest`, `ServerShowPanelsTest`, `ServerShowSshHintTest`);
|
||
Pint clean; R12 browser DE+EN at 375/768/1280 (200, 0 console, 0 leaked tokens); Codex clean after
|
||
3 review rounds. **Pre-existing** stock `tests/Feature/ExampleTest.php` still fails (asserts `/` =
|
||
200 but the app redirects to login) — unrelated; a task chip was spawned to delete/fix it.
|
||
|
||
## 3. Other open items / follow-ups
|
||
|
||
- `resources/js/app.js` `dualChart` is **dead code** — the dashboard chart is a server-rendered
|
||
SVG, nothing subscribes via Echo. Consider removing it (its `Echo.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.sh` for a clean
|
||
bare-IP realtime (REVERB_* in `.env` otherwise 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.key` text
|
||
(R17 — this is how the v0.3.0/v0.4.0 `<header>` bug slipped: 200 + clean console but garbage
|
||
markup). Recipe: headless Puppeteer image `ghcr.io/puppeteer/puppeteer:latest` on docker
|
||
network `clusev_clusev`, target `http://172.18.0.6`, `NODE_PATH=/home/pptruser/node_modules`.
|
||
Auth via a **TEMP** route `Route::get('/__peek', fn (Request $r) => [AuthFacade::loginUsingId(1),
|
||
redirect($r->query('to','/'))][1])` — **remove before commit** (route:clear after). Locale is
|
||
per-user (`user->locale` wins over session), so to test EN the peek must also persist
|
||
`user->locale`. Dashboard route is `/` (not `/dashboard`).
|
||
- **R15 — Codex:** `export PATH="$HOME/.npm-global/bin:$PATH"; codex review --uncommitted` until
|
||
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 via `Lang::has($k,'de'/'en')` and that
|
||
each `lang/de/*.php` group has the same keys as `lang/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.
|