# How NATS Pub/Sub Integrates with the TSS Protocol in mpcium

> Learn how mpcium integrates NATS pub/sub with the TSS protocol. Discover its three-layer architecture for secure cryptographic message broadcasting and point-to-point communication.

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

---

**mpcium runs threshold-signature sessions (TSS) over a cluster of nodes that communicate exclusively through NATS, using a three-layer architecture that encodes cryptographic shares into signed messages for broadcast pub/sub and encrypted payloads for point-to-point request-reply patterns.**

mpcium is an open-source threshold signature scheme implementation that relies on NATS as its sole network transport. By abstracting all peer-to-peer communication behind NATS pub/sub subjects and request-reply patterns, the repository decouples complex cryptographic state machines from networking concerns. This integration allows TSS protocol rounds to flow across distributed nodes while maintaining strict authentication and confidentiality through signatures and encryption applied before messages ever reach the wire.

## The Three-Layer Architecture

The integration is engineered around distinct responsibilities that separate concerns:

- **Message format layer** ([`pkg/types/tss.go`](https://github.com/fystack/mpcium/blob/main/pkg/types/tss.go)): Defines `TssMessage` structures with wallet IDs, routing metadata, and optional signatures. Functions `NewTssMessage`, `MarshalTssMessage`, and `UnmarshalTssMessage` handle wire encoding.

- **Transport abstraction layer** ([`pkg/messaging/pubsub.go`](https://github.com/fystack/mpcium/blob/main/pkg/messaging/pubsub.go) and [`pkg/messaging/point2point.go`](https://github.com/fystack/mpcium/blob/main/pkg/messaging/point2point.go)): Hides NATS details behind generic interfaces. `PubSub` provides broadcast capabilities via `Publish` and `Subscribe`, while `DirectMessaging` handles point-to-point `SendToOther` operations using NATS request-reply.

- **Session orchestration layer** ([`pkg/mpc/session.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/session.go)): Bridges the TSS library callbacks to NATS publishes and converts incoming NATS messages back into `tss.Party` updates through functions like `handleTssMessage`, `receiveBroadcastTssMessage`, and `receiveP2PTssMessage`.

## Encoding and Securing TSS Shares

Before any data leaves the node, the session constructs a `TssMessage` in [`pkg/types/tss.go`](https://github.com/fystack/mpcium/blob/main/pkg/types/tss.go). This struct encapsulates the raw TSS bytes along with routing information indicating whether the message is a broadcast and which parties are involved.

For broadcast messages, mpcium attaches a cryptographic signature to prove origin authenticity. The `identityStore` provides `SignMessage` and `VerifyMessage` operations used immediately before marshalling:

```go
// pkg/mpc/session.go – lines 95-135 (simplified)
tssMsg := types.NewTssMessage(s.walletID, data, routing.IsBroadcast, routing.From, routing.To)

if routing.IsBroadcast && len(routing.To) == 0 {
    signature, _ := s.identityStore.SignMessage(&tssMsg)
    tssMsg.Signature = signature
    payload, _ := types.MarshalTssMessage(&tssMsg)
    s.pubSub.Publish(s.topicComposer.ComposeBroadcastTopic(), payload)
}

```

For point-to-point messages, the payload is encrypted for the specific recipient using `identityStore.EncryptMessage` before transmission, ensuring only the intended peer can decrypt the share.

## Broadcasting Shares via NATS Pub/Sub

### Publishing Broadcast Messages

Broadcast dissemination relies on the `PubSub` interface implemented in [`pkg/messaging/pubsub.go`](https://github.com/fystack/mpcium/blob/main/pkg/messaging/pubsub.go). When the session publishes, it delegates to a thin NATS wrapper:

```go
// pkg/messaging/pubsub.go – lines 34-38
func (n *natsPubSub) Publish(topic string, message []byte) error {
    logger.Debug("[NATS] Publishing message", "topic", topic)
    return n.natsConn.Publish(topic, message)
}

```

The topic is dynamically composed (e.g., `mpc.tss.broadcast:<walletID>`) via the `TopicComposer`.

### Subscribing to Broadcast Topics

On the receiving side, [`session.go`](https://github.com/fystack/mpcium/blob/main/session.go) subscribes asynchronously:

```go
// pkg/mpc/session.go – inside subscribeBroadcastAsync
sub, err := s.pubSub.Subscribe(topic, func(natMsg *nats.Msg) {
    s.receiveBroadcastTssMessage(natMsg.Data)
})

```

The handler `receiveBroadcastTssMessage` unmarshals the payload, verifies the attached signature using `identityStore.VerifyMessage`, and forwards the verified share to `receiveTssMessage`, which ultimately feeds the TSS library:

```go
// pkg/mpc/session.go – lines 200-216 (excerpt)
msg, _ := types.UnmarshalTssMessage(rawMsg)
s.identityStore.VerifyMessage(msg)  // signature check
s.receiveTssMessage(msg)            // bridges to TSS party

```

## Point-to-Point Communication via NATS Request-Reply

### Sending Direct Messages

Non-broadcast TSS shares require direct node-to-node communication. The session encrypts the message for the destination node ID, then invokes the direct messaging abstraction:

```go
// pkg/mpc/session.go – lines 148-167 (simplified)
cipher, _ := s.identityStore.EncryptMessage(msg, toNodeID)
s.direct.SendToOther(topic, cipher)

```

The implementation in [`pkg/messaging/point2point.go`](https://github.com/fystack/mpcium/blob/main/pkg/messaging/point2point.go) leverages NATS's built-in request-reply pattern with a hard timeout:

```go
// pkg/messaging/point2point.go – lines 42-58 (excerpt)
func (d *natsDirectMessaging) SendToOther(topic string, message []byte) error {
    _, err := d.natsConn.Request(topic, message, 3*time.Second)
    return err
}

```

### Receiving and Decrypting Direct Messages

Incoming direct messages are handled by `receiveP2PTssMessage`, which first decrypts the ciphertext (unless the message originated from the local node), unmarshals the payload, and processes it through the same `receiveTssMessage` pipeline used for broadcasts.

## Bridging Messages into the TSS Library

The `receiveTssMessage` function in [`pkg/mpc/session.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/session.go) (lines 226-260) serves as the final bridge between NATS transport and the underlying TSS state machine. It determines the appropriate cryptographic round via `getRoundFunc` and updates the local party state:

```go
// pkg/mpc/session.go – lines 226-260 (excerpt)
round, _ := s.getRoundFunc(msg.MsgBytes, s.selfPartyID, msg.IsBroadcast)
s.party.UpdateFromBytes(msg.MsgBytes, msg.From, msg.IsBroadcast)

```

This call to `party.UpdateFromBytes` transitions the TSS protocol state, completing the round-trip from TSS library → NATS wire → remote peer → TSS library update.

## Summary

- **Three-layer design**: mpcium separates message formatting ([`pkg/types/tss.go`](https://github.com/fystack/mpcium/blob/main/pkg/types/tss.go)), NATS transport (`pkg/messaging/`), and session orchestration ([`pkg/mpc/session.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/session.go)) to keep cryptographic logic isolated from networking code.

- **Dual transport modes**: Broadcast shares use NATS pub/sub with attached signatures for authenticity, while point-to-point shares use NATS request-reply with payload encryption for confidentiality.

- **Immutable entry point**: All network traffic eventually flows through `session.receiveTssMessage`, which validates signatures or decrypts payloads before passing bytes to `tss.Party.UpdateFromBytes`.

- **Testable abstractions**: The `PubSub` and `DirectMessaging` interfaces allow the TSS protocol to be tested without running a live NATS server, as the concrete implementations merely wrap `natsConn.Publish` and `natsConn.Request`.

## Frequently Asked Questions

### Why does mpcium use NATS for TSS communication instead of TCP or gRPC?

NATS provides out-of-the-box pub/sub routing, request-reply patterns, and subject-based addressing that map naturally to TSS broadcast and p2p rounds. According to the mpcium source code, using NATS eliminates the need for custom peer discovery and connection management, allowing the repository to focus strictly on cryptographic correctness while relying on NATS for scalable message delivery.

### How are broadcast TSS messages authenticated?

Broadcast messages are signed before transmission using `identityStore.SignMessage` in [`pkg/mpc/session.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/session.go). Upon receipt, `receiveBroadcastTssMessage` calls `identityStore.VerifyMessage` to validate the signature against the sender's public key before the share ever touches the TSS library state machine.

### What happens if a point-to-point NATS request times out?

The `SendToOther` implementation in [`pkg/messaging/point2point.go`](https://github.com/fystack/mpcium/blob/main/pkg/messaging/point2point.go) uses `natsConn.Request` with a three-second timeout. If the remote peer does not respond within this window, the function returns an error that propagates up to the session layer, which can then trigger protocol-level retries or abort the signing round depending on the TSS scheme's fault tolerance requirements.

### Are TSS message payloads encrypted on the wire?

Point-to-point payloads are encrypted using `identityStore.EncryptMessage` before being passed to `SendToOther`, ensuring confidentiality between specific peers. Broadcast messages are not encrypted but carry cryptographic signatures to guarantee authenticity and integrity, which is sufficient for schemes where broadcast shares are designed to be public within the signing committee.