Vault & Key Backup
Back up and restore MPC keyshares with password-encrypted cloud storage.
The vault module provides secure keyshare backup and recovery through the embarkAI ShareVault service. Keyshares are encrypted client-side before upload — the server never sees plaintext key material.
Configuration
import { configureVaultModule } from "@embarkai/core";
configureVaultModule({
tssUrl: "https://tss.embarkai.io", // optional override
shareVaultUrl: "https://vault.embarkai.io", // optional override
});Default URLs are resolved automatically for supported chains.
The vault functions take a ServerWalletClient authenticated with your project API key:
import { createServerWalletClient } from "@embarkai/core/clients";
const client = createServerWalletClient({
apiKey: process.env.EMBARK_API_KEY!,
});Check Backup Status
Verify whether a backup exists for a user before attempting restore:
import { checkBackupExists } from "@embarkai/core";
import { createServerWalletClient } from "@embarkai/core/clients";
const client = createServerWalletClient({ apiKey: process.env.EMBARK_API_KEY! });
const { hasBackup, serviceAvailable } = await checkBackupExists(
client,
"user_abc123",
);
if (!serviceAvailable) {
console.warn("Vault service is temporarily unavailable");
} else if (hasBackup) {
console.log("Backup found, ready to restore");
}Back Up a Keyshare
import { backupToVault } from "@embarkai/core";
import type { KeyshareBackupData } from "@embarkai/core";
import { createServerWalletClient } from "@embarkai/core/clients";
const client = createServerWalletClient({ apiKey: process.env.EMBARK_API_KEY! });
const backupData: KeyshareBackupData = {
userId: "user_abc123",
sessionId: "session_xyz",
keyshare: "...base64-encoded-keyshare...",
ownerAddress: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
createdAt: Date.now(),
version: "1",
};
await backupToVault(client, backupData, "user-chosen-password");Restore a Keyshare
import { restoreFromVault } from "@embarkai/core";
import { createServerWalletClient } from "@embarkai/core/clients";
const client = createServerWalletClient({ apiKey: process.env.EMBARK_API_KEY! });
const recovered = await restoreFromVault(
client,
"user_abc123",
"user-chosen-password",
);
console.log("Owner:", recovered.ownerAddress);
console.log("Session:", recovered.sessionId);
// recovered.keyshare is now available for signingIf the password is incorrect, restoreFromVault throws an error. There is no password reset mechanism — the encryption is client-side and the server has no access to the plaintext.
KeyshareBackupData Type
| Field | Type | Description |
|---|---|---|
userId | string | User identifier |
sessionId | string | DKG session that produced this keyshare |
keyshare | string | Base64-encoded keyshare data |
ownerAddress | `0x${string}` | EOA address derived from the TSS public key |
createdAt | number | Timestamp of keyshare creation (ms) |
version | string | Backup format version |
Encryption Details
All encryption happens client-side before any data leaves the device:
- Key Derivation — The user password is stretched using PBKDF2 with 150,000 iterations and SHA-256 to produce a Key Encryption Key (KEK).
- Data Encryption Key — A random 256-bit Data Encryption Key (DEK) is generated per backup.
- Envelope Encryption — The keyshare payload is encrypted with AES-256-GCM using the DEK. The DEK is then wrapped (encrypted) with the KEK.
- Storage — The encrypted envelope (wrapped DEK + ciphertext + IV + salt) is uploaded to the ShareVault. The server stores only ciphertext and cannot decrypt without the password.
This envelope encryption scheme means changing the password only requires re-wrapping the DEK, not re-encrypting the entire payload.
Usage with ServerWalletManager
ServerWalletManager provides convenient shorthand methods that handle KeyshareBackupData assembly automatically:
import {
createServerWalletManager,
MemoryKeyshareStorage,
} from "@embarkai/core";
const manager = createServerWalletManager({
apiKey: process.env.EMBARK_API_KEY!,
chainId: 994873017,
storage: new MemoryKeyshareStorage(),
});
// Back up a wallet's keyshare
await manager.backupToVault("treasury_main", "secure-password");
// Check if a backup exists
const { hasBackup } = await manager.checkBackupExists("treasury_main");
// Restore keyshare to local storage
await manager.restoreFromVault("treasury_main", "secure-password");Security Notes
- Password strength matters. PBKDF2 slows brute-force attacks, but a weak password is still vulnerable. Enforce minimum length and complexity requirements in your UI.
- No password recovery. The server cannot decrypt backups. If the user forgets their password, the backup is permanently inaccessible.
- One backup per user. A new backup overwrites the previous one. Coordinate backup timing if multiple devices share the same user ID.
- Audit restore attempts. Log failed restore attempts to detect potential brute-force attacks against user passwords.
Next Steps
- Server Wallets — create wallets and use
manager.backupToVault()shorthand - Authentication — set up authenticated sessions required for vault API calls
- Bundler — submit transactions after restoring a keyshare