> ## Documentation Index
> Fetch the complete documentation index at: https://docs.toolsforhumanity.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Advanced Topics

> Encryption details, edge cases, and disaster recovery.

export const RootKey = () => {
  return <Tooltip tip="The root key is a 256-bit secret from which other keys are derived for use throughout World App.">Root Key</Tooltip>;
};

export const BackupMetadata = () => {
  return <Tooltip tip="The plain-text metadata that accompanies each backup. It is stored in the Backup Service.">Backup Metadata</Tooltip>;
};

export const BackupAccountKey = () => {
  return <Tooltip tip="The secp256k1 keypair derived deterministically from the user's Root Key. Its public key is encoded into the Backup Account ID; it is the disaster-recovery key that authorizes /v1/reset and the deletion of the user's Turnkey sub-organization.">Backup Account Key</Tooltip>;
};

export const UnsealedBackup = () => {
  return <Tooltip tip="The inner-most component of the backup. It is a collection of the raw files required to operate World App.">Unsealed Backup</Tooltip>;
};

export const SealedBackup = () => {
  return <Tooltip tip="The encrypted version of the Unsealed Backup. It is stored in the Backup Service. It is decrypted with the Backup Encryption Keypair.">Sealed Backup</Tooltip>;
};

export const FactorSecret = () => {
  return <Tooltip tip="A 32-byte secret derived from or held by a Main Factor. Used to encrypt the Backup Encryption Keypair.">Factor Secret</Tooltip>;
};

export const EncryptedBackupEncryptionKeypair = () => {
  return <Tooltip tip="An encrypted version of the Backup Encryption Keypair. It is encrypted with a Factor Secret.">Encrypted Backup Encryption Keypair</Tooltip>;
};

export const BackupEncryptionKeypair = () => {
  return <Tooltip tip="The single keypair used to encrypt the Unsealed Backup. It is randomly generated by World App and not persisted anywhere.">Backup Encryption Keypair</Tooltip>;
};

export const MainFactor = () => {
  return <Tooltip tip="A Main Factor (e.g. a Passkey, OIDC account or Keypair in iCloud Keychain) is the primary authentication mechanism for a backup. It grants the broadest permissions and requires a user interaction to be used.">Main Factor</Tooltip>;
};

export const SyncFactor = () => {
  return <Tooltip tip="A Sync Factor (an Elliptic Curve Keypair) is a secondary authentication mechanism for a backup. It grants limited permissions and does not require a user interaction to be used. It is primarily used to sync the backup.">Sync Factor</Tooltip>;
};

# Encryption

All asymmetric encryption in the backup uses libsodium's [sealed box](https://doc.libsodium.org/public-key_cryptography/sealed_boxes) primitive (X25519 key agreement + XSalsa20-Poly1305 AEAD), via the [`crypto_box`](https://crates.io/crates/crypto_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 <UnsealedBackup />.** Done with the public key of the freshly-generated <BackupEncryptionKeypair /> (`Curve25519`). The result is the <SealedBackup />. The keypair's secret key is destroyed after this seal, so even the original device cannot decrypt the <SealedBackup /> without going through a Main Factor.
* **Sealing the <BackupEncryptionKeypair />.** Done once per Main Factor, with a public key derived from that factor's <FactorSecret /> (the secret is encoded to a `Curve25519` point with modular reduction). The result is an <EncryptedBackupEncryptionKeypair /> stored in the <BackupMetadata />.

Randomness for ephemeral keys comes from the OS CSPRNG via Bedrock's `OsRng`. See [Cryptography](/world-app/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 <SyncFactor /> 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](/world-app/backup/flows#bf-5-remote-update-detection)) escalates to a <MainFactor /> 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 <SyncFactor /> 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 right `backup_id` was provided) or `backup_does_not_exist` (factor lookup found nothing).
* Turnkey returns `PUBLIC_KEY_NOT_FOUND`.

Triggers:

* The user deleted and re-created the entire backup from another device. Sibling devices become unauthorized.
* The user lost their <SyncFactor /> (e.g., the device was reset).
* (Future) The user explicitly revoked authorization for a specific device.

The remediation path is always the same: re-authenticate a <MainFactor /> and register a fresh <SyncFactor /> via `/v1/add-sync-factor`. The clients have explicit handling at three call sites:

1. On `/v1/create` returning `backup_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.
2. On `/v1/retrieve-metadata` returning `unauthorized_factor`, prompt for a Main Factor and refresh the Sync Factor. If `backup_does_not_exist` is returned instead, the backup has been deleted and the local state is wiped.
3. On `/v1/delete-factor` returning `PUBLIC_KEY_NOT_FOUND` from Turnkey, fall back to the user's Passkey to perform the Turnkey operation, then continue with the Backup Service deletion.

<Note>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.</Note>

## Stale Local Backup

If the local manifest hash differs from the remote on app startup or before a sync, Bedrock returns `RemoteAheadStaleError`. The UI escalates the user to a recovery-style prompt and runs [BF-5](/world-app/backup/flows#bf-5-remote-update-detection). The local state is overwritten unconditionally with the freshly-downloaded backup — the manifest itself is the arbiter.

## Last Main Factor Restriction

Removing the last <MainFactor /> 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](/world-app/backup/flows#bf-7-factor-deletion) and [BF-8](/world-app/backup/flows#bf-8-backup-deletion).

# Disaster Recovery via /v1/reset

The <BackupAccountKey /> (secp256k1, derived deterministically from the <RootKey />) is a last-resort recovery mechanism for the case where:

* The user has lost every <MainFactor /> (no enrolled passkey, OIDC, or iCloud Keychain factor remains accessible).
* The user has *also* lost every <SyncFactor /> (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.

In that situation, the user can sign a `/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 a `break_glass_user` (see [Components & Parties — Turnkey](/world-app/backup/components#turnkey)).

<Note>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`.</Note>

## 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](https://docs.turnkey.com/features/authentication/auth-proxy)'s endpoint `POST 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.
