Encryption
All asymmetric encryption in the backup uses libsodium’s sealed box primitive (X25519 key agreement + XSalsa20-Poly1305 AEAD), via thecrypto_box crate. A sealed box generates an ephemeral sender keypair per encryption, so each ciphertext is independently sealed.
Two distinct seals exist in the system:
- Sealing the . Done with the public key of the freshly-generated (
Curve25519). The result is the . The keypair’s secret key is destroyed after this seal, so even the original device cannot decrypt the without going through a Main Factor. - Sealing the . Done once per Main Factor, with a public key derived from that factor’s (the secret is encoded to a
Curve25519point with modular reduction). The result is an stored in the .
OsRng. See Cryptography.
Edge Cases
Multiple Device Sync
A user authenticated on multiple devices will produce divergent local backup state if both devices make changes. The system handles this conservatively: a is not authorized to retrieve the encrypted backup, so a device cannot silently catch up to remote updates. Instead, the manifest hash mismatch detected at sync (or startup, see BF-5) escalates to a re-authentication and a fresh download. The Backup Service enforces that every sync transitions from the current manifest hash, so two concurrent devices cannot accidentally clobber each other — the second one to arrive will be rejected and forced to re-fetch.Unauthorized Device
A device’s can become unauthorized in either or both of the systems it’s registered with (Backup Service, Turnkey). Symptoms:- Backup Service returns
unauthorized_factor(when the rightbackup_idwas provided) orbackup_does_not_exist(factor lookup found nothing). - Turnkey returns
PUBLIC_KEY_NOT_FOUND.
- The user deleted and re-created the entire backup from another device. Sibling devices become unauthorized.
- The user lost their (e.g., the device was reset).
- (Future) The user explicitly revoked authorization for a specific device.
/v1/add-sync-factor. The clients have explicit handling at three call sites:
- On
/v1/createreturningbackup_account_id_already_exists, prompt for a Main Factor and run the recovery-style sync-factor refresh — this turns a duplicate creation attempt into an idempotent re-authorization. - On
/v1/retrieve-metadatareturningunauthorized_factor, prompt for a Main Factor and refresh the Sync Factor. Ifbackup_does_not_existis returned instead, the backup has been deleted and the local state is wiped. - On
/v1/delete-factorreturningPUBLIC_KEY_NOT_FOUNDfrom Turnkey, fall back to the user’s Passkey to perform the Turnkey operation, then continue with the Backup Service deletion.
Error handling on
/v1/add-factor and /v1/delete-factor against the Backup Service does not need its own unauthorized-factor branch because every such call is preceded by /v1/retrieve-metadata, which surfaces the condition first.Stale Local Backup
If the local manifest hash differs from the remote on app startup or before a sync, Bedrock returnsRemoteAheadStaleError. The UI escalates the user to a recovery-style prompt and runs BF-5. The local state is overwritten unconditionally with the freshly-downloaded backup — the manifest itself is the arbiter.
Last Main Factor Restriction
Removing the last deletes the entire backup (the data is no longer recoverable). The Backup Service enforces this server-side; clients confirm with the user before issuing the request. See BF-7 and BF-8.Disaster Recovery via /v1/reset
The (secp256k1, derived deterministically from the ) is a last-resort recovery mechanism for the case where:- The user has lost every (no enrolled passkey, OIDC, or iCloud Keychain factor remains accessible).
- The user has also lost every (no authorized device). If they have a Sync Factor they can simply remove all the Main Factors to start over.
- But the user still has the original device (or the original Root Key in some other form), and therefore can reproduce the Backup Account Key and produce a signature with it.
/v1/reset challenge with the Backup Account Key’s secret key. The Backup Service verifies the signature against the public key encoded in the backup_account_id, then wipes the backup so the user can re-enroll from scratch.
This is not a recovery of the existing backup contents. It’s a clean wipe that lets the user re-enroll fresh (otherwise the system would reject duplicate backups). The Root Key (and therefore the wallet and on-chain identity) is preserved because the user already has it locally; what they’re rebuilding is the backup of their root key, PCPs and credential vault.
/v1/reset wipes the state across the board, and primarily: the entire backup-service state for the user (remote ciphertext backup), and their Turnkey sub-organization. The backup-service deletion is authoritative; the Turnkey teardown is described below.
Registering the break_glass_user
The teardown is only possible if the sub-organization has abreak_glass_user (see Components & Parties — Turnkey).
Support for deleting the stale Turnkey sub-organization was introduced around August 2026. Accounts set up before then initially could not be deleted — they had no
break_glass_user, and the user (having lost every Main and Sync Factor) cannot authenticate any other way. Those accounts become deletable once a migration backfills the break_glass_user.Tearing down the Turnkey sub-organization
After the backup-service reset completes, the client tears down the Turnkey sub-organization by first resolving the Turnkey sub-organization ID using the Auth Proxy’s endpointPOST https://authproxy.turnkey.com/v1/account, then sending a deleteSubOrganization stamped with the Backup Account Key (which is represented as the break_glass_user in Turnkey).
Best-effort. A Turnkey failure does not fail the reset. The backup-service deletion is authoritative and the user can still re-enroll.