diff --git a/dev/issues/0165-matrix-media-store-luks.md b/dev/issues/0165-matrix-media-store-luks.md new file mode 100644 index 00000000..c333f573 --- /dev/null +++ b/dev/issues/0165-matrix-media-store-luks.md @@ -0,0 +1,65 @@ +--- +id: "0165" +title: "Cifrar media_store/ Synapse con LUKS at-rest" +status: pendiente +type: infra +domain: + - matrix +scope: app:element_matrix_chat +priority: media +depends: [] +blocks: [] +related: ["0162"] +created: 2026-05-24 +updated: 2026-05-24 +tags: [matrix, synapse, encryption, security, luks] +--- +# 0165 — Cifrar media_store/ Synapse con LUKS at-rest + +**Status:** pendiente +**Created:** 2026-05-24 +**Type:** infra +**Priority:** media +**Domain:** matrix +**Scope:** app:element_matrix_chat +**Depends:** — +**Blocks:** — + +## Problema + +`synapse_data/media_store/` contiene archivos subidos (fotos, voice messages, attachments) + thumbnails. Rooms NO-E2EE: media cleartext en disco. Tabla `media_repository` Postgres: filename/mime/uploader/room_id siempre cleartext. Riesgo: VPS provider snapshot disk, backups desencriptados, disco fisico. + +## Objetivo + +`media_store/` cifrado at-rest. Synapse arranca y sirve media normal. Decrypt automatico via keyfile en TPM o passphrase al boot. + +## Plan + +1. Decidir estrategia: LUKS container file-based (loop device) vs LUKS sobre volumen Docker dedicado. +2. Crear LUKS container 50GB (ajustar segun crecimiento previsto). +3. Montar como `/home/ubuntu/CodeProyects/element_matrix_chat/synapse_data/media_store_encrypted/`. +4. Stop Synapse → rsync `media_store/` → `media_store_encrypted/` → swap mountpoint. +5. Verificar Synapse sirve thumbnails + uploads OK. +6. Configurar auto-unlock via keyfile en `/root/.luks-media.key` con permisos 0400. +7. Documentar recovery passphrase en `pass` (entry `matrix/luks-media-passphrase`). + +## Acceptance + +- [ ] `media_store/` montado sobre LUKS, `lsblk -f` muestra crypto_LUKS. +- [ ] Synapse arranca tras reboot completo del VPS sin intervencion manual. +- [ ] Test: subir imagen via Element, verificar thumb generado. +- [ ] Test: leer media_store via `dd if=/dev/sdX` directo retorna basura cifrada. +- [ ] Passphrase backed up en `pass`. + +## Definition of Done + +- [ ] Repetibilidad: reboot VPS, media accesible sin intervencion. +- [ ] Observabilidad: log entry en `journalctl -u systemd-cryptsetup@*`. +- [ ] User-facing: clientes Element no notan diferencia. +- [ ] Recovery probado: detach LUKS y reattach con passphrase. + +## Notas + +LUKS solo protege at-rest. VPS provider con acceso a RAM viva ve plaintext via memory dump. Sin TPM atestado, utilidad real = anti-snapshot/anti-backup-leak/anti-physical-theft. + +Caveat: si keyfile vive en mismo disco que LUKS device, no protege contra disk theft. Mover keyfile a USB removible o TPM2 (`systemd-cryptenroll`). diff --git a/dev/issues/0166-matrix-livekit-turn-deploy.md b/dev/issues/0166-matrix-livekit-turn-deploy.md new file mode 100644 index 00000000..9aae42ce --- /dev/null +++ b/dev/issues/0166-matrix-livekit-turn-deploy.md @@ -0,0 +1,70 @@ +--- +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/0167-matrix-livekit-stun-leak.md b/dev/issues/0167-matrix-livekit-stun-leak.md new file mode 100644 index 00000000..d455218b --- /dev/null +++ b/dev/issues/0167-matrix-livekit-stun-leak.md @@ -0,0 +1,62 @@ +--- +id: "0167" +title: "Eliminar STUN leak a Google en LiveKit (hardcode external_ip)" +status: pendiente +type: infra +domain: + - matrix +scope: app:element_matrix_chat +priority: baja +depends: [] +blocks: [] +related: ["0166"] +created: 2026-05-24 +updated: 2026-05-24 +tags: [matrix, livekit, privacy, stun] +--- +# 0167 — Eliminar STUN leak a Google en LiveKit (hardcode external_ip) + +**Status:** pendiente +**Created:** 2026-05-24 +**Type:** infra +**Priority:** baja +**Domain:** matrix +**Scope:** app:element_matrix_chat +**Depends:** — +**Blocks:** — + +## Problema + +`rtc.use_external_ip: true` con `external_ip` vacio → LiveKit hace STUN query a `stun.l.google.com:19302` cada arranque para descubrir IP publica. Leak metadata server (IP del VPS) a Google. Contradice premisa "self-host privacy first". + +## Objetivo + +LiveKit conoce su IP publica sin contactar STUN externos. + +## Plan + +1. Determinar IP publica VPS: `curl -s ifconfig.me`. +2. Editar `configs/livekit/livekit.yaml`: + ```yaml + rtc: + use_external_ip: false + node_ip: "" + ``` +3. Si TURN propio desplegado (issue 0166), usar coturn como STUN propio. +4. Restart `element_matrix_chat-livekit-1`. +5. Test: call funciona igual. +6. Auditar: `docker logs element_matrix_chat-livekit-1 | grep -i stun` no muestra queries a google. + +## Acceptance + +- [ ] `tcpdump -i eth0 dst stun.l.google.com` no captura paquetes tras restart. +- [ ] Calls Element Call siguen funcionando 1:1 y grupo. + +## Definition of Done + +- [ ] Repetibilidad: reboot VPS, 0 paquetes a stun.l.google.com. +- [ ] Observabilidad: log LiveKit confirma IP hardcoded. + +## Notas + +Bajo impacto operacional pero alta consistencia con doctrina self-host. Si IP del VPS cambia (rara vez con VPS estatico), actualizar config manual o automatizar con script de healthcheck. diff --git a/dev/issues/0168-matrix-livekit-udp-range-expand.md b/dev/issues/0168-matrix-livekit-udp-range-expand.md new file mode 100644 index 00000000..89f4a6ca --- /dev/null +++ b/dev/issues/0168-matrix-livekit-udp-range-expand.md @@ -0,0 +1,58 @@ +--- +id: "0168" +title: "Ampliar UDP range LiveKit de 200 a 500 ports" +status: pendiente +type: infra +domain: + - matrix +scope: app:element_matrix_chat +priority: baja +depends: [] +blocks: [] +related: ["0166"] +created: 2026-05-24 +updated: 2026-05-24 +tags: [matrix, livekit, scaling, webrtc] +--- +# 0168 — Ampliar UDP range LiveKit de 200 a 500 ports + +**Status:** pendiente +**Created:** 2026-05-24 +**Type:** infra +**Priority:** baja +**Domain:** matrix +**Scope:** app:element_matrix_chat +**Depends:** — +**Blocks:** — + +## Problema + +LiveKit configurado con `port_range_start: 50000`, `port_range_end: 50200` (200 ports UDP). Cada participante usa ~2 ports → cap **~100 participantes concurrentes** sumando TODAS las calls del server. OK para uso personal hoy, justo si se anaden grupos simultaneos o reuniones >10 personas. + +## Objetivo + +Sostener al menos 250 participantes concurrentes sin port exhaustion. + +## Plan + +1. Editar `configs/livekit/livekit.yaml`: `port_range_end: 50500`. +2. Actualizar `docker-compose.yml` para exponer rango ampliado (300 puertos UDP adicionales). +3. Abrir rango en firewall VPS (UFW/iptables). +4. Restart stack LiveKit. +5. Smoke test: call funciona. + +## Acceptance + +- [ ] `docker port element_matrix_chat-livekit-1` muestra 50000-50500 UDP. +- [ ] `ss -lun | grep -c "0.0.0.0:50"` >= 500 tras restart. +- [ ] Call test OK. + +## Definition of Done + +- [ ] Repetibilidad: stack reinicia limpio. + +## Notas + +`docker-compose.yml` actualmente lista los 200 ports uno a uno (verboso pero explicito). Considerar usar sintaxis `"50000-50500:50000-50500/udp"` para legibilidad. + +NO incrementar a >1000 sin medir consumo memoria LiveKit — cada port asignado tiene overhead minimo pero acumula. diff --git a/dev/issues/0169-matrix-livekit-secret-rotate.md b/dev/issues/0169-matrix-livekit-secret-rotate.md new file mode 100644 index 00000000..36faa8e3 --- /dev/null +++ b/dev/issues/0169-matrix-livekit-secret-rotate.md @@ -0,0 +1,60 @@ +--- +id: "0169" +title: "Rotar LIVEKIT_SECRET (expuesto en sesion auditoria)" +status: pendiente +type: bugfix +domain: + - matrix +scope: app:element_matrix_chat +priority: alta +depends: [] +blocks: [] +related: [] +created: 2026-05-24 +updated: 2026-05-24 +tags: [matrix, livekit, security, secret-rotation] +--- +# 0169 — Rotar LIVEKIT_SECRET (expuesto en sesion auditoria) + +**Status:** pendiente +**Created:** 2026-05-24 +**Type:** bugfix +**Priority:** alta +**Domain:** matrix +**Scope:** app:element_matrix_chat +**Depends:** — +**Blocks:** — + +## Problema + +Durante auditoria 2026-05-24 (sesion Claude), `docker inspect element_matrix_chat-livekit-jwt-1` volco `LIVEKIT_SECRET=b00e98f70722bc...` cleartext en stdout de la sesion. Aunque la sesion es del operador, el secret quedo en log de conversacion + potencialmente en backups del log + transcripts. Rotacion necesaria por higiene. + +## Objetivo + +Nuevo secret 32 bytes hex, mismo `api_key` (o regenerar ambos), stack restart sin perdida sesion. + +## Plan + +1. Generar nuevo secret: `openssl rand -hex 32`. +2. Editar `configs/livekit/livekit.yaml` → bloque `keys:` con nuevo valor. +3. Editar `.env` de docker-compose (var `LIVEKIT_SECRET` consumida por `livekit-jwt`). +4. Restart `element_matrix_chat-livekit-1` y `element_matrix_chat-livekit-jwt-1` en orden. +5. Test call Element Call → handshake JWT OK. +6. Guardar secret antiguo + nuevo en `pass` con timestamp rotacion. + +## Acceptance + +- [ ] `docker inspect ... --format "{{.Config.Env}}"` muestra secret nuevo. +- [ ] Element Call inicia call sin error "invalid token". +- [ ] Entry `pass matrix/livekit-secret` actualizada. + +## Definition of Done + +- [ ] Repetibilidad: rotacion documentada como funcion del registry (candidato `livekit_secret_rotate_bash_infra`). +- [ ] Observabilidad: rotation log con timestamp. + +## Notas + +Considerar promover el procedimiento a funcion del registry: `livekit_secret_rotate_bash_infra(ssh_host, compose_dir)` que automatiza pasos 1-5 y guarda en pass via `gpg_pass_write`. + +Patron similar para otros secrets del stack (Synapse macaroon, MAS encryption key, postgres passwords) → capability group nuevo `secret-rotation`. diff --git a/dev/issues/0170-matrix-livekit-config-rename.md b/dev/issues/0170-matrix-livekit-config-rename.md new file mode 100644 index 00000000..228d1e6d --- /dev/null +++ b/dev/issues/0170-matrix-livekit-config-rename.md @@ -0,0 +1,55 @@ +--- +id: "0170" +title: "Renombrar livekit.example.yaml -> livekit.yaml en bind mount" +status: pendiente +type: chore +domain: + - matrix +scope: app:element_matrix_chat +priority: baja +depends: [] +blocks: [] +related: [] +created: 2026-05-24 +updated: 2026-05-24 +tags: [matrix, livekit, hygiene] +--- +# 0170 — Renombrar livekit.example.yaml -> livekit.yaml en bind mount + +**Status:** pendiente +**Created:** 2026-05-24 +**Type:** chore +**Priority:** baja +**Domain:** matrix +**Scope:** app:element_matrix_chat +**Depends:** — +**Blocks:** — + +## Problema + +`configs/livekit/livekit.yaml` mantiene los comentarios "Copy this file..." del template original. Funciona pero confunde: parece config sin completar. El bind mount apunta directo a este archivo, asi que renombrar limpiamente el archivo template y mantener `livekit.yaml` limpio para mantenimiento. + +## Objetivo + +`livekit.yaml` limpio sin comentarios de "example", `livekit.example.yaml` separado como referencia template inicial en repo. + +## Plan + +1. Crear `configs/livekit/livekit.example.yaml` con plantilla limpia (placeholders). +2. Eliminar comentarios "Copy this file..." del `livekit.yaml` actual. +3. Verificar `.gitignore` cubre `livekit.yaml` real pero no `livekit.example.yaml`. +4. Commit en `egutierrez/element_matrix_chat`. + +## Acceptance + +- [ ] `head -3 configs/livekit/livekit.yaml` NO menciona "example". +- [ ] `configs/livekit/livekit.example.yaml` versionado. +- [ ] Stack restart sin cambios funcionales. + +## Definition of Done + +- [ ] PR mergeado en `dataforge/element_matrix_chat`. + +## Notas + +Tarea de higiene puro. Cero impacto runtime. Mejora onboarding futuro si otro operador clona el repo.