Skip to content

fix(sync): verify the vault key against the account; auto-heal installs broken by 2026.717.x#787

Merged
h4yfans merged 2 commits into
mainfrom
fix/vault-key-account-verification
Jul 18, 2026
Merged

fix(sync): verify the vault key against the account; auto-heal installs broken by 2026.717.x#787
h4yfans merged 2 commits into
mainfrom
fix/vault-key-account-verification

Conversation

@h4yfans

@h4yfans h4yfans commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

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)

  1. The local vault verifier only proves self-consistency: a freshly provisioned vault binds whatever master key the keychain holds — even a wrong one (vault-key-state.ts bind-on-null). The account's key verifier was never consulted.
  2. During vault download/link, pulls raced key finalization; every item failed decrypt/verify and the failure pipeline persisted the damage: quarantine (skipped forever), corrupt marks, security toasts. Convergence happened only by accident via the manifest check's cursor reset.

What this PR does

Account-truth verification (sync/key-verification.ts + server GET /auth/key-verifier)

  • Compares 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-info needs a setup token, long gone on broken installs).
  • Verdicts: match / mismatch / transition (sign-in/recovery/linking mid-flight, ~2 min activity window) / unknown (offline & no cache — never destructive).

Enforcement at three layers

  • Runtime start: mismatch → sync never starts, VAULT_RECOVERY_NEEDED prompts the feat(sync): recover an orphaned vault via the recovery phrase #786 recovery dialog. transition → defer; the auth flow restarts sync itself.
  • Pull: a page where EVERY item fails decrypt/verify consults the account before branding anything. 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).
  • Startup integrity check: a confirmed mismatch signs the install out — this is the auto-heal for users broken by 2026.717.x: update → app signs them out → sign in → enter recovery phrase → clean pull. Healthy installs are untouched (verifier matches). Offline / older-server → 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

  • server: /auth/key-verifier (3) — sync-server suite 716 green
  • key-verification unit (12) · pull gate via real SyncEngine (5) · startup sign-out (3) · vault-key-state purge (2)
  • desktop main suite 3783 green · typecheck node+web · eslint · ipc:check · docs:impact strict + docs:build green

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).

h4yfans added 2 commits July 18, 2026 10:29
…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.
@vercel

vercel Bot commented Jul 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
memry-docs Error Error Jul 18, 2026 7:39am
memrynote-landing Error Error Jul 18, 2026 7:39am

@github-actions github-actions Bot added bug Something isn't working documentation Improvements or additions to documentation test labels Jul 18, 2026
@github-actions

Copy link
Copy Markdown

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit 36ade42.

@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.85106% with 18 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
apps/desktop/src/main/sync/runtime.ts 19.04% 17 Missing ⚠️
apps/desktop/src/main/sync/device-registration.ts 75.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@h4yfans
h4yfans marked this pull request as ready for review July 18, 2026 08:12
@h4yfans
h4yfans merged commit b3db549 into main Jul 18, 2026
20 of 23 checks passed
@h4yfans
h4yfans deleted the fix/vault-key-account-verification branch July 18, 2026 08:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant