Recovery Kits
Create, verify, restore, and replace password-protected Recovery Kits without exposing vault keys to the server.
A Recovery Kit is a versioned JSON file containing the root vault key encrypted with a key derived from the user’s recovery secret. It never contains the recovery secret or plaintext root key. Kit creation, verification, and opening happen in the browser crypto engine; Ownfold transports send only the active kit identifier as coordination metadata.
The kit is the mandatory emergency fallback established during strict onboarding. For routine new-browser access, applications should prefer dedicated Ownfold passkey vault access when WebAuthn PRF is available, then use trusted-device pairing or this kit as fallback paths.
Losing every authorized device, usable passkey method, and the active Recovery Kit or its secret permanently loses access to encrypted records. The application cannot reset a login password to recover vault data.
Initial setup
createRecoveryKit(secret) generates the file locally. The user must download and re-import the
same file with the same secret before verifyRecoveryKit enrolls the browser and marks recovery as
verified. The host interface must preserve this safety gate and require the actual saved file.
Applications can configure both the authenticated format identifier and downloaded filename once:
const recoveryKitFile = createRecoveryKitFile({
format: "mori.recovery-kit",
filename: (kit) => `mori-recovery-${kit.vaultId}.json`,
})
const vault = createVaultClient({ transport, recoveryKit: recoveryKitFile })
Use recoveryKitFile.download(kit) for initial, replacement, and rotation kits. Format identifiers
must be lowercase application identifiers ending in .recovery-kit and contain at most 128
characters. The format is authenticated; renaming the JSON field after creation invalidates the
kit. Ownfold continues to parse older ownfold.recovery-kit files, so adding branding does not
strand existing users.
Standalone replacement
Changing a recovery password must not require root-key rotation. Replacement keeps the current root key and key version, creates a fresh salt, nonce, kit identifier, and password-derived wrapping key, then requires local re-import before cutover:
const prepared = await vault.prepareRecoveryKitReplacement(newSecret)
if (prepared.status === "ok") RecoveryKitFile.download(prepared.value)
const completed = await vault.completeRecoveryKitReplacement(importedContents, newSecret)
useRecoveryKit() exposes prepareReplacement, completeReplacement, and
getPendingReplacement. The host interface owns the complete download, re-import, and activation
flow; the hook never downloads a file automatically.
Activation is a compare-and-swap metadata update using the current kit ID and vault revision. A stale tab cannot overwrite a newer kit. After activation, restoration rejects older kits because their identifier no longer matches the authenticated vault. Encrypted records, record data keys, device envelopes, and the root key are unchanged.
Identifier rejection is an SDK and server-coordination control, not cryptographic erasure. An old kit still contains the same root key encrypted under its old secret. If an attacker may possess both the old file and its secret, standalone replacement does not remove that capability: rotate the root key and rewrap record and device envelopes instead.
The pending replacement file is persisted in IndexedDB before the server update. If the server commits but its success response is lost, the next initialization compares the pending kit ID with remote metadata, finishes the local update, and clears pending state. Neither the recovery secret nor Recovery Kit contents cross the transport boundary.
Replacement versus rotation
Use standalone replacement for routine credential maintenance or when only one recovery factor is suspected exposed. Use root-key rotation when both the old file and secret may be known, or when the root key itself may be compromised; rotation rewraps record data keys and replaces every active device envelope in addition to creating a new Recovery Kit.