# Remote-Betrieb über YunoHost (va.linix.de) Web-UI + Mikrofon vom Handy/Browser, abgesichert per HTTPS und YunoHost-SSO. Topologie: `https://va.linix.de` → nginx@YunoHost (TLS, SSO) → über LAN → `http://:8003` (Gateway). Der interne LAN-Hop ist unverschlüsselt (vertrautes LAN, wie bei Ollama). > **Warum HTTPS Pflicht ist:** Browser geben das Mikrofon (`getUserMedia`) nur in > einem „secure context" frei – also HTTPS oder `http://localhost`. Über einfaches > `http://` vom Handy gibt es **kein** Mikrofon. ## 1. Gateway auf der GPU-Box `deploy/voice-assistant.env.example` → `/etc/voice-assistant/voice-assistant.env` anpassen: - `HOST` = **LAN-IP** der GPU-Box (nicht `0.0.0.0`), `PORT=8003` - `AUTH_ENABLED=true` - `TRUSTED_AUTH_HEADER` (nach Discovery, s. u.), `TRUSTED_PROXY_IPS` = LAN-IP von linix.de - `ADMIN_USERS=atoor,dieterschlueter,dschlueter`, `SSO_LOGOUT_URL=…` > Kein `--forwarded-allow-ips` setzen: Das Identitäts-Vertrauen prüft die > **direkte Quell-IP** (= der Proxy). Würde uvicorn den Client aus > `X-Forwarded-For` überschreiben, schlüge der Proxy-IP-Check fehl. **Dauerhaft laufen lassen (systemd-User-Dienst, ohne root):** Vorlage `deploy/voice-assistant.user.service`. Die App liest `.env` aus dem WorkingDirectory. ```bash cp deploy/voice-assistant.user.service ~/.config/systemd/user/voice-assistant.service loginctl enable-linger "$USER" # sudo -> ueberlebt Logout/Reboot systemctl --user daemon-reload systemctl --user enable --now voice-assistant systemctl --user status voice-assistant journalctl --user -u voice-assistant -f # Logs ``` > Der llama.cpp-Container `va_llm` läuft separat via Docker (`restart unless-stopped`) > und kommt nach einem Reboot von selbst hoch. ## 2. Drei Sicherheits-Pflichten 1. **Bind:** Gateway nur an die LAN-IP (Schritt 1). 2. **Firewall:** Port 8003 der GPU-Box **nur** von der linix.de-IP erlauben, z. B.: ``` sudo ufw allow from to any port 8003 proto tcp sudo ufw deny 8003 ``` 3. **Header überschreiben:** nginx setzt den Identitäts-Header selbst; Client-Eingaben werden verworfen (siehe nginx-Conf). App-seitig zusätzlich der Proxy-IP-Check. ## 3. nginx auf YunoHost `deploy/va.linix.de.nginx.conf` als Vorlage → auf dem YunoHost-Server unter `/etc/nginx/conf.d/va.linix.de.d/assistant.conf` ablegen, `GPU_BOX_LAN_IP` eintragen, die `map $http_upgrade …` einmalig im http{}-Kontext anlegen, dann `nginx -t && systemctl reload nginx`. Subdomain `va.linix.de` in YunoHost anlegen (Let's Encrypt) und per SSO schützen (nur erlaubte Tester/Gruppe). ## 4. Identität: YunoHost liefert sie im Cookie (nicht als Header) Discovery (`/api/admin/request-headers?key=` hinter dem SSO) zeigt: YunoHost reicht den Usernamen **nicht** als eigenen Header durch, sondern im **JWT-Cookie `yunohost.portal`** (Claim `user`). Das Gateway liest diesen Cookie direkt — daher: ```ini # .env auf der GPU-Box: TRUSTED_AUTH_COOKIE=yunohost.portal TRUSTED_AUTH_COOKIE_CLAIM=user TRUSTED_PROXY_IPS= # optional (Härtung): HS256-Secret des Portals -> Signaturpruefung # TRUSTED_AUTH_JWT_SECRET=... ``` In der nginx-Conf ist **keine** `proxy_set_header`-Identitätszeile nötig — Cookies werden ohnehin durchgereicht. > **Sicherheit:** Ohne `TRUSTED_AUTH_JWT_SECRET` wird die Cookie-Payload ungeprüft > gelesen. Das ist nur sicher, weil (a) nur die Proxy-Quell-IP akzeptiert wird **und** > (b) die Subdomain **per SSO geschützt** sein muss (dann lässt YunoHost nur validierte > Cookies durch). Für Härtung das Portal-HS256-Secret in `TRUSTED_AUTH_JWT_SECRET` > setzen → die Signatur wird dann selbst geprüft. ## 5. Test - `https://va.linix.de/` lädt die UI, links „Angemeldet als ". - Text-Chat funktioniert; **Mikrofon-Button** nimmt auf und spielt die Antwort ab. - Admins (`ADMIN_USERS`) sehen den Admin-Bereich (Nutzerliste). ## 6. Chatterbox-TTS (optional, hohe Qualität + Voice-Cloning) Eigener HTTP-Dienst (`~/chatterbox-tts-cli`, Conda-Env, GPU). systemd-User-Unit `chatterbox-tts.service` (Port 9999). Wichtig: - **GPU pinnen per UUID** (CUDA-Default-Order ist „fastest first" → Index trügt): `Environment="CUDA_VISIBLE_DEVICES=GPU-"` (UUID via `nvidia-smi -L`). - **`TTS_PRELOAD_LANG=de`** lädt das Modell beim Start (Warm-up). - Der Dienst wird vom Gateway mit **`no_playback=true`** aufgerufen → er spielt **nicht** lokal ab, sondern liefert nur die WAV (`tts_service.py` unterstützt das Flag). ```bash systemctl --user enable --now chatterbox-tts curl -s http://127.0.0.1:9999/health # {"status":"ok","device":"cuda:0"} ``` Gateway: `tts_provider=chatterbox` (pro Request/Session) + `CHATTERBOX_VOICE` = Referenz-WAV. > Chatterbox ist ~echtzeit-langsam → als Qualitäts-Option gedacht; `piper` bleibt der > schnelle Default. Braucht ~3–4 GB GPU-Speicher.