Skip to content

fix(cli): devices join never adopts the paired vault key into the joining device's vault, breaking the first sync pull #323

Description

@kafkade

Found while implementing #298 (tenant-scoped vault identifiers). Pre-existing on main; not introduced by #298 and not made worse by itdevices join succeeds and the first sync pull fails identically with and without that branch.

Summary

ldgr devices join <token> completes pairing and installs the paired device's vault key K_A as the session key, but it never adopts K_A into the joining device's own vault file. The joining device's vault.db remains SQLCipher-encrypted under derive_db_key(K_local), so the first ldgr sync pull after a successful join fails with:

failed to open the encrypted working store — wrong key or an unmigrated plaintext database

Root cause predates the SQLCipher work

This is important for triage — it would be easy to misfile this as "SQLCipher broke device pairing".

So SQLCipher changed when it fails, not why.

The error message is actively destructive

The failure tells the user to run ldgr unlock, which overwrites the keystore entry holding K_A — the only copy. The pairing code is single-use (set_relay_response updates only WHERE response_data IS NULL), so recovery requires generating a fresh code on the other device.

What a real fix requires

  1. Re-wrap K_A under the joining device's password into its vault file, and
  2. Re-key or recreate vault.db under derive_db_key(K_A).

Note there is currently no PRAGMA rekey / cipher_migrate anywhere in crates/ (verified), so this needs new machinery. It also forces an explicit decision about any local data an earlier ldgr init already created on the joining device (merge, discard, or refuse to join a non-empty vault).

Suggested triage

Plausibly M0-adjacent given #300 covers iOS QR-scan device onboarding — a fix here should land before or alongside that, since onboarding a second device is exactly the flow this breaks.

Repro

  1. Device A: set up a vault and sync.
  2. Device B: ldgr init, then ldgr sync setup.
  3. Device A: ldgr devices add → note the pairing code.
  4. Device B: ldgr devices join <code> → reports success.
  5. Device B: ldgr sync pull → fails with the working-store error above.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/syncCross-device sync, conflict resolutionpriority/P1-highRequired for a credible v1.3 / core promisetype/bug

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions