Backend: - GET /api/calls/ice-servers: ephemeral HMAC TURN credentials (10-min TTL), iceTransportPolicy:"relay" (no IP leak), 503 until coturn configured - nitro runtimeConfig: turnHost/turnSecret/turnRealm (Infisical staging set) Ops: - ops/calls/ runbook + turnserver.conf (self-hosted coturn, force-relay, use-auth-secret, hardening). coturn provisioned + verified on rebreak-server. Frontend (DM header redesign): - removed standalone "i" button; header center (avatar+name+chevron) opens info sheet - call icon top-right, only when canCall (mutual-follow + callsEnabled); shows "coming soon" until the WebRTC client lands Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
59 lines
2.6 KiB
Markdown
59 lines
2.6 KiB
Markdown
# Voice-Calls — coturn TURN-Server Runbook
|
|
|
|
Self-hosted TURN (coturn) auf Hetzner für die DM-Voice-Calls. Force-Relay →
|
|
kein direkter IP-Austausch zwischen Usern (Anonymität, kein neuer
|
|
Sub-Auftragsverarbeiter).
|
|
|
|
## Architektur
|
|
|
|
```
|
|
App A ──┐ ┌── App B
|
|
│ WebRTC (DTLS-SRTP, E2E) │
|
|
├──────────────► coturn ◄──────┤ ← relay-only, sieht nur verschlüsselten Audio-Stream
|
|
│ │
|
|
Signaling (SDP/ICE) über Supabase Realtime (ephemerer Channel pro Call)
|
|
ICE-Credentials über GET /api/calls/ice-servers (HMAC, 10-min-TTL)
|
|
```
|
|
|
|
- coturn validiert ephemere Credentials per `use-auth-secret` (kein DB-Lookup).
|
|
- Das Backend mintet sie aus `TURN_SECRET` = coturn `static-auth-secret`.
|
|
|
|
## Provisioning (einmalig)
|
|
|
|
> ⚠️ Destruktive/Infra-Schritte — nur mit User-GO ausführen. Secrets NIE
|
|
> committen, nur in Infisical (staging).
|
|
|
|
1. **DNS**: A-Record `turn.rebreak.org` → Server-IP (Hetzner).
|
|
2. **Install**: `apt update && apt install -y coturn`
|
|
3. **TLS-Cert**: `certbot certonly --standalone -d turn.rebreak.org`
|
|
(Port 80 muss kurz frei sein). Auto-Renew via certbot-Timer.
|
|
4. **Config**: `ops/calls/turnserver.conf` → `/etc/turnserver.conf`.
|
|
`static-auth-secret` durch das echte Secret ersetzen (siehe Schritt 6).
|
|
5. **Firewall** (Hetzner Cloud Firewall + ufw): öffnen
|
|
- `3478/udp`, `3478/tcp` (STUN/TURN)
|
|
- `5349/tcp` (TURN over TLS)
|
|
- `49160-49200/udp` (Relay-Range, = min/max-port in der Config)
|
|
6. **Secret generieren** + in Infisical (env=staging) setzen:
|
|
- `TURN_SECRET` = `openssl rand -hex 32` (gleicher Wert in turnserver.conf)
|
|
- `TURN_HOST` = `turn.rebreak.org`
|
|
- `TURN_REALM` = `rebreak.org`
|
|
7. **Enable**: in `/etc/default/coturn` → `TURNSERVER_ENABLED=1`, dann
|
|
`systemctl enable --now coturn`.
|
|
8. **Verify**:
|
|
- `ss -lunp | grep 3478` (lauscht UDP)
|
|
- `turnutils_uclient -v -t -u <user> -w <pw> turn.rebreak.org` (oder
|
|
Trickle-ICE-Test im Browser gegen turns:turn.rebreak.org:5349).
|
|
|
|
## Backend
|
|
|
|
- `GET /api/calls/ice-servers` liefert `{ iceServers, iceTransportPolicy:"relay", ttl }`.
|
|
- Wirft `503 calls_not_configured`, solange `TURN_HOST`/`TURN_SECRET` fehlen →
|
|
kein IP-leakender Fallback.
|
|
- runtimeConfig-Keys in `backend/nitro.config.ts`: `turnHost`, `turnSecret`, `turnRealm`.
|
|
|
|
## Betrieb / Kosten
|
|
|
|
- Audio ≈ 50 kbps/Call → Bandbreite vernachlässigbar.
|
|
- coturn kann auf der bestehenden Hetzner-Box mitlaufen (eigene Ports).
|
|
- Log-Minimierung: coturn `no-cli`, kein verbose-Logging in Prod.
|