Skip to content

Invite codes and device pairing

Runa the Artificer — Backend Engineer

By the end of this chapter you’ll understand:

  • How the X25519 ECDH + HKDF-SHA256 handshake establishes a shared session key from an invite code without the relay ever seeing plaintext
  • Why Crockford-Base32 encoding is chosen for invite codes and what “untrusted message bus” means in this context
  • What the issuer and acceptor paths in invite_service.rs each do, step by step

Invite codes and device pairing

Status: Delivered
CAS: CAS-583
Delivered: 2026-04-25
PRs: #188 (F1 invite generation UI), #190 (F1–B3 invite backend + deep-link redemption)

What’s new

Casaconomy now supports a cryptographically secure invite system for adding a second device to an existing vault (device pairing) and for joining an existing group as a new participant. Invites are short alphanumeric codes generated on the issuing device and accepted on the new device — the relay that carries them can never synthesize or replay one.

How to use it

Generating an invite (issuer side):

  1. Open the invite screen and tap Generate invite.
  2. The app displays a short Crockford-Base32 code and a deep link QR code.
  3. The invite is valid for a limited window; the expiry is shown on screen.
  4. Share the code or QR with the person or device joining.
  5. The code can be cancelled at any time before it is redeemed.

Accepting an invite (acceptor side):

  1. On the new device, open the deep link or manually enter the invite code.
  2. The app verifies the issuer’s signature, performs an X25519 ECDH handshake, and derives a shared session key via HKDF-SHA256.
  3. The issuer sends an encrypted identity bundle (XSalsa20-Poly1305 AEAD) over the WebSocket relay channel.
  4. On successful decryption, all keys are persisted to the acceptor’s keystore and the device is fully paired — no further manual steps required.

What changed under the hood

  • InviteCode type — new domain type with a Crockford-Base32 codec that avoids ambiguous characters (I/1, O/0).
  • Relay integrationPOST /invites/{code} to publish and GET /invites/{code} to claim; the relay is treated as an untrusted message bus and never sees plaintext.
  • services/invite_service.rs — core of the device-pairing protocol:
    • Issuer path (invite_device_start): generates ephemeral X25519 keypair, signs an invite manifest with the device’s long-term Ed25519 key, publishes to relay, then waits over WebSocket for the acceptor’s ephemeral key to complete the ECDH.
    • Acceptor path (invite_device_accept): fetches and verifies the signed manifest, sends its own ephemeral key, derives the HKDF session key, decrypts the identity bundle, and imports the keys into the local keystore.
  • New Tauri commandsinvite_device_start, invite_device_cancel, invite_device_accept; progress reported on the invite://progress event channel.
  • encryption/keystore.rs — added import_symmetric_key and import_signing_keypair for the acceptor to persist received key material.
  • Invite generation UI — new screen with code display, QR code, expiry countdown, and cancel action.
  • Deep-link handlercasaconomy://invite/{code} URL scheme routes the acceptor into the redemption flow with the code pre-filled.

Why we built it

Without a secure pairing mechanism, adding a second device or new group member requires manual key export and import — error-prone and opaque to non-technical users. The invite code system makes onboarding a second device a single short-code exchange while keeping the relay server completely blind to the key material it carries. This is the foundational prerequisite for the full multi-device onboarding flow (CAS-149).

Known limitations / follow-on work

  • End-to-end smoke test across two local app instances is deferred to CAS-591.
  • Group join flow (Flow B — new user joining an existing group) shares the same transport but uses a different key exchange; the UI for that path is not yet wired up.
  • The relay is currently a Cloudflare Worker; failover and rate-limiting are not yet hardened for production traffic.
  • Deep-link handling requires casaconomy:// URL scheme to be registered on the OS; this is only tested on macOS so far.

Recap

  • The issuer generates an ephemeral X25519 keypair, signs an invite manifest with the long-term Ed25519 device key, and publishes it to the relay; the acceptor fetches it, verifies the signature, and completes the ECDH to derive a shared HKDF session key.
  • The relay is an untrusted message bus — it stores and forwards the handshake without ever seeing plaintext key material, because everything is encrypted before it leaves the device.
  • The casaconomy://invite/{code} deep-link handler pre-fills the invite code into the redemption flow, making pairing a single tap rather than a manual copy-paste.

What changed {#what-changed}

See: CHANGELOG → 2026-04-25