diff --git a/src/MatrixClientPeg.ts b/src/MatrixClientPeg.ts
index be2151044d..d09b8467fd 100644
--- a/src/MatrixClientPeg.ts
+++ b/src/MatrixClientPeg.ts
@@ -52,6 +52,7 @@ import ErrorDialog from "./components/views/dialogs/ErrorDialog";
 import PlatformPeg from "./PlatformPeg";
 import { formatList } from "./utils/FormattingUtils";
 import SdkConfig from "./SdkConfig";
+import { Features } from "./settings/Settings";
 
 export interface IMatrixClientCreds {
     homeserverUrl: string;
@@ -301,7 +302,7 @@ class MatrixClientPegClass implements IMatrixClientPeg {
             throw new Error("createClient must be called first");
         }
 
-        const useRustCrypto = SettingsStore.getValue("feature_rust_crypto");
+        const useRustCrypto = SettingsStore.getValue(Features.RustCrypto);
 
         // we want to make sure that the same crypto implementation is used throughout the lifetime of a device,
         // so persist the setting at the device layer
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 7dc6391982..5521b74331 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -1447,7 +1447,10 @@
         "report_to_moderators": "Report to moderators",
         "report_to_moderators_description": "In rooms that support moderation, the “Report” button will let you report abuse to room moderators.",
         "rust_crypto": "Rust cryptography implementation",
-        "rust_crypto_disabled_notice": "Can currently only be enabled via config.json",
+        "rust_crypto_in_config": "Rust cryptography cannot be disabled on this deployment of %(brand)s",
+        "rust_crypto_in_config_description": "Switching to the Rust cryptography requires a migration process that may take several minutes. It cannot be disabled; use with caution!",
+        "rust_crypto_optin_warning": "Switching to the Rust cryptography requires a migration process that may take several minutes. To disable you will need to log out and back in; use with caution!",
+        "rust_crypto_requires_logout": "Once enabled, Rust cryptography can only be disabled by logging out and in again",
         "sliding_sync": "Sliding Sync mode",
         "sliding_sync_checking": "Checking…",
         "sliding_sync_configuration": "Sliding Sync configuration",
diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx
index 18a5ecb9ab..3a21d6f6dd 100644
--- a/src/settings/Settings.tsx
+++ b/src/settings/Settings.tsx
@@ -46,6 +46,7 @@ import RustCryptoSdkController from "./controllers/RustCryptoSdkController";
 import ServerSupportUnstableFeatureController from "./controllers/ServerSupportUnstableFeatureController";
 import { WatchManager } from "./WatchManager";
 import { CustomTheme } from "../theme";
+import SettingsStore from "./SettingsStore";
 
 export const defaultWatchManager = new WatchManager();
 
@@ -94,6 +95,7 @@ export enum Features {
     VoiceBroadcastForceSmallChunks = "feature_voice_broadcast_force_small_chunks",
     NotificationSettings2 = "feature_notification_settings2",
     OidcNativeFlow = "feature_oidc_native_flow",
+    RustCrypto = "feature_rust_crypto",
 }
 
 export const labGroupNames: Record<LabGroup, TranslationKey> = {
@@ -480,15 +482,22 @@ export const SETTINGS: { [setting: string]: ISetting } = {
         description: _td("labs|oidc_native_flow_description"),
         default: false,
     },
-    "feature_rust_crypto": {
-        // use the rust matrix-sdk-crypto-js for crypto.
+    [Features.RustCrypto]: {
+        // use the rust matrix-sdk-crypto-wasm for crypto.
         isFeature: true,
         labsGroup: LabGroup.Developer,
-        configDisablesSetting: true,
+        // unlike most features, `configDisablesSetting` is false here.
         supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
         displayName: _td("labs|rust_crypto"),
-        description: _td("labs|under_active_development"),
-        // shouldWarn: true,
+        description: () => {
+            if (SettingsStore.getValueAt(SettingLevel.CONFIG, Features.RustCrypto)) {
+                // It's enabled in the config, so you can't get rid of it even by logging out.
+                return _t("labs|rust_crypto_in_config_description");
+            } else {
+                return _t("labs|rust_crypto_optin_warning");
+            }
+        },
+        shouldWarn: true,
         default: false,
         controller: new RustCryptoSdkController(),
     },
diff --git a/src/settings/controllers/RustCryptoSdkController.ts b/src/settings/controllers/RustCryptoSdkController.ts
index 8de2fb3d87..3bf8526feb 100644
--- a/src/settings/controllers/RustCryptoSdkController.ts
+++ b/src/settings/controllers/RustCryptoSdkController.ts
@@ -15,12 +15,35 @@ limitations under the License.
 */
 
 import { _t } from "../../languageHandler";
+import SettingsStore from "../SettingsStore";
+import { SettingLevel } from "../SettingLevel";
+import PlatformPeg from "../../PlatformPeg";
 import SettingController from "./SettingController";
+import { Features } from "../Settings";
+import { MatrixClientPeg } from "../../MatrixClientPeg";
+import SdkConfig from "../../SdkConfig";
 
 export default class RustCryptoSdkController extends SettingController {
+    public onChange(level: SettingLevel, roomId: string | null, newValue: any): void {
+        // If the crypto stack has already been initialized, we'll need to reload the app to make it take effect.
+        if (MatrixClientPeg.get()?.getCrypto()) {
+            PlatformPeg.get()?.reload();
+        }
+    }
+
     public get settingDisabled(): boolean | string {
-        // Currently this can only be changed via config.json. In future, we'll allow the user to *enable* this setting
-        // via labs, which will migrate their existing device to the rust-sdk implementation.
-        return _t("labs|rust_crypto_disabled_notice");
+        if (!SettingsStore.getValueAt(SettingLevel.DEVICE, Features.RustCrypto)) {
+            // If rust crypto has not yet been enabled for this device, you can turn it on, IF YOU DARE
+            return false;
+        }
+
+        if (SettingsStore.getValueAt(SettingLevel.CONFIG, Features.RustCrypto)) {
+            // It's enabled in the config, so you can't get rid of it even by logging out.
+            return _t("labs|rust_crypto_in_config", { brand: SdkConfig.get().brand });
+        }
+
+        // The setting is enabled at the device level, but not mandated at the config level.
+        // You can only turn it off by logging out and in again.
+        return _t("labs|rust_crypto_requires_logout");
     }
 }
diff --git a/test/MatrixClientPeg-test.ts b/test/MatrixClientPeg-test.ts
index 69306f09f3..8b137b310b 100644
--- a/test/MatrixClientPeg-test.ts
+++ b/test/MatrixClientPeg-test.ts
@@ -38,6 +38,9 @@ describe("MatrixClientPeg", () => {
     afterEach(() => {
         localStorage.clear();
         jest.restoreAllMocks();
+
+        // some of the tests assign `MatrixClientPeg.matrixClient`: clear it, to prevent leakage between tests
+        peg.unset();
     });
 
     it("setJustRegisteredUserId", () => {
diff --git a/test/components/views/settings/tabs/user/LabsUserSettingsTab-test.tsx b/test/components/views/settings/tabs/user/LabsUserSettingsTab-test.tsx
index ac832b88b2..0b34ef6343 100644
--- a/test/components/views/settings/tabs/user/LabsUserSettingsTab-test.tsx
+++ b/test/components/views/settings/tabs/user/LabsUserSettingsTab-test.tsx
@@ -15,15 +15,14 @@ limitations under the License.
 */
 
 import React from "react";
-import { render, screen } from "@testing-library/react";
+import { fireEvent, render, screen } from "@testing-library/react";
 
 import LabsUserSettingsTab from "../../../../../../src/components/views/settings/tabs/user/LabsUserSettingsTab";
 import SettingsStore from "../../../../../../src/settings/SettingsStore";
 import SdkConfig from "../../../../../../src/SdkConfig";
+import { SettingLevel } from "../../../../../../src/settings/SettingLevel";
 
 describe("<LabsUserSettingsTab />", () => {
-    const sdkConfigSpy = jest.spyOn(SdkConfig, "get");
-
     const defaultProps = {
         closeSettingsFn: jest.fn(),
     };
@@ -34,7 +33,9 @@ describe("<LabsUserSettingsTab />", () => {
     beforeEach(() => {
         jest.clearAllMocks();
         settingsValueSpy.mockReturnValue(false);
-        sdkConfigSpy.mockReturnValue(false);
+        SdkConfig.reset();
+        SdkConfig.add({ brand: "BrandedClient" });
+        localStorage.clear();
     });
 
     it("renders settings marked as beta as beta cards", () => {
@@ -43,6 +44,7 @@ describe("<LabsUserSettingsTab />", () => {
     });
 
     it("does not render non-beta labs settings when disabled in config", () => {
+        const sdkConfigSpy = jest.spyOn(SdkConfig, "get");
         render(getComponent());
         expect(sdkConfigSpy).toHaveBeenCalledWith("show_labs_settings");
 
@@ -52,7 +54,7 @@ describe("<LabsUserSettingsTab />", () => {
 
     it("renders non-beta labs settings when enabled in config", () => {
         // enable labs
-        sdkConfigSpy.mockImplementation((configName) => configName === "show_labs_settings");
+        SdkConfig.add({ show_labs_settings: true });
         const { container } = render(getComponent());
 
         // non-beta labs section
@@ -60,4 +62,82 @@ describe("<LabsUserSettingsTab />", () => {
         const labsSections = container.getElementsByClassName("mx_SettingsSubsection");
         expect(labsSections).toHaveLength(9);
     });
+
+    describe("Rust crypto setting", () => {
+        const SETTING_NAME = "Rust cryptography implementation";
+
+        beforeEach(() => {
+            SdkConfig.add({ show_labs_settings: true });
+        });
+
+        describe("Not enabled in config", () => {
+            it("can be turned on if not already", async () => {
+                // By the time the settings panel is shown, `MatrixClientPeg.initClientCrypto` has saved the current
+                // value to the settings store.
+                await SettingsStore.setValue("feature_rust_crypto", null, SettingLevel.DEVICE, false);
+
+                const rendered = render(getComponent());
+                const toggle = rendered.getByRole("switch", { name: SETTING_NAME });
+                expect(toggle.getAttribute("aria-disabled")).toEqual("false");
+                expect(toggle.getAttribute("aria-checked")).toEqual("false");
+
+                const description = toggle.closest(".mx_SettingsFlag")?.querySelector(".mx_SettingsFlag_microcopy");
+                expect(description).toHaveTextContent(/To disable you will need to log out and back in/);
+            });
+
+            it("cannot be turned off once enabled", async () => {
+                await SettingsStore.setValue("feature_rust_crypto", null, SettingLevel.DEVICE, true);
+
+                const rendered = render(getComponent());
+                const toggle = rendered.getByRole("switch", { name: SETTING_NAME });
+                expect(toggle.getAttribute("aria-disabled")).toEqual("true");
+                expect(toggle.getAttribute("aria-checked")).toEqual("true");
+
+                // Hover over the toggle to make it show the tooltip
+                fireEvent.mouseOver(toggle);
+
+                const tooltip = rendered.getByRole("tooltip");
+                expect(tooltip).toHaveTextContent(
+                    "Once enabled, Rust cryptography can only be disabled by logging out and in again",
+                );
+            });
+        });
+
+        describe("Enabled in config", () => {
+            beforeEach(() => {
+                SdkConfig.add({ features: { feature_rust_crypto: true } });
+            });
+
+            it("can be turned on if not already", async () => {
+                // By the time the settings panel is shown, `MatrixClientPeg.initClientCrypto` has saved the current
+                // value to the settings store.
+                await SettingsStore.setValue("feature_rust_crypto", null, SettingLevel.DEVICE, false);
+
+                const rendered = render(getComponent());
+                const toggle = rendered.getByRole("switch", { name: SETTING_NAME });
+                expect(toggle.getAttribute("aria-disabled")).toEqual("false");
+                expect(toggle.getAttribute("aria-checked")).toEqual("false");
+
+                const description = toggle.closest(".mx_SettingsFlag")?.querySelector(".mx_SettingsFlag_microcopy");
+                expect(description).toHaveTextContent(/It cannot be disabled/);
+            });
+
+            it("cannot be turned off once enabled", async () => {
+                await SettingsStore.setValue("feature_rust_crypto", null, SettingLevel.DEVICE, true);
+
+                const rendered = render(getComponent());
+                const toggle = rendered.getByRole("switch", { name: SETTING_NAME });
+                expect(toggle.getAttribute("aria-disabled")).toEqual("true");
+                expect(toggle.getAttribute("aria-checked")).toEqual("true");
+
+                // Hover over the toggle to make it show the tooltip
+                fireEvent.mouseOver(toggle);
+
+                const tooltip = rendered.getByRole("tooltip");
+                expect(tooltip).toHaveTextContent(
+                    "Rust cryptography cannot be disabled on this deployment of BrandedClient",
+                );
+            });
+        });
+    });
 });
diff --git a/test/components/views/settings/tabs/user/__snapshots__/LabsUserSettingsTab-test.tsx.snap b/test/components/views/settings/tabs/user/__snapshots__/LabsUserSettingsTab-test.tsx.snap
index 32a8facee1..4e65f7b61f 100644
--- a/test/components/views/settings/tabs/user/__snapshots__/LabsUserSettingsTab-test.tsx.snap
+++ b/test/components/views/settings/tabs/user/__snapshots__/LabsUserSettingsTab-test.tsx.snap
@@ -15,7 +15,7 @@ exports[`<LabsUserSettingsTab /> renders settings marked as beta as beta cards 1
     <div
       class="mx_SettingsSubsection_text"
     >
-      What's next for false? Labs are the best way to get things early, test out new features and help shape them before they actually launch.
+      What's next for BrandedClient? Labs are the best way to get things early, test out new features and help shape them before they actually launch.
     </div>
     <div
       class="mx_BetaCard"
@@ -42,10 +42,10 @@ exports[`<LabsUserSettingsTab /> renders settings marked as beta as beta cards 1
             class="mx_BetaCard_caption"
           >
             <p>
-              A new way to chat over voice and video in .
+              A new way to chat over voice and video in BrandedClient.
             </p>
             <p>
-              Video rooms are always-on VoIP channels embedded within a room in .
+              Video rooms are always-on VoIP channels embedded within a room in BrandedClient.
             </p>
           </div>
           <div
@@ -62,7 +62,7 @@ exports[`<LabsUserSettingsTab /> renders settings marked as beta as beta cards 1
           <div
             class="mx_BetaCard_refreshWarning"
           >
-            Joining the beta will reload .
+            Joining the beta will reload BrandedClient.
           </div>
           <div
             class="mx_BetaCard_faq"
@@ -104,7 +104,7 @@ exports[`<LabsUserSettingsTab /> renders settings marked as beta as beta cards 1
             class="mx_BetaCard_caption"
           >
             <p>
-              Introducing a simpler way to change your notification settings. Customize your , just the way you like.
+              Introducing a simpler way to change your notification settings. Customize your BrandedClient, just the way you like.
             </p>
           </div>
           <div