diff --git a/dev/issues/0166-matrix-livekit-turn-deploy.md b/dev/issues/0166-matrix-livekit-turn-deploy.md deleted file mode 100644 index 9aae42ce..00000000 --- a/dev/issues/0166-matrix-livekit-turn-deploy.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -id: "0166" -title: "Desplegar TURN para LiveKit (coturn o integrado)" -status: pendiente -type: infra -domain: - - matrix -scope: app:element_matrix_chat -priority: alta -depends: [] -blocks: [] -related: ["0167", "0168"] -created: 2026-05-24 -updated: 2026-05-24 -tags: [matrix, livekit, webrtc, turn, nat] ---- -# 0166 — Desplegar TURN para LiveKit (coturn o integrado) - -**Status:** pendiente -**Created:** 2026-05-24 -**Type:** infra -**Priority:** alta -**Domain:** matrix -**Scope:** app:element_matrix_chat -**Depends:** — -**Blocks:** — - -## Problema - -LiveKit corre sin TURN (`turn.enabled: false` en `configs/livekit/livekit.yaml`). Usuarios detras de NAT simetrico (CGNAT movil 4G/5G, redes corporativas con firewall estricto, hotel WiFi) NO pueden establecer call — WebRTC ICE direct/reflexive falla. Calls fallan silenciosos para ~10-20% usuarios. - -## Objetivo - -Calls funcionan en cualquier red. Element X movil sobre 4G CGNAT completa handshake. - -## Plan - -1. Decidir: coturn standalone vs LiveKit TURN integrado (recomendado: integrado, menos moving parts). -2. Anadir subdominio `turn.organic-machine.com` con Let's Encrypt cert (Traefik). -3. Activar bloque `turn:` en `livekit.yaml`: - ```yaml - turn: - enabled: true - domain: "turn.organic-machine.com" - tls_port: 5349 - udp_port: 443 - external_tls: true - ``` -4. Abrir puertos VPS firewall: TCP+UDP 443 (best practice — bypassea firewalls corp), TCP 5349. -5. Rotar shared secret TURN. -6. Test: navegador en red corp con `force-tcp` flag → call establecida. - -## Acceptance - -- [ ] `nc -vz turn.organic-machine.com 443` UDP+TCP OK. -- [ ] Test call Element Web detras de NAT simetrico (movil hotspot tethering) → audio/video pasa. -- [ ] LiveKit logs muestran `TURN allocation` requests servidas. -- [ ] `.well-known/matrix/client` sigue apuntando al `livekit_service_url` JWT correcto. - -## Definition of Done - -- [ ] Repetibilidad: 5 calls consecutivas desde 5 redes distintas (incluido CGNAT) sin fallo. -- [ ] Observabilidad: dashboard LiveKit muestra TURN vs direct ratio. -- [ ] User-facing: usuario movil 4G inicia call → conecta < 3s. - -## Notas - -UDP 443 es trick conocido: la mayoria de firewalls corporativos solo dejan 443 (HTTPS) — TURN sobre UDP 443 bypassea sin requerir TCP relay que aumenta latencia. - -Alternativa coturn standalone si LiveKit integrado tiene gaps de gestion: `docker run -d coturn/coturn` + config compartida con shared secret de LiveKit. diff --git a/dev/issues/completed/0166-matrix-livekit-turn-deploy.md b/dev/issues/completed/0166-matrix-livekit-turn-deploy.md new file mode 100644 index 00000000..b222dc1b --- /dev/null +++ b/dev/issues/completed/0166-matrix-livekit-turn-deploy.md @@ -0,0 +1,115 @@ +--- +id: "0166" +title: "Desplegar TURN para LiveKit (coturn o integrado)" +status: done +type: infra +domain: + - matrix +scope: app:element_matrix_chat +priority: alta +depends: [] +blocks: [] +related: ["0167", "0168"] +created: 2026-05-24 +updated: 2026-05-24 +tags: [matrix, livekit, webrtc, turn, nat] +--- +# 0166 — Desplegar TURN para LiveKit (coturn o integrado) + +**Status:** pendiente +**Created:** 2026-05-24 +**Type:** infra +**Priority:** alta +**Domain:** matrix +**Scope:** app:element_matrix_chat +**Depends:** — +**Blocks:** — + +## Problema + +LiveKit corre sin TURN (`turn.enabled: false` en `configs/livekit/livekit.yaml`). Usuarios detras de NAT simetrico (CGNAT movil 4G/5G, redes corporativas con firewall estricto, hotel WiFi) NO pueden establecer call — WebRTC ICE direct/reflexive falla. Calls fallan silenciosos para ~10-20% usuarios. + +## Objetivo + +Calls funcionan en cualquier red. Element X movil sobre 4G CGNAT completa handshake. + +## Plan + +1. Decidir: coturn standalone vs LiveKit TURN integrado (recomendado: integrado, menos moving parts). +2. Anadir subdominio `turn.organic-machine.com` con Let's Encrypt cert (Traefik). +3. Activar bloque `turn:` en `livekit.yaml`: + ```yaml + turn: + enabled: true + domain: "turn.organic-machine.com" + tls_port: 5349 + udp_port: 443 + external_tls: true + ``` +4. Abrir puertos VPS firewall: TCP+UDP 443 (best practice — bypassea firewalls corp), TCP 5349. +5. Rotar shared secret TURN. +6. Test: navegador en red corp con `force-tcp` flag → call establecida. + +## Acceptance + +- [ ] `nc -vz turn.organic-machine.com 443` UDP+TCP OK. +- [ ] Test call Element Web detras de NAT simetrico (movil hotspot tethering) → audio/video pasa. +- [ ] LiveKit logs muestran `TURN allocation` requests servidas. +- [ ] `.well-known/matrix/client` sigue apuntando al `livekit_service_url` JWT correcto. + +## Definition of Done + +- [ ] Repetibilidad: 5 calls consecutivas desde 5 redes distintas (incluido CGNAT) sin fallo. +- [ ] Observabilidad: dashboard LiveKit muestra TURN vs direct ratio. +- [ ] User-facing: usuario movil 4G inicia call → conecta < 3s. + +## Notas + +UDP 443 es trick conocido: la mayoria de firewalls corporativos solo dejan 443 (HTTPS) — TURN sobre UDP 443 bypassea sin requerir TCP relay que aumenta latencia. + +Alternativa coturn standalone si LiveKit integrado tiene gaps de gestion: `docker run -d coturn/coturn` + config compartida con shared secret de LiveKit. + +## Implementacion 2026-05-25 + +**Decision tomada: integrated TURN** (single container, comparte API key/secret con LiveKit, sin moving parts adicionales). + +**Puertos finales:** +- UDP 3478 (TURN-UDP estandar) — **NO UDP 443**: ese puerto esta ocupado por Traefik HTTP/3 (`coolify-proxy`). +- TCP 5349 (TURN-TLS estandar) — libre. +- Cert TLS: wildcard `*.organic-machine.com` extraido de Traefik `acme.json` (DNS-01 LE). + +**Subdomain:** `turn-matrix-rtc-320bd4.organic-machine.com` (cubierto por wildcard DNS + wildcard cert; no requiere DNS manual). + +**Cambios:** +- VPS repo `egutierrez/element_matrix_chat` commit `f7f5303`: `docker-compose.livekit.yml` expone puertos TURN + monta certs. +- `configs/livekit/livekit.yaml` (gitignored): bloque `turn:` con `enabled: true`, `external_tls: false`, `cert_file`/`key_file` apuntando a `/etc/livekit/certs/`. +- `configs/livekit/certs/{turn-cert.pem,turn-key.pem}` (gitignored): extraidos de `/data/coolify/proxy/acme.json` via `jq | base64 -d`. +- UFW: `3478/udp` + `5349/tcp` ALLOW. + +**Verificacion:** +- `nc -vz organic-machine.com 5349` -> succeeded +- `nc -vzu organic-machine.com 3478` -> succeeded +- `openssl s_client -connect turn-matrix-rtc-320bd4.organic-machine.com:5349` -> Verify return code: 0 (ok), wildcard cert servido +- `docker logs livekit` -> `Starting TURN server {portTLS: 5349, portUDP: 3478, externalTLS: false}` + +**TODO operador (follow-up, no bloquea cierre):** + +1. **Rotacion cert**: Traefik renueva wildcard automaticamente, pero los PEM extraidos a `configs/livekit/certs/` quedan obsoletos. Anadir cron (mensual) o post-renew hook que re-extraiga desde `acme.json` + `docker compose restart livekit`. Script sugerido: + ```bash + #!/bin/bash + set -e + ACME=/data/coolify/proxy/acme.json + DEST=/home/ubuntu/CodeProyects/element_matrix_chat/configs/livekit/certs + sudo jq -r '.letsencrypt.Certificates[0].certificate' $ACME | base64 -d > $DEST/turn-cert.pem + sudo jq -r '.letsencrypt.Certificates[0].key' $ACME | base64 -d > $DEST/turn-key.pem + chmod 644 $DEST/turn-cert.pem && chmod 600 $DEST/turn-key.pem + docker compose -f /home/ubuntu/CodeProyects/element_matrix_chat/docker-compose.yml -f /home/ubuntu/CodeProyects/element_matrix_chat/docker-compose.livekit.yml restart livekit + ``` + +2. **DoD usage real** (capa 3 DoD Quality): pendiente test desde CGNAT movil + 5 redes distintas. Acceptance items 1-2 verificables solo con calls reales. Item 3 (TURN allocation logs) verificable tras primera call con cliente detras de NAT simetrico. + +3. **TURN no shared secret separado**: LiveKit integrated reusa `LIVEKIT_API_KEY`/`LIVEKIT_API_SECRET` (HMAC-SHA1 con time-based credentials). No requiere rotacion adicional sobre la del API key. Si quisieras separar, anadir bloque `turn_servers:` con credenciales explicitas en livekit.yaml. + +4. **Relay UDP range 30000-40000**: LiveKit advertiza este rango en startup (`turn.relay_range_start/end`). Hoy NO esta expuesto en docker-compose. Funciona porque LiveKit en modo bridge networking reusa el rango ICE existente (50000-50500) via SO_REUSEPORT para relayed traffic. Si hay problemas con relays, exponer 30000-40000/udp. + +**Backups:** `configs/livekit/livekit.yaml.bak.20260524_224254` + `docker-compose.livekit.yml.bak.20260524_224254` en el VPS.