Key Lifecycle & Signing Flow

Keygen, signing and resharing from the cosigner's side.

Keygen

Creating a wallet in a workspace with a cosigner runs distributed key generation across the MPCium nodes and your cosigner.

  1. Orchestrator sends a signed session_start with KEYGEN, the key_id, protocol and threshold.
  2. Cosigner verifies the signature and asks your callback.
  3. Participants run tss-lib keygen. Each ends with its own share and the shared public key.
  4. Cosigner encrypts its share, stores it as shares:<protocol>:<key_id> and records the committee.
  5. Only the public key and a result hash leave the cosigner.
  6. Orchestrator checks all public keys match and returns them to Apex.

Wallets needing both ECDSA and EdDSA run two linked sessions. If one fails, both abort.

Signing

Signing sequence: Apex requests, orchestrator starts the session, cosigner asks the callback, participants sign, orchestrator returns the signature
Click to open the full-size diagram

1. Admission

A request that passes your workspace policies goes from Apex to the orchestrator, signed with the Apex initiator key. The orchestrator checks the committee against the keygen record, confirms everyone is online, and sends a signed session_start.

session_start carries key_id, the raw signing_input, optional signing_context (transaction details, max 64 KiB), HD derivation, threshold and participants.

2. Local checks

The cosigner rejects the session unless all pass, in order:

  1. Message under 128 KiB.
  2. Valid orchestrator signature (key pinned at pairing).
  3. Under 20 starts and 10 aborts per minute.
  4. Under 5 concurrent sessions.
  5. session_start written to the audit log.
  6. Your callback returns approve. Otherwise the session fails with CALLBACK_REJECTED.

If the orchestrator aborts while the callback is pending, the cosigner stops waiting.

3. Key exchange

  • Session must match the pinned committee: threshold, party keys, identity keys, approver flags.
  • Cosigner sends peer_joined, peer_ready and a signed X25519 key to each peer.
  • Each peer pair gets a per-direction ChaCha20-Poly1305 key (HKDF over X25519, bound to the session).

4. Threshold signing

The orchestrator sends mpc_begin. If the wallet has an approver (for example a Fystack mobile approver), mpc_begin must include its signed approval of this exact signing input. Without it the cosigner fails the session with APPROVAL_REQUIRED.

Participants then run tss-lib rounds. Every message is signed and sequence-numbered, so replays are dropped.

5. Result

Each participant reports a hash of its signature. The orchestrator requires all hashes to match, then returns the signed result (signature, r, s, recovery id) to Apex. The cosigner logs session_end and deletes its checkpoint.

Resharing

Replaces shares, optionally with a new committee or threshold. Public key and addresses don't change.

  1. Old committee and threshold come from the keygen record, not the caller.
  2. At least threshold + 1 old members must be online.
  3. Old and new members run tss-lib resharing. New shares are staged; old ones stay active.
  4. All participants report the same result and an unchanged public key.
  5. Orchestrator persists the decision, then sends a signed reshare_commit. Each participant swaps in the new share.

Abort before commit keeps the old shares. After commit it only rolls forward: the commit is retried until every participant confirms, including after a crash.

Failure codes

Code Meaning
CALLBACK_REJECTED Callback rejected, timed out or returned an invalid response.
APPROVAL_REQUIRED Wallet approver's signed approval missing.
UNAVAILABLE A participant was offline at submission.
PARTICIPANT_FAILED A participant reported a failure. See failure_reason.
PARTICIPANT_DISCONNECTED A participant's stream dropped mid-session.
SESSION_TIMEOUT Not done within 120 seconds.
RESULT_HASH_MISMATCH Participants disagreed on the result.
CONFLICT Another session is running for this wallet and protocol.