Skip to main content

Architecture

Both reference apps split into two layers:

  • App/ — UI, blockchain network code (RPC, address derivation), account state, faucet integration. Free to import any third-party library. Has no idea what a keyshare looks like.
  • Vault/VaultSessionManager, EncryptedStorageClient, EncryptedFileStorage, hardware-backed signing. The only place keyshare bytes ever touch disk. Vault types that aren't part of the public API surface are marked internal so the App layer can't reach them even by accident.

The split is a property of the example, not a requirement of the SDK — see Custom storage client for the minimum the SDK actually expects. The walls in the example exist so an integrator can copy the design knowing it has the property "the SDK is the only writer of keyshare bytes."

Source tree

silent-shard-sdk/with-android/duo-initiator/
├── app/ # Application module
│ └── src/main/kotlin/com/silencelaboratories/silentshard/demo/
│ ├── data/
│ │ ├── model/ # Chain, Token, WalletAccount
│ │ └── network/ # Ethereum / Solana / faucet RPC
│ ├── feature/
│ │ ├── setup/ # Wallet creation flow
│ │ ├── blockchain/ # Dashboard, send, fund, AccountManager
│ │ └── export/ # Backup & restore
│ ├── navigation/ # Routes, AppNavHost
│ ├── ui/ # Theme, components
│ ├── util/ # Constants, AddressUtils
│ ├── MainActivity.kt
│ └── WalletApp.kt
└── vault/ # Vault module (internal types)
└── src/main/kotlin/com/silencelaboratories/silentshard/demo/vault/
├── model/ # KeyResult
├── session/ # KeyType, VaultSessionManager
├── signing/ # HardwareMessageSigner, P256KeyManager
├── storage/ # EncryptedFileStorage, EncryptedStorageClient,
│ # VaultReconcileStoreDao
└── utils/ # VaultCryptoUtils, EllipticCurveEncodingUtils

How the SDK plugs in

The SDK exposes one factory call per algorithm. The example creates two sessions (one per KeyType), each bound to a dedicated storage client:

vault/.../session/VaultSessionManager.kt
private val ecdsaStorage = EncryptedStorageClient(store, KeyType.ECDSA)
private val eddsaStorage = EncryptedStorageClient(store, KeyType.EdDSA)

private val ecdsaSession: DuoSession =
SilentShard.ECDSA.createDuoSession(messageSigner, CLOUD_VERIFYING_KEY, websocketConfig, ecdsaStorage)

private val eddsaSession: DuoSession =
SilentShard.EdDSA.createDuoSession(messageSigner, CLOUD_VERIFYING_KEY, websocketConfig, eddsaStorage)
Vault/Session/VaultSessionManager.swift
self.ecdsaStorage = EncryptedStorageClient(store: fileStorage, keyType: .ecdsa)
self.eddsaStorage = EncryptedStorageClient(store: fileStorage, keyType: .eddsa)

let session = SilentShard.ECDSA.createDuoSession(
messageSigner: messageSigner,
cloudVerifyingKey: Self.cloudVerifyingKey,
websocketConfig: createWebsocketConfig(),
storageClient: ecdsaStorage
)

Why one storage client per KeyType

The SDK's ReconcileStoreDao carries the keyshare bytes, the staged keyshare, and a keyId — but no algorithm tag. When VaultSessionManager.sign(keyId) runs, the example needs to know whether to dispatch to the ECDSA or EdDSA session, and keyId alone doesn't say.

The fix is to bind a KeyType to each storage client at construction. Whenever the SDK calls storage.write(dao), the client knows its own algorithm and serializes that tag as the first line of the encrypted blob. On read, the client returns a VaultReconcileStoreDao (Android) / VaultReconcileRecord (iOS) — a small wrapper that exposes the algorithm alongside the SDK's dao fields. The session manager then dispatches on dao.keyType.

Both storage clients share the same EncryptedFileStorage, so a read from either instance can resolve any keyId regardless of which algorithm wrote it. The storage layout details are in Storage client.

The name VaultReconcileStoreDao isn't arbitrary — the SDK writes two fields (currentKeyshare and stagedKeyshare) specifically so a crashed keyRefresh can be committed on next launch. The two-slot state machine and the example's reconcile() + reconcileOnStartup() hooks are documented in Reconciliation.

Protocol-level architecture

This page covers the example app's layering. For how the duo protocol itself works (party roles, message exchange, threshold properties), see the Silent Shard Duo Architecture docs.

Dashboard