Skip to content

feat(sync): keep only the devices the user chooses on a restore - #453

Open
A-EDev wants to merge 2 commits into
mainfrom
feat/sync2-restore-choose-devices
Open

A-EDev wants to merge 2 commits into
mainfrom
feat/sync2-restore-choose-devices

Conversation

@A-EDev

@A-EDev A-EDev commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

A restore with the recovery key now asks which of the group's devices the user still has. None is ticked by default. The rest are signed out before this device seals anything new, as flow-sync #96 specifies.

  • Preview: after the words, the app reads the roster as the recovery member and lists the devices by name. It writes nothing. A name that can't be read shows a short id.
  • Commit: restore and recovery as before. The chosen devices are saved with the group record (migration 0039).
  • Finishing: while the list is saved, Session::open reads every log without saving anything, removes every current device not on the list, re-reads the roster, and only then clears the list. The list of devices to remove is worked out at that moment. Cuts come from the unsaved read, because saved positions are empty on a restored device. A log that stops at an object the store doesn't have, or one that doesn't verify, ends there, and the read goes on with the other logs. A read that hits this device's own limits removes nobody yet: it keeps its place in the group row (migration 0040), apart from the saved streams, goes on at the next sync and removes once it reaches the end. It doesn't try while the store is out of reach, and a stop anywhere resumes on the next open.
  • One check before sealing: Session::may_seal guards batches, acks, profile records, snapshot blobs and manifests, store logins and live messages. LAN sessions stay up, since the spec names objects and live messages.
  • New words: if rule 8 forces a replacement during this step, the words go to the page through LinkedStatus once, and the recovery words are marked unsaved before the roster re-read.
  • Vectors refreshed from flow-sync a7a1f56, which adds roster and fork cases and updates the fixtures README.

Follows flow-sync #96. Flow-Tube/Flow #1831 is the Android side.

Related issue

None.

Change type

  • Feature

Validation

Run on macOS arm64.

  • pnpm test: 1362 passed (not rerun for the last commit, which changes no frontend file)
  • pnpm build: built (not rerun for the last commit)
  • cargo fmt --check in src-tauri: exit 0
  • cargo clippy --all-targets --all-features in src-tauri: exit 0, no warnings in the sync files this adds or was checked on
  • cargo test --no-fail-fast in src-tauri, without --all-features: 1313 passed, 0 failed, 9 ignored
  • I smoke-tested the affected desktop behavior. Nothing ran in a real window, and the new step has no component test. That nothing is ticked by default is by reading only.

New tests in tests/sync2_restore_removal.rs (27) and tests/sync2_hybrid.rs (1). Checked by breaking the code on purpose, one change at a time:

Change Tests that failed
The seal check always passes 6, one per seal path
The removal is skipped 7
Cuts come from saved positions 1
The chosen devices are ignored 3
Replaced words don't reach the page 1
The removal ignores a read cut short 7
The place isn't saved 6
The read's limit doesn't mark the stop 7
A missing object stops every log 5
A store list too long to choose snapshots from fails the read 7
A log with nothing read isn't held when that list was too long 1
The place isn't cleared 1

The check for an unreachable store isn't separated by these: with it removed, the append is refused by the same outage in the test.

Risk and compatibility

  • Adds migrations 0039_sync2_restore_kept.sql and 0040_sync2_restore_read.sql.

  • A restore now signs out unticked devices. They can be linked again.

  • Other devices see an ordinary remove, so no format changes.

  • Desktop and Android have not been run against each other on this flow.

  • A store whose list is over the read limit can hold a restore pending, sealing nothing. When that list is too long and a log has nothing readable yet, the restore stays pending with an error rather than end that log with no cut, since the log may only start later.

  • After such a restore finishes, the first normal read on that store still fails, as it does for any device starting there. This PR doesn't change that limit.

  • An object listed ahead of its predecessor on an earlier page isn't read again once the read has passed that page, so that log ends at the predecessor.

  • No new secrets, private data, telemetry, or broad Tauri permissions were introduced.

  • User-facing documentation was updated where needed. (docs/recovery.md in flow-sync fix(ui): keep image fallbacks when a load fails instantly #96)

  • Dependency and lockfile changes are intentional and limited to this PR.

  • Breaking changes and upgrade steps are clearly documented.

Copilot AI balanced review requested due to automatic review settings November 7, 2026 14:46

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants