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).

SeamWhat it doesShipped plugins
RecoverySeals the recovery share against a credential the user holds (password / guardians / custody pubkey).PasswordRecoveryPlugin, SocialRecoveryPlugin, NoopRecoveryPlugin
StoragePersists the device share between sessions.EncryptedFileStorage, InMemoryStorage
Hardware sealingWraps 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.

On this page