# Rotate your revocable key

> Bump the revocable key index without losing notes or re-depositing, for key hygiene or compromise response.

## What this lets you do

Rotate your account's revocable key to a new `revocableKeyIndex`. Because the [revocable key](/concepts/keys) participates in authorization material, rotating it limits the blast radius if it's compromised, without forcing you to re-deposit or re-derive your other keys.

## Constraints & limits

-   One on-chain transaction, `Keystore.updateAuthPolicy`, taking the old and new `authDigest` plus a Merkle proof of your existing keystore leaf. It preserves the bound nullifying-key hash and swaps only the revocable-key `authDigest`.
-   Doesn't migrate or re-create your existing [notes](/concepts/notes). They stay spendable, but only with your current key bundle: a spend proves your current keystore leaf, so after rotating you use the new revocable key, not the old one. The nullifying key and the notes themselves don't change.
-   The index is monotonically increasing. You can't roll back to a previous index without re-rotating forward.
-   For a fresh account, the canonical starting value is `0x0` (no rotation performed yet).

## What it unlocks next

-   Post-rotation, your `ProtocolKeys` bundle must include the new `revocableKeyIndex` when building a session.
-   Compromise-response workflow: detect the leak, rotate, then revoke / blacklist the old auth material.

## How to use it

```ts

// The SDK does not ship an EIP-712 payload builder; construct the
// Signature-Based Derivation typed data yourself. It must match the
// payload signed at registration byte-for-byte.
const signerAddress = walletClient.account.address;
const addressHash   = keccak256(toBytes(signerAddress));
const purpose =
    "This signature is used to deterministically derive application-specific secrets from your master seed. It is not a transaction and will not cost any gas.";

const signature = await walletClient.signTypedData({
    domain: {
        name: "Standardized Secret Derivation",
        version: "1",
        verifyingContract: "0x0000000000000000000000000000000000000000",
        salt: keccak256(toBytes(APP_IDENTIFIER)), // exported by the SDK
    },
    types: {
        SecretDerivation: [
            { name: "purpose", type: "string" },
            { name: "addressHash", type: "bytes32" },
        ],
    },
    primaryType: "SecretDerivation",
    message: { purpose, addressHash },
});

const { newRevocableKeyIndex } = await session.rotateRevocableKey({
    signature,
    signerAddress,
    addressHash,
});

// The live session already applied the rotation internally. Future
// session builds must re-derive the key bundle at the new index: the
// revocable key is derived per-index, so the pre-rotation bundle no
// longer matches the on-chain auth digest.
const rotatedKeys = new CryptoService().deriveKeysFromSignature({
    signature,
    signerAddress,
    addressHash,
    revocableKeyIndex: newRevocableKeyIndex,
});
const newSession = await PoolSessionBuilder.fromConfig({
    ...baseConfig,
    protocolKeys: { ...rotatedKeys, revocableKeyIndex: newRevocableKeyIndex },
}).create();
```

The SDK derives the next revocable key from the signature and returns the new index. Persist `newRevocableKeyIndex`, and on later session builds re-derive the key bundle at that index; reusing a pre-rotation `privateRevocableKey` with the new index fails every proof.

## Behind the scenes

SDK call `session.rotateRevocableKey(...)`
Contract method `Keystore.updateAuthPolicy(oldAuthDigest, newAuthDigest, siblingNodes)`: rotation, not `setAuthPolicy` (which is first-time registration)
State change Your account's auth policy on Keystore is updated and revocableKeyIndex advances. The on-chain nullifying-key hash is unchanged.
