Hanzo MPC
Threshold multi-party signing for digital asset custody — distributed key generation, t-of-n signing, and proactive share refresh with post-quantum consensus.
Hanzo MPC
Hanzo MPC is wallet infrastructure for enterprise digital asset custody. Private keys are never assembled in one place — they are split into shares held by independent nodes, and a configurable threshold of nodes must cooperate to produce a valid signature. Think of it as a self-hosted alternative to Fireblocks: you own the infrastructure, control the key shares, and operate under your own policies.
| Custody API | https://mpc.hanzo.ai |
| API prefix | /v1 |
| Auth | Hanzo IAM (OIDC) bearer tokens |
| Default threshold | t-of-n, default t = ⌊n/2⌋ + 1 |
| Source | github.com/hanzoai/mpc |
Key Properties
- No single point of failure — no node ever holds a complete private key
- Distributed key generation (DKG) — keys are generated collaboratively; no dealer ever sees the whole key
- Configurable thresholds — any t-of-n scheme (2-of-3, 3-of-5, 5-of-9, …)
- Proactive share refresh — rotate shares without changing the public key or any derived address
- Identifiable aborts — a deviating participant is detected and attributed
- Multi-chain — one key plane derives addresses for Bitcoin, Ethereum/EVM, Solana, and more
- Encrypted at rest — key shares are stored AES-256-GCM encrypted; the encryption key is supplied by Hanzo KMS/HSM, never plaintext config
- Post-quantum consensus — cluster state is managed by a private BFT chain with dual-certificate finality (Ed25519 + ML-DSA-65, FIPS 204)
Supported Protocols
Hanzo MPC builds on the luxfi/threshold engine. Four threshold schemes are in production:
| Protocol | Type | Curve | Best for |
|---|---|---|---|
| CGGMP21 | Threshold ECDSA | secp256k1 | Bitcoin, Ethereum, EVM chains |
| FROST | Threshold Schnorr | secp256k1 (BIP-340), Ristretto255 | Bitcoin Taproot, Ed25519/sr25519 chains |
| LSS | Linear secret sharing | secp256k1 | Dynamic resharing, blinded signing |
| BLS | Threshold aggregate | BLS12-381 | Validator keys, aggregate signatures |
Key types accepted by the API: secp256k1, ed25519, sr25519, bls.
Authentication
The custody API trusts tokens issued by Hanzo IAM. Present an IAM-issued JWT as a bearer token:
curl -H "Authorization: Bearer $IAM_ACCESS_TOKEN" \
https://mpc.hanzo.ai/v1/infoServices that already hold an external OIDC token can exchange it for an MPC session token (issuer must be on the allowlist):
curl -X POST https://mpc.hanzo.ai/v1/auth/oidc \
-H "Content-Type: application/json" \
-d '{ "token": "<external-oidc-access-token>" }'Roles (owner, admin, signer, viewer) gate each route; signing additionally requires mpc:sign. Long-lived API keys are also supported for machine callers — store them in Hanzo KMS, never inline.
Quick Start
Signing is a three-step flow: create a wallet (DKG), open a signing session, then sign.
Create a wallet (DKG)
curl -X POST https://mpc.hanzo.ai/v1/mpc/wallets \
-H "Authorization: Bearer $IAM_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "protocol": "cggmp21" }'Response:
{
"walletId": "w_3fa85f64-5717-4562-b3fc-2c963f66afa6",
"address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
"protocol": "cggmp21",
"threshold": "2-of-3",
"status": "active",
"createdAt": "2026-06-26T10:30:00Z"
}Open a signing session
curl -X POST https://mpc.hanzo.ai/v1/mpc/wallets/$WALLET_ID/sessions \
-H "Authorization: Bearer $IAM_ACCESS_TOKEN"Returns a sessionId. Signing consumes a session, which makes each signature an explicit, auditable authorization.
Sign
curl -X POST https://mpc.hanzo.ai/v1/mpc/sign \
-H "Authorization: Bearer $IAM_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"walletId": "'$WALLET_ID'",
"sessionId": "'$SESSION_ID'",
"message": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"encoding": "hex"
}'Response (ECDSA):
{ "signature": "0x...", "r": "0x1a2b3c...", "s": "0x4d5e6f..." }Core Endpoints
All under https://mpc.hanzo.ai/v1.
| Method | Path | Purpose |
|---|---|---|
POST | /v1/mpc/wallets | Create a wallet (DKG); { "protocol": "cggmp21" | "frost" } |
GET | /v1/wallets/{id}/addresses | Derived chain addresses for a wallet |
POST | /v1/mpc/wallets/{id}/sessions | Open a signing session |
GET | /v1/mpc/wallets/{id}/sessions | List sessions |
POST | /v1/mpc/sign | Threshold-sign a message (requires sessionId) |
POST | /v1/wallets/{id}/reshare | Proactive share refresh / change t-of-n |
GET | /v1/status · /v1/info | Cluster status and capabilities |
GET | /healthz | Liveness (no auth) |
A single wallet derives addresses for many chains:
curl https://mpc.hanzo.ai/v1/wallets/$WALLET_ID/addresses \
-H "Authorization: Bearer $IAM_ACCESS_TOKEN"{
"ethereum": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
"bitcoin": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"solana": "7nYBzS3Yz5mY8Qm4iXqz1bWmHnf8QpHq3xWv6Yc9aBcD"
}Proactive Share Refresh
Rotate the key shares without changing the public key or any on-chain address. Old shares become useless after a refresh, so a share compromised in a past epoch cannot be combined with current shares.
curl -X POST https://mpc.hanzo.ai/v1/wallets/$WALLET_ID/reshare \
-H "Authorization: Bearer $IAM_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "new_threshold": 3, "new_participants": ["node-0","node-1","node-2","node-3","node-4"] }'In-cluster Go client (NATS)
Inside the cluster, the canonical interface is an asynchronous Go client over NATS — every keygen/sign/reshare request is Ed25519-signed by an initiator key and answered with a result event:
import (
"github.com/luxfi/mpc/pkg/client"
"github.com/luxfi/mpc/pkg/event"
)
mpc := client.NewMPCClient(client.Options{NatsConn: nc, KeyPath: "./event_initiator.key"})
mpc.OnWalletCreationResult(func(e event.KeygenResultEvent) {
// e.ECDSAPubKey (CGGMP21), e.EDDSAPubKey (FROST), …
})
mpc.CreateWallet(walletID)The REST custody API above is a facade over this same backend.
Security Model
- Key share isolation — each node runs in its own pod with a dedicated encrypted volume; no node holds enough to reconstruct a key.
- Encrypted shares — shares are AES-256-GCM encrypted in an embedded key-value store; the encryption key comes from an HSM or Hanzo KMS (
kms.hanzo.ai). In production, plaintext-password backends are rejected outright. - Signed messaging — every inter-node and initiator message is Ed25519-signed and verified before processing.
- Zero-knowledge proofs verify correctness at each protocol stage; a deviating party is identified and an operation aborts with a signed blame proof.
- Post-quantum cluster state — peers, keys, and wallets live in a private BFT chain with dual-certificate (Ed25519 + ML-DSA-65) threshold finality.
Next Steps
Complete REST API: wallets, sessions, signing, resharing
CGGMP21, FROST, LSS, and BLS threshold schemes
The private BFT chain and dual-certificate finality
Use MPC as a threshold signing backend for Hanzo KMS
How is this guide?
Last updated on