Я пытаюсь развернуть свой собственный стек для видеозвонков на Matrix. Конфигурация следующая:
- Matrix Synapse (v1.135.0, регистрация закрыта, federation отключён)
- Нет веб-интерфейса или контейнера Element Call (только signaling)
- LiveKit (v1.9.0, работает локально)
- lk-jwt-service (v0.3.0, commit 114f0f4560...)
- Все сервисы работают на одном сервере (Ubuntu 22.04.5 LTS) с сертификатами Let's Encrypt для TLS
- В качестве reverse proxy используется nginx (v1.18.0)
- Docker не используется, все сервисы запускаются нативно
- Стек полностью изолирован, federation не используется
Моя цель:
Включить 1:1 и групповые аудио/видеозвонки только в клиенте Element X (без веб-интерфейса). Документация по такой конфигурации крайне скудная и фрагментированная. Есть множество гайдов и источников, но многие из них противоречат друг другу или описывают только отдельные части процесса.
Я пытался следовать последним рекомендациям по установке Element X + LiveKit + JWT-only, но так и не смог добиться рабочей связки.
Главная проблема:
Входящие звонки появляются в клиенте Element X (push-уведомление приходит, вызов можно "принять"), но после ответа нет ни аудио, ни видео, а экран звонка зависает на “Ожидание медиа…”. Медиа-соединение не устанавливается, и звонок в итоге обрывается по таймауту.
Это мой конфиг homeserver.yaml для Matrix Synapse:
pid_file: "/var/run/matrix-synapse.pid"
listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
bind_addresses: ['127.0.0.1']
resources:
- names: [client, federation]
compress: false
database:
name: psycopg2
txn_limit: 10000
args:
user: matrix
password: ***********
database: matrix
host: localhost
port: 5432
cp_min: 5
cp_max: 20
public_baseurl: https://syn.example.com
log_config: "/etc/matrix-synapse/log.yaml"
media_store_path: /var/lib/matrix-synapse/media
signing_key_path: "/etc/matrix-synapse/homeserver.signing.key"
trusted_key_servers:
- server_name: "example.com"
suppress_key_server_warning: true
enable_registration: false
macaroon_secret_key: "**************"
registration_shared_secret: "*******************"
search_all_users: true
prefer_local_users: true
experimental_features:
msc3266_enabled: true
msc4222_enabled: true
max_event_delay_duration: 24h
rc_message:
per_second: 0.5
burst_count: 30
rc_delayed_event_mgmt:
per_second: 1
burst_count: 20
allow_public_rooms_over_federation: false
federation_domain_whitelist: ["example.com"]
federation_sender_enabled: false
admin_users:
- "@admin:example.com"
Вот переменные окружения, используемые для lk-jwt-service:
LIVEKIT_KEY=mykey
LIVEKIT_SECRET=mysecret
LIVEKIT_URL=wss://call.example.com/livekit
LIVEKIT_JWT_PORT=8080
LIVEKIT_FULL_ACCESS_HOMESERVERS=example.com
Вот мой конфиг livekit.yaml:
port: 7880
log_level: info
rtc:
port_range_start: 50000
port_range_end: 55000
tcp_port: 7881
use_external_ip: true
interfaces:
includes:
- eth0
turn:
enabled: true
tls_port: 5349
relay_range_start: 50000
relay_range_end: 55000
external_tls: true
domain: call.example.com
cert_file: /etc/letsencrypt/live/call.example.com/fullchain.pem
key_file: /etc/letsencrypt/live/call.example.com/privkey.pem
keys:
mykey: mysecret
Ниже приведены мои конфиги nginx.
syn.* — основной домен Matrix Synapse
call.* — используется для сигнальных соединений LiveKit (медиа/звонки по wss и https)
и главный домен — публичный основной домен, только для отдачи well-known файлов Matrix (для автообнаружения)
(Все домены работают на одном сервере с валидными сертификатами Let's Encrypt)
Вот мой nginx-конфиг для Matrix Synapse (syn.*):
server {
listen 80;
server_name syn.example.com;
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name syn.example.com;
ssl_certificate /etc/letsencrypt/live/syn.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/syn.example.com/privkey.pem;
access_log /var/log/nginx/matrix.access.log;
error_log /var/log/nginx/matrix.error.log warn;
location /_matrix {
proxy_pass http://127.0.0.1:8008;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
}
location /_synapse {
proxy_pass http://127.0.0.1:8008;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
}
location / {
root /var/www/syn;
index index.html;
}
}
Вот мой nginx-конфиг для звонков (call.*):
server {
listen 443 ssl http2;
server_name call.*;
ssl_certificate /etc/letsencrypt/live/call.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/call.example.com/privkey.pem;
access_log /var/log/nginx/element-call.access.log;
error_log /var/log/nginx/element-call.error.log;
location /livekit/sfu {
proxy_pass http://127.0.0.1:7880/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
location /livekit/jwt {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
server_name call.example.com;
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 404;
}
}
Вот мой nginx-конфиг для основного домена для раздачи файлов Matrix .well-known:
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
access_log /var/log/nginx/matrix.access.log;
error_log /var/log/nginx/matrix.error.log warn;
location /.well-known/matrix/client {
alias /var/www/example/.well-known/matrix/client;
add_header Content-Type application/json;
add_header Access-Control-Allow-Origin *;
}
root /var/www/example;
}
server {
listen 80;
server_name example.com;
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://$host$request_uri;
}
}
Вот мой файл .well-known/matrix/client (отдается по адресу
https://example.com/.well-known/matrix/client):
{
"m.homeserver": {
"base_url": "https://syn.example.com"
},
"org.matrix.msc4143.rtc_foci": [
{
"type": "livekit",
"livekit_service_url": "https://call.example.com/livekit/sfu"
}
]
}
Тестирую на Element X версии 25.07.1 (Android).
Я перепробовал всё, что только можно, включая несколько новых попыток настройки с нуля. Мои конфиги максимально близки к официальным мануалам и референсным примерам, но проблема в том, что эти руководства часто противоречат друг другу или не раскрывают важных деталей.
Буду очень благодарен, если кто-то сможет указать, что я упустил или делаю не так. Любые советы и предложения приветствуются!