Merge pull request #4153 from element-hq/valere/rtc/remove_well_known_fallback

Remove .well-known transport discovery
This commit is contained in:
Robin
2026-08-11 18:24:03 +02:00
committed by GitHub
19 changed files with 116 additions and 379 deletions

View File

@@ -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 For operating and deploying Element Call on your own server, refer to the
[**Self-Hosting Guide**](./docs/self_hosting.md). [**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 For proper operation of Element Call, each deployment needs to set up a
setup as outlined in the [Self-Hosting Guide](./docs/self_hosting.md). A typical MatrixRTC transport in the form of a LiveKit server as outlined in the
federated site deployment for three different sites A, B and C is depicted below. [Self-Hosting Guide](./docs/self_hosting.md). A typical federated site
deployment for three different sites A, B and C is depicted below.
<p align="center"> <p align="center">
<img src="./docs/Federated_Setup.drawio.png" alt="Element Call federated setup"> <img src="./docs/Federated_Setup.drawio.png" alt="Element Call federated setup">
</p> </p>
### Backend Discovery ### Transport Discovery
The MatrixRTC backend (according to Element Call discovers the available MatrixRTC transports (as defined by
[MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143)) is [MSC4519](https://github.com/matrix-org/matrix-spec-proposals/pull/4519)) by
announced by the Matrix site's `.well-known/matrix/client` file and discovered hitting the `GET /_matrix/client/unstable/org.matrix.msc4143/rtc/transports`
via the `org.matrix.msc4143.rtc_foci` key, e.g.: endpoint of the Client-Server API. An example response:
```json ```json
"org.matrix.msc4143.rtc_foci": [ {
"rtc_transports": [
{ {
"type": "livekit", "type": "livekit",
"livekit_service_url": "https://matrix-rtc.example.com/livekit/jwt" "livekit_service_url": "https://matrix-rtc.example.com/livekit/jwt"
}, }
] ]
}
``` ```
where the format for MatrixRTC using LiveKit backend is defined in where the format for MatrixRTC using LiveKit backend is defined in
@@ -149,7 +152,7 @@ via `livekit_service_url`.
### Backend Selection ### 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. `org.matrix.msc4143.rtc_foci` in their `org.matrix.msc3401.call.member` state event.
- For the **LiveKit** MatrixRTC backend - For the **LiveKit** MatrixRTC backend
([MSC4195](https://github.com/hughns/matrix-spec-proposals/blob/hughns/matrixrtc-livekit/proposals/4195-matrixrtc-livekit.md)), ([MSC4195](https://github.com/hughns/matrix-spec-proposals/blob/hughns/matrixrtc-livekit/proposals/4195-matrixrtc-livekit.md)),
@@ -244,10 +247,9 @@ pnpm backend
> [!NOTE] > [!NOTE]
> To ensure your local development frontend functions properly, youll need to > To ensure your local development frontend functions properly, youll need to
> add certificate exceptions in your browser for `https://localhost:3000`, > add certificate exceptions in your browser for `https://localhost:3000` and
> `https://matrix-rtc.m.localhost/livekit/jwt/healthz` and > `https://matrix-rtc.m.localhost/livekit/jwt/healthz`. This can be done either
> `https://synapse.m.localhost/.well-known/matrix/client`. This can be either > by adding the minimum localhost CA
> done by adding the minimum localhost CA
> ([./backend/dev_tls_local-ca.crt](./backend/dev_tls_local-ca.crt)) to your web > ([./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 > browser's trusted certificates or by simply copying and pasting each URL into
> your browsers address bar and follow the prompts to add the exception. > your browsers address bar and follow the prompts to add the exception.

View File

@@ -40,6 +40,8 @@ experimental_features:
msc4222_enabled: true msc4222_enabled: true
# sticky events for MatrixRTC user state # sticky events for MatrixRTC user state
msc4354_enabled: true msc4354_enabled: true
# MatrixRTC
msc4143_enabled: true
# The maximum allowed duration by which sent events can be delayed, as # The maximum allowed duration by which sent events can be delayed, as
# per MSC4140. Must be a positive value if set. Defaults to no # 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" registration_shared_secret: "test_shared_secret_for_local_dev_only"
report_stats: false report_stats: false
serve_server_wellknown: true
# Ratelimiting settings for client actions (registration, login, messaging). # 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 # Currently the heart-beat is every 5 seconds which translates into a rate of 0.2s
per_second: 0.5 per_second: 0.5
burst_count: 30 burst_count: 30
matrix_rtc:
transports:
- type: livekit
livekit_service_url: https://matrix-rtc.othersite.m.localhost/livekit/jwt

View File

@@ -40,6 +40,8 @@ experimental_features:
msc4222_enabled: true msc4222_enabled: true
# sticky events for MatrixRTC user state # sticky events for MatrixRTC user state
msc4354_enabled: true msc4354_enabled: true
# MatrixRTC
msc4143_enabled: true
# The maximum allowed duration by which sent events can be delayed, as # The maximum allowed duration by which sent events can be delayed, as
# per MSC4140. Must be a positive value if set. Defaults to no # 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" registration_shared_secret: "test_shared_secret_for_local_dev_only"
report_stats: false report_stats: false
serve_server_wellknown: true
# Ratelimiting settings for client actions (registration, login, messaging). # 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 # Currently the heart-beat is every 5 seconds which translates into a rate of 0.2s
per_second: 0.5 per_second: 0.5
burst_count: 30 burst_count: 30
matrix_rtc:
transports:
- type: livekit
livekit_service_url: https://matrix-rtc.m.localhost/livekit/jwt

View File

@@ -1,4 +1,4 @@
# Synapse reverse proxy including .well-known/matrix/client # Synapse reverse proxy
# domain synapse.m.localhost # domain synapse.m.localhost
server { server {
listen 80; listen 80;
@@ -11,18 +11,6 @@ server {
ssl_certificate /root/ssl/cert.pem; ssl_certificate /root/ssl/cert.pem;
ssl_certificate_key /root/ssl/key.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 # Reverse proxy for Matrix Synapse Homeserver
# This is also required for development environment. # This is also required for development environment.
# Reason: the lk-jwt-service uses the federation API for the openid token # 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 # domain synapse.othersite.m.localhost
server { server {
listen 80; listen 80;
@@ -57,18 +45,6 @@ server {
ssl_certificate /root/ssl/cert.pem; ssl_certificate /root/ssl/cert.pem;
ssl_certificate_key /root/ssl/key.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 # Reverse proxy for Matrix Synapse Homeserver
# This is also required for development environment. # This is also required for development environment.
# Reason: the lk-jwt-service uses the federation API for the openid token # Reason: the lk-jwt-service uses the federation API for the openid token

View File

@@ -40,6 +40,8 @@ experimental_features:
msc4222_enabled: true msc4222_enabled: true
# sticky events for MatrixRTC user state # sticky events for MatrixRTC user state
msc4354_enabled: true msc4354_enabled: true
# MatrixRTC
msc4143_enabled: true
# The maximum allowed duration by which sent events can be delayed, as # The maximum allowed duration by which sent events can be delayed, as
# per MSC4140. Must be a positive value if set. Defaults to no # 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" registration_shared_secret: "test_shared_secret_for_local_dev_only"
report_stats: false report_stats: false
serve_server_wellknown: true
# Ratelimiting settings for client actions (registration, login, messaging). # Ratelimiting settings for client actions (registration, login, messaging).
# #
@@ -84,3 +85,8 @@ rc_login:
rc_registration: rc_registration:
per_second: 10000 per_second: 10000
burst_count: 10000 burst_count: 10000
matrix_rtc:
transports:
- type: livekit
livekit_service_url: https://matrix-rtc.othersite.m.localhost/livekit/jwt

View File

@@ -40,6 +40,8 @@ experimental_features:
msc4222_enabled: true msc4222_enabled: true
# sticky events for MatrixRTC user state # sticky events for MatrixRTC user state
msc4354_enabled: true msc4354_enabled: true
# MatrixRTC
msc4143_enabled: true
# The maximum allowed duration by which sent events can be delayed, as # The maximum allowed duration by which sent events can be delayed, as
# per MSC4140. Must be a positive value if set. Defaults to no # 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" registration_shared_secret: "test_shared_secret_for_local_dev_only"
report_stats: false report_stats: false
serve_server_wellknown: true
# Ratelimiting settings for client actions (registration, login, messaging). # Ratelimiting settings for client actions (registration, login, messaging).
# #
@@ -84,3 +85,8 @@ rc_login:
rc_registration: rc_registration:
per_second: 10000 per_second: 10000
burst_count: 10000 burst_count: 10000
matrix_rtc:
transports:
- type: livekit
livekit_service_url: https://matrix-rtc.m.localhost/livekit/jwt

View File

@@ -68,10 +68,10 @@ As a prerequisite for the
make sure that your Synapse server has either a `federation` or `openid` 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). [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 In order to **guarantee smooth operation** of Element Call, a dedicated LiveKit
required for each site deployment. backend is required for each site deployment.
![MSC4195 compatible setup](MSC4195_setup.drawio.png) ![MSC4195 compatible setup](MSC4195_setup.drawio.png)
@@ -165,7 +165,7 @@ Using Haproxy, you can achieve this by:
use_backend mxrtc_auth_backend if is_mxrtc_auth matrixrtc_domain use_backend mxrtc_auth_backend if is_mxrtc_auth matrixrtc_domain
# Backend # Backend
## MatrixRTC backend ## LiveKit backend
backend sfu_backend backend sfu_backend
server livekit 127.0.0.1:7880 server livekit 127.0.0.1:7880
http-request set-path %[path,regsub(^/livekit/sfu/,/)] http-request set-path %[path,regsub(^/livekit/sfu/,/)]
@@ -187,43 +187,22 @@ backend mxrtc_auth_backend
``` ```
#### MatrixRTC backend announcement #### MatrixRTC transport announcement
> [!IMPORTANT] Enable the unstable feature flag `msc4143_enabled`, and update the
> As defined in [`matrix_rtc` section](https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#matrix_rtc)
> [MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143), of your Synapse config file:
> 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:
```json ```yaml
"org.matrix.msc4143.rtc_foci": [ matrix_rtc:
{ transports:
"type": "livekit", - type: livekit
"livekit_service_url": "https://matrix-rtc.example.com/livekit/jwt" livekit_service_url: https://matrix-rtc.example.com/livekit/jwt
},
{
"type": "livekit",
"livekit_service_url": "https://matrix-rtc-2.example.com/livekit/jwt"
}
]
``` ```
Make sure this file is served with the correct MIME type (`application/json`). The transport you specify will be made available to clients over the
Additionally, ensure the appropriate CORS headers are set to allow web clients `/_matrix/client/unstable/org.matrix.msc4143/rtc/transports` endpoint as defined
to access it across origins. For more details, refer to the in [MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143).
[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.
## Building Element Call ## 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). in [ConfigOptions.ts](../src/config/ConfigOptions.ts).
> [!CAUTION] > [!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 > Element Call is only available for developing and debug purposes. Relying on
> it might break Element Call going forward! > it might break Element Call going forward!

View File

@@ -61,7 +61,7 @@
"crypto_version": "Crypto version: {{version}}", "crypto_version": "Crypto version: {{version}}",
"custom_livekit_url": { "custom_livekit_url": {
"current_url": "Currently set to: ", "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", "label": "Custom Livekit-url",
"reset": "Reset overwrite", "reset": "Reset overwrite",
"save": "Save", "save": "Save",

View File

@@ -74,10 +74,7 @@ export interface ConfigOptions {
livekit?: { livekit?: {
// The link to the service that returns a livekit url and token to use it. // 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 // This is a fallback link in case the homeserver in use does not advertise
// a livekit service url in the client well-known. // a livekit service url over the transports endpoint.
// 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"
livekit_service_url: string; livekit_service_url: string;
}; };

View File

@@ -257,7 +257,7 @@ exports[`DeveloperSettingsTab > renders and matches snapshot 1`] = `
class="_message_1o4d9_86 _help-message_1o4d9_92" class="_message_1o4d9_86 _help-message_1o4d9_92"
id="radix-_r_8_" 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.
</span> </span>
</div> </div>
</form> </form>

View File

@@ -138,9 +138,6 @@ export function withCallViewModel(mode: MatrixRTCMode) {
public getSyncState(): SyncState { public getSyncState(): SyncState {
return syncState; return syncState;
} }
public getAccessToken(): string | null {
return "a-token";
}
})() as Partial<MatrixClient> as MatrixClient, })() as Partial<MatrixClient> as MatrixClient,
getMembers: () => roomMembers, getMembers: () => roomMembers,
getMembersWithMembership: () => roomMembers, getMembersWithMembership: () => roomMembers,

View File

@@ -20,7 +20,6 @@ import {
afterAll, afterAll,
beforeEach, beforeEach,
} from "vitest"; } from "vitest";
import { AutoDiscovery } from "matrix-js-sdk/lib/autodiscovery";
import { BehaviorSubject, map, of } from "rxjs"; import { BehaviorSubject, map, of } from "rxjs";
import { logger } from "matrix-js-sdk/lib/logger"; import { logger } from "matrix-js-sdk/lib/logger";
import { type LocalParticipant, type LocalTrack } from "livekit-client"; import { type LocalParticipant, type LocalTrack } from "livekit-client";
@@ -78,34 +77,10 @@ describe("LocalMembership", () => {
livekit_alias: "my-oldest-member-service-alias", 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({ mockConfig({
livekit: { livekit_service_url: "http://my-default-service-url.com" }, 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({ const mockedSession = vi.mocked({
room: { room: {
roomId: "roomId", roomId: "roomId",
@@ -132,7 +107,7 @@ describe("LocalMembership", () => {
ownMemberMock, ownMemberMock,
{ {
livekit_alias: "roomId", livekit_alias: "roomId",
livekit_service_url: "http://my-well-known-service-url.com", livekit_service_url: "http://my-livekit-service-url.com",
type: "livekit", type: "livekit",
}, },
{ {
@@ -150,7 +125,7 @@ describe("LocalMembership", () => {
[ [
{ {
livekit_alias: "roomId", livekit_alias: "roomId",
livekit_service_url: "http://my-well-known-service-url.com", livekit_service_url: "http://my-livekit-service-url.com",
type: "livekit", 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 = { const defaultCreateLocalMemberValues = {

View File

@@ -117,7 +117,6 @@ export type LocalMemberState =
}; };
/* /*
* - get well known
* - get oldest membership * - get oldest membership
* - get transport to use * - get transport to use
* - get openId + jwt token * - get openId + jwt token

View File

@@ -63,8 +63,7 @@ describe("LocalTransport", () => {
client: { client: {
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () => Promise.resolve([]), _unstable_getRTCTransports: async () => Promise.resolve([]),
getAccessToken: vi.fn().mockReturnValue("access_token"), getDomain: () => "example.org",
getDomain: () => "",
baseUrl: "example.org", baseUrl: "example.org",
// These won't be called in this error path but satisfy the type // These won't be called in this error path but satisfy the type
getOpenIdToken: vi.fn(), getOpenIdToken: vi.fn(),
@@ -77,9 +76,11 @@ describe("LocalTransport", () => {
await flushPromises(); await flushPromises();
expect(() => advertised$.value).toThrow( 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 () => { it("throws FailToGetOpenIdToken when OpenID fetch fails", async () => {
@@ -103,10 +104,8 @@ describe("LocalTransport", () => {
useOldestMember: false, useOldestMember: false,
memberships$: constant(new Epoch<CallMembership[]>([])), memberships$: constant(new Epoch<CallMembership[]>([])),
client: { client: {
baseUrl: "https://lk.example.org", baseUrl: "https://example.org",
// Use empty domain to skip .well-known and use config directly getDomain: () => "example.org",
getDomain: () => "",
getAccessToken: vi.fn().mockReturnValue("access_token"),
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () => Promise.resolve([]), _unstable_getRTCTransports: async () => Promise.resolve([]),
getOpenIdToken: vi.fn(), getOpenIdToken: vi.fn(),
@@ -150,11 +149,10 @@ describe("LocalTransport", () => {
client: { client: {
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () => Promise.resolve([]), _unstable_getRTCTransports: async () => Promise.resolve([]),
getDomain: () => "", getDomain: () => "example.org",
getOpenIdToken: vi.fn(), getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(), getDeviceId: vi.fn(),
baseUrl: "https://lk.example.org", baseUrl: "https://example.org",
getAccessToken: vi.fn().mockReturnValue("access_token"),
}, },
ownMembershipIdentity: ownMemberMock, ownMembershipIdentity: ownMemberMock,
forceJwtEndpoint: JwtEndpointVersion.Legacy, forceJwtEndpoint: JwtEndpointVersion.Legacy,
@@ -221,13 +219,12 @@ describe("LocalTransport", () => {
useOldestMember: true, useOldestMember: true,
memberships$: scope.behavior(memberships$.pipe(trackEpoch())), memberships$: scope.behavior(memberships$.pipe(trackEpoch())),
client: { client: {
getDomain: () => "", getDomain: () => "example.org",
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () => Promise.resolve([]), _unstable_getRTCTransports: async () => Promise.resolve([]),
getAccessToken: vi.fn().mockReturnValue("access_token"),
getOpenIdToken: vi.fn(), getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(), getDeviceId: vi.fn(),
baseUrl: "https://lk.example.org", baseUrl: "https://example.org",
}, },
ownMembershipIdentity: ownMemberMock, ownMembershipIdentity: ownMemberMock,
forceJwtEndpoint: JwtEndpointVersion.Legacy, forceJwtEndpoint: JwtEndpointVersion.Legacy,
@@ -278,14 +275,13 @@ describe("LocalTransport", () => {
useOldestMember: true, useOldestMember: true,
memberships$: scope.behavior(memberships$.pipe(trackEpoch())), memberships$: scope.behavior(memberships$.pipe(trackEpoch())),
client: { client: {
getDomain: () => "", getDomain: () => "example.org",
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () => _unstable_getRTCTransports: async () =>
Promise.resolve([aliceTransport]), Promise.resolve([aliceTransport]),
getAccessToken: vi.fn().mockReturnValue("access_token"),
getOpenIdToken: vi.fn(), getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(), getDeviceId: vi.fn(),
baseUrl: "https://lk.example.org", baseUrl: "https://example.org",
}, },
ownMembershipIdentity: ownMemberMock, ownMembershipIdentity: ownMemberMock,
forceJwtEndpoint: JwtEndpointVersion.Legacy, forceJwtEndpoint: JwtEndpointVersion.Legacy,
@@ -330,10 +326,9 @@ describe("LocalTransport", () => {
memberships$: constant(new Epoch<CallMembership[]>([])), memberships$: constant(new Epoch<CallMembership[]>([])),
client: { client: {
baseUrl: "https://example.org", baseUrl: "https://example.org",
getDomain: vi.fn().mockReturnValue(""), getDomain: vi.fn().mockReturnValue("example.org"),
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: vi.fn().mockResolvedValue([]), _unstable_getRTCTransports: vi.fn().mockResolvedValue([]),
getAccessToken: vi.fn().mockReturnValue("access_token"),
getOpenIdToken: vi.fn(), getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(), getDeviceId: vi.fn(),
}, },
@@ -433,51 +428,6 @@ describe("LocalTransport", () => {
).rejects.toThrow(expect.any(FailToGetOpenIdToken)); ).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 () => { it("throws if no options are available", async () => {
const { advertised$, active$ } = createLocalTransport$({ const { advertised$, active$ } = createLocalTransport$({
scope: testScope(), scope: testScope(),
@@ -488,11 +438,10 @@ describe("LocalTransport", () => {
delayId$: constant(null), delayId$: constant(null),
memberships$: constant(new Epoch<CallMembership[]>([])), memberships$: constant(new Epoch<CallMembership[]>([])),
client: { client: {
getDomain: () => "", getDomain: () => "example.org",
baseUrl: "https://example.org", baseUrl: "https://example.org",
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () => Promise.resolve([]), _unstable_getRTCTransports: async () => Promise.resolve([]),
getAccessToken: vi.fn().mockReturnValue("access_token"),
// These won't be called in this error path but satisfy the type // These won't be called in this error path but satisfy the type
getOpenIdToken: vi.fn(), getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(), getDeviceId: vi.fn(),
@@ -501,10 +450,10 @@ describe("LocalTransport", () => {
await flushPromises(); await flushPromises();
expect(() => advertised$.value).toThrow( expect(() => advertised$.value).toThrow(
new MatrixRTCTransportMissingError(""), new MatrixRTCTransportMissingError("example.org"),
); );
expect(() => active$.value).toThrow( expect(() => active$.value).toThrow(
new MatrixRTCTransportMissingError(""), new MatrixRTCTransportMissingError("example.org"),
); );
}); });
}); });
@@ -529,11 +478,10 @@ describe("LocalTransport", () => {
delayId$: delayId$, delayId$: delayId$,
memberships$: constant(new Epoch<CallMembership[]>([])), memberships$: constant(new Epoch<CallMembership[]>([])),
client: { client: {
getDomain: () => "", getDomain: () => "example.org",
baseUrl: "https://example.org", baseUrl: "https://example.org",
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () => Promise.resolve([]), _unstable_getRTCTransports: async () => Promise.resolve([]),
getAccessToken: vi.fn().mockReturnValue("access_token"),
// These won't be called in this error path but satisfy the type // These won't be called in this error path but satisfy the type
getOpenIdToken: vi.fn(), getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(), getDeviceId: vi.fn(),

View File

@@ -26,7 +26,6 @@ import {
tap, tap,
} from "rxjs"; } from "rxjs";
import { logger as rootLogger, type Logger } from "matrix-js-sdk/lib/logger"; 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 CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager";
import { type Behavior } from "../../Behavior.ts"; import { type Behavior } from "../../Behavior.ts";
@@ -57,7 +56,7 @@ interface Props {
memberships$: Behavior<Epoch<CallMembership[]>>; memberships$: Behavior<Epoch<CallMembership[]>>;
client: Pick< client: Pick<
MatrixClient, MatrixClient,
"getDomain" | "baseUrl" | "_unstable_getRTCTransports" | "getAccessToken" "getDomain" | "baseUrl" | "_unstable_getRTCTransports"
> & > &
OpenIDClientParts; OpenIDClientParts;
// Used by the jwt service to create the livekit room and compute the livekit alias. // 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({ const transportDiscovery = new RtcTransportAutoDiscovery({
client: client, client: client,
resolvedConfig: Config.get(), resolvedConfig: Config.get(),
wellKnownFetcher: AutoDiscovery.getRawClientConfig.bind(AutoDiscovery),
logger: logger, logger: logger,
}); });
@@ -309,7 +307,7 @@ async function doOpenIdAndJWTFromUrl(
roomId: string, roomId: string,
client: Pick< client: Pick<
MatrixClient, MatrixClient,
"getDomain" | "baseUrl" | "_unstable_getRTCTransports" | "getAccessToken" "getDomain" | "baseUrl" | "_unstable_getRTCTransports"
> & > &
OpenIDClientParts, OpenIDClientParts,
delayId?: string, delayId?: string,
@@ -339,7 +337,7 @@ function observeLocalTransportForOldestMembership(
preferredTransport$: Observable<LocalTransportWithSFUConfig>, preferredTransport$: Observable<LocalTransportWithSFUConfig>,
client: Pick< client: Pick<
MatrixClient, MatrixClient,
"getDomain" | "baseUrl" | "_unstable_getRTCTransports" | "getAccessToken" "getDomain" | "baseUrl" | "_unstable_getRTCTransports"
> & > &
OpenIDClientParts, OpenIDClientParts,
ownMembershipIdentity: CallMembershipIdentityParts, ownMembershipIdentity: CallMembershipIdentityParts,

View File

@@ -13,7 +13,7 @@ import {
type MockedObject, type MockedObject,
vi, vi,
} from "vitest"; } 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 { logger as rootLogger } from "matrix-js-sdk/lib/logger";
import { import {
type LivekitTransportConfig, type LivekitTransportConfig,
@@ -33,9 +33,9 @@ const backendTransport: LivekitTransportConfig = {
livekit_service_url: "https://backend.example.org", livekit_service_url: "https://backend.example.org",
}; };
const wellKnownTransport: LivekitTransportConfig = { const configTransport: LivekitTransportConfig = {
type: "livekit", type: "livekit",
livekit_service_url: "https://well-known.example.org", livekit_service_url: "https://config.example.org",
}; };
function makeClient(): MockedObject<DiscoveryClient> { function makeClient(): MockedObject<DiscoveryClient> {
@@ -43,7 +43,6 @@ function makeClient(): MockedObject<DiscoveryClient> {
getDomain: vi.fn().mockReturnValue("example.org"), getDomain: vi.fn().mockReturnValue("example.org"),
baseUrl: "https://matrix.example.org", baseUrl: "https://matrix.example.org",
_unstable_getRTCTransports: vi.fn().mockResolvedValue([]), _unstable_getRTCTransports: vi.fn().mockResolvedValue([]),
getAccessToken: vi.fn().mockReturnValue("access_token"),
getOpenIdToken: vi.fn(), getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(), getDeviceId: vi.fn(),
} as unknown as MockedObject<DiscoveryClient>; } as unknown as MockedObject<DiscoveryClient>;
@@ -59,12 +58,6 @@ function makeResolvedConfig(livekitServiceUrl?: string): ResolvedConfigOptions {
} as ResolvedConfigOptions; } as ResolvedConfigOptions;
} }
function makeWellKnown(rtcFoci?: Transport[]): IClientWellKnown {
return {
"org.matrix.msc4143.rtc_foci": rtcFoci,
} as unknown as IClientWellKnown;
}
describe("RtcTransportAutoDiscovery", () => { describe("RtcTransportAutoDiscovery", () => {
beforeEach(() => { beforeEach(() => {
vi.clearAllMocks(); vi.clearAllMocks();
@@ -75,33 +68,27 @@ describe("RtcTransportAutoDiscovery", () => {
{ transports: [{ type: "not_livekit" }, backendTransport] }, { transports: [{ type: "not_livekit" }, backendTransport] },
]; ];
it.each(VALID_TEST_CASES)( it.each(VALID_TEST_CASES)(
"prefers backend transport over well-known and app config $transports", "prefers backend transport other app config $transports",
async ({ transports }) => { async ({ transports }) => {
// it("prefers backend transport over well-known and app config", async () => {
const client = makeClient(); const client = makeClient();
client._unstable_getRTCTransports.mockResolvedValue(transports); client._unstable_getRTCTransports.mockResolvedValue(transports);
const wellKnownFetcher = vi
.fn<(domain: string) => Promise<IClientWellKnown>>()
.mockResolvedValue(makeWellKnown([wellKnownTransport]));
const discovery = new RtcTransportAutoDiscovery({ const discovery = new RtcTransportAutoDiscovery({
client, client,
resolvedConfig: makeResolvedConfig("https://config.example.org"), resolvedConfig: makeResolvedConfig(configTransport.livekit_service_url),
wellKnownFetcher,
logger: rootLogger, logger: rootLogger,
}); });
await expect( const discoveredTransport = await discovery.discoverPreferredTransport();
discovery.discoverPreferredTransport(),
).resolves.toStrictEqual(backendTransport); expect(discoveredTransport).toStrictEqual(backendTransport);
expect(discoveredTransport).not.toStrictEqual(configTransport);
expect(client._unstable_getRTCTransports).toHaveBeenCalledTimes(1); 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(); const client = makeClient();
client._unstable_getRTCTransports client._unstable_getRTCTransports
.mockRejectedValueOnce( .mockRejectedValueOnce(
@@ -116,14 +103,9 @@ describe("RtcTransportAutoDiscovery", () => {
) )
.mockResolvedValue([backendTransport]); .mockResolvedValue([backendTransport]);
const wellKnownFetcher = vi
.fn<(domain: string) => Promise<IClientWellKnown>>()
.mockResolvedValue(makeWellKnown([wellKnownTransport]));
const discovery = new RtcTransportAutoDiscovery({ const discovery = new RtcTransportAutoDiscovery({
client, client,
resolvedConfig: makeResolvedConfig("https://config.example.org"), resolvedConfig: makeResolvedConfig("https://config.example.org"),
wellKnownFetcher,
logger: rootLogger, logger: rootLogger,
}); });
@@ -132,7 +114,6 @@ describe("RtcTransportAutoDiscovery", () => {
); );
expect(client._unstable_getRTCTransports).toHaveBeenCalledTimes(2); expect(client._unstable_getRTCTransports).toHaveBeenCalledTimes(2);
expect(wellKnownFetcher).not.toHaveBeenCalled();
}); });
const INVALID_TEST_CASES: Array<{ transports: Transport[] }> = [ const INVALID_TEST_CASES: Array<{ transports: Transport[] }> = [
@@ -140,67 +121,30 @@ describe("RtcTransportAutoDiscovery", () => {
{ transports: [{ type: "not_livekit" }] }, { transports: [{ type: "not_livekit" }] },
]; ];
it.each(INVALID_TEST_CASES)( 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 }) => { async ({ transports }) => {
const client = makeClient(); const client = makeClient();
client._unstable_getRTCTransports.mockResolvedValue(transports); client._unstable_getRTCTransports.mockResolvedValue(transports);
const wellKnownFetcher = vi
.fn<(domain: string) => Promise<IClientWellKnown>>()
.mockResolvedValue(makeWellKnown([wellKnownTransport]));
const discovery = new RtcTransportAutoDiscovery({ const discovery = new RtcTransportAutoDiscovery({
client, client,
resolvedConfig: makeResolvedConfig("https://config.example.org"), resolvedConfig: makeResolvedConfig(configTransport.livekit_service_url),
wellKnownFetcher,
logger: rootLogger, logger: rootLogger,
}); });
await expect( const discoveredTransport = await discovery.discoverPreferredTransport();
discovery.discoverPreferredTransport(), expect(discoveredTransport).not.toStrictEqual(backendTransport);
).resolves.toStrictEqual(wellKnownTransport); expect(discoveredTransport).toStrictEqual(configTransport);
expect(wellKnownFetcher).toHaveBeenCalledWith("example.org");
}, },
); );
it("falls back to app config when backend fails and well-known has no rtc_foci", async () => { it("returns null when backend and config are all unavailable", async () => {
const client = makeClient();
client._unstable_getRTCTransports.mockRejectedValue(
new MatrixError({ errcode: "M_UNKNOWN" }, 404),
);
const wellKnownFetcher = vi
.fn<(domain: string) => Promise<IClientWellKnown>>()
.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 () => {
const client = makeClient(); const client = makeClient();
client._unstable_getRTCTransports.mockResolvedValue([]); client._unstable_getRTCTransports.mockResolvedValue([]);
const wellKnownFetcher = vi
.fn<(domain: string) => Promise<IClientWellKnown>>()
.mockResolvedValue({} as IClientWellKnown);
const discovery = new RtcTransportAutoDiscovery({ const discovery = new RtcTransportAutoDiscovery({
client, client,
resolvedConfig: makeResolvedConfig(undefined), resolvedConfig: makeResolvedConfig(undefined),
wellKnownFetcher,
logger: rootLogger, logger: rootLogger,
}); });

View File

@@ -8,7 +8,7 @@ import {
isLivekitTransportConfig, isLivekitTransportConfig,
type LivekitTransportConfig, type LivekitTransportConfig,
} from "matrix-js-sdk/lib/matrixrtc"; } 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 Logger } from "matrix-js-sdk/lib/logger";
import type { ResolvedConfigOptions } from "../../../config/ConfigOptions.ts"; import type { ResolvedConfigOptions } from "../../../config/ConfigOptions.ts";
@@ -16,33 +16,27 @@ import { doNetworkOperationWithRetry } from "../../../utils/matrix.ts";
type TransportDiscoveryClient = Pick< type TransportDiscoveryClient = Pick<
MatrixClient, MatrixClient,
"getDomain" | "_unstable_getRTCTransports" | "getAccessToken" "getDomain" | "_unstable_getRTCTransports"
>; >;
export interface RtcTransportAutoDiscoveryProps { export interface RtcTransportAutoDiscoveryProps {
client: TransportDiscoveryClient; client: TransportDiscoveryClient;
resolvedConfig: ResolvedConfigOptions; resolvedConfig: ResolvedConfigOptions;
wellKnownFetcher: (domain: string) => Promise<IClientWellKnown>;
logger: Logger; logger: Logger;
} }
export class RtcTransportAutoDiscovery { export class RtcTransportAutoDiscovery {
private readonly client: TransportDiscoveryClient; private readonly client: TransportDiscoveryClient;
private readonly resolvedConfig: ResolvedConfigOptions; private readonly resolvedConfig: ResolvedConfigOptions;
private readonly wellKnownFetcher: (
domain: string,
) => Promise<IClientWellKnown>;
private readonly logger: Logger; private readonly logger: Logger;
public constructor({ public constructor({
client, client,
resolvedConfig, resolvedConfig,
wellKnownFetcher,
logger, logger,
}: RtcTransportAutoDiscoveryProps) { }: RtcTransportAutoDiscoveryProps) {
this.client = client; this.client = client;
this.resolvedConfig = resolvedConfig; this.resolvedConfig = resolvedConfig;
this.wellKnownFetcher = wellKnownFetcher;
this.logger = logger.getChild("[RtcTransportAutoDiscovery]"); this.logger = logger.getChild("[RtcTransportAutoDiscovery]");
} }
@@ -56,21 +50,7 @@ export class RtcTransportAutoDiscovery {
return backendTransport; return backendTransport;
} }
this.logger.info("No backend transport found, falling back to well-known"); // 2) app config URL
// 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
const configTransport = this.tryConfigTransport(); const configTransport = this.tryConfigTransport();
if (configTransport) { if (configTransport) {
this.logger.info( this.logger.info(
@@ -110,44 +90,6 @@ export class RtcTransportAutoDiscovery {
return null; 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<LivekitTransportConfig | null> {
// 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 { private tryConfigTransport(): LivekitTransportConfig | null {
const url = this.resolvedConfig.livekit?.livekit_service_url; const url = this.resolvedConfig.livekit?.livekit_service_url;
if (url) { if (url) {

View File

@@ -12,7 +12,7 @@ import { i18nKey } from "./i18n";
export enum ErrorCode { 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", MISSING_MATRIX_RTC_TRANSPORT = "MISSING_MATRIX_RTC_TRANSPORT",
CONNECTION_LOST_ERROR = "CONNECTION_LOST_ERROR", 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 { export class MatrixRTCTransportMissingError extends ElementCallError {
public domain: string; public domain: string;

View File

@@ -198,7 +198,7 @@ export const initializeWidget = (
// be otherwise) // be otherwise)
await Config.init(); await Config.init();
seedSettingsFromConfig(Config.get().media_quality); seedSettingsFromConfig(Config.get().media_quality);
await client.startClient({ clientWellKnownPollPeriod: 60 * 10 }); await client.startClient();
return client; return client;
}; };