-
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 Complete ✓Review Checklist
Review Date: 2026-01-05 SummaryThis is a well-implemented feature with comprehensive test coverage and thoughtful error handling. The code quality is generally high, with good documentation and clear separation of concerns. I've identified a few issues that haven't been mentioned in previous reviews: Issues Found1. Missing Hidden Message Type Filtering in
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2772 +/- ##
==========================================
+ Coverage 74.24% 74.41% +0.16%
==========================================
Files 413 416 +3
Lines 53201 53953 +752
==========================================
+ Hits 39501 40147 +646
- Misses 13700 13806 +106 ☔ 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_node/src/content_types/decoded_message_body.rs
| pub const MINOR_VERSION: u32 = 0; | ||
|
|
||
| fn fallback() -> Option<String> { | ||
| Some("Deleted a message".to_string()) |
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 would appear as just Deleted a message in the message feed, which i don't think is very useful. i actually don't think we need to have a fallback for this message type as i'm not sure it would provide any value. WDYT?
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.
I was thinking that would be a good solution for backward compatibility. Wdyt?
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.
i'm just looking at it from a user perspective. if i saw "Deleted a message" in the message feed, i would be confused.
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.
it's OK to have no fallback. the default behavior in that case is to display nothing.
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.
Fair enough. Will remove it.
This reverts commit b7a733d.
# 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
| // Check if the original message exists | ||
| let original_msg_opt = storage.db().get_group_message(&target_message_id)?; | ||
|
|
||
| // Validate message belongs to this group (prevent cross-group deletion) |
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.
If original_msg_opt is None, cross‑group and deletability checks are skipped, so a super admin can still record a deletion for any ID. Consider returning early when the target message is missing (and only proceeding when it exists and belongs to this group) before authorization and storage.
| // Validate message belongs to this group (prevent cross-group deletion) | |
| if original_msg_opt.is_none() { | |
| tracing::warn!("Deletion for unknown message {}, skipping", delete_msg.message_id); | |
| return Ok(()); | |
| } |
🚀 Want me to fix this? Reply ex: "fix it for me".
| | ContentType::ReadReceipt | ||
| | ContentType::Actions | ||
| | ContentType::Intent | ||
| | ContentType::DeleteMessage => false, |
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.
ContentType::DeleteMessage exists, but TryFrom<ContentTypeSave> for ContentType doesn’t handle a ContentTypeSave::DeleteMessage. Consider adding the variant to ContentTypeSave and mapping it to ContentType::DeleteMessage, or document why DeleteMessage is intentionally not persisted.
🚀 Want me to fix this? Reply ex: "fix it for me".
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.📊 Macroscope summarized 5d3df84. 16 files reviewed, 12 issues evaluated, 10 issues filtered, 2 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 — 1 comment posted, 2 evaluated, 1 filtered
ContentType::Unknownis marked as deletable (trueon line 270), but unknown content types represent messages whose type is not recognized by the current client. This could be a newer content type from updated clients. Allowing deletion of unknown content types risks unintentional data loss when a user attempts to delete what appears to be an unrecognized message but is actually meaningful content that should be preserved. [ Low confidence ]xmtp_mls/src/groups/mls_sync.rs — 1 comment posted, 5 evaluated, 4 filtered
is_senderis set tofalseviaunwrap_or(false)at line 1367. This means non-super-admin users cannot delete their own messages if the deletion message arrives before the original message. The code at lines 1409-1415 suggests out-of-order handling is expected to work, but the authorization logic at line 1386 only allows super admins to create deletion records when the original message is missing. Regular users' valid deletions will be silently rejected as unauthorized. [ Already posted ]is_super_admin_without_lockcall at line 1381-1382 usesunwrap_or(false)to handle errors, which silently treats any error (e.g., storage/database issues) as "not a super admin". This could cause legitimate super admin deletion attempts to be rejected as unauthorized ifGroupMutableMetadataErroroccurs, with only a generic "Unauthorized deletion attempt" warning logged that doesn't indicate the actual cause was an error reading metadata. [ Low confidence ]process_delete_messageand replaces it withGroupError::InvalidGroupMembership. Ifprocess_delete_messagereturns an error for a legitimate reason other than malformed content (e.g., storage error), the test would fail with a misleading error type, making debugging difficult. The original error should be propagated or logged. [ Low confidence ]process_delete_messagewithGroupError::InvalidGroupMembership, losing the actual error information and potentially masking different failure modes. [ Low confidence ]xmtp_mls/src/groups/mod.rs — 0 comments posted, 3 evaluated, 3 filtered
is_message_deleted) and the store at line 764 are not atomic. Two concurrent calls todelete_messagefor the samemessage_idcould both pass theis_message_deletedcheck, resulting in duplicate deletion messages being sent to the network. While the comment mentionsstore_or_ignorehandles idempotent insertion during sync, the duplicate network messages are still sent. [ Previously rejected ]send_message_optimisticat line 746 succeeds butdeletion.store(&conn)at line 764 fails, the function returns an error to the caller even though the deletion message was already sent to the network. The caller may interpret this as a complete failure and retry, potentially causing confusion or duplicate attempts. The comment acknowledges crash recovery but doesn't address the case wherestorethrows an error (e.g., database constraint violation). [ Previously rejected ]find_enriched_messagesmethod does not callfilter_out_hidden_message_types_from_querybefore querying messages, unlike the existingfind_messages_v2method which filters hidden message types. This inconsistency could expose internal/hidden message types to callers that should not be visible. [ Already posted ]