chahinebrini 0cac3c9d1a feat(calls): Phase 1a — TURN ice-servers endpoint + coturn ops + DM call-button header
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>
2026-06-04 03:06:33 +02:00

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.