Recovery codes
A stored code that restores Starknet (and grandfathered Stellar). Native Solana and Stellar restore the MasterDEK instead.
Cavos cannot reset a wallet or authorize a replacement device by itself. The baseline recovery path costs nothing and involves no server: a high-entropy recovery code, generated on a device the user already controls, which derives a backup authority the account recognizes.
The trade-off is the one every code-based scheme has — the user has to keep it. For recovery that works by signing in again instead, see Hardware-isolated recovery.
Recovery capabilities
| Chain | Recovery code role | Restore path |
|---|---|---|
| Starknet | Derives a backup P-256 signer registered on the account | Cavos.recover(...) authorizes the fresh device |
| Solana | Not used. Native accounts have one spend key | Enclave login or approveDeviceWithPasskey() |
| Stellar (enclave) | Same MasterDEK wrap as Solana | Sign in again |
| Stellar (grandfathered) | Extra ed25519 Horizon signer | Reconnect, then approveThisDeviceWithRecovery(code) |
Set up once
Generate a code on a connected device, show it once, and ask the user to store it offline:
import { generateRecoveryCode } from "@cavos/kit";
const code = generateRecoveryCode();
await wallet.setupRecovery(code);
// Display `code` once. Never send it to analytics, logs, or your backend.setupRecovery is adapter-specific. Native Solana/Stellar ignore it. On
Starknet it is idempotent for the same factor and works on both undeployed
and ready wallets. For undeployed Starknet wallets, the recovery factor is
stored pending and included when the account is created on first execute.
Anyone with the recovery code may be able to restore wallet authority. Treat it like a private key: never log it, never email it in plaintext, and never persist it in application storage.
Restore Starknet
import { Cavos } from "@cavos/kit";
const wallet = await Cavos.recover({
code,
identity: { userId: user.id, email: user.email },
network: "testnet",
appSalt: "my-app",
appId: process.env.NEXT_PUBLIC_CAVOS_APP_ID,
paymasterApiKey, // obtain through your trusted sponsorship integration
});The derived backup signer authorizes the new local device signer. Cavos coordinates submission but cannot manufacture that authorization.
Restore Solana
Do not call CavosSolana.recover. Native Solana restores the MasterDEK:
// Enclave: the same Google/Apple login on the new device unwraps the DEK.
// Passkey:
const { approveDeviceWithPasskey } = useCavos();
await approveDeviceWithPasskey();Restore Stellar
Native DEK: do not call approveThisDeviceWithRecovery. Sign in again
(enclave) or approveDeviceWithPasskey().
Grandfathered:
import { Cavos } from "@cavos/kit";
const session = await Cavos.connect({
chains: ["stellar"],
defaultChain: "stellar",
network: "testnet",
identity: { userId: user.id, email: user.email },
appSalt: "my-app",
appId: process.env.NEXT_PUBLIC_CAVOS_APP_ID,
});
const wallet = session.wallet("stellar");
if (
wallet.chain === "stellar" &&
wallet.status === "needs-device-approval" &&
!wallet.nativeDek
) {
await wallet.approveThisDeviceWithRecovery(code);
}The recovery factor is itself a Horizon signer. It setOptions the fresh
device's ed25519 key onto the account.
Product guidance
- Encourage a second device or synced passkey before relying on a recovery code.
- Verify recovery on physical iOS and Android devices before production release.
- Treat the code as one factor among several, not the plan. Most users will not keep it, which is the problem hardware-isolated recovery exists to solve on Starknet, Solana, and native Stellar. Grandfathered Stellar wallets still use the code (or a synced passkey extra signer) as the way back from nothing.
- If the user loses every device and every recovery factor, and — on Starknet, Solana, or native Stellar — social recovery was never enrolled, Cavos cannot restore access. That is the cost of the guarantee, not a gap in it.
See Multi-device, Passkeys, and the selected chain guide.