my_voice_assistant_v3/deploy/README.md
Dieter Schlüter 56114309f5 docs(deploy): Chatterbox-TTS-Dienst (GPU-Pinning per UUID, no_playback) ergaenzen
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 09:58:15 +02:00

96 lines
4.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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://<GPU-Box>: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 <linix.de_LAN_IP> 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=<ADMIN_API_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=<LAN-IP des YunoHost-Servers>
# 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 <SSO-User>".
- 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 der freien 3090>"` (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 ~34 GB GPU-Speicher.