diff --git a/web/shared-worker/worker/process-operations.js b/web/shared-worker/worker/process-operations.js
--- a/web/shared-worker/worker/process-operations.js
+++ b/web/shared-worker/worker/process-operations.js
@@ -3,6 +3,7 @@
 import type { ClientDBMessageSearchStoreOperation } from 'lib/message-search-types.js';
 import type { ClientDBAuxUserStoreOperation } from 'lib/ops/aux-user-store-ops.js';
 import type { ClientDBCommunityStoreOperation } from 'lib/ops/community-store-ops.js';
+import type { ClientDBDMOperationStoreOperation } from 'lib/ops/dm-operations-store-ops.js';
 import type { ClientDBEntryStoreOperation } from 'lib/ops/entries-store-ops.js';
 import type { ClientDBIntegrityStoreOperation } from 'lib/ops/integrity-store-ops.js';
 import type { ClientDBKeyserverStoreOperation } from 'lib/ops/keyserver-store-ops.js';
@@ -378,6 +379,7 @@
     outboundP2PMessages,
     entryStoreOperations,
     messageSearchStoreOperations,
+    dmOperationStoreOperations,
   } = storeOperations;
 
   try {
@@ -485,6 +487,13 @@
         module,
       );
     }
+    if (dmOperationStoreOperations && dmOperationStoreOperations.length > 0) {
+      processDMOperationStoreOperations(
+        sqliteQueryExecutor,
+        dmOperationStoreOperations,
+        module,
+      );
+    }
     sqliteQueryExecutor.commitTransaction();
   } catch (e) {
     sqliteQueryExecutor.rollbackTransaction();
@@ -574,7 +583,7 @@
         const { id, entry } = operation.payload;
         sqliteQueryExecutor.replaceEntry({ id, entry });
       } else {
-        throw new Error('Unsupported thread activity operation');
+        throw new Error('Unsupported entry store operation');
       }
     } catch (e) {
       throw new Error(
@@ -617,6 +626,41 @@
   }
 }
 
+function processDMOperationStoreOperations(
+  sqliteQueryExecutor: SQLiteQueryExecutor,
+  operations: $ReadOnlyArray<ClientDBDMOperationStoreOperation>,
+  module: EmscriptenModule,
+) {
+  for (const operation: ClientDBDMOperationStoreOperation of operations) {
+    try {
+      if (operation.type === 'remove_all_dm_operations') {
+        sqliteQueryExecutor.removeAllDMOperations();
+      } else if (operation.type === 'remove_dm_operations') {
+        const { ids } = operation.payload;
+        sqliteQueryExecutor.removeDMOperations(ids);
+      } else if (operation.type === 'replace_dm_operation') {
+        const { id, type, operation: dmOperation } = operation.payload;
+        sqliteQueryExecutor.replaceDMOperation({
+          id,
+          type,
+          operation: dmOperation,
+        });
+      } else {
+        throw new Error('Unsupported DMOperation operation');
+      }
+    } catch (e) {
+      throw new Error(
+        `Error while processing ${
+          operation.type
+        } DMOperation operation: ${getProcessingStoreOpsExceptionMessage(
+          e,
+          module,
+        )}`,
+      );
+    }
+  }
+}
+
 function getClientStoreFromQueryExecutor(
   sqliteQueryExecutor: SQLiteQueryExecutor,
 ): ClientDBStore {
diff --git a/web/shared-worker/worker/shared-worker.js b/web/shared-worker/worker/shared-worker.js
--- a/web/shared-worker/worker/shared-worker.js
+++ b/web/shared-worker/worker/shared-worker.js
@@ -300,6 +300,16 @@
         message.messageIDs,
       ),
     };
+  } else if (
+    message.type === workerRequestMessageTypes.GET_DM_OPERATIONS_BY_TYPE
+  ) {
+    const operations = sqliteQueryExecutor.getDMOperationsByType(
+      message.operationType,
+    );
+    return {
+      type: workerResponseMessageTypes.DM_OPERATIONS,
+      operations,
+    };
   }
 
   // write operations
diff --git a/web/types/worker-types.js b/web/types/worker-types.js
--- a/web/types/worker-types.js
+++ b/web/types/worker-types.js
@@ -1,5 +1,6 @@
 // @flow
 
+import type { ClientDBDMOperation } from 'lib/ops/dm-operations-store-ops.js';
 import type { AuthMetadata } from 'lib/shared/identity-client-context.js';
 import type {
   PickledOLMAccount,
@@ -50,6 +51,7 @@
   RESET_OUTBOUND_P2P_MESSAGES: 24,
   FETCH_MESSAGES: 25,
   GET_INBOUND_P2P_MESSAGES_BY_ID: 26,
+  GET_DM_OPERATIONS_BY_TYPE: 27,
 });
 
 export const workerWriteRequests: $ReadOnlyArray<number> = [
@@ -231,6 +233,11 @@
   +messageIDs: $ReadOnlyArray<string>,
 };
 
+export type GetDMOperationsByTypeRequestMessage = {
+  +type: 27,
+  +operationType: string,
+};
+
 export type WorkerRequestMessage =
   | PingWorkerRequestMessage
   | InitWorkerRequestMessage
@@ -258,7 +265,8 @@
   | SearchMessagesRequestMessage
   | ResetOutboundP2PMessagesRequestMessage
   | FetchMessagesRequestMessage
-  | GetInboundP2PMessagesByIDRequestMessage;
+  | GetInboundP2PMessagesByIDRequestMessage
+  | GetDMOperationsByTypeRequestMessage;
 
 export type WorkerRequestProxyMessage = {
   +id: number,
@@ -277,6 +285,7 @@
   GET_OUTBOUND_P2P_MESSAGES: 7,
   GET_MESSAGES: 8,
   RESET_OUTBOUND_P2P_MESSAGES: 9,
+  DM_OPERATIONS: 10,
 });
 
 export type PongWorkerResponseMessage = {
@@ -329,6 +338,11 @@
   +messageIDs: $ReadOnlyArray<string>,
 };
 
+export type DMOperationsResponseMessage = {
+  +type: 10,
+  +operations: $ReadOnlyArray<ClientDBDMOperation>,
+};
+
 export type WorkerResponseMessage =
   | PongWorkerResponseMessage
   | ClientStoreResponseMessage
@@ -339,7 +353,8 @@
   | GetInboundP2PMessagesResponseMessage
   | GetOutboundP2PMessagesResponseMessage
   | GetMessagesResponse
-  | ResetOutboundP2PMessagesResponseMessage;
+  | ResetOutboundP2PMessagesResponseMessage
+  | DMOperationsResponseMessage;
 
 export type WorkerResponseProxyMessage = {
   +id?: number,