feat: MatrixRTC support: any-event notification, arbitrary state and MSC4354 sticky events
Proposed changes
What Rocket.Chat needs from the SDK to run LiveKit video calls in Matrix-federated rooms over MatrixRTC, the way
Element Call does. Rocket.Chat is the homeserver: it announces call memberships in the room on behalf of its users
and reads the announcements of other servers. Consumer: RocketChat/Rocket.Chat#42724 (draft, proof of concept).
- fix:
sendEventToAllServersInRoomderived a destination from anystate_key. Call memberships are keyed
_@alice:rc1_DEVICE_m.call, which produced the bogus destinationrc1_DEVICE_m.call(and a key without:
threw). Only a state key that is a user id adds a destination now. - feat:
homeserver.matrix.eventis emitted for every PDU accepted from federation, of any type, after the
typed emits. Until now unknown types were stored but never reached the consumer. - feat:
sendStateEvent(roomId, type, stateKey, content, sender)sends state of any type and state key
({}content included, which is how a call membership leaves), throwingEventRejectedErrorwhen the room's
rules refuse it. - feat: rooms the SDK creates give
org.matrix.msc3401.call.memberpower 0, as Element does, so any member
can join a call. - feat:
isUserJoined(roomId, userId)andgetJoinedServers(roomId), to authorize token requests from other
servers. - feat: MSC4354 sticky events, under the unstable
msc4354_stickyfield:- inbound sticky PDUs keep the field and get a sticky window,
min(received_ts, origin_server_ts) + min(duration_ms, 1h), indexed for querying; rejected, state and late events are never sticky; sendStickyEvent(roomId, type, content, sender, durationMs); the field is covered byhashes.sha256and not by
the event id or signatures, which are computed over the redacted form;getActiveStickyEvents(roomId, { type?, now? });homeserver.matrix.eventcarriessticky.expiresAt;- this server's own unexpired sticky events are pushed to a server that newly joins a room (MSC4354 obligation 2),
after a short delay so the joiner has stored the room.
- inbound sticky PDUs keep the field and get a sticky window,
- refactor: locally sent events share one build-and-persist path (enables
sendStickyEvent).
Each commit builds and passes the tests on its own.
Not done
- Soft-failure re-evaluation of sticky events (MSC4354 obligation 4).
- Catch-up after a destination was unreachable for long: the outgoing queue is in memory.
- Redactions received over federation are still not applied (pre-existing), so a redacted sticky event stays listed
until it expires. - Spam checks and rate limits on sticky events.
Steps to test or reproduce
RUN_MONGO_TESTS=1 MONGO_URI='mongodb://127.0.0.1:<port>/?directConnection=true' bun test from the repo root.
Locally: 594 pass, 3 skip, 4 fail. The 4 failures are the PerDestinationQueue timer tests, which need
jest.advanceTimersByTime from bun 1.3 (the local bun was 1.2.20); they fail identically on 0.8.1.
The wire formats this follows are documented in docs/features/video-conference-federation/protocol.md on the
Rocket.Chat PR.
š¤ Generated with Claude Code