Dashboard Pairing

The lpm.dev dashboard reads the same encrypted vaults the CLI manages — but it can't hold the wrapping key directly. Browser sessions are too short-lived and too exposed for a long-term decryption key. Instead, pairing uses an ECDH key exchange so the CLI hands the wrapping key to the browser, encrypted along the way, without the server seeing it.

What pairing unlocks

  • Read every vault you own (personal + org) in a browser.
  • Edit values from the dashboard — they re-encrypt locally in the browser using the wrapping key the CLI provided, then push through the same vault_sync endpoint the CLI uses.
  • Read schemas (envSchema) so the UI can mark required keys and secret keys.
  • View the audit log across all vaults you have access to.
  • Manage platform integrations — OAuth-heavy connections (Vercel, Netlify) live in the dashboard because the OAuth UX is hard to do from a terminal.

One pairing unlocks all your vaults — scope is per-user, not per-vault.

How to pair

lpm env pair <CODE>

The flow:

  1. On a device that doesn't yet have the wrapping key (your laptop's browser, a tablet), open lpm.dev/dashboard/secrets. The dashboard generates an ECDH P-256 keypair in the browser and sends the public key to POST /api/vault/pair. The server returns a 6-character alphanumeric pairing code with a 5-minute TTL.
  2. Run lpm env pair <CODE> on a machine that already has the wrapping key in its keychain (typically the same laptop where you first ran lpm env push).
  3. The CLI fetches the pending pairing session, wraps your wrapping key with the browser's public key (plus the CLI's ephemeral public key as the shared-secret peer), and sends both back.
  4. The dashboard fetches the wrapped blob, derives the shared secret with its ECDH private key, unwraps the wrapping key, and unlocks every vault you have access to.

Sessions move through pending → approved → consumed → expired. Once consumed, the same code can't be replayed — the server tears the session down after the dashboard fetches the wrapped key.

Revoking access

lpm env unpair                             # revoke this device's pairing

To revoke all paired sessions across all devices (you lost a laptop, ended a contractor's access, etc.):

  • Open the dashboard at Settings → Security → Paired Devices and revoke specific sessions.
  • Or rotate the per-vault AES key with lpm env rotate-key (see Cloud Sync) — any device that still has the old wrapped key can no longer decrypt fresh blobs.

Where pairing lives

SurfacePurpose
POST /api/vault/pairBrowser → server: register an ECDH public key, get a pairing code
GET /api/vault/pair/[code]CLI → server: read a pending session
POST /api/vault/pair/[code]CLI → server: deposit the wrapped wrapping key
POST /api/vault/pair/revoke-allDrop every paired session for the current user

The keychain item on each machine is dev.lpm.vault-key. The wrapping-key value itself is the same on every paired device for one user — the design choice is "every device that completes pairing can decrypt all of this user's vaults."

What the server still doesn't see

  • The wrapping key in plaintext (it's wrapped under the browser's public key in transit).
  • Any vault's per-vault AES key (it's wrapped by the wrapping key on disk).
  • Any secret value (each vault's blob is AES-GCM ciphertext).

What the server does see during pairing: the browser's ECDH public key, the CLI's ephemeral public key, the ciphertext of the wrapped wrapping key, and the pairing-code metadata. None of that is enough to derive the plaintext wrapping key without one of the matching private keys.

Plan requirements

Dashboard pairing requires a Pro or Org plan — the same plan tier that unlocks lpm env push / pull. The local vault doesn't need pairing; it's read directly from the keychain.

Next