# Complete Lifecycle of a Key Generation Session in mpcium: From Client Request to Persisted Key

> Explore the ten-step lifecycle of a key generation session in mpcium. Learn how mpcium transforms client requests into distributed threshold signature scheme keys, securing private material and returning public keys.

- Repository: [Fystack Labs/mpcium](https://github.com/fystack/mpcium)
- Tags: internals
- Published: 2026-03-02

---

**A key generation session in mpcium follows a ten-step pipeline that transforms a signed client request into a distributed threshold signature scheme (TSS) key, persisting the private material in a KV store and returning the public key to the client via NATS JetStream events.**

The mpcium repository implements a multi-party computation (MPC) wallet system that uses threshold signature schemes for secure key management. Understanding the complete lifecycle of a key generation session is essential for developers integrating with this system, as it involves coordinated interactions between clients, brokers, consumers, and TSS peer nodes.

## Phase 1: Initiating the Request from Client to Broker

### Step 1: Client Builds and Signs the GenerateKeyMessage

The lifecycle begins when a client calls `CreateWallet` (internally routed through `CreateWalletWithAuthorizers` in [`pkg/client/client.go`](https://github.com/fystack/mpcium/blob/main/pkg/client/client.go)). This method constructs a `GenerateKeyMessage` containing the wallet ID and threshold parameters, signs the payload using the configured initiator key, and publishes it to the *keygen* JetStream stream.

```go
// Simplified client initiation flow
mpC := client.NewMPCClient(client.Options{
    NatsConn: nc,
    Signer:   signer,
})
mpC.CreateWallet(walletID) // Signs and publishes GenerateKeyMessage

```

According to the mpcium source code, this implementation resides in [`pkg/client/client.go`](https://github.com/fystack/mpcium/blob/main/pkg/client/client.go) within lines 11-37, where the client establishes the NATS connection and initiator signature before transmission.

### Step 2: Broker Routes to the KeygenConsumer

Once the message hits the NATS server, the `keygenBroker`—initialized in [`cmd/mpcium/main.go`](https://github.com/fystack/mpcium/blob/main/cmd/mpcium/main.go) (lines 191-206)—routes the message to the **KeygenConsumer** subscribed to the `mpc.keygen_request.*` subject pattern. This broker acts as the traffic coordinator, ensuring that key generation requests reach the appropriate processing nodes.

## Phase 2: Validation and Session Construction

### Step 3: Consumer Validates Peer Readiness

The `keygenConsumer.handleKeygenEvent` method in [`pkg/eventconsumer/keygen_consumer.go`](https://github.com/fystack/mpcium/blob/main/pkg/eventconsumer/keygen_consumer.go) (lines 41-52) receives the message and immediately validates the system state. It invokes `peerRegistry.ArePeersReady` to verify that all required peers are online and available. If the check fails, the consumer issues a NAK (negative acknowledgment) to retry later and reports an error, preventing session creation with insufficient participants.

### Step 4: Node Creates the TSS Session

Upon validation, the consumer calls `Node.CreateKeyGenSession` defined in [`pkg/mpc/node.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/node.go) (lines 81-104). The node selects its own party ID, constructs the complete party ID list based on the current peer set, and instantiates either an **ECDSA** or **EDDSA** session via `newECDSAKeygenSession` or `newEDDSAKeygenSession` factory functions. This factory pattern ensures the correct cryptographic curve parameters are loaded for the specific key type requested.

### Step 5: Session Initialization with tss-lib

The session's `Init` method—found in [`pkg/mpc/ecdsa_keygen_session.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/ecdsa_keygen_session.go) (lines 81-86) for ECDSA and [`pkg/mpc/eddsa_keygen_session.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/eddsa_keygen_session.go) (lines 69-75) for EdDSA—creates a **tss-lib** `LocalParty` instance. It configures the appropriate elliptic curve (ecdsa.EC256 for ECDSA, edwards.Ed25519 for EdDSA) along with pre-parameters, initializing the local state required for the distributed protocol.

## Phase 3: Distributed Key Generation Execution

### Step 6: Running the TSS Protocol with Peer Message Exchange

The `GenerateKey` method spawns the TSS party via `party.Start()` and enters a coordination loop. As implemented in [`pkg/mpc/ecdsa_keygen_session.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/ecdsa_keygen_session.go) (lines 89-115) and the EdDSA equivalent (lines 77-115), this loop performs two critical functions:

- **Outbound message handling**: Forwards TSS protocol messages via `handleTssMessage`, which encrypts and publishes them to peer-specific NATS topics
- **Completion monitoring**: Waits for the local party's `saveData` on the `endCh` channel, signaling that the distributed computation has finished successfully

This phase represents the core cryptographic work, where peers exchange encrypted shares and zero-knowledge proofs to collaboratively compute the key without any single party learning the full private key.

## Phase 4: Persistence and Cleanup

### Step 7: Serializing and Storing Private Key Material

When `saveData` arrives on the channel, the session proceeds to persist the sensitive material. In [`pkg/mpc/ecdsa_keygen_session.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/ecdsa_keygen_session.go) (lines 102-128) and the EdDSA variant (lines 94-112), the session:

1. Serializes the private key data and stores it via `kvstore.Put` for secure, encrypted storage
2. Creates a `KeyInfo` struct containing participants, threshold, and version metadata, saving it to `keyinfoStore`
3. Extracts and encodes the public key, storing it in `s.pubkeyBytes` for later retrieval

This separation of concerns ensures that cryptographic material is never held in memory longer than necessary and is immediately persisted to durable storage.

### Step 8: Session Teardown and Resource Cleanup

Following successful persistence, the session invokes `s.Close()` from [`pkg/mpc/session.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/session.go). This method cleans up active NATS subscriptions, clears internal state buffers, and invokes the supplied `done()` callback to signal the consumer that processing is complete. Proper teardown prevents resource leaks and ensures the node can accept new sessions immediately.

## Phase 5: Result Publication and Client Delivery

### Step 9: Publishing the KeygenResultEvent

After the session finishes, the consumer enqueues a `KeygenResultEvent` on the `genKeyResultQueue` (handled in [`pkg/eventconsumer/keygen_consumer.go`](https://github.com/fystack/mpcium/blob/main/pkg/eventconsumer/keygen_consumer.go), lines 155-169). This event contains the wallet ID, the generated public key bytes (ECDSA and/or EDDSA), and a success status code. The consumer then ACKs the original NATS message, removing it from the stream and confirming successful processing.

### Step 10: Client Receives the Wallet Creation Result

Clients that previously registered via `MPCClient.OnWalletCreationResult` (implemented in [`pkg/client/client.go`](https://github.com/fystack/mpcium/blob/main/pkg/client/client.go), lines 40-51) receive the `KeygenResultEvent` through their subscription callback. At this point, the wallet ID is fully operational for signing operations or key resharing, completing the lifecycle.

## Summary

The lifecycle of a key generation session in mpcium comprises these distinct phases:

- **Client initiation**: Signed `GenerateKeyMessage` published to JetStream via `CreateWallet`
- **Broker routing**: `keygenBroker` directs traffic to the subscribed consumer
- **Validation**: `peerRegistry.ArePeersReady` ensures sufficient peers are available before proceeding
- **Session fabrication**: `Node.CreateKeyGenSession` builds ECDSA or EdDSA TSS sessions using tss-lib
- **Protocol execution**: `GenerateKey` runs the distributed computation, exchanging encrypted peer messages
- **Persistence**: Private key material serialized to KV store with metadata saved to `keyinfoStore`
- **Cleanup**: `session.Close()` releases resources and invokes completion callbacks
- **Result delivery**: `KeygenResultEvent` published and ACKed, notifying the client via `OnWalletCreationResult`

## Frequently Asked Questions

### What happens if peer nodes are not ready during key generation?

If `peerRegistry.ArePeersReady` returns false in [`pkg/eventconsumer/keygen_consumer.go`](https://github.com/fystack/mpcium/blob/main/pkg/eventconsumer/keygen_consumer.go), the consumer immediately NAKs the message and reports an error through the error handling pipeline. This negative acknowledgment triggers a redelivery attempt by NATS JetStream, ensuring the request retries once the required peers come online rather than failing permanently or creating an incomplete session.

### How does mpcium support both ECDSA and EdDSA key generation?

The system uses a factory pattern in [`pkg/mpc/node.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/node.go) where `CreateKeyGenSession` accepts a session type parameter. For `SessionTypeECDSA`, it invokes `newECDSAKeygenSession`; for EdDSA, it calls `newEDDSAKeygenSession`. Each session type implements the same interface but initializes distinct tss-lib curves (ecdsa.EC256 vs edwards.Ed25519) and handles curve-specific serialization in their respective `GenerateKey` implementations.

### Where is the private key material stored after the session completes?

Upon receiving `saveData` from the tss-lib party, the session serializes the private key and calls `kvstore.Put` to store it in the configured key-value store (implemented in [`pkg/kvstore/kvstore.go`](https://github.com/fystack/mpcium/blob/main/pkg/kvstore/kvstore.go)). Simultaneously, it creates a `KeyInfo` record in [`pkg/keyinfo/keyinfo.go`](https://github.com/fystack/mpcium/blob/main/pkg/keyinfo/keyinfo.go) containing metadata about participants and thresholds. This dual-storage approach separates sensitive key data from descriptive metadata.

### How does the client receive confirmation that key generation succeeded?

Clients subscribe to results using `MPCClient.OnWalletCreationResult` in [`pkg/client/client.go`](https://github.com/fystack/mpcium/blob/main/pkg/client/client.go), which sets up a subscription to the result stream. When the `keygenConsumer` finishes processing, it publishes a `KeygenResultEvent` containing the wallet ID and public key bytes. The client's callback function receives this event, allowing the application to proceed with wallet operations knowing the MPC key generation completed successfully across all participating nodes.