diff --git a/lib/actions/user-actions.js b/lib/actions/user-actions.js
--- a/lib/actions/user-actions.js
+++ b/lib/actions/user-actions.js
@@ -76,6 +76,7 @@
 } from '../types/tunnelbroker/peer-to-peer-message-types.js';
 import {
   userActionsP2PMessageTypes,
+  type PrimaryDeviceLogoutP2PMessage,
   type SecondaryDeviceLogoutP2PMessage,
 } from '../types/tunnelbroker/user-actions-peer-to-peer-message-types.js';
 import type {
@@ -253,11 +254,82 @@
 });
 
 function usePrimaryDeviceLogOut(): () => Promise<LogOutResult> {
+  const identityContext = React.useContext(IdentityClientContext);
+  if (!identityContext) {
+    throw new Error('Identity service client is not initialized');
+  }
+
+  const { sendMessage } = useTunnelbroker();
   const broadcastDeviceListUpdates = useBroadcastDeviceListUpdates();
   const foreignPeerDevices = useSelector(getForeignPeerDevices);
 
   const logOut = useLogOut(primaryDeviceLogOutOptions);
   return React.useCallback(async () => {
+    const { identityClient, getAuthMetadata } = identityContext;
+    const authMetadata = await getAuthMetadata();
+    const { userID, deviceID: thisDeviceID } = authMetadata;
+    if (!thisDeviceID || !userID) {
+      throw new Error('No auth metadata');
+    }
+    const {
+      devices: [primaryDeviceID, ...secondaryDevices],
+    } = await fetchLatestDeviceList(identityClient, userID);
+    if (thisDeviceID !== primaryDeviceID) {
+      throw new Error('Used primary device logout on a non-primary device');
+    }
+
+    // create and send Olm Tunnelbroker messages to secondaryDevices
+    const { olmAPI } = getConfig();
+    await olmAPI.initializeCryptoAccount();
+    const messageContents: PrimaryDeviceLogoutP2PMessage = {
+      type: userActionsP2PMessageTypes.LOG_OUT_PRIMARY_DEVICE,
+    };
+    for (const deviceID of secondaryDevices) {
+      try {
+        const encryptedData = await olmAPI.encrypt(
+          JSON.stringify(messageContents),
+          deviceID,
+        );
+        const encryptedMessage: EncryptedMessage = {
+          type: peerToPeerMessageTypes.ENCRYPTED_MESSAGE,
+          senderInfo: { deviceID: thisDeviceID, userID },
+          encryptedData,
+        };
+        await sendMessage({
+          deviceID,
+          payload: JSON.stringify(encryptedMessage),
+        });
+      } catch {
+        try {
+          await createOlmSessionWithPeer(
+            authMetadata,
+            identityClient,
+            sendMessage,
+            userID,
+            deviceID,
+          );
+          const encryptedData = await olmAPI.encrypt(
+            JSON.stringify(messageContents),
+            deviceID,
+          );
+          const encryptedMessage: EncryptedMessage = {
+            type: peerToPeerMessageTypes.ENCRYPTED_MESSAGE,
+            senderInfo: { deviceID: thisDeviceID, userID },
+            encryptedData,
+          };
+          await sendMessage({
+            deviceID,
+            payload: JSON.stringify(encryptedMessage),
+          });
+        } catch (err) {
+          console.warn(
+            `Error sending primary device logout message to device ${deviceID}:`,
+            err,
+          );
+        }
+      }
+    }
+
     // - logOut() performs device list update by calling Identity RPC
     // - broadcastDeviceListUpdates asks peers to download it from identity
     // so we need to call them in this order to make sure peers have latest
@@ -267,7 +339,13 @@
     const logOutResult = await logOut();
     await broadcastDeviceListUpdates(foreignPeerDevices);
     return logOutResult;
-  }, [broadcastDeviceListUpdates, foreignPeerDevices, logOut]);
+  }, [
+    broadcastDeviceListUpdates,
+    foreignPeerDevices,
+    identityContext,
+    logOut,
+    sendMessage,
+  ]);
 }
 
 const secondaryDeviceLogOutOptions = Object.freeze({