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

# Save and restore SDK recovery data

Store the recovery key with its envelope and restore the same private bundle before handling value.

The SDK recovery backup contains two pieces: the 32-byte recovery key and the encrypted recovery envelope. Keep them together in a secret-bearing file or encrypted local store.

## Save recovery data

The starter serializes one small JSON record and validates it on import.

```ts
import type {
  Nullark,
  NullarkRecoveryEnvelope,
  PreparedBundledChildDeposit
} from "@nullark/sdk";

const RECOVERY_SCHEMA = "nullark-sdk-starter-recovery-v1" as const;

export type SavedRecoveryData = Readonly<{
  schema: typeof RECOVERY_SCHEMA;
  recoveryKey: string;
  envelope: NullarkRecoveryEnvelope;
}>;

export function saveRecoveryData(
  deposit: PreparedBundledChildDeposit,
  recoveryKey: Uint8Array
): string {
  if (!(recoveryKey instanceof Uint8Array) || recoveryKey.length !== 32) {
    throw new Error("Recovery key must contain exactly 32 bytes.");
  }

  return JSON.stringify({
    schema: RECOVERY_SCHEMA,
    recoveryKey: bytesToHex(recoveryKey),
    envelope: deposit.recovery.envelope
  } satisfies SavedRecoveryData, null, 2);
}

export function readRecoveryData(raw: string): {
  recoveryKey: Uint8Array;
  envelope: NullarkRecoveryEnvelope;
} {
  let value: unknown;
  try {
    value = JSON.parse(raw);
  } catch {
    throw new Error("Recovery backup is not valid JSON.");
  }

  if (!isRecord(value) || value.schema !== RECOVERY_SCHEMA) {
    throw new Error("Recovery backup uses an unsupported schema.");
  }
  if (typeof value.recoveryKey !== "string" || !/^[0-9a-fA-F]{64}$/.test(value.recoveryKey)) {
    throw new Error("Recovery backup contains an invalid recovery key.");
  }
  if (!isRecord(value.envelope)) {
    throw new Error("Recovery backup is missing its recovery envelope.");
  }

  return {
    recoveryKey: hexToBytes(value.recoveryKey),
    envelope: value.envelope as NullarkRecoveryEnvelope
  };
}

export async function restoreFromRecoveryData(nullark: Nullark, raw: string) {
  const saved = readRecoveryData(raw);
  return nullark.recovery.restore(saved);
}

function bytesToHex(bytes: Uint8Array): string {
  return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
}

function hexToBytes(hex: string): Uint8Array {
  return Uint8Array.from(hex.match(/.{2}/g) ?? [], (byte) => Number.parseInt(byte, 16));
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return !!value && typeof value === "object" && !Array.isArray(value);
}
```

## Restore from the backup

`restoreFromRecoveryData` parses the file, rejects malformed keys and envelopes, and asks the SDK to reconstruct the bundle against the active runtime.

Run this restore once before submitting the first deposit. A successful roundtrip proves the app saved usable bytes, rather than a label or stale object reference.

## Read private balance

After confirmation, `balances.get` reads the pool's encrypted deposit events, opens the matching bundles locally, and checks each note's nullifier.

```ts
import type { Nullark } from "@nullark/sdk";

export function readPrivateBalance(nullark: Nullark, recoveryKey: Uint8Array) {
  if (!(recoveryKey instanceof Uint8Array) || recoveryKey.length !== 32) {
    throw new Error("Recovery key must contain exactly 32 bytes.");
  }

  return nullark.balances.get({ recoveryKey });
}
```

| Result | Use |
| --- | --- |
| `availableBalanceWei` | Total value across recovered notes that remain unspent |
| `deposits[].bundle` | Restored bundle passed to withdrawal preparation |
| `deposits[].notes` | Child index, amount, commitment, nullifier, and current status |

Refresh this result after a confirmed deposit or withdrawal. If the event or nullifier reader fails, keep the previous display and show the read error.

> **Warning:**
> Anyone holding this backup can control the recovered notes. Keep it out of logs, analytics, URLs, support messages, and cloud clipboard history.

For the field-level envelope shape, see [Recovery data](/developers/recovery-format.md).
