-
Notifications
You must be signed in to change notification settings - Fork 74
add message deletion #2772
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
add message deletion #2772
Conversation
How to use the Graphite Merge QueueAdd the label mergequeue to this PR to add it to the merge queue. You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. |
|
Claude finished @mchenani's task —— View job Code Review — January 6, 2026, 17:02 UTCReviewed deletion handling across core, DB, and bindings. Here are unique findings not covered in existing comments: Critical Issues1. Inconsistent deletion validation logic between send and receive paths (xmtp_mls/src/groups/mod.rs:716, xmtp_mls/src/groups/mls_sync.rs:1336) When sending a deletion, both
Recommendation: In the out-of-order case, either (a) reject all deletions when the target message is missing, or (b) defer validation and re-check deletability when the target message eventually arrives. 2. Missing transaction boundary in The sequence is:
If step 3 fails (DB error), the local state becomes inconsistent:
Recommendation: Either use a database transaction encompassing both the store and a send callback, or pre-generate the deletion message ID deterministically so failures can be detected and retried. Design Concerns3. The code marks Recommendation: Consider making 4. Deletion ID collision in The function uses deletions.into_iter()
.map(|deletion| (deletion.deleted_message_id.clone(), deletion))
.collect()If multiple deletions exist for the same message (e.g., sender deletes, then admin also deletes), only the last one survives. The iteration order from the DB is not guaranteed, so this is non-deterministic. While the current schema doesn't explicitly prevent multiple deletion records, the behavior is inconsistent. Recommendation: Either enforce uniqueness in the schema (unique constraint on Test Coverage Gaps5. Missing validation tests (87.25% coverage, 97 lines missing) Based on the codecov report, several critical paths lack coverage:
Recommendation: Add tests for:
Minor Issues6. Hex encoding inconsistency (xmtp_mls/src/groups/mod.rs:721) The wire format uses hex-encoded string ( Recommendation: Document this clearly in both the proto definition and the DB schema comments, or consider standardizing on one representation. 7. The codec has Recommendation: Either add SummaryThe implementation is generally solid with good test coverage for happy paths. The main concerns are around out-of-order deletion authorization, transaction boundaries, and the deletability of unknown content types. These issues could lead to security vulnerabilities or data inconsistencies in edge cases. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2772 +/- ##
==========================================
+ Coverage 74.24% 74.29% +0.04%
==========================================
Files 413 416 +3
Lines 53201 53933 +732
==========================================
+ Hits 39501 40071 +570
- Misses 13700 13862 +162 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
# Conflicts: # bindings_ffi/src/message.rs # bindings_node/src/content_types/decoded_message_body.rs # bindings_wasm/src/content_types/decoded_message_content.rs # xmtp_mls/src/messages/decoded_message.rs
# Conflicts: # xmtp_proto/proto_version # xmtp_proto/src/gen/proto_descriptor.bin # xmtp_proto/src/gen/xmtp.mls.message_contents.rs
|
This PR addresses #2912. See XIP 76 for more detail: https://github.com/xmtp/XIPs/blob/main/XIPs/xip-76-delete-messages.md |
# Conflicts: # bindings_ffi/src/message.rs # xmtp_db/src/encrypted_store/mod.rs # xmtp_db/src/encrypted_store/schema_gen.rs # xmtp_db/src/lib.rs # xmtp_db/src/mock.rs # xmtp_db/src/traits.rs # xmtp_mls/src/groups/mls_sync.rs # xmtp_mls/src/messages/decoded_message.rs
# Conflicts: # bindings_ffi/src/message.rs # bindings_ffi/src/mls/tests/mod.rs # bindings_wasm/src/content_types/decoded_message_content.rs # xmtp_content_types/src/lib.rs # xmtp_db/src/encrypted_store/group_message.rs # xmtp_mls/src/messages/decoded_message.rs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please rename this file to deleted_message.rs
| WalletSendCalls { content: WalletSendCalls }, | ||
| Intent { content: Option<Intent> }, | ||
| Actions { content: Option<Actions> }, | ||
| DeleteMessage, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is not necessary, right?
# Conflicts: # bindings_node/src/content_types/decoded_message_content.rs # bindings_wasm/src/content_types/decoded_message_content.rs # xmtp_db/src/encrypted_store/group_message.rs # xmtp_mls/src/groups/error.rs # xmtp_mls/src/groups/message_list.rs # xmtp_mls/src/groups/mls_sync.rs
| ContentType::Unknown => Self::Unknown, | ||
| _ => Self::Unknown, | ||
|
|
||
| // question: do we need to include these in the backup? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@codabrink what's you opinion here?
Add message deletion across core, DB, and bindings to represent, validate, persist, and surface
DeletedMessagecontentIntroduce
ContentType::DeleteMessage, aDeletedBymodel, and deletion records; validate deletions in enrichment, persist viamessage_deletions, and expose deleted state in Node, WASM, and FFI bindings. Add group APIs to send and process delete messages and filter deletion entries from lists.📍Where to Start
Start with deletion handling in
MlsGroup: reviewprocess_delete_messagein xmtp_mls/src/groups/mls_sync.rs, thendelete_messagein xmtp_mls/src/groups/mod.rs, followed by enrichment logic in xmtp_mls/src/messages/enrichment.rs.Changes since #2772 opened
From<ContentType>implementation forContentTypeSaveto explicitly map multiple variants toUnknown[534d631]process_delete_messagehandler ingroups.mls_syncto support out-of-order deletions where the target message has not yet arrived [f36cbc3]messages.enrichmentto determineDeletedBybased on sender identity rather than stored flags [f36cbc3]groups.tests.test_delete_messageto validate out-of-order deletion behavior [f36cbc3]Group.delete_messagemethod ingroups.Groupby streamlining comments and encoding flow [f36cbc3]📊 Macroscope summarized a4ce98f. 16 files reviewed, 10 issues evaluated, 10 issues filtered, 0 comments posted
🗂️ Filtered Issues
db_tools/src/tasks/migrations.rs — 0 comments posted, 2 evaluated, 2 filtered
migration_statusfunction assumes that values in theappliedslice are already purely numeric versions, but this assumption may be incorrect. The old code filtered bothnameand each applied versionato numeric characters before comparing. The new code only filtersnameviaextract_version()and compares directly witha == &name_version. Ifdb.applied_migrations()returns versions containing non-numeric characters (e.g., dashes like2025-11-15-232503), the comparison will always fail and migrations will incorrectly be reported as[pending]when they are actually applied. [ Low confidence ]migration_numeric_versionfunction silently returns0viaunwrap_or(0)when parsing fails. If a migration name doesn't parse to a validu64(e.g., version string is too long or malformed), multiple migrations could get the same sort key of0, resulting in incorrect sorting order and potentially causing test failures or unexpected rollback behavior intest_migration_status_applied_and_pending. [ Low confidence ]xmtp_db/src/encrypted_store/group_message.rs — 0 comments posted, 1 evaluated, 1 filtered
Deletableimplementation marksContentType::Unknownas deletable (true), which could allow deletion of messages with unrecognized content types. If a newer protocol version introduces a critical system message type that an older client doesn't recognize, that client would classify it asUnknownand permit its deletion, potentially causing data integrity issues. [ Low confidence ]xmtp_db/src/encrypted_store/group_message/convert.rs — 0 comments posted, 1 evaluated, 1 filtered
From<ContentType> for ContentTypeSaveimplementation,ContentType::DeleteMessageis mapped toContentTypeSave::Unknown. When a message withContentType::DeleteMessageis backed up and later restored viaTryFrom<ContentTypeSave> for ContentType, it will be restored asContentType::Unknowninstead ofContentType::DeleteMessage, causing permanent data loss of the delete message semantics during backup/restore cycles. [ Already posted ]xmtp_mls/src/groups/mls_sync.rs — 0 comments posted, 2 evaluated, 2 filtered
is_authorizedis unconditionally set totrue. This allows ANY user to delete ANY message by timing their deletion request to arrive before the target message. The code checks if the deleter is a super admin but doesn't use that check to gate authorization - it only uses it to setis_super_admin_deletionfor metadata. This is an authorization bypass vulnerability. [ Already posted ]if !is_authorized) is dead code that can never execute. In theSomebranch, unauthorized requests return early at line 1361. In theNonebranch,is_authorizedis always set totrue. This suggests the out-of-order case authorization logic is incomplete - it should likely restrict deletions to only super admins when the original message is unavailable. [ Already posted ]xmtp_mls/src/groups/mod.rs — 0 comments posted, 3 evaluated, 3 filtered
is_message_deletedcheck at line 704 and theis_super_admincheck at line 710 could become stale if another thread/process deletes the same message or revokes super admin status beforedeletion.store(&conn)completes at line 741, potentially allowing duplicate deletions or unauthorized deletions in a narrow time window. [ Previously rejected ]deletion.store(&conn)fails aftersend_message_optimisticsucceeds. At line 728, the deletion message is sent to the network, but if line 741 fails (e.g., database error), the localmessage_deletionstable won't have a record. Subsequent calls todelete_messagefor the same message will pass theis_message_deletedcheck at line 704 and could send duplicate deletion messages to the network. [ Previously rejected ]find_enriched_messagesmethod does not callfilter_out_hidden_message_types_from_queryon theargsbefore callingget_group_messages, unlike the existingfind_messages_v2_with_connmethod which filters the query. This inconsistency could cause hidden/internal message types to be returned to callers when they should be filtered out. [ Already posted ]xmtp_mls/src/messages/enrichment.rs — 0 comments posted, 1 evaluated, 1 filtered
get_deletions, if the database returns multipleStoredMessageDeletionrecords for the samedeleted_message_id(e.g., if a message was deleted by multiple parties or if there are duplicate records), only the last one survives the HashMap collection. This could lead to non-deterministic behavior depending on database iteration order, potentially losing information about who deleted the message or their admin status. [ Previously rejected ]