Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions apps/web/src/MatrixClientPeg.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import fetchMock from "@fetch-mock/vitest";
import { advanceDateAndTime, stubClient, createTestClient } from "test-utils";

import { type IMatrixClientPeg, MatrixClientPeg as peg } from "./MatrixClientPeg";
import SdkConfig from "./SdkConfig";

vi.useFakeTimers();

Expand Down Expand Up @@ -116,5 +117,30 @@ describe("MatrixClientPeg", () => {
await testPeg.start();
expect(mockInitRustCrypto).toHaveBeenCalledTimes(1);
});

it("should poll the client well-known by default", async () => {
vi.spyOn(testPeg.safeGet(), "initRustCrypto").mockResolvedValue(undefined);
const startClient = vi.spyOn(testPeg.safeGet(), "startClient").mockResolvedValue(undefined);

await testPeg.start();

const opts = startClient.mock.calls[0][0];
expect(opts?.clientWellKnownPollPeriod).toBe(2 * 60 * 60);
});

it("should not poll the client well-known when enable_client_well_known_lookups is false", async () => {
const sdkConfigGet = SdkConfig.get;
vi.spyOn(SdkConfig, "get").mockImplementation((key?: any, altCaseName?: string): any => {
if (key === "enable_client_well_known_lookups") return false;
return sdkConfigGet(key, altCaseName);
});
vi.spyOn(testPeg.safeGet(), "initRustCrypto").mockResolvedValue(undefined);
const startClient = vi.spyOn(testPeg.safeGet(), "startClient").mockResolvedValue(undefined);

await testPeg.start();

const opts = startClient.mock.calls[0][0];
expect(opts?.clientWellKnownPollPeriod).toBeUndefined();
});
});
});
6 changes: 5 additions & 1 deletion apps/web/src/MatrixClientPeg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,11 @@ class MatrixClientPegClass implements IMatrixClientPeg {
// the react sdk doesn't work without this, so don't allow
opts.pendingEventOrdering = PendingEventOrdering.Detached;
opts.lazyLoadMembers = true;
opts.clientWellKnownPollPeriod = 2 * 60 * 60; // 2 hours
// Poll the user's `<server_name>/.well-known/matrix/client` for client config unless disabled by config.
// Leaving `clientWellKnownPollPeriod` unset stops the SDK fetching it.
if (SdkConfig.get("enable_client_well_known_lookups")) {
opts.clientWellKnownPollPeriod = 2 * 60 * 60; // 2 hours
}
opts.threadSupport = true;
if (SettingsStore.getValue("feature_user_status")) {
opts.unstableMSC4429SyncUserProfileFields = ["org.matrix.msc4426.status"];
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/SdkConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const DEFAULTS = {
integrations_rest_url: "https://scalar.vector.im/api",
show_labs_settings: false,
force_verification: false,
enable_client_well_known_lookups: true,

jitsi: {
preferred_domain: "meet.element.io",
Expand Down
12 changes: 8 additions & 4 deletions apps/web/src/stores/CallStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
import WidgetStore from "./WidgetStore";
import SettingsStore from "../settings/SettingsStore";
import { SettingLevel } from "../settings/SettingLevel";
import SdkConfig from "../SdkConfig";
import { Call, CallEvent, ConnectionState } from "../models/Call";

export enum CallStoreEvent {
Expand Down Expand Up @@ -71,10 +72,13 @@ export class CallStore extends AsyncStoreWithClient<EmptyObject> {
}
// See https://github.com/matrix-org/matrix-spec-proposals/blob/d61969a9a3696b6c54d7987b1643b5bc03670927/proposals/4143-matrix-rtc.md#discovery-of-foci-using-well-knownmatrixclient
// This well-known option has since been removed from the spec but is still widely deployed.
await this.matrixClient.waitForClientWellKnown();
const foci = this.matrixClient.getClientWellKnown()?.["org.matrix.msc4143.rtc_foci"];
if (Array.isArray(foci)) {
foci.forEach((foci) => this.configuredMatrixRTCTransports.add(foci));
// Reading it can be disabled via config; the modern endpoint above is unaffected.
if (SdkConfig.get("enable_client_well_known_lookups")) {
await this.matrixClient.waitForClientWellKnown();
const foci = this.matrixClient.getClientWellKnown()?.["org.matrix.msc4143.rtc_foci"];
if (Array.isArray(foci)) {
foci.forEach((foci) => this.configuredMatrixRTCTransports.add(foci));
}
}
this.emit(CallStoreEvent.TransportsUpdated);
}
Expand Down
17 changes: 17 additions & 0 deletions apps/web/test/unit-tests/stores/CallStore-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { type MockedObject } from "jest-mock";

import { ElementCall } from "../../../src/models/Call";
import { CallStore } from "../../../src/stores/CallStore";
import SdkConfig from "../../../src/SdkConfig";
import {
setUpClientRoomAndStores,
cleanUpClientRoomAndStores,
Expand Down Expand Up @@ -66,4 +67,20 @@ describe("CallStore", () => {
{ type: "type-d", other_data: "baz" },
]);
});
it("does not fall back to client well-known when enable_client_well_known_lookups is false", async () => {
const sdkConfigGet = SdkConfig.get;
jest.spyOn(SdkConfig, "get").mockImplementation((key?: any, altCaseName?: string): any => {
if (key === "enable_client_well_known_lookups") return false;
return sdkConfigGet(key, altCaseName);
});
client._unstable_getRTCTransports.mockResolvedValue([{ type: "type-a", some_data: "value" }]);
client.getClientWellKnown.mockReturnValue({
"org.matrix.msc4143.rtc_foci": [{ type: "type-c", other_data: "bar" }],
});
await setupAsyncStoreWithClient(CallStore.instance, client);
// Only the modern endpoint contributes; the legacy well-known fallback is skipped entirely.
expect(CallStore.instance.getConfiguredRTCTransports()).toEqual([{ type: "type-a", some_data: "value" }]);
expect(client.waitForClientWellKnown).not.toHaveBeenCalled();
expect(client.getClientWellKnown).not.toHaveBeenCalled();
});
});
6 changes: 6 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ If both `default_server_config` and `default_server_name` are used, Element will
information using `.well-known`, and if that fails, take `default_server_config` as the homeserver connection
information.

1. `enable_client_well_known_lookups`: Controls whether Element makes runtime requests to the logged-in user's
[`<server_name>/.well-known/matrix/...`](https://spec.matrix.org/latest/client-server-api/#getwell-knownmatrixclient)
endpoints (for example, polling for client configuration after login). Set it to `false` to stop Element making these
requests, so it only contacts the homeserver base URL; the `well_known` object returned inline in the `/login` response
is unaffected. Defaults to `true`.

## Labs flags

Labs flags are optional, typically beta or in-development, features that can be turned on or off. The full range of
Expand Down
8 changes: 8 additions & 0 deletions packages/shared-types/lib/config.json.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ export interface WebConfigJson {
disable_login_language_selector?: boolean;
disable_3pid_login?: boolean;

/**
* Whether the app may make runtime requests to the user's `<server_name>/.well-known/matrix/...`
* endpoints (such as the post-login client well-known poll). Set to false to disable these, so the app
* only contacts the homeserver base URL. Does not affect the `well_known` returned inline in the
* `/login` response. Defaults to true.
*/
enable_client_well_known_lookups?: boolean;

brand?: string;
branding?: {
welcome_background_url?: string | string[]; // chosen at random if array
Expand Down
Loading