Group voice and video calls with MatrixRTC and LiveKit
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
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
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>
This commit is contained in:
1 parent
325431a5be
commit
76dfc98a86
49 files changed
+3849
-432
No files matched your search
@@ -0,0 +1,117 @@
|
||||
# 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:
|
||||
@@ -12,6 +12,12 @@
|
||||
# http://localhost:8009; with the web profile, the web client is on
|
||||
# http://localhost:8080.
|
||||
#
|
||||
# For voice and video calls (LiveKit and its JWT service) add calls.yml:
|
||||
#
|
||||
# docker compose -f docker-compose.yml -f calls.yml --profile web up -d
|
||||
#
|
||||
# WARP_PORT moves the homeserver off host port 8008.
|
||||
#
|
||||
# Create the first administrator once the server is up:
|
||||
#
|
||||
# docker compose exec warp warp create-account --username admin --admin
|
||||
@@ -63,7 +69,7 @@ services:
|
||||
volumes:
|
||||
- warp-data:/data
|
||||
ports:
|
||||
- "8008:8008"
|
||||
- "${WARP_PORT:-8008}:8008"
|
||||
|
||||
# The admin panel is optional. Start it with the admin profile:
|
||||
# docker compose --profile admin up -d
|
||||
|
||||
Reference in new issue
Block a user