Skip to main content

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.

MPC Communication between Duo Server and Mobile App

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.

Welcome screenWallet dashboardSend a transactionTransaction confirmed

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:

vault/.../session/VaultSessionManager.kt
// 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:

vault/.../session/VaultSessionManager.kt
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:

vault/.../session/VaultSessionManager.kt
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 leave write:packages untouched/unchecked as we only need read access
    • It will end up looking like this : Two items checked everything else unchecked. See below. github_pat_checked_scopes
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

curl -fsSL https://get.docker.com | sh

Install OpenSSL

# Ubuntu/Debian
sudo apt-get update && sudo apt-get install openssl

Login to Docker

  • Login to docker using Personal Access Token(PAT) and your username.
Guide to generate GitHub Personal Access Token
  1. Go to GitHub Personal Access Tokens
  2. Click on "Generate new token", we are using the classic token for this guide.
  3. Enter a name for the token
  4. Select the "read::packages Download packages from GitHub Package Registry" scope.
  5. 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

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

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