docs: CLAUDE.md + ROADMAP auf v3 (Design-System, Beginner-UX, Security, Self-Update)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-21 13:01:33 +02:00
parent 70c7cba613
commit aafc41f4b2
2 changed files with 45 additions and 15 deletions
+26 -14
View File
@@ -6,27 +6,29 @@ FastAPI-Backend + Vanilla-JS-Dashboard. **Leitprinzip: KISS — kein Build-Schri
## Architektur
**Backend** (Top-Level-Helfer + ein Router je Bereich):
- **`app.py`** — dünner Einstieg: baut `FastAPI`, hängt die Router ein, liefert das statische UI aus, registriert den Exception-Handler. Sonst nichts.
- **`config.py`** — alle Env-Vars + die gemeinsame `ruamel.yaml`-Instanz.
- **`auth.py`** — optionale Token-Auth (`X-MC-Token`).
- **`jobengine.py`** — In-Memory-Job-System (Threads + Subprocess) mit Live-Log; fährt Downloads/Updates.
- **`app.py`** — dünner Einstieg: `FastAPI`, hängt die Router ein, liefert das statische UI aus, Exception-Handler + eine `Cache-Control: no-cache`-Middleware für `/` und `/static` (UI wirkt sofort nach rsync, kein Stale-JS im Browser).
- **`config.py`** — alle Env-Vars + die gemeinsame `ruamel.yaml`-Instanz. Auch `SOURCE_DIR`/`PROD_DIR` fürs Self-Update.
- **`auth.py`** — optionale Token-Auth: Header `X-MC-Token` **oder** Query `?token=` (Query ist Pflicht für WebSockets, da Browser dort keine Header senden).
- **`jobengine.py`** — In-Memory-Job-System (Threads + Subprocess) mit Live-Log. `start_job(..., stdin_data=, log_cmd=)`: Secrets (sudo-PW) gehen über **stdin**, die Log-Zeile wird sanitisiert (kein Passwort im Log/`ps`).
- **`llamaswap.py`** — spricht `llama-swap` an (`/running`, `/v1/models`, unload) und liest/schreibt dessen `config.yaml` per `ruamel.yaml` (Kommentare bleiben erhalten).
- **`routers/*.py`** — ein Router je Bereich. Aktuell: `models.py` (`status`, `download`, `register`, `unload`, `chat`), `jobs.py` (`jobs`), `maintenance.py` (`update`, `logs`-WebSocket), `system.py` (`status`, `stream`-WebSocket), `cookbook.py` (`analyze`). Alle Endpoints unter `/api/*`.
- **`hw_math.py`** — Odysseus-Hardware-Fit-Mathe (VRAM/RAM-Bedarf + tps-Schätzung) fürs Cookbook.
- **`routers/*.py`** — ein Router je Bereich: `models.py` (`status` mit Meta/Caps, `download`, `register`, `update_model`, `unload`, `chat`), `jobs.py` (`jobs`), `maintenance.py` (`update` = **llama.cpp**, `self-update` = **MC selbst** via git pull+rsync+restart, `os-update`, `service/{name}/restart`, `reboot`, `logs`-WebSocket), `system.py` (`status` + `stream`-WebSocket, Live-Metriken via psutil/sysfs), `cookbook.py` (`analyze`/`evaluate`). Alle unter `/api/*`.
**Frontend** (`static/`, dünne Hülle + ES-Module, kein Build):
- **`index.html`** — nur Gerüst: Sidebar-Nav, Topbar, Alert-Banner, ein `.view`-Container je Bereich (Hash-Routing). Lädt `css/*` und `js/main.js` als Modul.
- **`css/base.css`** — Design-Tokens (`:root`), Reset, App-Layout (Sidebar/Topbar/Content). **`css/components.css`** — Karten, KPI-Kacheln, Listen, Forms, Log, Toast.
- **`js/core/*`** — `api.js` (Fetch + Token), `ui.js` (DOM-Helfer, Toast, Icons), `nav.js` (View-Switch).
- **`js/panels/*`** — ein Panel je Bereich (`overview`, `models`, `maintenance`, `jobs`). Panel-Vertrag: `{ id, mount?(), onStatus?(s), onJobs?(jobs) }`.
- **`js/main.js`** — bootet Panels, pflegt Topbar/Alert, baut die WebSocket-Verbindung für Live-System-Metriken (`/api/system/stream`) auf, fährt das reguläre Polling (`/api/status` 3 s, `/api/jobs` 1.5 s) und verteilt an die Panels.
- **Design-System (v3):** **EINE** Akzentfarbe (Teal `#2dd4bf`) für alles Klickbare; Grün/Gelb/Rot **nur** für Status. Dichtes Control-Plane-Layout, Monospace-Zahlen, Bento. **Keine Inline-Styles in Panels** — alles über Klassen aus `components.css` (`.card`, `.tile`, `.qa`, `.fit-badge`, `.modal-*`, `.badge`, `.bar`/`.meter`, `.chip` …). Tokens in `css/base.css` (`:root`).
- **`js/core/*`** — `api.js` (Fetch + Token), `ui.js` (DOM-Helfer, Toast, Inline-Icon-Set, **`confirmModal`/`promptModal`** für Beginner-UX, `fmtBytes`/`fmtPct`), `nav.js` (beschrifteter View-Switch).
- **`js/panels/*`** — ein Panel je Bereich (`overview`, `models`, `server`, `cookbook`, `jobs`=Aktivität, `guides`). Panel-Vertrag: `{ id, mount?(), onStatus?(s), onJobs?(jobs), onSystem?(sys) }`.
- **`js/main.js`** — bootet Panels, pflegt Topbar/Alert + **ehrlichen Security-Chip** (`status.secured`), WebSocket für Live-Metriken (`/api/system/stream`, 2 Hz), Polling (`/api/status` 3 s, `/api/jobs` 1.5 s).
- **Leitprinzip UX:** verständlich/idiotensicher — Klartext-Microcopy, Fachbegriffe übersetzt (CPU→Prozessor, VRAM→Grafikspeicher), geführte Aktionen, heikle Aktionen mit `confirmModal` (Klartext-Konsequenz).
- **`mission-control.service`** — systemd-Unit (uvicorn auf Port 9000).
- **Konfiguration** rein über Env-Vars: `MC_LLAMA_SWAP_URL`, `MC_CONFIG_PATH`, `MC_MODELS_DIR`, `MC_CMD_TEMPLATE`, `MC_UPDATE_CMD`, `MC_DEFAULT_TTL`, `MC_TOKEN`.
- **Konfiguration** rein über Env-Vars: `MC_LLAMA_SWAP_URL`, `MC_CONFIG_PATH`, `MC_MODELS_DIR`, `MC_CMD_TEMPLATE`, `MC_UPDATE_CMD`, `MC_DEFAULT_TTL`, `MC_TOKEN`, `MC_SOURCE_DIR` (Self-Update-Quelle, Default `~/mission-control`).
## Der Stack drumherum (Kontext)
- **Bosgame M5**: AMD Strix Halo (gfx1151), Ubuntu 26.04, Kernel 7.0, ~124 GB GTT-Speicher. LAN-IP `192.168.178.151`.
- **Inferenz**: vorgebaute llama.cpp-ROCm-Binaries → `llama-swap` auf `:8080` (läuft mit `-watch-config`).
- **Inferenz**: vorgebaute llama.cpp-ROCm-Binaries → `llama-swap` auf **`*:8080` (LAN-offen**, mit `-watch-config`) → die Guides-Integrationen funktionieren von anderen Geräten. Engine-Update via `MC_UPDATE_CMD=/usr/local/bin/update-llamacpp` (lädt llama.cpp-ROCm für gfx1151).
- **3 Modelle / 5 Rollen**: `coder`, `scout`, `vision` (Qwen3-Familie). Modelle werden geswappt, nicht parallel geladen.
- **Mission Control**: Produktiv unter `/opt/mission-control` (als Dienst), Source-Repo unter `~/mission-control`.
@@ -47,7 +49,9 @@ Ohne llama-swap ist alles im Offline-Zustand (rote Pill, Warn-Banner) — das is
```bash
ssh -i ~/.ssh/id_ed25519_hermes -o IdentitiesOnly=yes hitonabi@192.168.178.151
```
User ist **`hitonabi`** (klein!). `sudo` braucht ein Passwort (kein passwortloses sudo).
User ist **`hitonabi`** (klein!). `sudo` braucht generell ein Passwort **außer** einer engen NOPASSWD-Whitelist für genau `systemctl restart mission-control|llama-swap` und `journalctl` (dadurch laufen Self-Update, Dienst-Restarts und Log-Stream passwortlos). OS-Update/Reboot fragen das Passwort weiterhin ab.
**Self-Update statt manuellem Deploy:** Der Button „Mission Control aktualisieren" (`POST /api/self-update`) macht `git pull` (Source) → rsync → `systemctl restart` — der dokumentierte manuelle Weg unten ist nur noch Fallback/Erstinstallation.
**Deploy-Kette (Windows → Gitea → Bosgame):**
1. Lokal bauen + Smoke-Test → `git push` (origin = Gitea `http://192.168.178.153:3000/Hitonabi/mission-control.git`).
@@ -77,9 +81,17 @@ rsync; **Python-Code-Änderungen brauchen den Restart**.
- HuggingFace-Downloads mit **`HF_HUB_DISABLE_XET=1`** (sonst reproduzierbarer Hänger bei ~6 MB).
- Vision-Modelle in llama.cpp brauchen zusätzlich **`--mmproj <projektor>` und `--jinja`**.
## Gotchas v3 (zusätzlich)
- **`.qa` ist ein `<button>`** → die globale `button.danger`-Regel (Vollrot) schlägt durch. Für gefährliche Aktionszeilen `.qa-danger` nutzen (rotes Icon), NICHT `.danger`.
- **CSS rückwärtskompatibel halten:** Panels werden schrittweise migriert; beim Token-/Klassen-Umbau alte Klassennamen + Vars (`--hi`, `--red`, `--red-dim`) bedienen, sonst brechen noch nicht migrierte Panels.
- **Browser-ESM-Cache:** Beim lokalen Testen bustet ein Soft-Reload den Modul-Cache nicht zuverlässig → Preview-Server neu starten oder cache-bustend dynamisch importieren. In Prod erledigt das die `no-cache`-Middleware (einmal Strg+Shift+R nach dem ersten Deploy).
- **Gitea-Push** kann transient mit „Failed to authenticate user" fehlschlagen (Server kurz weg) → einfach erneut versuchen.
## Projektstatus & Roadmap
Die v2-Roadmap ist **vollständig umgesetzt** (siehe `ROADMAP.md`).
Wir befinden uns nun im **Feinschliff- und Wartungsmodus**.
**v3 ist umgesetzt** (siehe `ROADMAP.md`): kompletter Redesign auf ein einheitliches Design-System,
Beginner-UX (Klartext/Führung), Security-Härtung (Passwort-Leak dicht, ehrlicher Chip) und ein
**Self-Update-Button**. Wir sind im **Feinschliff- und Wartungsmodus**.
**Nordstern:** den Server nie wieder via SSH/Putty anfassen müssen — 100 % Automatisierung / Klicki-Bunti.
+19 -1
View File
@@ -4,7 +4,25 @@
---
## Projektstand (Stand 2026-06-20)
## v3 — Redesign + Beginner-UX + Security (Stand 2026-06-21, live)
Nach der funktional kompletten v2 (unten) umgesetzt — Fokus: **schick, selbsterklärend, wasserdicht.**
- **Design-System:** EINE Akzentfarbe (Teal), Bento, Monospace-Zahlen, beschriftete Sidebar; Inline-Styles
raus → alles über `components.css`. Mockup-getrieben (Übersicht + Cookbook).
- **Beginner-UX:** Klartext-Microcopy, Fachbegriffe übersetzt, menschliches Hero-Urteil, geführte Aktionen,
`confirmModal`/`promptModal` für heikle Aktionen, Fit-Ampel + Sortierung im Cookbook, Guides mit Kopier-Buttons.
- **Security:** sudo-Passwort über **stdin** (kein Leak mehr im Job-Log/`ps`), ehrlicher Security-Chip
(`status.secured`), WS-Token (Query). Bind bleibt `0.0.0.0` (LAN-only), Token optional.
- **Self-Update:** Button „Mission Control aktualisieren" (`/api/self-update`: git pull → rsync → restart) —
end-to-end verifiziert. `no-cache`-Middleware für sofortige UI-Wirkung nach Deploy.
- **Infra geklärt:** llama-swap lauscht `*:8080` (LAN); `MC_UPDATE_CMD` = llama.cpp-Engine-Update (≠ MC selbst).
Details/Konventionen: **CLAUDE.md**. Modus jetzt: Feinschliff & Bug-Hunting.
---
## Projektstand v2 (Stand 2026-06-20)
**✅ Schritt 1 erledigt & live auf `:9000`** — *SoC-Refactor + Design 2.0 als Fundament* (Commit `3649394`).
Backend in Helfer + `routers/*` zerlegt, Frontend in ES-Module (`js/core` + `js/panels`) + ausgelagertes