Export & Import Key Share
This example shows how to securely back up or migrate an MPC key share to another node. The share is wrapped with age encryption so the key material never travels in plaintext — only the encrypted blob is transferred.
Use case
- Backup: Export a key share from a primary MPC node and store the encrypted blob in cold storage.
- Migration: Move a key share to a new MPC node (e.g. when rotating infrastructure).
- Key escrow: Hand off encrypted key shares to a trusted third party without exposing the raw key.
Workflow canvas
GET_NODE_RECIPIENT_KEY (target node)
│ recipientKey (age1...)
▼
GENERATE_AGE_ENCRYPTION
│ publicKey, encryptedPrivateKey
▼
EXPORT_KEY_SHARE (keyId, ageRecipient: recipientKey)
│ wrappedKeyShare
▼
[transfer wrappedKeyShare out-of-band]
│
▼
IMPORT_KEY_SHARE (wrappedKeyShare → target node)
│ keyId (new ID on target node)
TypeScript example
import { WorkspaceClient, ComponentModule } from 'caller-sdk';
const workspace = new WorkspaceClient({ apiKey: process.env.WR_API_KEY! });
// The keyId of the key share you want to export/migrate
const sourceKeyId = 'YOUR_EXISTING_KEY_ID';
// -------------------------------------------------------------------
// Step 1: Fetch the target MPC node's age public key
// This is the recipient key that will wrap (encrypt) your key share.
// -------------------------------------------------------------------
const recipientResult = await workspace.call(ComponentModule.GET_NODE_RECIPIENT_KEY, {}).promise();
const { recipientKey } = recipientResult;
console.log('Target node recipient key:', recipientKey); // age1...
// -------------------------------------------------------------------
// Step 2: (Optional) Generate a fresh age identity for your own backup
// If you want a personal copy encrypted to your own key, use this step.
// Skip if you only need to import directly into the target node.
// -------------------------------------------------------------------
const ageResult = await workspace.call(ComponentModule.GENERATE_AGE_ENCRYPTION, {}).promise();
const { publicKey: myAgePublicKey, encryptedPrivateKey: myAgeEncryptedPrivateKey } = ageResult;
// Store myAgeEncryptedPrivateKey securely — you'll need it to decrypt later.
await secrets.store('age-private-key', myAgeEncryptedPrivateKey);
// -------------------------------------------------------------------
// Step 3: Export the key share, encrypted to the target node's key
// The wrappedKeyShare is safe to transmit — it cannot be decrypted
// without the target node's private key.
// -------------------------------------------------------------------
const exportResult = await workspace.call(ComponentModule.EXPORT_KEY_SHARE, {
keyId: sourceKeyId,
ageRecipient: recipientKey, // encrypt to the target node
}).promise();
const { wrappedKeyShare } = exportResult;
console.log('Wrapped key share (safe to transfer):', wrappedKeyShare.slice(0, 40) + '...');
// -------------------------------------------------------------------
// Step 4: Transfer the wrappedKeyShare to the target environment
// This can be done via any channel — database, API, file, etc.
// The blob is encrypted and safe to transmit.
// -------------------------------------------------------------------
// -------------------------------------------------------------------
// Step 5: Import the wrapped key share into the target MPC node
// The target node decrypts it with its own private key and stores the share.
// -------------------------------------------------------------------
const importResult = await workspace.call(ComponentModule.IMPORT_KEY_SHARE, {
wrappedKeyShare,
}).promise();
const { keyId: newKeyId } = importResult;
console.log('New keyId on target node:', newKeyId);
// Persist newKeyId — this is the identifier for the key on the new node.
await db.saveKeyId(newKeyId);
Re-wrapping for identity rotation
If you need to re-encrypt an existing wrapped share to a new age identity (without importing it first), use REWRAPPING_KEY_SHARE:
// Re-encrypt from old identity to new identity — key material never exposed
const rewrapResult = await workspace.call(ComponentModule.REWRAPPING_KEY_SHARE, {
ageIdentity: oldAgePrivateKey, // decrypts the current wrapping
wrappedKeyShare: existingWrappedShare,
}).promise();
const { rewrappedKeyShare } = rewrapResult;
// Now encrypted to the new identity — import or store as needed
Notes
GET_NODE_RECIPIENT_KEYreturns the target node's age public key — it is used as the encryption recipient so only that node can unwrap the share.GENERATE_AGE_ENCRYPTIONis useful when you want a personal backup copy encrypted to your own key rather than (or in addition to) the node's key.- The
wrappedKeyShareblob is ciphertext — it is safe to store in databases, object storage, or transmit over networks. - After a successful
IMPORT_KEY_SHARE, the newkeyIdis independent from the sourcekeyId. Both can coexist in the network.