Files
tajniak81andClaude Opus 5.5 76dfc98a86
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Test (windows-latest) (push) Canceled after 0s
CI / Storage tests on PostgreSQL (push) Canceled after 0s
CI / Complement (federation off) (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
CI / Smoke test the binary (push) Canceled after 0s
CI / Admin panel (push) Canceled after 0s
CI / Web client (push) Canceled after 0s
Group voice and video calls with MatrixRTC and LiveKit
Every call in the web client now goes through MatrixRTC with a LiveKit
SFU, the model Element Call and Element X use; the legacy 1:1 m.call.*
calls are gone. In a room of two the other person is rung and can answer
or decline; in a bigger room a banner offers to join the call. Calls show
a grid of participant tiles with mute, camera, screen sharing and leave,
and media is end-to-end encrypted in encrypted rooms.

Server:
- MSC4140 delayed events (send/state with a delay, plus the
  delayed_events list/restart/cancel/send API), stored and scheduled, so
  a member whose client drops off is taken out of the call.
- POST /user/{userId}/openid/request_token, which lk-jwt-service uses to
  hand out LiveKit tokens.
- org.matrix.msc4143.rtc_foci in /.well-known/matrix/client, configured
  under client_api.rtc.
- Room presets let members send call membership state.
- Migration 0014 for both SQL dialects.

Deploy: LiveKit and lk-jwt-service in compose (calls.yml) and the helm
chart. Docs: docs/calls.md, configuration and deployment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 19:33:29 +02:00

118 lines
4.5 KiB
YAML

# Calls for a local Warp: a LiveKit SFU and the LiveKit JWT service
# (lk-jwt-service), layered over docker-compose.yml.
#
# cd deploy/compose
# docker compose -f docker-compose.yml -f calls.yml --profile web up -d --build
#
# Then open http://localhost:8080 in a few browsers (or private windows),
# sign in as different users, and press the voice or video call button in
# a room: in a room of two it rings the other person.
#
# How the pieces find each other:
#
# browser ── /.well-known/matrix/client ──▶ warp rtc_foci: http://localhost:8070
# browser ── openid/request_token ────────▶ warp an OpenID token
# browser ── POST /sfu/get ───────────────▶ jwt {url, jwt} for the LiveKit room
# jwt ───── /_matrix/federation/v1/openid/userinfo ─▶ warp (https://<server_name>:8448)
# browser ── ws://localhost:7880 + UDP 7882 ─▶ livekit
#
# The JWT service checks a token by asking the user's homeserver over the
# federation port, with TLS. To make that work with the default server name
# "localhost", warp gets a federation listener on :8448 with a self-signed
# certificate, and the JWT service and LiveKit share warp's network
# namespace, so that "localhost" means warp for all three. This is a local
# setup: in production, give each its own hostname and real certificates
# (see docs/calls.md).
services:
# Makes the self-signed certificate for warp's federation listener once.
calls-cert:
image: alpine:3.21
restart: "no"
entrypoint: ["/bin/sh", "-c"]
command:
- >-
test -f /tls/tls.crt ||
(apk add --no-cache openssl >/dev/null &&
openssl req -x509 -newkey rsa:2048 -nodes -days 3650
-subj "/CN=$${WARP_SERVER_NAME}" -addext "subjectAltName=DNS:$${WARP_SERVER_NAME}"
-keyout /tls/tls.key -out /tls/tls.crt &&
chmod 644 /tls/tls.key /tls/tls.crt)
environment:
WARP_SERVER_NAME: ${WARP_SERVER_NAME:-localhost}
volumes:
- warp-tls:/tls
warp:
depends_on:
calls-cert:
condition: service_completed_successfully
environment:
WARP_RTC_LIVEKIT_SERVICE_URL: ${WARP_LIVEKIT_JWT_PUBLIC_URL:-http://localhost:8070}
WARP_LISTEN_FEDERATION_ADDRESS: ":8448"
WARP_TLS_CERT_FILE: /tls/tls.crt
WARP_TLS_KEY_FILE: /tls/tls.key
volumes:
- warp-tls:/tls:ro
# LiveKit and the JWT service live in this container's network
# namespace, so their ports are published here.
ports:
- "8070:8070" # lk-jwt-service
- "7880:7880" # LiveKit signalling (WebSocket)
- "7881:7881" # LiveKit media over TCP, the fallback
- "7882:7882/udp" # LiveKit media over UDP
livekit:
image: livekit/livekit-server:v1.13.7
restart: unless-stopped
network_mode: service:warp
depends_on:
- warp
environment:
# node_ip is what LiveKit tells browsers to send media to. 127.0.0.1
# works for browsers on this machine; set LIVEKIT_NODE_IP to the
# machine's LAN address to call from other devices.
LIVEKIT_CONFIG: |
port: 7880
bind_addresses: [""]
rtc:
tcp_port: 7881
udp_port: 7882
use_external_ip: false
node_ip: ${LIVEKIT_NODE_IP:-127.0.0.1}
room:
auto_create: false
keys:
${LIVEKIT_KEY:-warp}: ${LIVEKIT_SECRET:-warp-local-livekit-secret-change-me-0123456789}
logging:
level: info
livekit-jwt:
image: ghcr.io/element-hq/lk-jwt-service:0.8.0
restart: unless-stopped
network_mode: service:warp
depends_on:
- warp
- livekit
environment:
LIVEKIT_JWT_BIND: ":8070"
# The URL handed to browsers, and the one the service creates rooms
# through: both reach LiveKit at localhost here.
LIVEKIT_URL: ${LIVEKIT_PUBLIC_URL:-ws://localhost:7880}
LIVEKIT_KEY: ${LIVEKIT_KEY:-warp}
LIVEKIT_SECRET: ${LIVEKIT_SECRET:-warp-local-livekit-secret-change-me-0123456789}
# Users of this server may create call rooms on the SFU.
LIVEKIT_FULL_ACCESS_HOMESERVERS: ${WARP_SERVER_NAME:-localhost}
# warp's federation certificate is self-signed.
LIVEKIT_INSECURE_SKIP_VERIFY_TLS: YES_I_KNOW_WHAT_I_AM_DOING
web:
environment:
# The browser connects to the JWT service and to LiveKit directly.
WARP_WEB_CALL_ORIGINS: >-
${WARP_LIVEKIT_JWT_PUBLIC_URL:-http://localhost:8070}
${LIVEKIT_PUBLIC_URL:-ws://localhost:7880}
volumes:
warp-tls: