Skip to content

Cross-device discovery — paired devices appear automatically

Status: Delivered
CAS: CAS-3704 (implemented via CAS-3705)
Delivered: 2026-05-18
PRs: #1019

What’s new

After a pairing completes, both devices now fetch the full list of devices registered to the shared vault and populate their local database with the results. Before this change, each device registered itself with the sync relay during pairing but never read back who else was registered — so neither device could see the other. Now, the moment pairing is accepted, both sides pull the peer list from the relay and upsert it locally, so your phone appears on the desktop (and vice versa) without any manual step.

How to use it

No user action required. The discovery happens automatically at the end of the pairing handshake:

  1. You pair your phone to the desktop using an invite code, as before.
  2. The moment the desktop accepts the pairing, it calls the relay for the full device list for that vault.
  3. Both devices store the discovered peers in their local devices table.
  4. The device list is immediately available for sync routing — the baseline sync cycle also fires right after pairing so the phone receives vault data without waiting for the next scheduled sync.

What changed under the hood

  • workers/sync-relay/src/handlers/devices.ts — new GET /vaults/:vaultId/devices endpoint; returns all non-revoked registered devices for the vault, authenticated by a signed X-Cc-Device-Id header from a registered peer.
  • workers/sync-relay/src/db/devices.ts — relay-side DB query returning active vault devices with shape { device_id, vault_id, public_key, registered_at }.
  • src-tauri/src/services/invite_service.rsinvite_device_accept now calls fetch_vault_devices_from_relay() after relay registration, signing the request with the local Ed25519 key; results are upserted via the new upsert_discovered_vault_device repository helper (preserves existing name/type while updating discovered peers).
  • src-tauri/src/commands/invite_commands.rs — post-pair sync cycle (vault_sync_service.run_manual_cycle) triggered immediately after identity is set, so freshly paired devices pull baseline data right away.
  • 15 relay-side tests cover: happy path, revoked-device exclusion, missing header, and non-member caller.

Why we built it

CAS-3073 multi-device sync had two blocking layers. CAS-3676 fixed the first: devices lacked a stable identity, so nothing could be registered. This epic fixed the second: even after registration, neither device ever fetched the peer list, so neither could route sync traffic to the other. Without cross-device discovery, the sync relay was a write-only registry — pairing wrote rows in, but no client read them back.

Known limitations / follow-on work

  • Discovery currently fires once at pairing time. Devices added later (third, fourth device) are not automatically pushed to existing devices; a future background refresh or push-notify mechanism is needed.
  • The device_name and device_type fields for discovered peers are placeholder-filled until the relay starts carrying them in registration payloads.