Repository navigation
feat(sdk): let a writer member adopt and restore an asset first seen deleted on an album page - #1200
Merged
Merged
Conversation
…ombstone omits from the fetched history A device that first sees an asset at its delete on an album page has only the delete record: the page names no metadata blob and no blob, so a writer member's device had nothing to adopt and restore from. history_sources reads the live metadata blob off the newest record that binds one and the original off the delete's own commitment, sized to its exact ciphertext; the FFI exports it as synced_history_sources. The blobs are then fetched through the window-gated blob route and opened with open_synced_history. The album page keeps its record-only shape.
… delete's records The album page keeps its record-only tombstone, so a member's device names the live metadata blob and the original from the fetched history and fetches both from the window-gated blob route.
Deploying capsule with
|
| Latest commit: |
e62404b
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://82217261.capsule-22k.pages.dev |
| Branch Preview URL: | https://feat-1198-member-adopt-at-de.capsule-22k.pages.dev |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Before this change, an album page served a
deleteas a record-only tombstone: no metadata blob, no blob,original_held: false. That shape is the one peers rely on (see PR 1197). A writer member's device that first saw an asset at its delete therefore had nothing to adopt the asset from, so it could not sign thetrash-restorethatroutes/ops.rsadmits. This PR lets that device find the blobs from the delete's provenance records, which it already fetches. The album page itself does not change.capsule-sdk/src/sync.rs: new purehistory_sources(head_record, history) -> HistorySources.metadata_blob_hashwhen the head binds one. Otherwise it comes from the newest record in the history that binds one, which is the same ruleWorkspace::adopt_remote_historyuses.ciphertext_hash, sized withciphertext_len(plaintext_size).HistoryError::Unboundfor a chain where no record binds a metadata blob.Malformednamesheadorhistory[i].HistoryErrordocs say this.capsule-sdk/src/sync/tests.rs: three unit tests on a real create → edit → edit → delete chain.capsule-sdk/src/ffi.rs: exportssynced_history_sources(manifest_cbor, history) -> FfiHistorySources { metadata_blob_hash, original: FfiBlobRef }as a uniffi free function, which is pure and offline. It maps errors toFfiError::Syncthrough the existingFrom<HistoryError>. The docs offetch_metadata_blobandfetch_provenance_historynow point at it for an album-page tombstone.capsule-sdk/src/ffi/workspace.rs: theFfiWorkspace::open_synced_historydocs describe the album-page flow. They also say that an entry left with an emptymetadata_blobis refused.capsule-sdk/src/ffi/tests.rs: one new end-to-end test and one error test.metadata_blob. It runsfetch_provenance_history, thensynced_history_sources, thenfetch_metadata_blobandfetch_blob_to_pathagainst the mock blob route, thenopen_synced_history. The asset is held in trash with its edited tags, andrestore_assetsucceeds. The same entry with the empty metadata blob is refused and places nothing.capsule-sdk/bindings-surface.json: regenerated withmise run gen-bindingsfor the new record and function.capsule-docs/src/content/docs/design/sync.md: states how a writer member's device restores from the records alone.capsule-server/src/routes/sync.rs: doc comments only (module docs andTombstoneDisclosure). The disclosure match at the album page and the page shape are unchanged.Client flow for a device that first sees an asset at its delete and does not hold it:
fetch_provenance_history(manifest_cbor).synced_history_sources(manifest_cbor, history).fetch_metadata_blob(metadata_blob_hash)andfetch_blob_to_path(original.ciphertext_hash, original.size, path).open_synced_history({ ..entry, metadata_blob }, history, path, dest).On the owner's own feed the answer matches the blob the entry already names.
Validation
cargo test -p capsule-sdk --features ffi --lib -- history_sources named_sources names_the_newest names_its_own unbound_or_malformed first_seen_at_a_delete: 6 passed. These are the 5 new tests plus the existing at-delete adoption test.mise run lint-check-ffi(clippy withCLIPPY_FLAGS,--features ffi --all-targets): clean.RUSTDOCFLAGS="-D warnings" cargo doc -p capsule-sdk --no-deps --document-private-items, with and without--features ffi: clean.mise run gen-bindings: surface check passed;bindings-surface.jsoncommitted.cargo fmt -p capsule-sdk,cargo fmt -p capsule-server --check, and the pre-commit hooks: clean. The hooks ran format-rust, format-docs, lint-md, lint-docs, convco and check-commits.lint-check,test-rust,doc-check-rust,check-docs) and clippy on capsule-sdk with default features.Coverage gaps
synced_history_sourcesyet, and Swift is not compiled in this environment. The Swift drain (FFISyncDriver.swiftaround line 415) fetches no history for an entry with an empty metadata blob, so it opens an album-page tombstone with an empty blob, whichopen_synced_historyrefuses; Android has no managed drain yet. That client wiring is outside this SDK change; see Wire synced_history_sources into the Swift and Kotlin sync drains for an asset first seen deleted on an album page #1201.Risks and rollout
HistoryErrorgains a variant. Its only matches are incapsule-sdk(the FFIFromfalls through toSync).Issue
Closes #1198
Decisions taken
Issue 1198 - Let a writer member adopt and restore an asset first seen deleted
ciphertext_hashand size.history_sourcesandsynced_history_sources, and giveTombstoneDisclosure::Restoreto an account reader on an album page incapsule-server/src/routes/sync.rs.fetch_metadata_blobandfetch_blob_to_pathdo the fetching.synced_history_sourceswith anFfiSessionmethod that composes the fetches.plaintext_size(capsule_core::crypto::encryption::ciphertext_len), which is whatverify.rsalready does. That allows the bounded, sizedfetch_blob_to_path.HistorySources::original_sizetoOptionand fetch the original unsized.