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

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:
tajniak81andClaude Opus 5.5 committed 2026-10-18 19:33:29 +02:00
1 parent 325431a5be
commit 76dfc98a86
49 files changed
+3849 -432

No files matched your search

+32
View File
@@ -19,6 +19,7 @@ import (
"github.com/dariuszjaniak/warp/server/internal/buildinfo"
"github.com/dariuszjaniak/warp/server/internal/clientapi"
"github.com/dariuszjaniak/warp/server/internal/config"
"github.com/dariuszjaniak/warp/server/internal/delayedevents"
"github.com/dariuszjaniak/warp/server/internal/federation"
"github.com/dariuszjaniak/warp/server/internal/federationapi"
"github.com/dariuszjaniak/warp/server/internal/httputil"
@@ -61,6 +62,7 @@ type Server struct {
clientAPI *clientapi.ClientAPI
appservices *appservice.API
delayed *delayedevents.Service
notices *servernotices.API
regTokens *userapi.RegistrationTokens
@@ -407,6 +409,24 @@ func New(ctx context.Context, cfg *config.Config, opts Options) (*Server, error)
}
emit.add(s.appservices)
// Delayed events are sent by the roomserver as the user who
// scheduled them, and hear about state changes on the emit stage:
// someone else setting a piece of state cancels the delayed
// events that would overwrite it.
s.delayed, err = delayedevents.New(delayedevents.Options{
Store: s.db.DelayedEvents(),
Rooms: s.rooms,
MaxDelay: cfg.ClientAPI.DelayedEvents.MaxDelay,
MaxPerUser: cfg.ClientAPI.DelayedEvents.MaxPerUser,
DescribeError: clientapi.DescribeRoomError,
Logger: log,
})
if err != nil {
_ = s.closeDB()
return nil, err
}
emit.add(s.delayed)
// Deactivating an account leaves its rooms, which only the
// roomserver can do.
s.users.SetAccountObserver(roomLeaver{rooms: s.rooms, log: log})
@@ -490,6 +510,9 @@ func New(ctx context.Context, cfg *config.Config, opts Options) (*Server, error)
AppServices: s.appservices,
RateLimit: cfg.ClientAPI.RateLimit,
TURN: cfg.ClientAPI.TURN,
RTC: cfg.ClientAPI.RTC,
DelayedEvents: s.delayed,
OpenIDTokens: s.db.OpenIDTokens(),
TrustXForwardedFor: cfg.ClientAPI.TrustXForwardedFor,
Logger: log,
})
@@ -522,6 +545,11 @@ func New(ctx context.Context, cfg *config.Config, opts Options) (*Server, error)
// talks to the services, and tests that drive the handler directly
// need it running.
s.appservices.Start(ctx)
// So do delayed events: a call member who drops off has to be removed
// whether or not anything is listening yet.
if s.delayed != nil {
s.delayed.Start(ctx)
}
s.handler = httputil.Chain(s.routes(),
httputil.Recoverer,
@@ -749,6 +777,10 @@ func (s *Server) Close() error {
// cursors.
s.appservices.Close()
}
if s.delayed != nil {
// And the delayed event scheduler, which sends events.
s.delayed.Close()
}
if s.stopTracing != nil {
// Spans still in the batch are sent rather than lost.
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)