Plugins
Extension points for recovery, storage, and hardware-backed sealing — P256Plugin, PasswordRecoveryPlugin, SocialRecoveryPlugin, EncryptedFileStorage, HardwareWrappedStorage.
Plugins
The MPC SDK ships extension points along three seams — recovery,
storage, and hardware sealing — so you can wire EmbeddedAgent
into whichever credential / persistence / device flow your app already
uses. Every plugin is opt-in; the SDK works end-to-end with no plugin
configured (the default is in-memory storage + RecoveryKind.Noop).
| Seam | What it does | Shipped plugins |
|---|---|---|
| Recovery | Seals the recovery share against a credential the user holds (password / guardians / custody pubkey). | PasswordRecoveryPlugin, SocialRecoveryPlugin, NoopRecoveryPlugin |
| Storage | Persists the device share between sessions. | EncryptedFileStorage, InMemoryStorage |
| Hardware sealing | Wraps any KeyStorage with a key only the device holds (Secure Enclave / Keystore). | P256Plugin (the new ECIES wrapper), HardwareWrappedStorage (composer) |
Pick one from each row that matches your platform. All examples below assume the SDK is initialised:
import { Mpc } from '@zafeguard/mpc-sdk';
await Mpc.init(process.env.ZAFEGUARD_LICENSE_CERT!);P256Plugin — iOS Secure Enclave / ECIES
Seals the device share under a P-256 public key using the SDK's built-in ECIES primitive. The private half stays in the Secure Enclave (or any other hardware that exposes ECDH on a P-256 key); the open half delegates back to your bridge through a callback.
Pair with HardwareWrappedStorage to bind the at-rest blob to the
specific Secure Enclave key the device generated at first launch.
import {
EmbeddedAgent,
HardwareWrappedStorage,
InMemoryKeyStorage,
P256Plugin,
Curve,
RecoveryKind,
} from '@zafeguard/mpc-sdk';
// Public half lives in the SDK. Private half lives in the Secure
// Enclave — the `unwrap` callback bridges into SecKey / CryptoKit.
const license = await Mpc.init(process.env.ZAFEGUARD_LICENSE_CERT!);
const publicKeyBytes = await SecureEnclave.getPublicKey('mpc-key');
const sealer = new P256Plugin({
publicKeyBytes,
licenseFingerprint: license.fingerprint,
unwrap: ({ ciphertext, licenseFingerprint }) =>
SecureEnclave.ecdhUnwrap('mpc-key', ciphertext, licenseFingerprint),
});
const storage = new HardwareWrappedStorage({
sealer,
backend: new InMemoryKeyStorage(),
});
const agent = new EmbeddedAgent({
agents: [{ baseUrl: process.env.MPC_NODE_URL!, apiKey: process.env.MPC_NODE_API_KEY! }],
threshold: 2,
curve: Curve.Secp256k1,
});
const keyShare = await agent.create({
keyId: `mpc-${Date.now()}`,
recovery: { kind: RecoveryKind.Noop },
passphrase: 'derived-from-secure-enclave',
});
// Persist the share under the Secure-Enclave-sealed wrapper.
await storage.put(keyShare.keyId, keyShare);Use it for: iOS apps that want the device share at-rest to require biometric unlock (Face ID / Touch ID) on every load.
PasswordRecoveryPlugin — passphrase-sealed recovery
Argon2id-derives a key from a user passphrase, then AES-256-GCM-seals
the recovery share to it. The seal is bound to
(licenseFingerprint, keyId, epoch) so a leaked blob cannot be
opened against a different licensee.
import {
EmbeddedAgent,
PasswordRecoveryPlugin,
Curve,
RecoveryKind,
} from '@zafeguard/mpc-sdk';
const recovery = new PasswordRecoveryPlugin({
password: 'a strong passphrase the user remembers',
});
const agent = new EmbeddedAgent({
agents: [{ baseUrl: process.env.MPC_NODE_URL!, apiKey: process.env.MPC_NODE_API_KEY! }],
threshold: 2,
curve: Curve.Secp256k1,
});
const keyShare = await agent.create({
keyId: `mpc-${Date.now()}`,
recovery: { kind: RecoveryKind.Password, password: recovery.password },
passphrase: 'cold-backup-passphrase',
});
// Persist `keyShare.recoveryBundle` to a safe place — the user's
// password recovers it after a device wipe.Use it for: consumer wallets where the only recovery factor is something the user memorises.
SocialRecoveryPlugin — multi-guardian Shamir split
Shamir-splits the recovery share across N guardian X25519 public keys; reassembly needs threshold-many of them.
import {
EmbeddedAgent,
SocialRecoveryPlugin,
Curve,
RecoveryKind,
} from '@zafeguard/mpc-sdk';
const recovery = new SocialRecoveryPlugin({
threshold: 2,
recipients: [guardian1PubKeyHex, guardian2PubKeyHex, guardian3PubKeyHex],
});
const agent = new EmbeddedAgent({
agents: [{ baseUrl: process.env.MPC_NODE_URL!, apiKey: process.env.MPC_NODE_API_KEY! }],
threshold: 2,
curve: Curve.Ed25519,
});
const keyShare = await agent.create({
keyId: `mpc-${Date.now()}`,
recovery: {
kind: RecoveryKind.Social,
socialThreshold: recovery.threshold,
socialRecipientsHex: recovery.recipients,
},
passphrase: 'cold-backup-passphrase',
});Use it for: any flow where the user picks trusted contacts to hold recovery slivers — group accounts, family wallets, DAO co-signers.
EncryptedFileStorage — desktop / Node.js persistence
Passphrase-wrapped file backend. Stores each key share under a label in the configured directory; opening requires the same passphrase.
import {
EmbeddedAgent,
EncryptedFileStorage,
Curve,
RecoveryKind,
} from '@zafeguard/mpc-sdk';
const storage = new EncryptedFileStorage({
dir: './keys',
passphrase: process.env.STORAGE_PASSPHRASE!,
});
const agent = new EmbeddedAgent({
agents: [{ baseUrl: process.env.MPC_NODE_URL!, apiKey: process.env.MPC_NODE_API_KEY! }],
threshold: 2,
curve: Curve.Secp256k1,
});
const keyShare = await agent.create({
keyId: `mpc-${Date.now()}`,
recovery: { kind: RecoveryKind.Noop },
passphrase: 'cold-backup-passphrase',
});
await storage.put(keyShare.keyId, keyShare);Use it for: server-side custody backends, desktop wallets, headless signing services.
HardwareWrappedStorage — compose any sealer with any backend
HardwareWrappedStorage is the composer that pairs an OpaqueSealer
(P256Plugin or a custom one) with any KeyStorage
(InMemoryKeyStorage, IndexedDB adapter, localStorage adapter,
your own). The share's serialisation goes through the sealer; the
resulting opaque blob lands in the raw backend.
import {
HardwareWrappedStorage,
InMemoryKeyStorage,
P256Plugin,
} from '@zafeguard/mpc-sdk';
const storage = new HardwareWrappedStorage({
sealer: new P256Plugin({ /* ...as above... */ }),
backend: new InMemoryKeyStorage(),
// Bind blobs to the keyId + epoch as well as the label.
additionalAad: Buffer.from(`keyId=${keyId}|epoch=${epoch}`),
});Use it for: combining a hardware-bound key with a platform-specific persistence layer (IndexedDB, AsyncStorage, cloud KV).
Writing a custom plugin
Every plugin slot is an interface — implement it against your own backend.
import { type OpaqueSealer, PluginId } from '@zafeguard/mpc-sdk';
class MyKmsSealer implements OpaqueSealer {
readonly id = 'my-kms';
async seal(plaintext: Uint8Array, aad?: Uint8Array): Promise<Uint8Array> {
return await myKmsClient.wrap(plaintext, aad);
}
async open(ciphertext: Uint8Array, aad?: Uint8Array): Promise<Uint8Array> {
return await myKmsClient.unwrap(ciphertext, aad);
}
}Drop the custom sealer into HardwareWrappedStorage exactly like the
shipped P256Plugin — the SDK never reaches for the raw key material.
Utilities
Pure-function helper classes — public-key encoding, hashing, payload envelope wrapping, and library initialization.
SDK boundaries
Honest disclosure of what the SDK does and does not support. Read this before scoping a system that needs air-gapped DKG, multi-party board signing, custom MPC protocols, or compliance flows the SDK doesn't ship today.