> ## 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.

# Backup Structure & Sync

> What's inside the backup, how the manifest enforces consistent updates, and how sync works.

export const PersonalCustodyPackage = () => {
  return <Tooltip tip="A Personal Custody Package (PCP) is a collection of files that accompany a user's World ID Credential. It is stored on the user's device and optionally the user's backup.">Personal Custody Package</Tooltip>;
};

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 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 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>;
};

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 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 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>;
};

# What's in the Backup

The <UnsealedBackup /> contains the user's <RootKey /> plus a set of files needed to fully restore World App on a new device. Today this includes the user's <PersonalCustodyPackage />s (Orb, Document, Face) and the credential vault. Each file is tagged with a *designator* identifying its kind.

The exact list of designators is owned by [Bedrock](/world-app/bedrock) and evolves as new credentials are added. The authoritative enumeration lives in [`BackupFileDesignator`](https://github.com/worldcoin/bedrock/blob/main/bedrock/src/backup/mod.rs); refer to it directly rather than duplicating the list here.

The backup format itself is versioned ([`BackupFormat`](https://github.com/worldcoin/bedrock/blob/main/bedrock/src/backup/backup_format)). The current version is V0: a tar+gzip archive of CBOR-encoded files with per-file BLAKE3 checksums.

# The Manifest

<Note>Because the backup is end-to-end encrypted, the Backup Service cannot enforce content-level rules. If Client M observes that the user just verified their passport (and now holds a new PCP), the Backup Service has no way to require that a sync from Client N — which doesn't have that file — must include it. The manifest mechanism described below is what prevents accidental data loss in that case.</Note>

Every backup ships with a **manifest**: a versioned, hashed ledger of every file in the <UnsealedBackup />. The manifest entries record each file's path, designator, and content checksum. The hash of the manifest is the backup's *state*.

Both the client and the Backup Service track this hash. The Backup Service uses it to enforce that every update transitions from the *current* state — preventing an out-of-date client from silently overwriting a newer backup with a stale one.

The canonical encoding and BLAKE3 hashing are computed by Bedrock so they're identical across iOS and Android. See [`BackupManifest`](https://github.com/worldcoin/bedrock/blob/main/bedrock/src/backup/manifest.rs) for the implementation.

# State Transitions

A successful update transitions the backup from state $m_t$ (the current manifest hash) to $m_{t+1}$ (the updated manifest hash). The rules:

* **Client.** Bears responsibility for performing updates against the current state. Verifies it holds the latest manifest before making changes.
* **Backup Service.** Maintains the current state hash. Validates each update is a transition from the recorded state. **Cannot** inspect encrypted contents.

If the client tries to sync from a stale state, the service rejects with `manifest_hash_mismatch` (HTTP 412). The client must re-authenticate a <MainFactor />, download the latest <SealedBackup />, apply local changes on top, and retry. See [Multiple Device Sync](/world-app/backup/advanced#multiple-device-sync) for why this happens in practice.

# ManifestManager API

Clients interact with the manifest through Bedrock's `ManifestManager`. It gates every operation on a fresh remote-hash check before mutating local state, so the client cannot accidentally produce an update from a stale base:

* `store_file(designator, path)` — Add a file. Computes its checksum, appends to the manifest, packs and uploads.
* `list_files(designator)` — List files for a designator.
* `replace_all_files_for_designator(designator, new_path)` — Atomically replace every file under a designator with a new one.
* `remove_file(path)` — Remove a file entry.

If the local manifest is missing or its hash differs from the remote, every method short-circuits with `RemoteAheadStaleError`. The client surfaces this as a prompt to re-authenticate and download the latest backup before retrying the change.

See [`ManifestManager`](https://github.com/worldcoin/bedrock/blob/main/bedrock/src/backup/manifest.rs) for signatures and behavior.

# Pack & Upload

When building a new backup version (see [BF-4: Sync](/world-app/backup/flows#bf-4-backup-sync)):

1. Validate the local manifest exists and matches the remote state.
2. Build the <UnsealedBackup /> from the files referenced in the manifest. Verify each file's checksum.
3. Encrypt with the <SealedBackup />'s keypair public key.
4. POST `/v1/sync` with the new <SealedBackup />, the current manifest hash, and the new manifest hash.
5. Backup Service checks the current hash matches its recorded state, then atomically swaps the blob and the recorded hash.

# Download & Unpack

When restoring or catching up (see [BF-2](/world-app/backup/flows#bf-2-backup-retrieval) and [BF-5](/world-app/backup/flows#bf-5-remote-update-detection)):

1. Decrypt the <SealedBackup /> with the <MainFactor />-unlocked <BackupEncryptionKeypair />.
2. Replace the local manifest unconditionally with the manifest from the unpacked backup.
3. Write each file to its declared path. On checksum mismatch the file is overwritten with the version from the backup and the mismatch is logged — the backup is the source of truth.

# Startup Verification

On every app launch the client fetches the remote manifest hash. If it differs from the local hash, the user is prompted to re-authenticate a <MainFactor /> and download the remote backup. This handles the multi-device case where the user updated their backup from Device A and then opens World App on Device B.

# Backend Safety Measures

* **Short-lived locks.** The Backup Service holds a short-lived per-`backup_id` lock during create and sync to close the narrow race window between the hash check and the write. The authoritative consistency check remains the manifest hash comparison.
