# Export a backup

The backup export creates an encrypted archive of all key material in the vault. Store the resulting file in immutable, off-vault storage and guard the recovery keys that can decrypt it. This archive is the foundation of your disaster recovery plan.

See the full [Keys Backup API](/api-reference#tag/keysbackup) in the reference.

## Call the export endpoint

```
GET /v1/keys-backup
```

Despite being a `GET`, this endpoint is a state-changing operation: it triggers a `keys_backup_created` audit event and requires all four authentication headers (including nonce, timestamp, and assertion). The caller must have the `access_keys_backup` capability in the Administrative Policy.

The response is a ZIP archive (`application/zip`) containing a `backup.json` file. Stream or save it directly to disk; do not buffer large backups in memory.

**curl example:**

```bash
curl -s -o backup.zip \
  "$CV_BASE/v1/keys-backup" \
  -H "Authorization: ApiKey $API_KEY_UUID" \
  -H "X-Request-Timestamp: $TS_HEX" \
  -H "X-Request-Nonce: $NONCE_HEX" \
  -H "X-Request-Assertion: $ASSERTION"
```

**Node.js example (stream to file):**

```js
const fs = require('fs');

async function exportBackup(destPath) {
  const path   = '/v1/keys-backup';
  const method = 'GET';

  // Build auth headers for a GET with no body.
  const headers = buildAuthHeaders({ method, path, body: Buffer.alloc(0) });

  const res = await fetch(`${CV_BASE}${path}`, { method, headers });
  if (!res.ok) throw new Error(`Backup export failed: ${res.status} ${await res.text()}`);

  const writer = fs.createWriteStream(destPath);
  for await (const chunk of res.body) {
    writer.write(chunk);
  }
  writer.end();
  await new Promise((resolve, reject) => writer.on('finish', resolve).on('error', reject));
  console.log(`Backup written to ${destPath}`);
}
```

## What the archive contains

The `backup.json` inside the ZIP contains the encrypted key shares from every key store node. Each key is encrypted independently using hybrid encryption: RSA-OAEP-SHA-256 wraps a per-key ChaCha20-Poly1305 data encryption key. Each node has its own backup key, so the per-node encrypted shares are distinct.

The raw key material is never present in the archive in recoverable form without the corresponding recovery keys.

## Recovery keys

The archive is useless without the recovery keys held by each key store node. Those recovery keys are generated offline (or via HSM) during deployment, one per node. They are never stored inside the vault.

Keep the recovery keys in a location physically and logically separate from the backup archive. If an attacker has both the archive and the recovery keys, they can reconstruct key material.

## Restoring from backup

Restore is a deployment-time operation performed when rebuilding a cluster after data loss. It is not an API call against a running vault. See [Backup & disaster recovery](/deploy/operations/backup-dr) for the recovery procedure.

## Required permission

The `access_keys_backup` capability must be granted to the caller's group in the Administrative Policy.

:::tip
Automate backup export on a schedule (for example, nightly) and store the resulting archive in immutable cloud storage (AWS S3 Object Lock or GCP Bucket Lock). Each export produces a fresh `keys_backup_created` audit event, giving you a record of when backups were taken.
:::
