Skip to main content

MPC Sessions & Key Generation

The Silent Shard SDK works through these session types:

  • ECDSA session — used for EVM chains (Ethereum, Base, etc.)
  • EdDSA session — used for Solana
  • ML-DSA session — post-quantum signing path, available on Duo only

Each session represents the device-side half of the MPC key pair. The cloud-side half lives on duo-server. Neither side ever has the full key.

SDK session bootstrap

Sessions are initialized once on app startup in libs/silent-shard/index.ts, after hooks/service-config/useServiceConfig.ts loads the selected Duo / Trio backend preset:

// libs/silent-shard/index.ts
async initSessions(config: ServiceConfig, messageSigner: MessageSigner) {
const url = new URL(config.cloudNodeUrl);
const isSecure = url.protocol === 'https:';
const stripeHttp = `${url.hostname}${url.port ? `:${url.port}` : ''}`;
const cloudClient = new CloudWebSocketClient(stripeHttp, isSecure);

const sessionConfig = {
client: cloudClient, // WebSocket connection to duo-server
cloudVerifyingKey: config.cloudNodeVerifyingKey,
storage: storageProvider, // Persists keyshares via expo-secure-store
messageSigner, // Signs SDK transport messages using the device TEE key
};

if (config.cloudNodeType.toLowerCase() === 'trio') {
this._ecdsaSession = await createEcdsaTrioSession(sessionConfig);
this._eddsaSession = await createEddsaTrioSession(sessionConfig);
this._mldsaSession = undefined;
} else {
this._ecdsaSession = await createEcdsaDuoSession(sessionConfig);
this._eddsaSession = await createEddsaDuoSession(sessionConfig);
this._mldsaSession = await createMldsaDuoSession(sessionConfig);
}
}

export const silentShardSDK = new SilentShardSDK();

The messageSigner comes from libs/silent-shard/secure-key.ts — it wraps the device's TEE key and signs every message sent to the cloud node, preventing tampering in transit.

MPC protocol session setup

The diagram below focuses on the SDK transport and MPC session state prepared by initSessions(config, messageSigner). It is the setup that lets later keygen() and signing calls exchange authenticated DKG/DSG protocol messages with the cloud node.

Keyshare storage

Keyshares are persisted automatically via libs/silent-shard/storage-provider.ts, which wraps expo-secure-store. This means sessions survive app restarts — on the next launch, the SDK reloads the keyshares from secure storage and the sessions are ready to sign without needing keygen again.

Key generation

Key generation (keygen) runs once to create the wallet. It is triggered from the Create Wallet screen via queries/useKeygen.ts:

// queries/useKeygen.ts
for (const protocol of supportedProtocols) {
switch (protocol) {
case 'ecdsa':
setEcdsaMpcShare(await silentShardSDK.ecdsaSession.keygen());
break;
case 'eddsa':
setEddsaMpcShare(await silentShardSDK.eddsaSession.keygen());
break;
case 'mldsa':
setMldsaMpcShare(await silentShardSDK.mldsaSession.keygen('MlDsa44'));
break;
}
}

Each keygen() call triggers the MPC protocol between the device and duo-server. duo-server checks auth-svc via hook before proceeding. On completion, each party stores their share — the device in expo-secure-store, duo-server on its own storage.

Duo and Trio both generate ECDSA and EdDSA keyshares. ML-DSA keyshares are supported on Duo only.

The resulting keyshare object contains publicKeyHex, which is used to derive the wallet address (see Multi-Chain Signing).

Keygen is a one-time operation per device. The keyshare ID is persisted to walletStore (MMKV). If the app is uninstalled or the device keystore is cleared without a backup, the wallet cannot be recovered.