Quick Start
If you don't have have access to the docker image or don't have access to the android sdk repository, please contact us at [email protected].
Here is how you can setup the Mobile SDK and perform MPC operations in less than a minute.
You will create a working MPC two-party setup, where the first party, an Android application interacts with Duo Server as a second party. We are providing a demo server endpoint for your convenience.
What you'll build
The example-hub reference wallet is a complete Android app that creates a 2-of-2 MPC wallet and signs real Ethereum (Base Sepolia) and Solana (Devnet) transactions. This guide walks through its core MPC code — session setup, keygen, and signing. Clone the app to run the whole thing: wallet UI, funding, and on-chain transactions.




Prerequisites
For quick testing, the demo server is already deployed at demo-server.silencelaboratories.com. We are using this server for the quickstart guide.
The Cloud Verifying Key for the demo server is cfa1ff5424d14eb60614d7ddf65a32243d26ddf7000d10007853d7336395efe4 (the 32-byte Ed25519 key).
The Cloud Node Endpoint for the demo server is demo-server.silencelaboratories.com.
Setup the Mobile SDK (Android)
The quickest way to get running is to clone the reference app and launch it:
git clone https://github.com/silence-laboratories/examples-hub.git
cd examples-hub/silent-shard-sdk/with-android/duo-initiator
Add your GitHub Packages credentials to local.properties (see Dependency Installation below for the read-only token), then open the project in Android Studio and run it on a device or emulator. It talks to the public demo server out of the box, so you can create a wallet and sign a transaction right away. The full file-by-file walkthrough (keygen, signing, export, key refresh, and more) is in Android & iOS reference apps.
Session Creation
A DuoSession is the handle for all MPC operations (keygen, sign, and so on). The reference app builds one session per algorithm inside its VaultSessionManager:
// HardwareMessageSigner is the app's own implementation of the SDK's MessageSigner
// interface, signing with a hardware-backed key (Android Keystore).
private val messageSigner = HardwareMessageSigner()
private val websocketConfig = WebsocketConfig(url = DEMO_SERVER_URL, port = DEMO_SERVER_PORT, isSecure = true)
// EncryptedStorageClient is the app's own implementation of the SDK's StorageClient
// interface. Since that interface carries a keyType, ECDSA and EdDSA keyshares are
// persisted through separate clients — one per algorithm.
private val ecdsaStorage = EncryptedStorageClient(store, KeyType.ECDSA)
private val eddsaStorage = EncryptedStorageClient(store, KeyType.EdDSA)
// One DuoSession per algorithm, each bound to the message signer, the cloud
// verifying key, the websocket config, and its storage client.
private val ecdsaSession: DuoSession =
SilentShard.ECDSA.duoInitiator(messageSigner, CLOUD_VERIFYING_KEY, websocketConfig, ecdsaStorage)
private val eddsaSession: DuoSession =
SilentShard.EdDSA.duoInitiator(messageSigner, CLOUD_VERIFYING_KEY, websocketConfig, eddsaStorage)
HardwareMessageSigner and EncryptedStorageClient are not SDK types — they are the reference app's own implementations of the SDK's MessageSigner and StorageClient interfaces (hardware-backed P-256 signing and encrypted keyshare storage). Supply your own implementations, or clone the example-hub app to use these as-is.
Run the MPC operations
After creating the session, you can perform MPC operations.
Key Generation
Generate MPC keyshares with the keygen method. It returns a keyId (a String); the SDK persists the keyshare to your StorageClient and every later operation is addressed by that keyId. Here is how the reference app does it:
suspend fun keygen(keyType: KeyType): KeyResult {
Log.i(TAG, "Keygen started for type: ${keyType.name}")
// sessionFor(...) is this app's own helper (not an SDK method) — it returns the DuoSession
// we created earlier for this algorithm (ECDSA or EdDSA).
val session: DuoSession = sessionFor(keyType)
// keygen() is the SDK call: runs the MPC protocol, persists the keyshare to your
// StorageClient, and returns the keyId that addresses it.
val keyId = session.keygen().getOrThrow()
val publicKey = extractPublicKey(keyId)
Log.i(TAG, "Keygen completed for keyId: $keyId")
return KeyResult(keyId, publicKey)
}
Signature Generation
Sign a message using the sign method, passing the keyId returned from keygen:
suspend fun sign(keyId: String, message: ByteArray, derivationPath: String): ByteArray {
val dao = readDao(keyId)
// sessionFor(...) is this app's own helper (not an SDK method) — it returns the DuoSession
// we created earlier for this key's algorithm (the stored record's keyType).
val session: DuoSession = sessionFor(dao.keyType)
// sign() is the SDK call: it produces the threshold signature for the key addressed by keyId.
val signature = session
.sign(keyId, message.toHex(), derivationPath)
.getOrThrow()
Log.i(TAG, "Sign completed (${signature.size} bytes, type=${dao.keyType.name})")
return signature
}
The HardwareMessageSigner, EncryptedStorageClient, and WebSocket client shown are the reference app's implementations — hence for reference only, not a recommendation. Provide your own for production.
Setup your own app
Prefer to wire the SDK into your own app instead of the reference project? Create a new Android Studio project by following the official guide (or use an existing one), then add the SilentShard dependency.
Dependency Installation
Step 1: Create access token for the repository
- Get access to the repository https://github.com/silence-laboratories/silent-shard-artifacts from the Silence Laboratories team.
- Create a personal access token at https://github.com/settings/tokens with the following scopes
checked[✓] to access the repository, and it's
associated GithubPackages
through gradle.
- Under
repo-> [✓]public_repo - Under
write:packages-> [✓]read:packages
We can leavewrite:packagesuntouched/unchecked as we only need read access - It will end up looking like this : Two items checked everything else unchecked. See below.

- Under
Step 2: Configure settings.gradle.kts
- Add the silentshard maven repo with the access credentials under the dependencyResolutionManagement -> repositories.
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven {
url =
uri("https://maven.pkg.github.com/silence-laboratories/silent-shard-artifacts")
credentials {
username = "github_username"
password = "token_we_just_created"
}
}
}
}
Step 3: Configure app level build.gradle.kts
Add the silentshard dependency in your dependencies block:
dependencies {
implementation("com.silencelaboratories.silentshard:duo:1.0.0")
}
Step 4: Gradle-Sync
- Sync gradle with project files.
The SDK talks to the Duo Server over the network, so your app needs the internet permission. Add it to your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
With the dependency in place, wire it up the same way the reference app does — create a session, then run keygen and signing.
Optional: Run your own Duo Server
Install Docker
- Linux
- macOS
- Windows
curl -fsSL https://get.docker.com | sh
Download and install Docker Desktop from the official website.
Alternatively, you can install using Homebrew:
brew install --cask docker
Download and install Docker Desktop from the official website.
Make sure to enable WSL 2 backend during installation for better performance.
Install OpenSSL
- Linux
- macOS
- Windows
# Ubuntu/Debian
sudo apt-get update && sudo apt-get install openssl
OpenSSL is usually pre-installed on macOS. To install or update using Homebrew:
brew install openssl
Download and install from the official OpenSSL website.
Alternatively, you can install using Chocolatey:
choco install openssl
Or using Windows Package Manager:
winget install OpenSSL.OpenSSL
Login to Docker
- Login to docker using Personal Access Token(PAT) and your username.
Guide to generate GitHub Personal Access Token
- Go to GitHub Personal Access Tokens
- Click on "Generate new token", we are using the classic token for this guide.
- Enter a name for the token
- Select the "read::packages Download packages from GitHub Package Registry" scope.
- Generate the token.
Learn more about GitHub Personal Access Tokens here.
echo <PAT> | docker login ghcr.io -u <username> --password-stdin
Start the Server
For local setup, let's use this example docker-compose.yml
services:
duo-server:
image: ghcr.io/silence-laboratories/dkls23-rs/duo-server:v4
command: /usr/local/bin/sigpair-node
environment:
RUST_LOG: info
LISTEN: 0.0.0.0:8080
DEV_MASTER_SIGN_KEY: /run/secrets/duo-signing-master-key
FILE_STORAGE_URL: file:///data/
ports:
- 8080:8080
volumes:
- duo-server-data:/data
secrets:
- duo-signing-master-key
volumes:
duo-server-data:
secrets:
duo-signing-master-key:
file: ${MESSAGE_SIGNING_KEY:-./party_0_sk}
The docker compose file is configured to use the party_0_sk file in the root of the project. party_0_sk is the random bytes used as seed to create the server private key.
Create the party_0_sk file with random bytes in the root of the project.
openssl rand 32 > party_0_sk
Now let's start the server by running
docker compose up
If everything was setup correctly, the server should be running with these logs
Attaching to duo-server-1
duo-server-1 | WARN: The dotenv file is not set
duo-server-1 | 2025-09-16T09:59:09.200234Z INFO sigpair_node: Creating SimpleStorage
duo-server-1 | 2025-09-16T09:59:09.201975Z INFO sigpair_node: Party VK <YOUR_CLOUD_VERIFYING_KEY_HEX_STRING>
duo-server-1 | 2025-09-16T09:59:09.202413Z INFO sigpair_node: listening on 0.0.0.0:8080
Great! The server is now setup to perform MPC actions with our mobile.
In logs you will notice a hex string of length 66 (33 bytes). This is the YOUR_CLOUD_VERIFYING_KEY_HEX_STRING which will be used by server to verify the requests from other party (In this case, our mobile app).
The SDK's cloudVerifyingKey expects the 32-byte (64 hex char) Ed25519 key. If the server logs a 33-byte Party VK, drop the leading 01 byte and use the remaining 32 bytes.
The YOUR_CLOUD_NODE_ENDPOINT is the endpoint of the server. In this case, it is 0.0.0.0:8080 for ios and 10.0.2.2:8080 for android. Also you can use the IP address of your machine if you are running the server on your machine.