Conversation
|
Claude finished @codabrink's task —— View job Code Review CompleteReview Date: January 28, 2026, 6:54 PM UTC I've reviewed this PR focusing on areas not already covered by previous reviewers. Here are my findings on the most important issues: Critical Issues1. Hardcoded key size check inconsistencyFiles: All three bindings use hardcoded if key.len() < 32 { // Should be: if key.len() < ENC_KEY_SIZE {While High-Priority Issues2. Silent data loss in metadata conversionFiles: All three bindings ( The elements: value
.elements
.into_iter()
.filter_map(|selection| selection.try_into().ok()) // Drops unknown elements silently
.collect(),Recommendation: Either log a warning when elements are dropped, or document this behavior explicitly in the function documentation. 3. Message processing retry logic needs clarificationFile: The error handling increments attempts on error but marks as processed on success. However, the
Current flow: if let Err(err) = self.process_message(handle, &msg, content).await {
// Increments attempts regardless of is_retryable()
self.context.db().increment_device_sync_msg_attempt(&msg.id, MAX_ATTEMPTS)?;
} else {
self.context.db().mark_device_sync_msg_as_processed(&msg.id)?;
}Recommendation: Either use 4. Request acknowledgement without checking if reply was requestedFile: In let reply = match (pin, content) {
(None, ContentProto::Reply(reply)) => reply, // No check here
(Some(pin), ContentProto::Reply(reply)) if reply.request_id == pin => reply,
_ => continue,
};
return self.process_archive(&msg, reply).await;This differs from the normal flow (line 385) which calls Medium-Priority Issues5.
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #3053 +/- ##
==========================================
- Coverage 73.92% 73.77% -0.15%
==========================================
Files 445 447 +2
Lines 54553 54990 +437
==========================================
+ Hits 40330 40571 +241
- Misses 14223 14419 +196 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
190e58c to
43d17b6
Compare
43d17b6 to
6380755
Compare
| tracing::info!("Inspecting sync payload."); | ||
|
|
||
| // Check if this reply was asked for by this installation. | ||
| if !self.is_reply_requested_by_installation(&reply).await? { |
There was a problem hiding this comment.
Moved this check higher in the stack. This function assumes that you've already checked whether or not you want to import it.
ed21187 to
fd8f314
Compare
c64a7ad to
3e81ae0
Compare
3e81ae0 to
6f735ee
Compare
c16cbc1 to
bd51436
Compare
66c3f1e to
91d8ecb
Compare
01e6cfe to
0f07a31
Compare
| } | ||
|
|
||
| /// Import a previous archive from file. | ||
| pub async fn import_archive(&self, path: String, key: Vec<u8>) -> Result<(), GenericError> { |
There was a problem hiding this comment.
WDYT of returning a report of sorts on successful import?
something like this:
pub struct ArchiveImportReport {
elements_imported: u64 // or usize?
}
not sure if anything else would make sense, but the purpose would be to see if any elements weren't imported from the archive. i know it's possible that archives would overlap on some elements so this would allow some insight into that.
There was a problem hiding this comment.
Sure. Follow-up PR? This PR is getting kind of big.
rygine
left a comment
There was a problem hiding this comment.
bindings look good to me!
f8c2edc to
4d4197c
Compare
4d4197c to
6e154e8
Compare
Add device sync APIs and processing, switch clients to device sync groups, and require server URL with PIN-based archives across Rust core and WASM/Node/mobile bindings
Introduce explicit device sync flows with PIN-based archives and server URL handling, add message attempt/state tracking with a 3-attempt cap, replace history sync with device sync in client methods and tests, and expose device sync operations in WASM, Node, and mobile bindings. Core updates include new listing/processing by PIN, paging of sync messages, refined retryability for
DeviceSyncError, and proto changes addingserver_url.📍Where to Start
Start with device sync message flow in
SyncWorker—reviewprocess_message,send_archive,send_sync_archive,process_archive_with_pin, andlist_available_archivesin worker.rs, then follow the new DB paging/state APIs in processed_device_sync_messages.rs and the client entrypoints in client.rs.Changes since #3053 opened
inner_client()method calls with directinner_clientfield access across allDeviceSyncmethods [ab0536e]Clientimport withRustXmtpClientand addedstd::sync::Arcimport in thebindings.wasm.device_syncmodule [ab0536e]BackupElementSelectionOptionenum [ab0536e]bindings/wasmmodule [70446f5]📊 Macroscope summarized a386e3b. 14 files reviewed, 12 issues evaluated, 12 issues filtered, 0 comments posted
🗂️ Filtered Issues
bindings/mobile/src/mls/device_sync/mod.rs — 0 comments posted, 4 evaluated, 4 filtered
create_archivefunction returnsBackupMetadata(the internal type fromxmtp_archive) instead ofFfiBackupMetadata. This is inconsistent witharchive_metadatawhich returnsFfiBackupMetadata. If this function is exported via UniFFI,BackupMetadatalacks the#[derive(uniffi::Record)]attribute and cannot be serialized for FFI. The return type should likely beFfiBackupMetadatawith a conversion:Ok(BackupMetadata::from_metadata_save(metadata, BACKUP_VERSION).into())[ Already posted ]check_keyfunction uses a hardcoded value32for the minimum key length check, but usesENC_KEY_SIZEfor truncation and in the error message. IfENC_KEY_SIZEdiffers from 32, this creates inconsistent behavior: either the validation allows keys that are too short (ifENC_KEY_SIZE > 32), or rejects valid keys that meet the actual requirement (ifENC_KEY_SIZE < 32). The check on line 171 should useENC_KEY_SIZEinstead of the hardcoded32. [ Already posted ]From<BackupMetadata> for FfiBackupMetadataconversion at line 198 usesfilter_map(|selection| selection.try_into().ok())which silently drops anyBackupElementSelectionvalues that fail to convert toFfiBackupElementSelection. When users callarchive_metadatato inspect an archive, they may receive an incomplete list of elements with no indication that some were dropped, potentially leading to confusion about archive contents. [ Low confidence ]filter_map(|selection| selection.try_into().ok())pattern silently discards anyBackupElementSelectionelements that fail conversion toFfiBackupElementSelection. This could cause data loss where the caller receives an incompleteelementslist without any indication that elements were dropped. If certain element types are unsupported in the FFI layer, this should either error out or be explicitly documented/logged. [ Already posted ]bindings/node/src/device_sync.rs — 0 comments posted, 5 evaluated, 5 filtered
From<ArchiveOptions> for BackupOptionsimplementation,n.get_i64().0silently discards the overflow indicator boolean. When JavaScript passes aBigIntvalue larger thani64::MAXor smaller thani64::MIN,get_i64()returns(truncated_value, false)wherefalseindicates the conversion was lossy. The code ignores this and uses the potentially incorrect truncated value, which could silently corruptstart_nsandend_nstimestamp boundaries, causing the archive to include wrong data ranges. [ Already posted ]BigInt::get_i64()method returns a tuple(i64, bool)where the boolean indicates whether the conversion was lossless. The code uses.0to extract only thei64value, ignoring the overflow indicator. If aBigIntvalue exceedsi64::MAXor is belowi64::MIN, the timestamp will be silently truncated to an incorrect value, corrupting thestart_nsorend_nsfilter and potentially causing wrong data to be archived. [ Already posted ]end_ns-BigIntvalues outsidei64range will be silently converted to incorrect values without any error or warning to the caller. [ Already posted ]BackupMetadatatoArchiveMetadatasilently drops anyBackupElementSelectionelements that fail to convert via.filter_map(|selection| selection.try_into().ok()). If any element'stry_into()fails, it is silently discarded rather than propagating an error or logging a warning. This could result in data loss where callers receive an incompleteelementslist without any indication that some elements were dropped during conversion. [ Already posted ]32while the error message andtruncatecall useENC_KEY_SIZE. IfENC_KEY_SIZEis not 32, the check and the error message would be inconsistent, and keys could be rejected or accepted incorrectly. The comparison on line 120 should useENC_KEY_SIZEinstead of the magic number32to ensure consistency. [ Already posted ]bindings/wasm/src/device_sync.rs — 0 comments posted, 3 evaluated, 3 filtered
From<BackupMetadata>implementation at lines 102-106 usesfilter_map(|selection| selection.try_into().ok())which silently drops anyBackupElementSelectionvariants that fail conversion. Whenarchive_metadatais called, the returnedArchiveMetadata.elementsmay contain fewer elements than the actual archive contains, giving callers an incomplete view of the archive contents without any indication that data was omitted. [ Already posted ]filter_map(|selection| selection.try_into().ok())pattern silently discards anyBackupElementSelectionelements that fail conversion toBackupElementSelectionOption. This could cause data loss where the caller receives an incompleteelementslist without any indication that elements were dropped. If certain element types are unsupported in the WASM layer, this should either error out or be explicitly documented/logged. [ Already posted ]32but the error message and truncation useENC_KEY_SIZE. IfENC_KEY_SIZEis greater than 32, keys with length between 32 andENC_KEY_SIZEwill pass the validation check buttruncate(ENC_KEY_SIZE)will have no effect, returning a key shorter thanENC_KEY_SIZE. This could cause cryptographic failures downstream when the code expects exactlyENC_KEY_SIZEbytes. [ Already posted ]