SYNC_STATUS.md

15.0 KB · raw

rendered · source

Peach Sync — Honest Status

Snapshot as of 2026-04-30, HEAD 7d738aa11c96, package v2.14.9, native v0.1.14.

This document distinguishes what is implemented and verified, what is implemented but not yet exercised on real hardware (compiles, tests in isolation, walking-skeleton ready but not yet pair-tested between two real devices), and what is deliberately deferred.

The previous version of this document (under v2.13.33) marked the system as "two devices cannot sync today." That is no longer accurate. The blockers it listed (B1–B5, M1–M8) have all been resolved in the v2.14.x rewrite per peach_sync_and_history_spec.md. The only remaining gate is M9 — the manual two-device E2E — which the user (June) is running personally on their daily driver.


TL;DR — can two devices sync today?

Yes, by the walking skeleton:

  1. ✓ runBlobSyncSession is wired into both TRIGGER_SYNC (manual) and the discovered-peer auto-sync (60s debounced).
  2. ✓ First-time pairing succeeds — native host gates an pairing_mode flag that admits otherwise-untrusted peers; INITIATE_PAIR_WITH_PEER actually sends PAIR_REQUEST over the encrypted Noise channel and awaits PAIR_ACCEPT / PAIR_REJECT (30s timeout).
  3. ✓ Android runs Noise NK in JS via @stablelib. Cross-check test pins the auth-proof hex against the Rust side byte-for-byte.
  4. ✓ Auto-advertise lifecycle (sync-lifecycle.ts) calls advertise() when Pro + unlocked + paired devices > 0, stops on lock.

What's still unverified is manual two-device E2E (Phase 10 in the spec checklist) — that requires a release host build + manifest install


What is solid (implemented + verified)

Native Rust host (native/peach-sync-host/, v0.1.14)

Extension TypeScript

Vault History (peach_vault_history, new in v2.14.0)

Replaces the old peach_vault_snapshots system. Snapshots the about-to-be-overwritten encrypted blob BEFORE every real-mode vault write. Cap 10. Reasons: local_save / sync_received / import / restore. Dedup on byte-identical blobs. Restoring locks the vault. DESTROY_VAULT clears history.

The Lock-screen self-recovery UI that used the old system (which let a user attempt a forgotten password against an old snapshot) was removed — that was a different feature from "undo a recent sync overwrite." Forgotten-password recovery may be brought back in a future spec.

Android

Verification matrix (all green as of v2.14.9)


Spec deviations worth knowing

Auth proof scheme

The spec text (Part 8.3) showed an auth proof computed as SHA256(DH || "peach-sync-auth/v1" || iPub || rPub || nonce). The existing native auth.rs::proof_for already used a different scheme: HMAC-SHA256(key=DH, "peach-sync-v1-initiator-auth" || iPub || rPub || nonce). The JS implementation in noise-js.ts::computeAuthProof matches the native side (source of truth) — both are pinned by the print_known_proof_for_js_cross_check test in auth.rs and the matching test in noise-js.test.ts. Change one, the other fails.

Vault history replaces lock-screen recovery

The spec replaced peach_vault_snapshots with peach_vault_history. A side effect was removing the lock-screen UI that let a user attempt a forgotten password against one of the last 6 snapshots. The new system only helps when you can already unlock — it's "undo a recent sync overwrite," not "recover from a forgotten password." This is by design per the spec, but if forgotten-password recovery is wanted back it needs a separate spec.

unlimitedStorage not requested

10 × ~200KB blobs + slot A/B + settings fits comfortably under chrome.storage.local's 5MB quota. If vaults grow past ~400 entries, history alone could approach 4MB; consider adding unlimitedStorage then. Not requested in v2.14.x.


What still needs hands-on verification

M9. Manual two-device E2E

The only honest verification that the system works end-to-end. Steps:

  1. cargo build --release --manifest-path native/peach-sync-host/Cargo.toml
  2. npm run native:install -- --browser chrome --extension-id <extension-id> on each device.
  3. Pair via Settings → Sync → "Pair a new device" on Device A; accept on Device B's pairing banner.
  4. Verify peach_paired_devices populated on both sides; verify both sides' trust stores carry each other's public key.
  5. Make a vault change on Device A; tap Sync now → Device B should show the change.
  6. Make a vault change on Device B; tap Sync now on Device A → should pull the change.
  7. Auto-sync: change something on A while B is also unlocked and advertising → within 60s, B should pick it up via the onPeerDiscovered auto-sync path.
  8. Vault History: open Settings → Vault → VAULT HISTORY; verify snapshots from the writes above appear with correct reasons. Tap Restore on a recent one; vault locks; unlock with current password succeeds.
  9. Three devices: same flow with phone (once Kotlin SyncKey plugin lands) → desktop → laptop. TRIGGER_SYNC should walk the reachable paired devices sequentially.
  10. After E2E passes, run koh checkpoint "SYN-001 + vault history E2E verified".

This is being run by June manually — daily-driver verification.

Kotlin SyncKey Capacitor plugin

Required for Android sync. Not included in the v2.14.x TS-side rewrite. Until it ships, the Capacitor sync bridge will throw cleanly when getAndroidSyncIdentity() is invoked. Desktop↔desktop sync is unaffected.


What changed from the v2.13.33 status

For posterity / reviewers comparing the old document to this one:

Plus, since the v2.13.33 doc was written, the entire per-entry sync protocol was retired and replaced with a blob-transfer protocol. At ~200KB for an 800-entry vault local-WiFi transfer takes under 200ms, which made the whole manifest/diff/conflict-resolver layer unnecessary. Vault history is the safety net for last-write-wins. See Projects.md §3.9 + §3.12 for the full architecture.