diff --git a/lib/shared/dm-ops/add-members-spec.js b/lib/shared/dm-ops/add-members-spec.js
--- a/lib/shared/dm-ops/add-members-spec.js
+++ b/lib/shared/dm-ops/add-members-spec.js
@@ -62,6 +62,7 @@
       return {
         rawMessageInfos: [],
         updateInfos: [],
+        blobOps: [],
       };
     }
 
@@ -129,6 +130,7 @@
     return {
       rawMessageInfos,
       updateInfos,
+      blobOps: [],
     };
   },
   canBeProcessed: async (
diff --git a/lib/shared/dm-ops/add-viewer-to-thread-members-spec.js b/lib/shared/dm-ops/add-viewer-to-thread-members-spec.js
--- a/lib/shared/dm-ops/add-viewer-to-thread-members-spec.js
+++ b/lib/shared/dm-ops/add-viewer-to-thread-members-spec.js
@@ -67,6 +67,7 @@
         return {
           rawMessageInfos: [],
           updateInfos: [],
+          blobOps: [],
         };
       }
 
@@ -127,7 +128,11 @@
           rawEntryInfos: [],
         },
       ];
-      return { rawMessageInfos, updateInfos };
+      return {
+        rawMessageInfos,
+        updateInfos,
+        blobOps: [],
+      };
     },
     canBeProcessed: async (
       dmOperation: DMAddViewerToThreadMembersOperation,
diff --git a/lib/shared/dm-ops/change-thread-read-status-spec.js b/lib/shared/dm-ops/change-thread-read-status-spec.js
--- a/lib/shared/dm-ops/change-thread-read-status-spec.js
+++ b/lib/shared/dm-ops/change-thread-read-status-spec.js
@@ -33,6 +33,7 @@
         return {
           rawMessageInfos: [],
           updateInfos: [],
+          blobOps: [],
         };
       }
 
@@ -48,6 +49,7 @@
       return {
         rawMessageInfos: [],
         updateInfos,
+        blobOps: [],
       };
     },
     canBeProcessed: async (
diff --git a/lib/shared/dm-ops/change-thread-settings-spec.js b/lib/shared/dm-ops/change-thread-settings-spec.js
--- a/lib/shared/dm-ops/change-thread-settings-spec.js
+++ b/lib/shared/dm-ops/change-thread-settings-spec.js
@@ -161,6 +161,7 @@
       return {
         rawMessageInfos,
         updateInfos,
+        blobOps: [],
       };
     },
     canBeProcessed: async (
diff --git a/lib/shared/dm-ops/change-thread-subscription.js b/lib/shared/dm-ops/change-thread-subscription.js
--- a/lib/shared/dm-ops/change-thread-subscription.js
+++ b/lib/shared/dm-ops/change-thread-subscription.js
@@ -27,6 +27,7 @@
         return {
           updateInfos: [],
           rawMessageInfos: [],
+          blobOps: [],
         };
       }
 
@@ -75,7 +76,11 @@
         },
       ];
 
-      return { updateInfos, rawMessageInfos: [] };
+      return {
+        updateInfos,
+        rawMessageInfos: [],
+        blobOps: [],
+      };
     },
     canBeProcessed: async (
       dmOperation: DMChangeThreadSubscriptionOperation,
diff --git a/lib/shared/dm-ops/create-entry-spec.js b/lib/shared/dm-ops/create-entry-spec.js
--- a/lib/shared/dm-ops/create-entry-spec.js
+++ b/lib/shared/dm-ops/create-entry-spec.js
@@ -72,6 +72,7 @@
     return {
       rawMessageInfos,
       updateInfos: [entryUpdateInfo],
+      blobOps: [],
     };
   },
   canBeProcessed: async (
diff --git a/lib/shared/dm-ops/create-sidebar-spec.js b/lib/shared/dm-ops/create-sidebar-spec.js
--- a/lib/shared/dm-ops/create-sidebar-spec.js
+++ b/lib/shared/dm-ops/create-sidebar-spec.js
@@ -181,6 +181,7 @@
       return {
         rawMessageInfos: [], // included in updateInfos below
         updateInfos: [threadJoinUpdateInfo],
+        blobOps: [],
       };
     },
     canBeProcessed: async (
diff --git a/lib/shared/dm-ops/create-thread-spec.js b/lib/shared/dm-ops/create-thread-spec.js
--- a/lib/shared/dm-ops/create-thread-spec.js
+++ b/lib/shared/dm-ops/create-thread-spec.js
@@ -209,6 +209,7 @@
       return {
         rawMessageInfos: [], // included in updateInfos below
         updateInfos: [threadJoinUpdateInfo],
+        blobOps: [],
       };
     },
     canBeProcessed: async () => {
diff --git a/lib/shared/dm-ops/delete-entry-spec.js b/lib/shared/dm-ops/delete-entry-spec.js
--- a/lib/shared/dm-ops/delete-entry-spec.js
+++ b/lib/shared/dm-ops/delete-entry-spec.js
@@ -66,6 +66,7 @@
       return {
         rawMessageInfos,
         updateInfos: [],
+        blobOps: [],
       };
     }
 
@@ -94,6 +95,7 @@
     return {
       rawMessageInfos,
       updateInfos: [entryUpdateInfo],
+      blobOps: [],
     };
   },
   canBeProcessed: async (
diff --git a/lib/shared/dm-ops/edit-entry-spec.js b/lib/shared/dm-ops/edit-entry-spec.js
--- a/lib/shared/dm-ops/edit-entry-spec.js
+++ b/lib/shared/dm-ops/edit-entry-spec.js
@@ -67,6 +67,7 @@
       return {
         rawMessageInfos,
         updateInfos: [],
+        blobOps: [],
       };
     }
 
@@ -95,6 +96,7 @@
     return {
       rawMessageInfos,
       updateInfos: [entryUpdateInfo],
+      blobOps: [],
     };
   },
   canBeProcessed: async (
diff --git a/lib/shared/dm-ops/join-thread-spec.js b/lib/shared/dm-ops/join-thread-spec.js
--- a/lib/shared/dm-ops/join-thread-spec.js
+++ b/lib/shared/dm-ops/join-thread-spec.js
@@ -59,6 +59,7 @@
       return {
         rawMessageInfos: [],
         updateInfos: [],
+        blobOps: [],
       };
     }
 
@@ -78,6 +79,7 @@
       return {
         rawMessageInfos: joinThreadMessageInfos,
         updateInfos: [],
+        blobOps: [],
       };
     }
 
@@ -164,6 +166,7 @@
     return {
       rawMessageInfos,
       updateInfos,
+      blobOps: [],
     };
   },
   canBeProcessed: async (
diff --git a/lib/shared/dm-ops/leave-thread-spec.js b/lib/shared/dm-ops/leave-thread-spec.js
--- a/lib/shared/dm-ops/leave-thread-spec.js
+++ b/lib/shared/dm-ops/leave-thread-spec.js
@@ -98,6 +98,7 @@
             threadInfos,
           ),
         ],
+        blobOps: [],
       };
     }
 
@@ -105,6 +106,7 @@
       return {
         rawMessageInfos,
         updateInfos: [],
+        blobOps: [],
       };
     }
 
@@ -137,6 +139,7 @@
           threadInfo: updatedThreadInfo,
         },
       ],
+      blobOps: [],
     };
   },
   canBeProcessed: async (
diff --git a/lib/shared/dm-ops/remove-members-spec.js b/lib/shared/dm-ops/remove-members-spec.js
--- a/lib/shared/dm-ops/remove-members-spec.js
+++ b/lib/shared/dm-ops/remove-members-spec.js
@@ -110,6 +110,7 @@
       return {
         rawMessageInfos,
         updateInfos,
+        blobOps: [],
       };
     },
     canBeProcessed: async (
diff --git a/lib/shared/dm-ops/send-edit-message-spec.js b/lib/shared/dm-ops/send-edit-message-spec.js
--- a/lib/shared/dm-ops/send-edit-message-spec.js
+++ b/lib/shared/dm-ops/send-edit-message-spec.js
@@ -44,6 +44,7 @@
       return {
         rawMessageInfos,
         updateInfos: [],
+        blobOps: [],
       };
     },
     canBeProcessed: async (
diff --git a/lib/shared/dm-ops/send-multimedia-message-spec.js b/lib/shared/dm-ops/send-multimedia-message-spec.js
--- a/lib/shared/dm-ops/send-multimedia-message-spec.js
+++ b/lib/shared/dm-ops/send-multimedia-message-spec.js
@@ -45,6 +45,7 @@
       return {
         rawMessageInfos,
         updateInfos,
+        blobOps: [],
       };
     },
     canBeProcessed: async (
diff --git a/lib/shared/dm-ops/send-reaction-message-spec.js b/lib/shared/dm-ops/send-reaction-message-spec.js
--- a/lib/shared/dm-ops/send-reaction-message-spec.js
+++ b/lib/shared/dm-ops/send-reaction-message-spec.js
@@ -52,6 +52,7 @@
       return {
         rawMessageInfos,
         updateInfos: [],
+        blobOps: [],
       };
     },
     canBeProcessed: async (
diff --git a/lib/shared/dm-ops/send-text-message-spec.js b/lib/shared/dm-ops/send-text-message-spec.js
--- a/lib/shared/dm-ops/send-text-message-spec.js
+++ b/lib/shared/dm-ops/send-text-message-spec.js
@@ -43,6 +43,7 @@
       return {
         rawMessageInfos,
         updateInfos,
+        blobOps: [],
       };
     },
     canBeProcessed: async (
diff --git a/lib/shared/dm-ops/update-relationship-spec.js b/lib/shared/dm-ops/update-relationship-spec.js
--- a/lib/shared/dm-ops/update-relationship-spec.js
+++ b/lib/shared/dm-ops/update-relationship-spec.js
@@ -84,6 +84,7 @@
       return {
         rawMessageInfos,
         updateInfos: [],
+        blobOps: [],
       };
     },
     canBeProcessed: async (
diff --git a/lib/types/dm-ops.js b/lib/types/dm-ops.js
--- a/lib/types/dm-ops.js
+++ b/lib/types/dm-ops.js
@@ -3,6 +3,7 @@
 import t, { type TInterface, type TUnion } from 'tcomb';
 
 import { clientAvatarValidator, type ClientAvatar } from './avatar-types.js';
+import type { BlobOperation } from './holder-types.js';
 import { type Media, mediaValidator } from './media-types.js';
 import type { RawMessageInfo } from './message-types.js';
 import type { RelationshipOperation } from './messages/update-relationship.js';
@@ -509,9 +510,15 @@
   dmUpdateRelationshipOperationValidator,
 ]);
 
+export type DMBlobOperation = $ReadOnly<{
+  ...BlobOperation,
+  +dmOpType: 'inbound_only' | 'outbound_only' | 'inbound_and_outbound',
+}>;
+
 export type DMOperationResult = {
   rawMessageInfos: Array<RawMessageInfo>,
   updateInfos: Array<ClientUpdateInfo>,
+  blobOps: Array<DMBlobOperation>,
 };
 
 export const processDMOpsActionType = 'PROCESS_DM_OPS';
diff --git a/lib/types/holder-types.js b/lib/types/holder-types.js
--- a/lib/types/holder-types.js
+++ b/lib/types/holder-types.js
@@ -17,3 +17,8 @@
     +[blobHash: string]: HolderInfo,
   },
 };
+
+export type BlobOperation = {
+  +type: 'establish_holder' | 'remove_holder',
+  +blobHash: string,
+};