163 lines
5.4 KiB
Markdown
163 lines
5.4 KiB
Markdown
# MatrixRTC / LiveKit — Element X Calls
|
|
|
|
**Last updated:** 2026-03-19
|
|
|
|
MatrixRTC enables voice/video calls in Element X using a LiveKit SFU backend. Both homeservers (`mx.vish.gg` and `matrix.thevish.io`) share the same LiveKit SFU on `matrix-ubuntu`.
|
|
|
|
---
|
|
|
|
## Service Overview
|
|
|
|
| Property | Value |
|
|
|----------|-------|
|
|
| **Host** | matrix-ubuntu (`192.168.0.154`) |
|
|
| **Matrix homeservers** | `mx.vish.gg` (synapse-mx.service) and `matrix.thevish.io` (synapse.service) — both on Synapse 1.148.0 |
|
|
| **Compose file** | `hosts/vms/matrix-ubuntu/livekit.yml` (deployed manually at `/opt/livekit/`) |
|
|
| **LiveKit version** | 1.9.12 |
|
|
| **JWT service** | `ghcr.io/element-hq/lk-jwt-service:latest-ci` |
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
```
|
|
Element X ──→ mx.vish.gg (.well-known) ──→ livekit.mx.vish.gg/livekit/jwt (JWT service)
|
|
──→ matrix.thevish.io (.well-known) ──→ livekit.mx.vish.gg/livekit/sfu (LiveKit SFU)
|
|
livekit.mx.vish.gg/ (LiveKit WS)
|
|
```
|
|
|
|
Both homeservers share the same LiveKit backend.
|
|
|
|
- **NPM** on Calypso proxies `livekit.mx.vish.gg` → matrix-ubuntu
|
|
- `/livekit/jwt/` → JWT service port 8089 (container 8080)
|
|
- `/livekit/sfu/` → LiveKit SFU port 7880
|
|
- `/` → LiveKit SFU port 7880 (WebSocket for direct connections)
|
|
- **DNS**: `livekit.mx.vish.gg` A record unproxied → `184.23.52.14` (home WAN)
|
|
- **TLS**: Let's Encrypt cert issued via Cloudflare DNS challenge on matrix-ubuntu, copied to NPM as `npm-7`
|
|
|
|
---
|
|
|
|
## Endpoints
|
|
|
|
| Endpoint | URL | Purpose |
|
|
|----------|-----|---------|
|
|
| JWT service healthz | `https://livekit.mx.vish.gg/livekit/jwt/healthz` | Health check |
|
|
| JWT service SFU get | `https://livekit.mx.vish.gg/livekit/jwt/sfu/get` | Token exchange |
|
|
| LiveKit SFU WS | `wss://livekit.mx.vish.gg/livekit/sfu/` | WebSocket signalling |
|
|
| LiveKit HTTP | `https://livekit.mx.vish.gg/` | SFU API |
|
|
| .well-known | `https://mx.vish.gg/.well-known/matrix/client` | RTC foci advertisement |
|
|
|
|
---
|
|
|
|
## Configuration Files on matrix-ubuntu
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `/opt/livekit/docker-compose.yml` | LiveKit + JWT service deployment |
|
|
| `/opt/livekit/livekit.yaml` | LiveKit SFU config (keys, RTC ports, external IP) |
|
|
| `/opt/synapse-mx/homeserver.yaml` | Synapse config (MSCs, rate limits) |
|
|
| `/etc/nginx/sites-available/mx-vish-gg` | nginx serving `.well-known` and Element static files |
|
|
| `/etc/letsencrypt/live/livekit.mx.vish.gg/` | TLS cert (auto-renews, copies to NPM via deploy hook) |
|
|
|
|
---
|
|
|
|
## Synapse homeserver.yaml additions
|
|
|
|
```yaml
|
|
# MatrixRTC / Element Call support
|
|
experimental_features:
|
|
msc3266_enabled: true # Room Summary API (knocking over federation)
|
|
msc4222_enabled: true # state_after in sync v2
|
|
msc4140_enabled: true # Delayed events (call participation signalling)
|
|
|
|
max_event_delay_duration: 24h
|
|
|
|
rc_message:
|
|
per_second: 0.5
|
|
burst_count: 30
|
|
|
|
rc_delayed_event_mgmt:
|
|
per_second: 1
|
|
burst_count: 20
|
|
```
|
|
|
|
---
|
|
|
|
## .well-known/matrix/client
|
|
|
|
Served by nginx at `https://mx.vish.gg/.well-known/matrix/client`:
|
|
|
|
```json
|
|
{
|
|
"m.homeserver": {"base_url": "https://mx.vish.gg"},
|
|
"org.matrix.msc4143.rtc_foci": [
|
|
{
|
|
"type": "livekit",
|
|
"livekit_service_url": "https://livekit.mx.vish.gg/livekit/jwt"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## LiveKit SFU config (/opt/livekit/livekit.yaml)
|
|
|
|
Key settings:
|
|
- `use_external_ip: true` — auto-detects WAN IP `184.23.52.14`
|
|
- `use_ice_lite: true` — optimised for server-side NAT traversal
|
|
- `room.auto_create: false` — only lk-jwt-service creates rooms (security)
|
|
- RTC ports: 7880 TCP (API/WS), 7881 TCP (RTC), 50000-60000 UDP (media)
|
|
- **Note:** UDP 50000-60000 port range is NOT currently forwarded on the router — TURN relay is used instead via coturn at `turn:mx.vish.gg:3479`
|
|
|
|
---
|
|
|
|
## TLS Certificate Renewal
|
|
|
|
Cert is issued on matrix-ubuntu via certbot + Cloudflare DNS plugin. A deploy hook copies it to NPM on Calypso after renewal:
|
|
|
|
```
|
|
/etc/letsencrypt/renewal-hooks/deploy/copy-to-npm.sh
|
|
```
|
|
|
|
If the hook fails, manually copy:
|
|
```bash
|
|
ssh matrix-ubuntu
|
|
sudo cp /etc/letsencrypt/live/livekit.mx.vish.gg/fullchain.pem \
|
|
/tmp/lk.crt
|
|
sudo cp /etc/letsencrypt/live/livekit.mx.vish.gg/privkey.pem \
|
|
/tmp/lk.key
|
|
scp -P 62000 /tmp/lk.crt Vish@100.103.48.78:/volume1/docker/nginx-proxy-manager/data/custom_ssl/npm-7/fullchain.pem
|
|
scp -P 62000 /tmp/lk.key Vish@100.103.48.78:/volume1/docker/nginx-proxy-manager/data/custom_ssl/npm-7/privkey.pem
|
|
ssh -p 62000 Vish@100.103.48.78 'sudo /usr/local/bin/docker exec nginx-proxy-manager nginx -s reload'
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Calls not working in Element X
|
|
1. Check `.well-known` is advertising foci: `curl https://mx.vish.gg/.well-known/matrix/client`
|
|
2. Check JWT service: `curl https://livekit.mx.vish.gg/livekit/jwt/healthz`
|
|
3. Check LiveKit is running: `ssh matrix-ubuntu "sudo docker ps | grep livekit"`
|
|
4. Check LiveKit logs: `ssh matrix-ubuntu "sudo docker logs livekit 2>&1 | tail -20"`
|
|
|
|
### Stuck calls
|
|
See [How to resolve stuck MatrixRTC calls](https://sspaeth.de/2025/02/how-to-resolve-stuck-matrixrtc-calls/) — usually caused by delayed events not cleaning up.
|
|
|
|
### JWT service returns 400
|
|
Normal for unauthenticated requests. Means the service is running correctly.
|
|
|
|
### Restarting services
|
|
```bash
|
|
ssh matrix-ubuntu
|
|
cd /opt/livekit
|
|
sudo docker compose restart
|
|
```
|
|
|
|
### Restarting Synapse
|
|
```bash
|
|
ssh matrix-ubuntu
|
|
sudo systemctl restart synapse-mx.service
|
|
```
|