fix(sync): verify the vault key against the account; auto-heal installs broken by 2026.717.x#787
Merged
Merged
Conversation
…uto-heal installs broken by 2026.717.x A device can end up holding a master key that no longer matches its account (the 2026.717.x safeStorage regression did exactly that). The local vault verifier cannot catch this — a fresh vault binds whatever key the keychain holds — so every pull failed item-by-item: quarantine toasts, "item corrupt" marks, "all items failed with crypto errors", an empty vault, and convergence only by accident when the manifest check forced a re-pull. Account-truth gate: - server: GET /auth/key-verifier (access-token auth) returns the account's kdfSalt + keyVerifier for established sessions (/auth/recovery-info needs a setup token, long gone by the time an install breaks). - desktop: sync/key-verification.ts compares generateKeyVerifier(local master key) against the account verifier — local store copy first (offline-safe, persisted at sign-in/recovery/linking), server fetch fallback. Verdicts: match / mismatch / transition (key material mid-swap) / unknown. Enforcement: - runtime start: 'mismatch' → no sync, emit VAULT_RECOVERY_NEEDED (recovery dialog); 'transition' → defer, the auth/linking flow restarts sync itself. - pull page where EVERY item fails decrypt/verify: consult the account before branding anything. 'mismatch' → stop the cycle, no quarantine, no corrupt marks, no security toasts, escalate once (recovery prompt + sign-out so the ordinary sign-in + recovery-phrase flow restores the correct key). 'transition' → stop quietly. 'match'/'unknown'/no-checker → existing per-item corruption handling unchanged. - startup integrity check: a confirmed mismatch signs the install out (teardownSession) — this is the auto-heal for users broken by 2026.717.x: update → app signs them out → they sign in + enter the recovery phrase → everything pulls cleanly. Hygiene: - bindLocalVaultToMasterKey: rewriting the verifier to a NEW key purges the pull cursor and persisted quarantine so the corrected key starts clean (quarantined items were skipped forever; a stale cursor missed items). - persistKeysAndRegisterDevice marks key-material activity (suppresses mismatch reactions during sign-in/recovery/linking) and caches the account verifier locally (best-effort). Deploy order: server before desktop release (additive route; older clients unaffected; desktop degrades to 'unknown' = today's behavior if the endpoint is missing). Tests: server route (3), key-verification unit (12), pull gate via real SyncEngine (5: mismatch suppress+escalate, transition quiet-stop, match keeps quarantine, no-checker back-compat, partial failure never consults), integrity sign-out (3), vault-key-state purge (2). Desktop main suite 3783 green; sync-server 716 green.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
React Doctor found no new issues. 🎉 Reviewed by React Doctor for commit |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
h4yfans
marked this pull request as ready for review
July 18, 2026 08:12
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.
The real fix for the vault-key mismatch class of failures
Follow-up to #784 (stop new breakage) and #786 (recovery dialog). This PR makes it structurally impossible for a wrong vault key to produce the failure mode users hit on 2026.717.x: 100-item decrypt-error walls, security-warning toast storms, items branded corrupt/quarantined forever, an "empty vault" after a completed download — and it auto-heals installs that are already broken.
Root cause (production-log proven)
vault-key-state.tsbind-on-null). The account's key verifier was never consulted.What this PR does
Account-truth verification (
sync/key-verification.ts+ serverGET /auth/key-verifier)generateKeyVerifier(local master key)against the account's verifier — local store copy first (persisted at sign-in/recovery/linking; offline-safe), server fetch fallback (access-token route;/auth/recovery-infoneeds a setup token, long gone on broken installs).match/mismatch/transition(sign-in/recovery/linking mid-flight, ~2 min activity window) /unknown(offline & no cache — never destructive).Enforcement at three layers
mismatch→ sync never starts,VAULT_RECOVERY_NEEDEDprompts the feat(sync): recover an orphaned vault via the recovery phrase #786 recovery dialog.transition→ defer; the auth flow restarts sync itself.mismatch→ stop the cycle with zero side effects (no quarantine, no corrupt marks, no toasts), escalate once → recovery prompt + sign-out so ordinary sign-in + recovery-phrase restores the correct key.transition→ stop quietly.match/unknown/no-checker → existing per-item corruption machinery unchanged (single genuinely-corrupt items still quarantine).unknown→ no action (graceful).State hygiene
bindLocalVaultToMasterKey: rewriting the verifier to a NEW key purges the pull cursor + persisted quarantine — the corrected key starts clean by design, not by manifest accident.persistKeysAndRegisterDevice: marks key-material activity (suppresses mismatch reactions during auth flows) and caches the account verifier (best-effort).Deploy order
Server first (auto-deploys from main), then the desktop release. Additive route only; old clients unaffected; new desktop against old server degrades to
unknown= today's behavior.Edge cases covered (tested)
wrong key + toast storm → single recovery prompt · mid-linking key swap → quiet stop, no teardown under an active flow · offline broken install → no false sign-out, heals when online · older server (no endpoint) → graceful no-op · genuinely corrupt single item → existing quarantine path intact · fresh sign-in race (TOCTOU) → activity-window guard · store write failure → registration not rolled back · repeated failing cycles → escalation fires once per process
Tests
/auth/key-verifier(3) — sync-server suite 716 greenkey-verificationunit (12) · pull gate via realSyncEngine(5) · startup sign-out (3) · vault-key-state purge (2)Docs
architecture/cryptography.md(new Vault-Key Mismatch Detection section),architecture/sync-protocol.md(endpoint + error mode),user-guide/sync/conflict-health.md,user-guide/sync/recovery-rotation.md(Automatic Recovery Prompt).