CreaRack-SL

ADR: WebRTC peer-to-peer reemplaza Jitsi en el workspace del equipo

Estado

Aceptada — implementada en s79, commit fc83bf5 (22-05-2026).


Contexto

El workspace interno del equipo (Edu/Dani/Txell) necesitaba videollamadas integradas. La solución anterior usaba un <iframe> embebido apuntando a meet.jit.si (servicio público gratuito de Jitsi).

Problema detectado en s79

Durante las pruebas se descubrió que meet.jit.si público impone dos fricciones no suprimibles vía URL params:

  1. Pantalla prejoin “Join Meeting”: aunque prejoinPageEnabled=false funciona para algunos usuarios, el servidor público forzaba la pantalla en ciertos contextos.
  2. “Sign in as moderator”: el primer participante en una sala vacía recibe una pantalla de login para reclamar el rol de moderador. Es un mecanismo anti-spam de Jitsi público, no controlable por parámetros de URL.

El componente JitsiMeetingModal.tsx ya tenía documentado en su código que esta segunda pantalla era una “restricción no controlable de meet.jit.si público”. La alternativa de self-hostearse Jitsi requería un servidor dedicado (~5 €/mes + mantenimiento Docker).

Opciones evaluadas

OpciónCosteFricción UXPrivacidadComplejidad
Jitsi self-hosted (Hetzner)~5€/mes + opsMínimaAltaMedia
Daily.co / Whereby~15€/mesMínimaMediaBaja
WebRTC nativo browser + signaling propio0€CeroMáximaMedia
Jitsi público (status quo)0€Alta (screens no suprimibles)Media—

Decisión

Migrar a WebRTC nativo del browser con un endpoint de señalización mínimo sobre D1.

El vídeo va directo peer-to-peer (browser ↔ browser) usando:

  • STUN público de Google (stun.l.google.com:19302) para el ICE hole-punching.
  • D1 como canal de señalización efímero (TTL 60s, polling 1.5s).
  • Sin TURN en primera iteración (LAN/WiFi interno del equipo funciona sin NAT estricto).

Consecuencias

Positivas

  • Cero fricción UX: el usuario entra directo a la sala con cámara/mic activos, sin pantallas intermedias.
  • Cero coste: STUN Google es gratuito; D1 gestiona bytes mínimos (~30 rows/sesión × 60s TTL).
  • Cero infra nueva: no hay servidor de medios, no hay Docker, no hay servicio a mantener.
  • Privacidad máxima: el vídeo nunca pasa por Jitsi, Cloudflare ni ningún tercero.
  • Control total: la UX (grid de vídeos, controles, drag/resize modal) es código propio.

Negativas / riesgos

  • NAT estricto: sin TURN server, peers en redes corporativas o CGNAT pueden no conectar. Mitigación: añadir Cloudflare TURN (tier gratuito) si se detecta el problema.
  • Sin grabación server-side: por diseño (privacidad). Si se necesita grabación futura, requiere un enfoque diferente.
  • Polling latency: hasta ~3s de latencia en la señalización inicial. Aceptable para el caso de uso (3 personas del equipo).
  • Mesh a 3 peers: solución válida solo para grupos pequeños. No escala a >5-6 peers sin SFU.
  • Mantenimiento propio: si hay bugs en WebRTC, el equipo debe resolverlos (vs. Jitsi como producto maduro).

Implementación

Ver [[feature—workspace—webrtc-peer-to-peer]] para la arquitectura técnica completa.

Componentes creados:

  • functions/api/webrtc/signal.ts — Endpoint de señalización (CF Pages Function)
  • migrations/0035_webrtc_signaling.sql — Tabla D1 con TTL inline
  • src/components/widgets/MeetingRoom.tsx — Componente React mesh WebRTC

Eliminado:

  • src/components/widgets/JitsiMeetingModal.tsx (291 LOC)

Trabajo futuro pendiente

  • Evaluar TURN de Cloudflare si algún peer reporta fallos de conexión.
  • Monitorizar tasa de ICE failures en producción.
  • Considerar WebSocket para signaling si el polling 1.5s resulta demasiado lento.

Véase también

  • [[feature—workspace—webrtc-peer-to-peer]]
  • [[entity—workspace—endpoint—webrtc-signal]]
  • [[entity—workspace—table—webrtc-signaling]]
  • [[workspace—que-es-workspace]]