diff --git a/docs/self_hosting.md b/docs/self_hosting.md index e8ea2f6d8..fc65ba953 100644 --- a/docs/self_hosting.md +++ b/docs/self_hosting.md @@ -191,10 +191,24 @@ backend mxrtc_auth_backend > [!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: +> the MatrixRTC backend(s) must be announced to the client via `/_matrix/client/unstable/org.matrix.msc4143/rtc/transports`. + +Enable the unstable feature flag `msc4143_enabled`, and update the synapse config file: + +```yaml + +matrix_rtc: + - transports: + - type: livekit + livekit_service_url: https://matrix-rtc.example.com/livekit/jwt +``` + + + +**⚠️ Well-known discovery will soon be deprecated, but needed if MSC4143 is not supported on your Homeserver** + +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 "org.matrix.msc4143.rtc_foci": [ diff --git a/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.test.ts b/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.test.ts index 9314b9932..0245c757f 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 { @@ -59,12 +59,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 +69,30 @@ 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 +107,10 @@ 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 +119,6 @@ describe("RtcTransportAutoDiscovery", () => { ); expect(client._unstable_getRTCTransports).toHaveBeenCalledTimes(2); - expect(wellKnownFetcher).not.toHaveBeenCalled(); }); const INVALID_TEST_CASES: Array<{ transports: Transport[] }> = [ @@ -140,91 +126,32 @@ 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); + const discoveredTransport = await discovery.discoverPreferredTransport(); + expect(discoveredTransport).not.toStrictEqual(backendTransport); + expect(discoveredTransport).toStrictEqual(configTransport); - expect(wellKnownFetcher).toHaveBeenCalledWith("example.org"); }, ); - it("skips backend discovery in widget mode and uses well-known", async () => { - const client = makeClient(); - // widget mode is detected by the absence of an access token - client.getAccessToken.mockReturnValue(null); - - const wellKnownFetcher = vi - .fn<(domain: string) => Promise>() - .mockResolvedValue(makeWellKnown([wellKnownTransport])); - - const discovery = new RtcTransportAutoDiscovery({ - client, - resolvedConfig: makeResolvedConfig("https://config.example.org"), - wellKnownFetcher, - logger: rootLogger, - }); - - await expect(discovery.discoverPreferredTransport()).resolves.toStrictEqual( - wellKnownTransport, - ); - - expect(client._unstable_getRTCTransports).not.toHaveBeenCalled(); - expect(wellKnownFetcher).toHaveBeenCalledWith("example.org"); - }); - - 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 b32b7b613..11adbac20 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"; @@ -22,27 +22,21 @@ type TransportDiscoveryClient = Pick< 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) {