diff --git a/README.md b/README.md
index 546e491a1..ecbabcf2c 100644
--- a/README.md
+++ b/README.md
@@ -115,30 +115,33 @@ For more details on the packages, see the
For operating and deploying Element Call on your own server, refer to the
[**Self-Hosting Guide**](./docs/self_hosting.md).
-## đź§ MatrixRTC Backend Discovery and Selection
+## MatrixRTC Transports
-For proper Element Call operation each site deployment needs a MatrixRTC backend
-setup as outlined in the [Self-Hosting Guide](./docs/self_hosting.md). A typical
-federated site deployment for three different sites A, B and C is depicted below.
+For proper operation of Element Call, each deployment needs to set up a
+MatrixRTC transport in the form of a LiveKit server as outlined in the
+[Self-Hosting Guide](./docs/self_hosting.md). A typical federated site
+deployment for three different sites A, B and C is depicted below.
-### Backend Discovery
+### Transport Discovery
-The MatrixRTC backend (according to
-[MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143)) is
-announced by the Matrix site's `.well-known/matrix/client` file and discovered
-via the `org.matrix.msc4143.rtc_foci` key, e.g.:
+Element Call discovers the available MatrixRTC transports (as defined by
+[MSC4519](https://github.com/matrix-org/matrix-spec-proposals/pull/4519)) by
+hitting the `GET /_matrix/client/unstable/org.matrix.msc4143/rtc/transports`
+endpoint of the Client-Server API. An example response:
```json
-"org.matrix.msc4143.rtc_foci": [
+{
+ "rtc_transports": [
{
- "type": "livekit",
- "livekit_service_url": "https://matrix-rtc.example.com/livekit/jwt"
- },
-]
+ "type": "livekit",
+ "livekit_service_url": "https://matrix-rtc.example.com/livekit/jwt"
+ }
+ ]
+}
```
where the format for MatrixRTC using LiveKit backend is defined in
@@ -149,7 +152,7 @@ via `livekit_service_url`.
### Backend Selection
-- Each call participant proposes their discovered MatrixRTC backend from
+- Each call participant proposes their discovered MatrixRTC transport from
`org.matrix.msc4143.rtc_foci` in their `org.matrix.msc3401.call.member` state event.
- For the **LiveKit** MatrixRTC backend
([MSC4195](https://github.com/hughns/matrix-spec-proposals/blob/hughns/matrixrtc-livekit/proposals/4195-matrixrtc-livekit.md)),
@@ -244,10 +247,9 @@ pnpm backend
> [!NOTE]
> To ensure your local development frontend functions properly, you’ll need to
-> add certificate exceptions in your browser for `https://localhost:3000`,
-> `https://matrix-rtc.m.localhost/livekit/jwt/healthz` and
-> `https://synapse.m.localhost/.well-known/matrix/client`. This can be either
-> done by adding the minimum localhost CA
+> add certificate exceptions in your browser for `https://localhost:3000` and
+> `https://matrix-rtc.m.localhost/livekit/jwt/healthz`. This can be done either
+> by adding the minimum localhost CA
> ([./backend/dev_tls_local-ca.crt](./backend/dev_tls_local-ca.crt)) to your web
> browser's trusted certificates or by simply copying and pasting each URL into
> your browser’s address bar and follow the prompts to add the exception.
diff --git a/backend/dev_homeserver-othersite.yaml b/backend/dev_homeserver-othersite.yaml
index 7eb8f294a..3f4c18413 100644
--- a/backend/dev_homeserver-othersite.yaml
+++ b/backend/dev_homeserver-othersite.yaml
@@ -40,6 +40,8 @@ experimental_features:
msc4222_enabled: true
# sticky events for MatrixRTC user state
msc4354_enabled: true
+ # MatrixRTC
+ msc4143_enabled: true
# The maximum allowed duration by which sent events can be delayed, as
# per MSC4140. Must be a positive value if set. Defaults to no
@@ -54,7 +56,6 @@ enable_registration_without_verification: true
registration_shared_secret: "test_shared_secret_for_local_dev_only"
report_stats: false
-serve_server_wellknown: true
# Ratelimiting settings for client actions (registration, login, messaging).
#
@@ -67,3 +68,8 @@ rc_message:
# Currently the heart-beat is every 5 seconds which translates into a rate of 0.2s
per_second: 0.5
burst_count: 30
+
+matrix_rtc:
+ transports:
+ - type: livekit
+ livekit_service_url: https://matrix-rtc.othersite.m.localhost/livekit/jwt
diff --git a/backend/dev_homeserver.yaml b/backend/dev_homeserver.yaml
index 0aea2ece2..e38e91e5f 100644
--- a/backend/dev_homeserver.yaml
+++ b/backend/dev_homeserver.yaml
@@ -40,6 +40,8 @@ experimental_features:
msc4222_enabled: true
# sticky events for MatrixRTC user state
msc4354_enabled: true
+ # MatrixRTC
+ msc4143_enabled: true
# The maximum allowed duration by which sent events can be delayed, as
# per MSC4140. Must be a positive value if set. Defaults to no
@@ -54,7 +56,6 @@ enable_registration_without_verification: true
registration_shared_secret: "test_shared_secret_for_local_dev_only"
report_stats: false
-serve_server_wellknown: true
# Ratelimiting settings for client actions (registration, login, messaging).
#
@@ -67,3 +68,8 @@ rc_message:
# Currently the heart-beat is every 5 seconds which translates into a rate of 0.2s
per_second: 0.5
burst_count: 30
+
+matrix_rtc:
+ transports:
+ - type: livekit
+ livekit_service_url: https://matrix-rtc.m.localhost/livekit/jwt
diff --git a/backend/dev_nginx.conf b/backend/dev_nginx.conf
index 6ec0d7010..c227370df 100644
--- a/backend/dev_nginx.conf
+++ b/backend/dev_nginx.conf
@@ -1,4 +1,4 @@
-# Synapse reverse proxy including .well-known/matrix/client
+# Synapse reverse proxy
# domain synapse.m.localhost
server {
listen 80;
@@ -11,18 +11,6 @@ server {
ssl_certificate /root/ssl/cert.pem;
ssl_certificate_key /root/ssl/key.pem;
- # well-known config adding rtc_foci backend
- # Note well-known is currently not effective due to:
- # https://spec.matrix.org/v1.12/client-server-api/#well-known-uri the spec
- # says it must be at https://$server_name/... (implied port 443) Hence, we
- # currently rely for local development environment on deprecated config.json
- # setting for livekit_service_url
- location /.well-known/matrix/client {
- add_header Access-Control-Allow-Origin *;
- return 200 '{"m.homeserver": {"base_url": "https://synapse.m.localhost"}, "org.matrix.msc4143.rtc_foci": [{"type": "livekit", "livekit_service_url": "https://matrix-rtc.m.localhost/livekit/jwt"}]}';
- default_type application/json;
- }
-
# Reverse proxy for Matrix Synapse Homeserver
# This is also required for development environment.
# Reason: the lk-jwt-service uses the federation API for the openid token
@@ -44,7 +32,7 @@ server {
}
-# Synapse reverse proxy including .well-known/matrix/client
+# Synapse reverse proxy
# domain synapse.othersite.m.localhost
server {
listen 80;
@@ -57,18 +45,6 @@ server {
ssl_certificate /root/ssl/cert.pem;
ssl_certificate_key /root/ssl/key.pem;
- # well-known config adding rtc_foci backend
- # Note well-known is currently not effective due to:
- # https://spec.matrix.org/v1.12/client-server-api/#well-known-uri the spec
- # says it must be at https://$server_name/... (implied port 443) Hence, we
- # currently rely for local development environment on deprecated config.json
- # setting for livekit_service_url
- location /.well-known/matrix/client {
- add_header Access-Control-Allow-Origin *;
- return 200 '{"m.homeserver": {"base_url": "https://synapse.othersite.m.localhost"}, "org.matrix.msc4143.rtc_foci": [{"type": "livekit", "livekit_service_url": "https://matrix-rtc.othersite.m.localhost/livekit/jwt"}]}';
- default_type application/json;
- }
-
# Reverse proxy for Matrix Synapse Homeserver
# This is also required for development environment.
# Reason: the lk-jwt-service uses the federation API for the openid token
diff --git a/backend/playwright_homeserver-othersite.yaml b/backend/playwright_homeserver-othersite.yaml
index 86c77b35f..5bce4c1d5 100644
--- a/backend/playwright_homeserver-othersite.yaml
+++ b/backend/playwright_homeserver-othersite.yaml
@@ -40,6 +40,8 @@ experimental_features:
msc4222_enabled: true
# sticky events for MatrixRTC user state
msc4354_enabled: true
+ # MatrixRTC
+ msc4143_enabled: true
# The maximum allowed duration by which sent events can be delayed, as
# per MSC4140. Must be a positive value if set. Defaults to no
@@ -54,7 +56,6 @@ enable_registration_without_verification: true
registration_shared_secret: "test_shared_secret_for_local_dev_only"
report_stats: false
-serve_server_wellknown: true
# Ratelimiting settings for client actions (registration, login, messaging).
#
@@ -84,3 +85,8 @@ rc_login:
rc_registration:
per_second: 10000
burst_count: 10000
+
+matrix_rtc:
+ transports:
+ - type: livekit
+ livekit_service_url: https://matrix-rtc.othersite.m.localhost/livekit/jwt
diff --git a/backend/playwright_homeserver.yaml b/backend/playwright_homeserver.yaml
index 8f4375241..d0439e42c 100644
--- a/backend/playwright_homeserver.yaml
+++ b/backend/playwright_homeserver.yaml
@@ -40,6 +40,8 @@ experimental_features:
msc4222_enabled: true
# sticky events for MatrixRTC user state
msc4354_enabled: true
+ # MatrixRTC
+ msc4143_enabled: true
# The maximum allowed duration by which sent events can be delayed, as
# per MSC4140. Must be a positive value if set. Defaults to no
@@ -54,7 +56,6 @@ enable_registration_without_verification: true
registration_shared_secret: "test_shared_secret_for_local_dev_only"
report_stats: false
-serve_server_wellknown: true
# Ratelimiting settings for client actions (registration, login, messaging).
#
@@ -84,3 +85,8 @@ rc_login:
rc_registration:
per_second: 10000
burst_count: 10000
+
+matrix_rtc:
+ transports:
+ - type: livekit
+ livekit_service_url: https://matrix-rtc.m.localhost/livekit/jwt
diff --git a/docs/self_hosting.md b/docs/self_hosting.md
index 224a82cd8..0bdfe6284 100644
--- a/docs/self_hosting.md
+++ b/docs/self_hosting.md
@@ -68,10 +68,10 @@ As a prerequisite for the
make sure that your Synapse server has either a `federation` or `openid`
[listener configured](https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#listeners).
-### MatrixRTC Backend
+### LiveKit backend
-In order to **guarantee smooth operation** of Element Call, a MatrixRTC backend is
-required for each site deployment.
+In order to **guarantee smooth operation** of Element Call, a dedicated LiveKit
+backend is required for each site deployment.

@@ -165,7 +165,7 @@ Using Haproxy, you can achieve this by:
use_backend mxrtc_auth_backend if is_mxrtc_auth matrixrtc_domain
# Backend
-## MatrixRTC backend
+## LiveKit backend
backend sfu_backend
server livekit 127.0.0.1:7880
http-request set-path %[path,regsub(^/livekit/sfu/,/)]
@@ -187,43 +187,22 @@ backend mxrtc_auth_backend
```
-#### MatrixRTC backend announcement
+#### MatrixRTC transport announcement
-> [!IMPORTANT]
-> As defined in
-> [MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143),
-> the MatrixRTC backend(s) must be announced to the client via your **Matrix site's
-> `.well-known/matrix/client`** file (e.g.
-> `example.com/.well-known/matrix/client` matching the site deployment example
-> from above). The configuration is a list of Foci configs:
+Enable the unstable feature flag `msc4143_enabled`, and update the
+[`matrix_rtc` section](https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#matrix_rtc)
+of your Synapse config file:
-```json
-"org.matrix.msc4143.rtc_foci": [
- {
- "type": "livekit",
- "livekit_service_url": "https://matrix-rtc.example.com/livekit/jwt"
- },
- {
- "type": "livekit",
- "livekit_service_url": "https://matrix-rtc-2.example.com/livekit/jwt"
- }
-]
+```yaml
+matrix_rtc:
+ transports:
+ - type: livekit
+ livekit_service_url: https://matrix-rtc.example.com/livekit/jwt
```
-Make sure this file is served with the correct MIME type (`application/json`).
-Additionally, ensure the appropriate CORS headers are set to allow web clients
-to access it across origins. For more details, refer to the
-[Matrix Client-Server API: 2. Web Browser Clients](https://spec.matrix.org/latest/client-server-api/#web-browser-clients).
-
-```
-Access-Control-Allow-Origin: *
-Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
-Access-Control-Allow-Headers: X-Requested-With, Content-Type, Authorization
-```
-
-> [!NOTE]
-> Most `org.matrix.msc4143.rtc_foci` configurations will only have one entry in
-> the array.
+The transport you specify will be made available to clients over the
+`/_matrix/client/unstable/org.matrix.msc4143/rtc/transports` endpoint as defined
+in [MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143).
## Building Element Call
@@ -278,7 +257,7 @@ runtime. Documentation and default values for `public/config.json` can be found
in [ConfigOptions.ts](../src/config/ConfigOptions.ts).
> [!CAUTION]
-> Please note configuring MatrixRTC backend via `config.json` of
+> Please note configuring LiveKit backend via `config.json` of
> Element Call is only available for developing and debug purposes. Relying on
> it might break Element Call going forward!
diff --git a/locales/en/app.json b/locales/en/app.json
index bd35997b2..2b3e08358 100644
--- a/locales/en/app.json
+++ b/locales/en/app.json
@@ -61,7 +61,7 @@
"crypto_version": "Crypto version: {{version}}",
"custom_livekit_url": {
"current_url": "Currently set to: ",
- "from_config": "Currently, no overwrite is set. Url from well-known or config is used.",
+ "from_config": "Currently, no overwrite is set. Url from config is used.",
"label": "Custom Livekit-url",
"reset": "Reset overwrite",
"save": "Save",
diff --git a/src/config/ConfigOptions.ts b/src/config/ConfigOptions.ts
index 69e083993..75704cfc8 100644
--- a/src/config/ConfigOptions.ts
+++ b/src/config/ConfigOptions.ts
@@ -74,10 +74,7 @@ export interface ConfigOptions {
livekit?: {
// The link to the service that returns a livekit url and token to use it.
// This is a fallback link in case the homeserver in use does not advertise
- // a livekit service url in the client well-known.
- // The well known needs to be formatted like so:
- // {"type":"livekit", "livekit_service_url":"https://livekit.example.com"}
- // and stored under the key: "org.matrix.msc4143.rtc_foci"
+ // a livekit service url over the transports endpoint.
livekit_service_url: string;
};
diff --git a/src/settings/__snapshots__/DeveloperSettingsTab.test.tsx.snap b/src/settings/__snapshots__/DeveloperSettingsTab.test.tsx.snap
index 94fa0826b..14e03b04d 100644
--- a/src/settings/__snapshots__/DeveloperSettingsTab.test.tsx.snap
+++ b/src/settings/__snapshots__/DeveloperSettingsTab.test.tsx.snap
@@ -257,7 +257,7 @@ exports[`DeveloperSettingsTab > renders and matches snapshot 1`] = `
class="_message_1o4d9_86 _help-message_1o4d9_92"
id="radix-_r_8_"
>
- Currently, no overwrite is set. Url from well-known or config is used.
+ Currently, no overwrite is set. Url from config is used.
diff --git a/src/state/CallViewModel/CallViewModelTestUtils.ts b/src/state/CallViewModel/CallViewModelTestUtils.ts
index 1d3d0fef3..2e1525c06 100644
--- a/src/state/CallViewModel/CallViewModelTestUtils.ts
+++ b/src/state/CallViewModel/CallViewModelTestUtils.ts
@@ -138,9 +138,6 @@ export function withCallViewModel(mode: MatrixRTCMode) {
public getSyncState(): SyncState {
return syncState;
}
- public getAccessToken(): string | null {
- return "a-token";
- }
})() as Partial as MatrixClient,
getMembers: () => roomMembers,
getMembersWithMembership: () => roomMembers,
diff --git a/src/state/CallViewModel/localMember/LocalMember.test.ts b/src/state/CallViewModel/localMember/LocalMember.test.ts
index 8bca91824..16ffe1493 100644
--- a/src/state/CallViewModel/localMember/LocalMember.test.ts
+++ b/src/state/CallViewModel/localMember/LocalMember.test.ts
@@ -20,7 +20,6 @@ import {
afterAll,
beforeEach,
} from "vitest";
-import { AutoDiscovery } from "matrix-js-sdk/lib/autodiscovery";
import { BehaviorSubject, map, of } from "rxjs";
import { logger } from "matrix-js-sdk/lib/logger";
import { type LocalParticipant, type LocalTrack } from "livekit-client";
@@ -78,34 +77,10 @@ describe("LocalMembership", () => {
livekit_alias: "my-oldest-member-service-alias",
};
- const focusConfigFromWellKnown = {
- type: "livekit",
- livekit_service_url: "http://my-well-known-service-url.com",
- };
- const focusConfigFromWellKnown2 = {
- type: "livekit",
- livekit_service_url: "http://my-well-known-service-url2.com",
- };
- const clientWellKnown = {
- "org.matrix.msc4143.rtc_foci": [
- focusConfigFromWellKnown,
- focusConfigFromWellKnown2,
- ],
- };
-
mockConfig({
livekit: { livekit_service_url: "http://my-default-service-url.com" },
});
- vi.spyOn(AutoDiscovery, "getRawClientConfig").mockImplementation(
- async (domain) => {
- if (domain === "example.org") {
- return Promise.resolve(clientWellKnown);
- }
- return Promise.resolve({});
- },
- );
-
const mockedSession = vi.mocked({
room: {
roomId: "roomId",
@@ -132,7 +107,7 @@ describe("LocalMembership", () => {
ownMemberMock,
{
livekit_alias: "roomId",
- livekit_service_url: "http://my-well-known-service-url.com",
+ livekit_service_url: "http://my-livekit-service-url.com",
type: "livekit",
},
{
@@ -150,7 +125,7 @@ describe("LocalMembership", () => {
[
{
livekit_alias: "roomId",
- livekit_service_url: "http://my-well-known-service-url.com",
+ livekit_service_url: "http://my-livekit-service-url.com",
type: "livekit",
},
],
@@ -161,50 +136,6 @@ describe("LocalMembership", () => {
}),
);
});
-
- it("It should not fail with configuration error if homeserver config has livekit url but not fallback", () => {
- mockConfig({});
- vi.spyOn(AutoDiscovery, "getRawClientConfig").mockResolvedValue({
- "org.matrix.msc4143.rtc_foci": [
- {
- type: "livekit",
- livekit_service_url: "http://my-well-known-service-url.com",
- },
- ],
- });
-
- const mockedSession = vi.mocked({
- room: {
- roomId: "roomId",
- client: {
- getDomain: vi.fn().mockReturnValue("example.org"),
- getOpenIdToken: vi.fn().mockResolvedValue({
- access_token: "ACCCESS_TOKEN",
- token_type: "Bearer",
- matrix_server_name: "localhost",
- expires_in: 10000,
- }),
- },
- },
- memberships: [],
- getFocusInUse: vi.fn(),
- joinRTCSession: vi.fn(),
- }) as unknown as MatrixRTCSession;
-
- enterRTCSession(
- mockedSession,
- ownMemberMock,
- {
- livekit_alias: "roomId",
- livekit_service_url: "http://my-well-known-service-url.com",
- type: "livekit",
- },
- {
- encryptMedia: true,
- matrixRTCMode: MATRIX_RTC_MODE,
- },
- );
- });
});
const defaultCreateLocalMemberValues = {
diff --git a/src/state/CallViewModel/localMember/LocalMember.ts b/src/state/CallViewModel/localMember/LocalMember.ts
index d53304973..fcc8ed0a6 100644
--- a/src/state/CallViewModel/localMember/LocalMember.ts
+++ b/src/state/CallViewModel/localMember/LocalMember.ts
@@ -117,7 +117,6 @@ export type LocalMemberState =
};
/*
- * - get well known
* - get oldest membership
* - get transport to use
* - get openId + jwt token
diff --git a/src/state/CallViewModel/localMember/LocalTransport.test.ts b/src/state/CallViewModel/localMember/LocalTransport.test.ts
index ac45af2a1..89cb831da 100644
--- a/src/state/CallViewModel/localMember/LocalTransport.test.ts
+++ b/src/state/CallViewModel/localMember/LocalTransport.test.ts
@@ -63,8 +63,7 @@ describe("LocalTransport", () => {
client: {
// eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () => Promise.resolve([]),
- getAccessToken: vi.fn().mockReturnValue("access_token"),
- getDomain: () => "",
+ getDomain: () => "example.org",
baseUrl: "example.org",
// These won't be called in this error path but satisfy the type
getOpenIdToken: vi.fn(),
@@ -77,9 +76,11 @@ describe("LocalTransport", () => {
await flushPromises();
expect(() => advertised$.value).toThrow(
- new MatrixRTCTransportMissingError(""),
+ new MatrixRTCTransportMissingError("example.org"),
+ );
+ expect(() => active$.value).toThrow(
+ new MatrixRTCTransportMissingError("example.org"),
);
- expect(() => active$.value).toThrow(new MatrixRTCTransportMissingError(""));
});
it("throws FailToGetOpenIdToken when OpenID fetch fails", async () => {
@@ -103,10 +104,8 @@ describe("LocalTransport", () => {
useOldestMember: false,
memberships$: constant(new Epoch([])),
client: {
- baseUrl: "https://lk.example.org",
- // Use empty domain to skip .well-known and use config directly
- getDomain: () => "",
- getAccessToken: vi.fn().mockReturnValue("access_token"),
+ baseUrl: "https://example.org",
+ getDomain: () => "example.org",
// eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () => Promise.resolve([]),
getOpenIdToken: vi.fn(),
@@ -150,11 +149,10 @@ describe("LocalTransport", () => {
client: {
// eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () => Promise.resolve([]),
- getDomain: () => "",
+ getDomain: () => "example.org",
getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(),
- baseUrl: "https://lk.example.org",
- getAccessToken: vi.fn().mockReturnValue("access_token"),
+ baseUrl: "https://example.org",
},
ownMembershipIdentity: ownMemberMock,
forceJwtEndpoint: JwtEndpointVersion.Legacy,
@@ -221,13 +219,12 @@ describe("LocalTransport", () => {
useOldestMember: true,
memberships$: scope.behavior(memberships$.pipe(trackEpoch())),
client: {
- getDomain: () => "",
+ getDomain: () => "example.org",
// eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () => Promise.resolve([]),
- getAccessToken: vi.fn().mockReturnValue("access_token"),
getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(),
- baseUrl: "https://lk.example.org",
+ baseUrl: "https://example.org",
},
ownMembershipIdentity: ownMemberMock,
forceJwtEndpoint: JwtEndpointVersion.Legacy,
@@ -278,14 +275,13 @@ describe("LocalTransport", () => {
useOldestMember: true,
memberships$: scope.behavior(memberships$.pipe(trackEpoch())),
client: {
- getDomain: () => "",
+ getDomain: () => "example.org",
// eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () =>
Promise.resolve([aliceTransport]),
- getAccessToken: vi.fn().mockReturnValue("access_token"),
getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(),
- baseUrl: "https://lk.example.org",
+ baseUrl: "https://example.org",
},
ownMembershipIdentity: ownMemberMock,
forceJwtEndpoint: JwtEndpointVersion.Legacy,
@@ -330,10 +326,9 @@ describe("LocalTransport", () => {
memberships$: constant(new Epoch([])),
client: {
baseUrl: "https://example.org",
- getDomain: vi.fn().mockReturnValue(""),
+ getDomain: vi.fn().mockReturnValue("example.org"),
// eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: vi.fn().mockResolvedValue([]),
- getAccessToken: vi.fn().mockReturnValue("access_token"),
getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(),
},
@@ -433,51 +428,6 @@ describe("LocalTransport", () => {
).rejects.toThrow(expect.any(FailToGetOpenIdToken));
});
- it("supports getting transport via well-known", async () => {
- localTransportOpts.client.getDomain.mockReturnValue("example.org");
- fetchMock.getOnce("https://example.org/.well-known/matrix/client", {
- "org.matrix.msc4143.rtc_foci": [
- { type: "livekit", livekit_service_url: "https://lk.example.org" },
- ],
- });
- const { advertised$, active$ } =
- createLocalTransport$(localTransportOpts);
- openIdResolver.resolve?.(openIdResponse);
- expect(advertised$.value).toBe(null);
- expect(active$.value).toBe(null);
- await flushPromises();
- const expectedTransport = {
- livekit_service_url: "https://lk.example.org",
- type: "livekit",
- };
- expect(advertised$.value).toStrictEqual(expectedTransport);
- expect(active$.value).toStrictEqual({
- transport: expectedTransport,
- sfuConfig: {
- jwt: "e30=.eyJzdWIiOiJAbWU6ZXhhbXBsZS5vcmc6QUJDREVGIiwidmlkZW8iOnsicm9vbSI6IiFleGFtcGxlX3Jvb21faWQifX0=.e30=",
- livekitAlias: "Akph4alDMhen",
- livekitIdentity: "@lk_user:ABCDEF",
- url: "https://lk.example.org",
- },
- });
- expect(fetchMock.done()).toEqual(true);
- });
-
- it("fails fast if the openId request fails for the well-known config", async () => {
- localTransportOpts.client.getDomain.mockReturnValue("example.org");
- fetchMock.getOnce("https://example.org/.well-known/matrix/client", {
- "org.matrix.msc4143.rtc_foci": [
- { type: "livekit", livekit_service_url: "https://lk.example.org" },
- ],
- });
- openIdResolver.reject(
- new FailToGetOpenIdToken(new Error("Test driven error")),
- );
- await expect(async () =>
- lastValueFrom(createLocalTransport$(localTransportOpts).active$),
- ).rejects.toThrow(expect.any(FailToGetOpenIdToken));
- });
-
it("throws if no options are available", async () => {
const { advertised$, active$ } = createLocalTransport$({
scope: testScope(),
@@ -488,11 +438,10 @@ describe("LocalTransport", () => {
delayId$: constant(null),
memberships$: constant(new Epoch([])),
client: {
- getDomain: () => "",
+ getDomain: () => "example.org",
baseUrl: "https://example.org",
// eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () => Promise.resolve([]),
- getAccessToken: vi.fn().mockReturnValue("access_token"),
// These won't be called in this error path but satisfy the type
getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(),
@@ -501,10 +450,10 @@ describe("LocalTransport", () => {
await flushPromises();
expect(() => advertised$.value).toThrow(
- new MatrixRTCTransportMissingError(""),
+ new MatrixRTCTransportMissingError("example.org"),
);
expect(() => active$.value).toThrow(
- new MatrixRTCTransportMissingError(""),
+ new MatrixRTCTransportMissingError("example.org"),
);
});
});
@@ -529,11 +478,10 @@ describe("LocalTransport", () => {
delayId$: delayId$,
memberships$: constant(new Epoch([])),
client: {
- getDomain: () => "",
+ getDomain: () => "example.org",
baseUrl: "https://example.org",
// eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () => Promise.resolve([]),
- getAccessToken: vi.fn().mockReturnValue("access_token"),
// These won't be called in this error path but satisfy the type
getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(),
diff --git a/src/state/CallViewModel/localMember/LocalTransport.ts b/src/state/CallViewModel/localMember/LocalTransport.ts
index e255ca946..1a6dddc1f 100644
--- a/src/state/CallViewModel/localMember/LocalTransport.ts
+++ b/src/state/CallViewModel/localMember/LocalTransport.ts
@@ -26,7 +26,6 @@ import {
tap,
} from "rxjs";
import { logger as rootLogger, type Logger } from "matrix-js-sdk/lib/logger";
-import { AutoDiscovery } from "matrix-js-sdk/lib/autodiscovery";
import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager";
import { type Behavior } from "../../Behavior.ts";
@@ -57,7 +56,7 @@ interface Props {
memberships$: Behavior>;
client: Pick<
MatrixClient,
- "getDomain" | "baseUrl" | "_unstable_getRTCTransports" | "getAccessToken"
+ "getDomain" | "baseUrl" | "_unstable_getRTCTransports"
> &
OpenIDClientParts;
// Used by the jwt service to create the livekit room and compute the livekit alias.
@@ -150,7 +149,6 @@ export const createLocalTransport$ = ({
const transportDiscovery = new RtcTransportAutoDiscovery({
client: client,
resolvedConfig: Config.get(),
- wellKnownFetcher: AutoDiscovery.getRawClientConfig.bind(AutoDiscovery),
logger: logger,
});
@@ -309,7 +307,7 @@ async function doOpenIdAndJWTFromUrl(
roomId: string,
client: Pick<
MatrixClient,
- "getDomain" | "baseUrl" | "_unstable_getRTCTransports" | "getAccessToken"
+ "getDomain" | "baseUrl" | "_unstable_getRTCTransports"
> &
OpenIDClientParts,
delayId?: string,
@@ -339,7 +337,7 @@ function observeLocalTransportForOldestMembership(
preferredTransport$: Observable,
client: Pick<
MatrixClient,
- "getDomain" | "baseUrl" | "_unstable_getRTCTransports" | "getAccessToken"
+ "getDomain" | "baseUrl" | "_unstable_getRTCTransports"
> &
OpenIDClientParts,
ownMembershipIdentity: CallMembershipIdentityParts,
diff --git a/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.test.ts b/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.test.ts
index cd0008142..f81223deb 100644
--- a/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.test.ts
+++ b/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.test.ts
@@ -13,7 +13,7 @@ import {
type MockedObject,
vi,
} from "vitest";
-import { type IClientWellKnown, MatrixError } from "matrix-js-sdk";
+import { MatrixError } from "matrix-js-sdk";
import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
import {
type LivekitTransportConfig,
@@ -33,9 +33,9 @@ const backendTransport: LivekitTransportConfig = {
livekit_service_url: "https://backend.example.org",
};
-const wellKnownTransport: LivekitTransportConfig = {
+const configTransport: LivekitTransportConfig = {
type: "livekit",
- livekit_service_url: "https://well-known.example.org",
+ livekit_service_url: "https://config.example.org",
};
function makeClient(): MockedObject {
@@ -43,7 +43,6 @@ function makeClient(): MockedObject {
getDomain: vi.fn().mockReturnValue("example.org"),
baseUrl: "https://matrix.example.org",
_unstable_getRTCTransports: vi.fn().mockResolvedValue([]),
- getAccessToken: vi.fn().mockReturnValue("access_token"),
getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(),
} as unknown as MockedObject;
@@ -59,12 +58,6 @@ function makeResolvedConfig(livekitServiceUrl?: string): ResolvedConfigOptions {
} as ResolvedConfigOptions;
}
-function makeWellKnown(rtcFoci?: Transport[]): IClientWellKnown {
- return {
- "org.matrix.msc4143.rtc_foci": rtcFoci,
- } as unknown as IClientWellKnown;
-}
-
describe("RtcTransportAutoDiscovery", () => {
beforeEach(() => {
vi.clearAllMocks();
@@ -75,33 +68,27 @@ describe("RtcTransportAutoDiscovery", () => {
{ transports: [{ type: "not_livekit" }, backendTransport] },
];
it.each(VALID_TEST_CASES)(
- "prefers backend transport over well-known and app config $transports",
+ "prefers backend transport other app config $transports",
async ({ transports }) => {
- // it("prefers backend transport over well-known and app config", async () => {
const client = makeClient();
client._unstable_getRTCTransports.mockResolvedValue(transports);
- const wellKnownFetcher = vi
- .fn<(domain: string) => Promise>()
- .mockResolvedValue(makeWellKnown([wellKnownTransport]));
-
const discovery = new RtcTransportAutoDiscovery({
client,
- resolvedConfig: makeResolvedConfig("https://config.example.org"),
- wellKnownFetcher,
+ resolvedConfig: makeResolvedConfig(configTransport.livekit_service_url),
logger: rootLogger,
});
- await expect(
- discovery.discoverPreferredTransport(),
- ).resolves.toStrictEqual(backendTransport);
+ const discoveredTransport = await discovery.discoverPreferredTransport();
+
+ expect(discoveredTransport).toStrictEqual(backendTransport);
+ expect(discoveredTransport).not.toStrictEqual(configTransport);
expect(client._unstable_getRTCTransports).toHaveBeenCalledTimes(1);
- expect(wellKnownFetcher).not.toHaveBeenCalled();
},
);
- it("Retries limit_exceeded backend transport over well-known", async () => {
+ it("Retries limit_exceeded backend transport", async () => {
const client = makeClient();
client._unstable_getRTCTransports
.mockRejectedValueOnce(
@@ -116,14 +103,9 @@ describe("RtcTransportAutoDiscovery", () => {
)
.mockResolvedValue([backendTransport]);
- const wellKnownFetcher = vi
- .fn<(domain: string) => Promise>()
- .mockResolvedValue(makeWellKnown([wellKnownTransport]));
-
const discovery = new RtcTransportAutoDiscovery({
client,
resolvedConfig: makeResolvedConfig("https://config.example.org"),
- wellKnownFetcher,
logger: rootLogger,
});
@@ -132,7 +114,6 @@ describe("RtcTransportAutoDiscovery", () => {
);
expect(client._unstable_getRTCTransports).toHaveBeenCalledTimes(2);
- expect(wellKnownFetcher).not.toHaveBeenCalled();
});
const INVALID_TEST_CASES: Array<{ transports: Transport[] }> = [
@@ -140,67 +121,30 @@ describe("RtcTransportAutoDiscovery", () => {
{ transports: [{ type: "not_livekit" }] },
];
it.each(INVALID_TEST_CASES)(
- "falls back to well-known when backend has no (valid) livekit transports $transports",
+ "falls back to config when backend has no (valid) livekit transports $transports",
async ({ transports }) => {
const client = makeClient();
client._unstable_getRTCTransports.mockResolvedValue(transports);
- const wellKnownFetcher = vi
- .fn<(domain: string) => Promise>()
- .mockResolvedValue(makeWellKnown([wellKnownTransport]));
-
const discovery = new RtcTransportAutoDiscovery({
client,
- resolvedConfig: makeResolvedConfig("https://config.example.org"),
- wellKnownFetcher,
+ resolvedConfig: makeResolvedConfig(configTransport.livekit_service_url),
logger: rootLogger,
});
- await expect(
- discovery.discoverPreferredTransport(),
- ).resolves.toStrictEqual(wellKnownTransport);
-
- expect(wellKnownFetcher).toHaveBeenCalledWith("example.org");
+ const discoveredTransport = await discovery.discoverPreferredTransport();
+ expect(discoveredTransport).not.toStrictEqual(backendTransport);
+ expect(discoveredTransport).toStrictEqual(configTransport);
},
);
- it("falls back to app config when backend fails and well-known has no rtc_foci", async () => {
- const client = makeClient();
- client._unstable_getRTCTransports.mockRejectedValue(
- new MatrixError({ errcode: "M_UNKNOWN" }, 404),
- );
-
- const wellKnownFetcher = vi
- .fn<(domain: string) => Promise>()
- .mockResolvedValue({} as IClientWellKnown);
-
- const discovery = new RtcTransportAutoDiscovery({
- client,
- resolvedConfig: makeResolvedConfig("https://config.example.org"),
- wellKnownFetcher,
- logger: rootLogger,
- });
-
- await expect(discovery.discoverPreferredTransport()).resolves.toStrictEqual(
- {
- type: "livekit",
- livekit_service_url: "https://config.example.org",
- },
- );
- });
-
- it("returns null when backend, well-known and config are all unavailable", async () => {
+ it("returns null when backend and config are all unavailable", async () => {
const client = makeClient();
client._unstable_getRTCTransports.mockResolvedValue([]);
- const wellKnownFetcher = vi
- .fn<(domain: string) => Promise>()
- .mockResolvedValue({} as IClientWellKnown);
-
const discovery = new RtcTransportAutoDiscovery({
client,
resolvedConfig: makeResolvedConfig(undefined),
- wellKnownFetcher,
logger: rootLogger,
});
diff --git a/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.ts b/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.ts
index 8b3db0679..1f295649f 100644
--- a/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.ts
+++ b/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.ts
@@ -8,7 +8,7 @@ import {
isLivekitTransportConfig,
type LivekitTransportConfig,
} from "matrix-js-sdk/lib/matrixrtc";
-import { type IClientWellKnown, type MatrixClient } from "matrix-js-sdk";
+import { type MatrixClient } from "matrix-js-sdk";
import { type Logger } from "matrix-js-sdk/lib/logger";
import type { ResolvedConfigOptions } from "../../../config/ConfigOptions.ts";
@@ -16,33 +16,27 @@ import { doNetworkOperationWithRetry } from "../../../utils/matrix.ts";
type TransportDiscoveryClient = Pick<
MatrixClient,
- "getDomain" | "_unstable_getRTCTransports" | "getAccessToken"
+ "getDomain" | "_unstable_getRTCTransports"
>;
export interface RtcTransportAutoDiscoveryProps {
client: TransportDiscoveryClient;
resolvedConfig: ResolvedConfigOptions;
- wellKnownFetcher: (domain: string) => Promise;
logger: Logger;
}
export class RtcTransportAutoDiscovery {
private readonly client: TransportDiscoveryClient;
private readonly resolvedConfig: ResolvedConfigOptions;
- private readonly wellKnownFetcher: (
- domain: string,
- ) => Promise;
private readonly logger: Logger;
public constructor({
client,
resolvedConfig,
- wellKnownFetcher,
logger,
}: RtcTransportAutoDiscoveryProps) {
this.client = client;
this.resolvedConfig = resolvedConfig;
- this.wellKnownFetcher = wellKnownFetcher;
this.logger = logger.getChild("[RtcTransportAutoDiscovery]");
}
@@ -56,21 +50,7 @@ export class RtcTransportAutoDiscovery {
return backendTransport;
}
- this.logger.info("No backend transport found, falling back to well-known");
- // 2) .well-known transports
- const wellKnownTransport = await this.tryWellKnownTransports();
- if (wellKnownTransport) {
- this.logger.info(
- `Found .well-known transport: ${wellKnownTransport.livekit_service_url}`,
- );
- return wellKnownTransport;
- }
-
- this.logger.info(
- "No .well-known transport found, falling back to app config",
- );
-
- // 3) app config URL
+ // 2) app config URL
const configTransport = this.tryConfigTransport();
if (configTransport) {
this.logger.info(
@@ -110,44 +90,6 @@ export class RtcTransportAutoDiscovery {
return null;
}
- /**
- * Fetches the first rtc_foci from the .well-known/matrix/client.
- * This will not throw errors, but instead just log them and return null if the expected config is not found or malformed.
- * @private
- */
- private async tryWellKnownTransports(): Promise {
- // Legacy MSC4143 (to be removed) WELL_KNOWN: Prioritize the .well-known/matrix/client, if available.
- const client = this.client;
- const domain = client.getDomain();
- if (domain) {
- // we use AutoDiscovery instead of relying on the MatrixClient having already
- // been fully configured and started
-
- const wellKnownFoci = await this.wellKnownFetcher(domain);
-
- const fociConfig = wellKnownFoci["org.matrix.msc4143.rtc_foci"];
- if (fociConfig) {
- if (!Array.isArray(fociConfig)) {
- this.logger.warn(
- `org.matrix.msc4143.rtc_foci is not an array in .well-known`,
- );
- } else {
- return fociConfig[0];
- }
- } else {
- this.logger.info(
- `No .well-known "org.matrix.msc4143.rtc_foci" found for ${domain}`,
- wellKnownFoci,
- );
- }
- } else {
- // Should never happen, but just in case
- this.logger.warn(`No domain configured for client`);
- }
-
- return null;
- }
-
private tryConfigTransport(): LivekitTransportConfig | null {
const url = this.resolvedConfig.livekit?.livekit_service_url;
if (url) {
diff --git a/src/utils/errors.ts b/src/utils/errors.ts
index 7112702d1..0ac569278 100644
--- a/src/utils/errors.ts
+++ b/src/utils/errors.ts
@@ -12,7 +12,7 @@ import { i18nKey } from "./i18n";
export enum ErrorCode {
/**
- * Configuration problem due to no MatrixRTC backend/SFU is exposed via .well-known and no fallback configured.
+ * Configuration problem due to no MatrixRTC transport provided by homeserver and no fallback configured.
*/
MISSING_MATRIX_RTC_TRANSPORT = "MISSING_MATRIX_RTC_TRANSPORT",
CONNECTION_LOST_ERROR = "CONNECTION_LOST_ERROR",
@@ -67,7 +67,7 @@ export class ElementCallError extends Error {
}
/**
- * Configuration problem due to no MatrixRTC backend/SFU is exposed via .well-known and no fallback configured.
+ * Configuration problem due to no MatrixRTC transport provided by homeserver and no fallback configured.
*/
export class MatrixRTCTransportMissingError extends ElementCallError {
public domain: string;
diff --git a/src/widget.ts b/src/widget.ts
index 1a6bd56a4..af6f28b6d 100644
--- a/src/widget.ts
+++ b/src/widget.ts
@@ -198,7 +198,7 @@ export const initializeWidget = (
// be otherwise)
await Config.init();
seedSettingsFromConfig(Config.get().media_quality);
- await client.startClient({ clientWellKnownPollPeriod: 60 * 10 });
+ await client.startClient();
return client;
};