docs(spec): in-panel Help page design + drop product name from external-proxy hint

Spec for a bilingual /help page (left topic nav like Settings) covering all
settings, a generic reverse-proxy setup guide, and the 2FA access-path note
(security key only over the HTTPS domain; backup code on the bare-IP/HTTP path;
TOTP works everywhere). Also generalize the external-TLS hint copy (no product
name).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat/v1-foundation
boban 2026-06-19 22:02:25 +02:00
parent a709ef772c
commit 60295a0aea
3 changed files with 102 additions and 2 deletions

View File

@ -0,0 +1,100 @@
# Hilfe-Seite (In-Panel-Dokumentation) — Design
**Goal:** Eine eigene, bilinguale Hilfe-Seite im Panel, die alle Einstellungen und Abläufe erklärt — inklusive einer generischen Schritt-für-Schritt-Anleitung, wie man Clusev hinter einem externen Reverse-Proxy ("Proxy Manager") betreibt.
**Architecture:** Eine neue Full-Page-Livewire-Komponente unter `/help` mit linker Themen-Navigation (wie die Einstellungen). Kurze Chrome-Strings (Nav-Labels, Seitentitel) liegen in `lang/{de,en}/help.php`; der lange Fließtext liegt als Blade-Partials pro Sprache und wird nach aktiver Locale eingebunden. Keine neuen Tabellen, kein State außer dem gewählten Thema.
**Tech Stack:** Laravel 13, Livewire 3 (class-based), Tailwind v4 `@theme`-Tokens, bestehende Blade-Komponenten (`x-panel`, `x-icon`, …).
---
## 1. Platzierung & Navigation
- **Komponente:** `App\Livewire\Help\Index` (Ordnerkarte R6), View `resources/views/livewire/help/index.blade.php`.
- **Route:** `Route::get('/help', Help\Index::class)->name('help')` in der onboarded-Gruppe von `routes/web.php` (gleiche Middleware-Gruppe wie Settings/System; hinter Auth + `EnsureSecurityOnboarded`).
- **Sidebar:** neuer Eintrag **„Hilfe"** unten (nach „Version"), Icon z. B. `help-circle` (Lucide, inline via `x-icon`). Label lokalisiert (`__('shell.nav_help')`).
- **Command-Palette:** Eintrag in `resources/views/components/command-palette.blade.php` (`$nav`) + Chord `g h` in `CMDK_GO` (analog zu den bestehenden `g <key>`).
- **Layout:** `layouts.app`.
- **Route-Pfad englisch (R13)**, sichtbares Label deutsch/englisch (R9/R16).
## 2. Aufbau der Seite
- Öffentliche Property `public string $topic = 'overview';` — der gewählte Themenschlüssel.
- `mount()` akzeptiert optional ein `?topic`-Query/Param, validiert gegen die bekannte Themenliste, fällt sonst auf `overview` zurück (kein Lockout, kein 404 für unbekannte Keys → Default).
- Links: Themenliste (Buttons `wire:click="$set('topic', '<key>')"`, aktives Thema hervorgehoben — gleiche Optik wie die Settings-Tabs in `settings/index.blade.php`).
- Rechts: `@include('livewire.help.content.'.app()->getLocale().'.'.$topic)` mit Existenzprüfung; fehlt das Locale-Partial, Fallback auf `de`.
- Responsive (R7): links Themen-Nav, die auf schmal über dem Inhalt als horizontale/aufklappbare Liste erscheint (gleiche Technik wie Settings-Tabs).
### Themenliste (Reihenfolge)
| Key | Label (de) | Inhalt-Kurzfassung |
|---|---|---|
| `overview` | Überblick / Erste Schritte | Was Clusev ist, Erst-Login (`admin@clusev.local` / `clusev`, Zwangswechsel), Navigation, Bare-IP vs. Domain. |
| `domain-tls` | Domain, TLS & Reverse-Proxy | Drei Modi (Bare-IP, eingebautes TLS/Let's Encrypt, externer Proxy); **Schritt-für-Schritt-Proxy-Anleitung**; `TRUSTED_PROXY_CIDR`; Neustart-Boundary. |
| `security` | Sicherheit & 2FA | TOTP, Security-Keys (YubiKey), Backup-Codes; **2FA-Zugangspfad-Hinweis** (s. u.); Passwort-Manager-Extension-Hinweis (Bitwarden/Kaspersky). |
| `updates` | Updates & Versionen | Auto-Check, „Jetzt aktualisieren"-Knopf, CLI `sudo ./update.sh`, was beim Update passiert (Build/Migrate). |
| `servers` | Server & SSH | Server hinzufügen, SSH-Credential-Vault, Hardening, SSH-Key-Provisioning. |
| `sessions` | Sitzungen & Mehrbenutzer | Aktive Sitzungen sehen/widerrufen, weitere Admins, Audit-Attribution. |
| `email` | E-Mail (SMTP) | SMTP für Passwort-Reset konfigurieren + Testmail. |
| `audit` | Audit-Log | Was protokolliert wird, Retention. |
| `recovery` | Konto-Wiederherstellung | Forgot-Password (E-Mail-Link / 2FA-Proof), Bare-IP-Recovery, `clusev:reset-admin`. |
## 3. Inhalt zweisprachig
- **Chrome (kurz):** `lang/de/help.php` + `lang/en/help.php` — Seitentitel, Eyebrow, Untertitel, die Themen-Labels (`topic_overview`, `topic_domain_tls`, …), `nav_help` in `shell.php`.
- **Lange Texte:** Blade-Partials je Sprache:
- `resources/views/livewire/help/content/de/<topic>.blade.php`
- `resources/views/livewire/help/content/en/<topic>.blade.php`
- Reines Markup mit Überschriften, Absätzen, Listen und Code-Blöcken (für Proxy-/CLI-Beispiele). **Nur `@theme`-Token-Utilities (R3), keine Inline-Styles (R4), kein Emoji (R9).** Native Tokens (`X-Forwarded-Proto`, `TRUSTED_PROXY_CIDR`, `sudo ./update.sh`) bleiben als `font-mono`.
- Locale-Auflösung im View über `app()->getLocale()`; Fallback `de`, wenn das EN-Partial (noch) fehlt — so bricht nichts, falls eine Sprache nachgezogen wird.
## 4. Schlüssel-Inhalte (verbindlich)
### 4a. Proxy-Anleitung (in `domain-tls`) — generisch, OHNE Produktnamen
1. **Im Panel:** System → Domain & TLS → Domain eintragen → TLS-Terminierung **„Externer Reverse-Proxy"** → speichern → **Stack neu starten** (Knopf).
2. **Im Proxy Manager:** neuen Host/Eintrag anlegen:
- Eingehende Domain = `panel.<deine-domain>`
- Ziel/Backend = `http://<Clusev-Server-IP>:80` (**HTTP**, nicht HTTPS)
- **Host-Header durchreichen** und **`X-Forwarded-Proto: https` setzen**
- **WebSocket-Weiterleitung aktivieren** (für Realtime: Pfade `/app/*` und `/apps/*`)
3. **In der `.env`** (Clusev-Server): `TRUSTED_PROXY_CIDR` auf die Adresse/CIDR des Proxys setzen → korrekte `Secure`-Cookies + echte Besucher-IP im Audit-Log; danach `sudo ./update.sh` (oder Caddy-Neustart).
4. **Firewall:** den HTTP-Port (80) des Clusev-Servers so absichern, dass er **nur vom Proxy** erreichbar ist.
5. Hinweis: In diesem Modus stellt Clusev **kein** eigenes Zertifikat aus — TLS kommt vom Proxy.
### 4b. 2FA-Zugangspfade (in `security`, Querverweis in `recovery`)
- **Security-Key (YubiKey)** funktioniert **nur über die HTTPS-Domain** (WebAuthn braucht einen sicheren Kontext + die Domain als rpId). Über die **Domain** wird die Security-Key-Anmeldung angeboten und funktioniert.
- Über den **Bare-IP-/HTTP-Recovery-Pfad** (`http://<Server-IP>`) lässt sich **nicht** per Security-Key anmelden — dort muss ein **Backup-Code** verwendet werden.
- **TOTP / „Google Authenticator"** funktioniert **überall** — auch über Bare-IP/HTTP. Wer TOTP nutzt, braucht für den Bare-IP-Pfad **keinen** Backup-Code.
- Praxis-Empfehlung: Wer ausschließlich einen Security-Key nutzt, sollte die **Backup-Codes aufbewahren** (einziger Weg über den Bare-IP-Recovery-Pfad). Wer zusätzlich/alternativ TOTP hat, ist auf allen Pfaden abgedeckt.
### 4c. Passwort-Manager-Extensions (in `security`)
- Browser-Erweiterungen wie Bitwarden/Kaspersky können die Security-Key-Registrierung abfangen („Passkey speichern"). Clusev signalisiert korrekt einen Hardware-Key (cross-platform, non-resident, `hints: ['security-key']`), aber Erweiterungen lassen sich seitenseitig nicht abschalten. Lösung: die Passkey-Übernahme der Erweiterung für die Seite deaktivieren, oder die Registrierung in einem Browser/Fenster ohne diese Erweiterung durchführen (z. B. privates Fenster).
## 5. Komponenten-Logik (klein halten)
`App\Livewire\Help\Index`:
- `public string $topic`
- `mount(?string $topic = null)`: validiert gegen `self::TOPICS` (Liste der Keys), Default `overview`.
- `render()`: übergibt die Themenliste (Keys + lokalisierte Labels) + den aktiven `$topic`; die View bindet das passende Locale-Partial ein.
- Keine Persistenz, keine externen Aufrufe, keine Berechnungen — reine Anzeige.
## 6. Dateistruktur
- Neu: `app/Livewire/Help/Index.php`
- Neu: `resources/views/livewire/help/index.blade.php`
- Neu: `resources/views/livewire/help/content/{de,en}/{overview,domain-tls,security,updates,servers,sessions,email,audit,recovery}.blade.php` (18 Partials)
- Neu: `lang/de/help.php`, `lang/en/help.php`
- Ändern: `routes/web.php` (Route), `resources/views/partials/sidebar.blade.php` (Nav-Eintrag), `lang/{de,en}/shell.php` (`nav_help`), `resources/views/components/command-palette.blade.php` + `CMDK_GO` (cmdk-Eintrag + `g h`).
## 7. Test & Verifizierung
- **Livewire-Test** (`tests/Feature/HelpPageTest.php`): Default-Thema = `overview`; `$set('topic', 'domain-tls')` rendert den Proxy-Abschnitt (assertSee eines bekannten Strings, z. B. `X-Forwarded-Proto`); unbekannter Topic-Key fällt auf `overview` zurück; Seite lädt für einen eingeloggten Nutzer (HTTP 200).
- **R12 Browser** (auf der Domain): `/help` lädt mit HTTP 200, keine Konsolenfehler, keine geleakten `{{ }}`/`group.key`-Tokens; Themenwechsel funktioniert; DE/EN-Umschalter zeigt den jeweiligen Inhalt; 375/768/1280.
- **R15 Codex** über den Diff.
## 8. Bewusst NICHT enthalten (YAGNI)
- Keine Volltextsuche in der Hilfe (Themen-Nav reicht).
- Keine kontextuellen „?"-Sprungmarken aus jeder Einstellung (kann später kommen).
- Keine aus Markdown gerenderte Doku-Engine — statische Blade-Partials genügen.
- Keine Versionierung/Changelog der Hilfe-Inhalte (das CHANGELOG deckt das ab).

View File

@ -70,7 +70,7 @@ return [
'tls_mode_caddy' => 'Eingebautes TLS',
'tls_mode_caddy_desc' => 'Das Panel holt selbst ein Zertifikat für die Domain.',
'tls_mode_external' => 'Externer Reverse-Proxy',
'tls_mode_external_desc' => 'Ein Proxy davor (z. B. Zoraxy) terminiert TLS; das Panel liefert HTTP.',
'tls_mode_external_desc' => 'Ein vorgelagerter Proxy Manager terminiert TLS; das Panel liefert HTTP.',
'tls_external_hint' => 'Externer Proxy: setze TRUSTED_PROXY_CIDR auf die Adresse deines Proxys und schütze den HTTP-Port per Firewall, sodass er nur vom Proxy erreichbar ist. Das Panel holt dann kein Zertifikat.',
'change_tls_heading' => 'TLS-Modus ändern',
'change_tls_body_external' => 'Auf „Externer Reverse-Proxy" umstellen? Das Panel holt kein Zertifikat mehr; TLS muss der Proxy davor liefern. Gilt nach Neustart des Stacks.',

View File

@ -70,7 +70,7 @@ return [
'tls_mode_caddy' => 'Built-in TLS',
'tls_mode_caddy_desc' => 'The panel fetches its own certificate for the domain.',
'tls_mode_external' => 'External reverse proxy',
'tls_mode_external_desc' => 'A proxy in front (e.g. Zoraxy) terminates TLS; the panel serves HTTP.',
'tls_mode_external_desc' => 'An upstream proxy manager terminates TLS; the panel serves HTTP.',
'tls_external_hint' => 'External proxy: set TRUSTED_PROXY_CIDR to your proxy\'s address and firewall the HTTP port so it is only reachable from the upstream proxy. The panel will not fetch a certificate.',
'change_tls_heading' => 'Change TLS mode',
'change_tls_body_external' => 'Switch to "External reverse proxy"? The panel will no longer fetch a certificate; TLS must be provided by the proxy in front. Takes effect after a stack restart.',