diff --git a/lib/hooks/flag-hooks.js b/lib/hooks/flag-hooks.js
new file mode 100644
--- /dev/null
+++ b/lib/hooks/flag-hooks.js
@@ -0,0 +1,14 @@
+// @flow
+
+import { useIsCurrentUserStaff } from '../shared/staff-utils.js';
+import { isDev } from '../utils/dev-utils.js';
+
+// If this returns true, then DM creation will use E2EE DMs encrypted via Olm
+// and brokered by Tunnelbroker, instead of creating chats under GENESIS on the
+// authoritative keyserver.
+function useAllowOlmViaTunnelbrokerForDMs(): boolean {
+  const isCurrentUserStaff = useIsCurrentUserStaff();
+  return isDev || isCurrentUserStaff;
+}
+
+export { useAllowOlmViaTunnelbrokerForDMs };
diff --git a/lib/shared/thread-actions-utils.js b/lib/shared/thread-actions-utils.js
--- a/lib/shared/thread-actions-utils.js
+++ b/lib/shared/thread-actions-utils.js
@@ -59,6 +59,7 @@
   +viewerID: ?string,
   +handleError?: () => mixed,
   +calendarQuery: CalendarQuery,
+  +usingOlmViaTunnelbrokerForDMs: boolean,
 };
 
 async function createRealThreadFromPendingThread({
@@ -69,6 +70,7 @@
   sourceMessageID,
   viewerID,
   calendarQuery,
+  usingOlmViaTunnelbrokerForDMs,
 }: CreateRealThreadParameters): Promise<string> {
   if (!threadIsPending(threadInfo.id)) {
     return threadInfo.id;
@@ -125,7 +127,11 @@
     );
     if (threadTypeIsThick(threadInfo.type)) {
       const type = assertThickThreadType(
-        pendingThreadType(otherMemberIDs.length, 'thick'),
+        pendingThreadType(
+          otherMemberIDs.length,
+          'thick',
+          usingOlmViaTunnelbrokerForDMs,
+        ),
       );
 
       invariant(
@@ -140,7 +146,11 @@
       });
     } else {
       const type = assertThinThreadType(
-        pendingThreadType(otherMemberIDs.length, 'thin'),
+        pendingThreadType(
+          otherMemberIDs.length,
+          'thin',
+          usingOlmViaTunnelbrokerForDMs,
+        ),
       );
 
       invariant(
diff --git a/lib/shared/thread-utils.js b/lib/shared/thread-utils.js
--- a/lib/shared/thread-utils.js
+++ b/lib/shared/thread-utils.js
@@ -15,6 +15,7 @@
 import ashoat from '../facts/ashoat.js';
 import genesis from '../facts/genesis.js';
 import { useLoggedInUserInfo } from '../hooks/account-hooks.js';
+import { useAllowOlmViaTunnelbrokerForDMs } from '../hooks/flag-hooks.js';
 import { extractKeyserverIDFromIDOptional } from '../keyserver-conn/keyserver-call-utils.js';
 import {
   hasPermission,
@@ -109,7 +110,6 @@
 import { entries, values } from '../utils/objects.js';
 import { useSelector } from '../utils/redux-utils.js';
 import { userSurfacedPermissionsFromRolePermissions } from '../utils/role-utils.js';
-import { usingOlmViaTunnelbrokerForDMs } from '../utils/services-utils.js';
 import { firstLine } from '../utils/string-utils.js';
 import {
   pendingThreadIDRegex,
@@ -601,13 +601,14 @@
   loggedInUserInfo: LoggedInUserInfo,
   userID: string,
   username: ?string,
+  allowOlmViaTunnelbrokerForDMs: boolean,
 ): PendingPersonalThread {
   const pendingPersonalThreadUserInfo = {
     id: userID,
     username: username,
   };
 
-  const threadType = usingOlmViaTunnelbrokerForDMs
+  const threadType = allowOlmViaTunnelbrokerForDMs
     ? threadTypes.PERSONAL
     : threadTypes.GENESIS_PERSONAL;
 
@@ -623,9 +624,15 @@
 function createPendingThreadItem(
   loggedInUserInfo: LoggedInUserInfo,
   user: UserIDAndUsername,
+  allowOlmViaTunnelbrokerForDMs: boolean,
 ): ChatThreadItem {
   const { threadInfo, pendingPersonalThreadUserInfo } =
-    createPendingPersonalThread(loggedInUserInfo, user.id, user.username);
+    createPendingPersonalThread(
+      loggedInUserInfo,
+      user.id,
+      user.username,
+      allowOlmViaTunnelbrokerForDMs,
+    );
 
   return {
     type: 'chatThreadItem',
@@ -694,6 +701,7 @@
 function pendingThreadType(
   numberOfOtherMembers: number,
   thickOrThin: 'thick' | 'thin',
+  usingOlmViaTunnelbrokerForDMs: boolean,
 ): 4 | 6 | 7 | 13 | 14 | 15 {
   if (usingOlmViaTunnelbrokerForDMs && thickOrThin === 'thick') {
     if (numberOfOtherMembers === 0) {
@@ -1244,6 +1252,7 @@
   const pendingToRealizedThreadIDs = useSelector(state =>
     pendingToRealizedThreadIDsSelector(state.threadStore.threadInfos),
   );
+  const usingOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs();
   return React.useCallback(
     (params: ExistingThreadInfoFinderParams): ?ThreadInfo => {
       if (!baseThreadInfo) {
@@ -1272,7 +1281,11 @@
 
       if (searching) {
         const pendingThinThreadID = getPendingThreadID(
-          pendingThreadType(userInfoInputArray.length, 'thin'),
+          pendingThreadType(
+            userInfoInputArray.length,
+            'thin',
+            usingOlmViaTunnelbrokerForDMs,
+          ),
           [...userInfoInputArray.map(user => user.id), viewerID],
           sourceMessageID,
         );
@@ -1282,7 +1295,11 @@
           return threadInfos[realizedThinThreadID];
         }
         const pendingThickThreadID = getPendingThreadID(
-          pendingThreadType(userInfoInputArray.length, 'thick'),
+          pendingThreadType(
+            userInfoInputArray.length,
+            'thick',
+            usingOlmViaTunnelbrokerForDMs,
+          ),
           [...userInfoInputArray.map(user => user.id), viewerID],
           sourceMessageID,
         );
@@ -1307,14 +1324,24 @@
       const updatedThread = searching
         ? createPendingThread({
             viewerID,
-            threadType: pendingThreadType(userInfoInputArray.length, 'thick'),
+            threadType: pendingThreadType(
+              userInfoInputArray.length,
+              'thick',
+              usingOlmViaTunnelbrokerForDMs,
+            ),
             members: [loggedInUserInfo, ...userInfoInputArray],
           })
         : baseThreadInfo;
 
       return updatedThread;
     },
-    [baseThreadInfo, threadInfos, loggedInUserInfo, pendingToRealizedThreadIDs],
+    [
+      baseThreadInfo,
+      threadInfos,
+      loggedInUserInfo,
+      usingOlmViaTunnelbrokerForDMs,
+      pendingToRealizedThreadIDs,
+    ],
   );
 }
 
@@ -1412,6 +1439,7 @@
   threadSearchResults: $ReadOnlySet<string>,
   usersSearchResults: $ReadOnlyArray<GlobalAccountUserInfo>,
   loggedInUserInfo: ?LoggedInUserInfo,
+  allowOlmViaTunnelbrokerForDMs: boolean,
 ): $ReadOnlyArray<ChatThreadItem> {
   if (!searchText) {
     return chatListData.filter(
@@ -1444,7 +1472,11 @@
   if (loggedInUserInfo) {
     chatItems.push(
       ...usersSearchResults.map(user =>
-        createPendingThreadItem(loggedInUserInfo, user),
+        createPendingThreadItem(
+          loggedInUserInfo,
+          user,
+          allowOlmViaTunnelbrokerForDMs,
+        ),
       ),
     );
   }
@@ -1677,6 +1709,8 @@
 
   const usersWithPersonalThread = useSelector(usersWithPersonalThreadSelector);
 
+  const allowOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs();
+
   return React.useMemo(() => {
     if (!loggedInUserInfo || !userID || !username) {
       return null;
@@ -1701,10 +1735,12 @@
       loggedInUserInfo,
       userID,
       username,
+      allowOlmViaTunnelbrokerForDMs,
     );
 
     return pendingPersonalThreadInfo;
   }, [
+    allowOlmViaTunnelbrokerForDMs,
     isViewerProfile,
     loggedInUserInfo,
     personalThreadInfos,
diff --git a/lib/utils/services-utils.js b/lib/utils/services-utils.js
--- a/lib/utils/services-utils.js
+++ b/lib/utils/services-utils.js
@@ -18,11 +18,6 @@
 // an authoritative keyserver for things like DMs.
 const relyingOnAuthoritativeKeyserver = true;
 
-// If this is true, then DM creation will use E2EE DMs encrypted via Olm and
-// brokered by Tunnelbroker, instead of creating chats under GENESIS on the
-// authoritative keyserver.
-const usingOlmViaTunnelbrokerForDMs = false;
-
 function handleHTTPResponseError(response: Response): void {
   if (!response.ok) {
     const { status, statusText } = response;
@@ -52,7 +47,6 @@
   usingCommServicesAccessToken,
   supportingMultipleKeyservers,
   relyingOnAuthoritativeKeyserver,
-  usingOlmViaTunnelbrokerForDMs,
   createHTTPAuthorizationHeader,
   createDefaultHTTPRequestHeaders,
 };
diff --git a/native/chat/chat-thread-list.react.js b/native/chat/chat-thread-list.react.js
--- a/native/chat/chat-thread-list.react.js
+++ b/native/chat/chat-thread-list.react.js
@@ -23,6 +23,7 @@
 import { useSharedValue } from 'react-native-reanimated';
 
 import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js';
+import { useAllowOlmViaTunnelbrokerForDMs } from 'lib/hooks/flag-hooks.js';
 import { useThreadListSearch } from 'lib/hooks/thread-search-hooks.js';
 import {
   type ChatThreadItem,
@@ -35,7 +36,6 @@
 import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js';
 import { threadTypes } from 'lib/types/thread-types-enum.js';
 import type { UserInfo } from 'lib/types/user-types.js';
-import { usingOlmViaTunnelbrokerForDMs } from 'lib/utils/services-utils.js';
 
 import { ChatThreadListItem } from './chat-thread-list-item.react.js';
 import ChatThreadListSearch from './chat-thread-list-search.react.js';
@@ -132,6 +132,8 @@
     [],
   );
 
+  const usingOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs();
+
   const composeThread = React.useCallback(() => {
     if (!loggedInUserInfo) {
       return;
@@ -145,7 +147,7 @@
       members: [loggedInUserInfo],
     });
     navigateToThread({ threadInfo, searching: true });
-  }, [loggedInUserInfo, navigateToThread]);
+  }, [loggedInUserInfo, navigateToThread, usingOlmViaTunnelbrokerForDMs]);
 
   const onSearchFocus = React.useCallback(() => {
     if (searchStatus !== 'inactive') {
@@ -277,6 +279,8 @@
     ],
   );
 
+  const allowOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs();
+
   const listData: $ReadOnlyArray<Item> = React.useMemo(() => {
     const chatThreadItems = getThreadListSearchResults(
       boundChatListData,
@@ -285,6 +289,7 @@
       threadSearchResults,
       usersSearchResults,
       loggedInUserInfo,
+      allowOlmViaTunnelbrokerForDMs,
     );
 
     const chatItems: Item[] = [...chatThreadItems];
@@ -299,6 +304,7 @@
 
     return chatItems;
   }, [
+    allowOlmViaTunnelbrokerForDMs,
     boundChatListData,
     emptyItem,
     filterThreads,
diff --git a/native/chat/compose-thread-button.react.js b/native/chat/compose-thread-button.react.js
--- a/native/chat/compose-thread-button.react.js
+++ b/native/chat/compose-thread-button.react.js
@@ -4,9 +4,9 @@
 import { StyleSheet } from 'react-native';
 
 import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js';
+import { useAllowOlmViaTunnelbrokerForDMs } from 'lib/hooks/flag-hooks.js';
 import { createPendingThread } from 'lib/shared/thread-utils.js';
 import { threadTypes } from 'lib/types/thread-types-enum.js';
-import { usingOlmViaTunnelbrokerForDMs } from 'lib/utils/services-utils.js';
 
 import type { ChatNavigationProp } from './chat.react.js';
 import Button from '../components/button.react.js';
@@ -20,6 +20,7 @@
 function ComposeThreadButton(props: Props) {
   const { navigate } = props;
   const loggedInUserInfo = useLoggedInUserInfo();
+  const usingOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs();
   const onPress = React.useCallback(() => {
     if (!loggedInUserInfo) {
       return;
@@ -38,7 +39,7 @@
         searching: true,
       },
     });
-  }, [navigate, loggedInUserInfo]);
+  }, [loggedInUserInfo, usingOlmViaTunnelbrokerForDMs, navigate]);
 
   const { listForegroundSecondaryLabel } = useColors();
   return (
diff --git a/native/chat/message-list-container.react.js b/native/chat/message-list-container.react.js
--- a/native/chat/message-list-container.react.js
+++ b/native/chat/message-list-container.react.js
@@ -7,6 +7,7 @@
 import { Text, View } from 'react-native';
 
 import genesis from 'lib/facts/genesis.js';
+import { useAllowOlmViaTunnelbrokerForDMs } from 'lib/hooks/flag-hooks.js';
 import { threadInfoSelector } from 'lib/selectors/thread-selectors.js';
 import { userInfoSelectorForPotentialMembers } from 'lib/selectors/user-selectors.js';
 import {
@@ -95,6 +96,7 @@
   // withOverlayContext
   +overlayContext: ?OverlayContextType,
   +measureMessages: MessagesMeasurer,
+  +usingOlmViaTunnelbrokerForDMs: boolean,
 };
 type State = {
   +listDataWithHeights: ?$ReadOnlyArray<ChatMessageItemWithHeight>,
@@ -170,6 +172,7 @@
             childThreadType={pendingThreadType(
               userInfoInputArray.length,
               'thick',
+              this.props.usingOlmViaTunnelbrokerForDMs,
             )}
           />
         );
@@ -180,6 +183,7 @@
             childThreadType={pendingThreadType(
               userInfoInputArray.length,
               'thin',
+              this.props.usingOlmViaTunnelbrokerForDMs,
             )}
           />
         );
@@ -417,6 +421,8 @@
       colors.panelBackgroundLabel,
     ]);
 
+    const usingOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs();
+
     return (
       <MessageListContextProvider threadInfo={threadInfo}>
         {pinnedCountBanner}
@@ -435,6 +441,7 @@
           styles={styles}
           overlayContext={overlayContext}
           measureMessages={measureMessages}
+          usingOlmViaTunnelbrokerForDMs={usingOlmViaTunnelbrokerForDMs}
         />
       </MessageListContextProvider>
     );
diff --git a/native/input/input-state-container.react.js b/native/input/input-state-container.react.js
--- a/native/input/input-state-container.react.js
+++ b/native/input/input-state-container.react.js
@@ -30,6 +30,7 @@
   useBlobServiceUpload,
 } from 'lib/actions/upload-actions.js';
 import commStaffCommunity from 'lib/facts/comm-staff-community.js';
+import { useAllowOlmViaTunnelbrokerForDMs } from 'lib/hooks/flag-hooks.js';
 import { useNewThickThread } from 'lib/hooks/thread-hooks.js';
 import type {
   CallSingleKeyserverEndpointOptions,
@@ -175,6 +176,7 @@
   ) => Promise<NewThreadResult>,
   +newThickThread: (request: NewThickThreadRequest) => Promise<string>,
   +textMessageCreationSideEffectsFunc: CreationSideEffectsFunc<RawTextMessageInfo>,
+  +usingOlmViaTunnelbrokerForDMs: boolean,
 };
 type State = {
   +pendingUploads: PendingMultimediaUploads,
@@ -611,6 +613,7 @@
         sourceMessageID: threadInfo.sourceMessageID,
         viewerID: this.props.viewerID,
         calendarQuery,
+        usingOlmViaTunnelbrokerForDMs: this.props.usingOlmViaTunnelbrokerForDMs,
       });
       this.pendingThreadCreations.set(threadInfo.id, threadCreationPromise);
     }
@@ -1819,6 +1822,7 @@
     const textMessageCreationSideEffectsFunc =
       useMessageCreationSideEffectsFunc<RawTextMessageInfo>(messageTypes.TEXT);
     const processAndSendDMOperation = useProcessAndSendDMOperation();
+    const usingOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs();
 
     return (
       <InputStateContainer
@@ -1840,6 +1844,7 @@
         dispatch={dispatch}
         staffCanSee={staffCanSee}
         textMessageCreationSideEffectsFunc={textMessageCreationSideEffectsFunc}
+        usingOlmViaTunnelbrokerForDMs={usingOlmViaTunnelbrokerForDMs}
       />
     );
   });
diff --git a/web/chat/thread-list-provider.js b/web/chat/thread-list-provider.js
--- a/web/chat/thread-list-provider.js
+++ b/web/chat/thread-list-provider.js
@@ -4,6 +4,7 @@
 import * as React from 'react';
 
 import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js';
+import { useAllowOlmViaTunnelbrokerForDMs } from 'lib/hooks/flag-hooks.js';
 import { useThreadListSearch } from 'lib/hooks/thread-search-hooks.js';
 import {
   type ChatThreadItem,
@@ -179,6 +180,7 @@
   );
   const threadFilter =
     activeTab === 'Muted' ? threadInBackgroundChatList : threadInHomeChatList;
+  const allowOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs();
   const chatListDataWithoutFilter = getThreadListSearchResults(
     chatListData,
     searchText,
@@ -186,6 +188,7 @@
     threadSearchResults,
     usersSearchResults,
     loggedInUserInfo,
+    allowOlmViaTunnelbrokerForDMs,
   );
   const activeTopLevelChatThreadItem = useChatThreadItem(
     activeTopLevelThreadInfo,
diff --git a/web/input/input-state-container.react.js b/web/input/input-state-container.react.js
--- a/web/input/input-state-container.react.js
+++ b/web/input/input-state-container.react.js
@@ -40,6 +40,7 @@
 } from 'lib/components/modal-provider.react.js';
 import blobService from 'lib/facts/blob-service.js';
 import commStaffCommunity from 'lib/facts/comm-staff-community.js';
+import { useAllowOlmViaTunnelbrokerForDMs } from 'lib/hooks/flag-hooks.js';
 import { useNewThickThread } from 'lib/hooks/thread-hooks.js';
 import { useLegacyAshoatKeyserverCall } from 'lib/keyserver-conn/legacy-keyserver-call.js';
 import { getNextLocalUploadID } from 'lib/media/media-utils.js';
@@ -177,6 +178,7 @@
   +unregisterSendCallback: (() => mixed) => void,
   +textMessageCreationSideEffectsFunc: CreationSideEffectsFunc<RawTextMessageInfo>,
   +identityContext: ?IdentityClientContextType,
+  +usingOlmViaTunnelbrokerForDMs: boolean,
 };
 type WritableState = {
   pendingUploads: {
@@ -610,6 +612,7 @@
         sourceMessageID: threadInfo.sourceMessageID,
         viewerID: this.props.viewerID,
         calendarQuery,
+        usingOlmViaTunnelbrokerForDMs: this.props.usingOlmViaTunnelbrokerForDMs,
       });
       this.pendingThreadCreations.set(threadInfo.id, threadCreationPromise);
     }
@@ -1771,6 +1774,7 @@
     );
     const textMessageCreationSideEffectsFunc =
       useMessageCreationSideEffectsFunc<RawTextMessageInfo>(messageTypes.TEXT);
+    const usingOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs();
 
     return (
       <InputStateContainer
@@ -1797,6 +1801,7 @@
         unregisterSendCallback={unregisterSendCallback}
         textMessageCreationSideEffectsFunc={textMessageCreationSideEffectsFunc}
         identityContext={identityContext}
+        usingOlmViaTunnelbrokerForDMs={usingOlmViaTunnelbrokerForDMs}
       />
     );
   });
diff --git a/web/utils/thread-utils.js b/web/utils/thread-utils.js
--- a/web/utils/thread-utils.js
+++ b/web/utils/thread-utils.js
@@ -4,6 +4,7 @@
 import * as React from 'react';
 
 import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js';
+import { useAllowOlmViaTunnelbrokerForDMs } from 'lib/hooks/flag-hooks.js';
 import { threadInfoSelector } from 'lib/selectors/thread-selectors.js';
 import { userInfoSelectorForPotentialMembers } from 'lib/selectors/user-selectors.js';
 import {
@@ -13,7 +14,6 @@
 import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js';
 import { threadTypes } from 'lib/types/thread-types-enum.js';
 import type { AccountUserInfo } from 'lib/types/user-types.js';
-import { usingOlmViaTunnelbrokerForDMs } from 'lib/utils/services-utils.js';
 
 import { useSelector } from '../redux/redux-utils.js';
 
@@ -54,6 +54,8 @@
     }),
   );
 
+  const usingOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs();
+
   const threadType = usingOlmViaTunnelbrokerForDMs
     ? threadTypes.PRIVATE
     : threadTypes.GENESIS_PRIVATE;