mirror of
https://github.com/luxfi/mpc.git
synced 2026-09-24 00:26:24 +00:00
300 lines
8.8 KiB
Markdown
300 lines
8.8 KiB
Markdown
# MPC Installation Guide
|
|
|
|
This guide walks an operator from a clean machine to a running `mpcd`
|
|
cluster in **consensus mode** — the production path the README leads
|
|
with: peer-to-peer ZAP transport, embedded ZapDB storage, no external
|
|
broker, no service-discovery layer.
|
|
|
|
If you have an existing deployment running NATS + Consul, see the
|
|
[Legacy transport appendix](#appendix-a-legacy-transport-nats--consul-deprecated)
|
|
at the bottom of this document.
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
- **Go** 1.23+ — [install instructions](https://go.dev/doc/install)
|
|
- **make** (build orchestration)
|
|
- For containerised deployments: **Docker** 24+ with the Compose v2 plugin
|
|
- For Kubernetes deployments: a cluster with **Kustomize** available
|
|
(`kubectl kustomize`) and a `StorageClass` for persistent volumes
|
|
|
|
Consensus mode has **no external broker or service-discovery
|
|
dependency**. Inter-node messaging, peer registry, and the encrypted
|
|
key-share store (ZapDB) are all embedded in the `mpcd` binary.
|
|
|
|
---
|
|
|
|
## Clone and Build
|
|
|
|
```bash
|
|
git clone https://github.com/luxfi/mpc.git
|
|
cd mpc
|
|
```
|
|
|
|
```bash
|
|
make # builds ./bin/mpcd (and ./bin/mpc CLI helpers)
|
|
# or
|
|
go install ./cmd/mpcd
|
|
go install ./cmd/mpc
|
|
```
|
|
|
|
### Available binaries
|
|
|
|
| Binary | Purpose |
|
|
|--------|---------|
|
|
| `mpcd` | The MPC daemon. One binary, all topologies (standalone, t-of-n cluster). See `cmd/mpcd/README.md`. |
|
|
| `mpc` | CLI helper for one-shot tasks: `generate-peers`, `generate-identity`, `register-peers`, `recovery`. |
|
|
|
|
---
|
|
|
|
## Consensus Mode Concepts
|
|
|
|
Consensus mode (the default; selectable explicitly via `--mode consensus`
|
|
in `config.yaml`) is what the README calls the *production path*. The
|
|
properties an operator needs to know:
|
|
|
|
- **Transport.** Peer-to-peer ZAP wire protocol over TLS 1.3 between
|
|
`mpcd` nodes. Each node listens on a single `--listen` address (default
|
|
`:9999`).
|
|
- **Peer discovery.** Static. Every node is started with one
|
|
`--peer node<id>@host:port` flag per remote node. The cluster size is
|
|
`n = len(--peer) + 1`.
|
|
- **Threshold.** `--threshold t` is the **minimum number of signers**
|
|
required to produce a signature. The daemon refuses to start when
|
|
`t > n`. See `pkg/transport/registry.go::HasSigningQuorum` for the
|
|
arithmetic.
|
|
- **Identity.** Each node has a long-lived Ed25519 keypair under
|
|
`<data>/keys/<node-id>_identity.json`. It is generated on first start
|
|
if not present. Inter-node messages are signed and verified with this
|
|
key.
|
|
- **State.** All persistent state — key shares, key metadata, peer
|
|
registry, dashboard DB — lives under `--data` (default SQLite +
|
|
ZapDB). Mount a volume here.
|
|
- **Backups.** A periodic ZapDB backup runs every
|
|
`backup_period_seconds` (default 300 s) into `<data>/backups`. Optional
|
|
S3 upload is configured via the standard `MPC_BACKUP_S3_*` env vars
|
|
read by `pkg/backup`.
|
|
|
|
---
|
|
|
|
## Quick Start: 3-Node Local Cluster
|
|
|
|
The fastest way to see consensus mode work end-to-end on a single host.
|
|
|
|
### 1. Pick a threshold
|
|
|
|
`t-of-n` with `t >= floor(n/2) + 1`. For 3 nodes use `t = 2`.
|
|
|
|
### 2. Start each node
|
|
|
|
Run each block in its own terminal:
|
|
|
|
```bash
|
|
# Node 0
|
|
mpcd start \
|
|
--node-id node0 --listen :9990 --api :9800 \
|
|
--data ./data/node0 --threshold 2 \
|
|
--peer [email protected]:9991 --peer [email protected]:9992
|
|
```
|
|
|
|
```bash
|
|
# Node 1
|
|
mpcd start \
|
|
--node-id node1 --listen :9991 --api :9801 \
|
|
--data ./data/node1 --threshold 2 \
|
|
--peer [email protected]:9990 --peer [email protected]:9992
|
|
```
|
|
|
|
```bash
|
|
# Node 2
|
|
mpcd start \
|
|
--node-id node2 --listen :9992 --api :9802 \
|
|
--data ./data/node2 --threshold 2 \
|
|
--peer [email protected]:9990 --peer [email protected]:9991
|
|
```
|
|
|
|
### 3. Verify quorum
|
|
|
|
```bash
|
|
curl -s http://127.0.0.1:9800/healthz | jq
|
|
```
|
|
|
|
Expected response once all peers connect:
|
|
|
|
```json
|
|
{
|
|
"status": "healthy",
|
|
"mode": "consensus",
|
|
"signing_quorum": true,
|
|
"ready_count": 3,
|
|
"threshold": 2
|
|
}
|
|
```
|
|
|
|
The node reports `healthy` while `ready_count >= threshold + 1`,
|
|
`healthy-reduced` while signing quorum holds but a non-critical peer is
|
|
unreachable, and `degraded` (HTTP 503) once quorum is lost. See
|
|
`cmd/mpcd/main.go::healthHandler` for the full contract.
|
|
|
|
### 4. Generate a wallet
|
|
|
|
```bash
|
|
curl -X POST http://127.0.0.1:9800/keygen \
|
|
-H "Authorization: Bearer ${MPC_INTERNAL_API_KEY}" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"org_id": "demo", "wallet_id": "w-001"}'
|
|
```
|
|
|
|
`MPC_INTERNAL_API_KEY` is required for every internal endpoint except
|
|
`/healthz`. If you do not set it, `mpcd` derives a deterministic key from
|
|
the node identity and logs a warning — fine for local testing, **not**
|
|
acceptable in production.
|
|
|
|
---
|
|
|
|
## Docker Compose
|
|
|
|
The repo ships a minimal single-node compose file at `compose.yml` for
|
|
demos and integration testing:
|
|
|
|
```bash
|
|
export JWT_SECRET=$(openssl rand -hex 32)
|
|
docker compose up
|
|
```
|
|
|
|
For multi-node clusters in containers, run one `ghcr.io/luxfi/mpc-api`
|
|
container per node and wire the `--peer` flags to the other containers'
|
|
network addresses. See `deployments/onprem/README.md` for a worked
|
|
example.
|
|
|
|
---
|
|
|
|
## Kubernetes
|
|
|
|
The repo ships a production-grade StatefulSet:
|
|
|
|
```bash
|
|
cd k8s && kubectl kustomize . | kubectl apply -f -
|
|
```
|
|
|
|
`k8s/mpc-statefulset.yaml` builds the peer list from the StatefulSet
|
|
ordinal — each pod resolves its siblings via the headless service
|
|
`mpc-node-headless.lux-mpc.svc` and starts as:
|
|
|
|
```bash
|
|
mpcd start --node-id node${ORDINAL} \
|
|
--listen :9999 --api :9800 --data /data/mpc --threshold 3 \
|
|
--peer [email protected]:9999 \
|
|
--peer ...
|
|
```
|
|
|
|
The manifests include `secrets.yaml` (KMS-synced), a NetworkPolicy
|
|
restricting east-west traffic to the cluster, and an IngressRoute for
|
|
the dashboard. The default kustomization is `--threshold 3` over 5 pods;
|
|
adjust both in `mpc-statefulset.yaml` if you size differently.
|
|
|
|
For environment-specific overlays, see `deployments/k8s-primary/` and
|
|
`deployments/k8s-private/`.
|
|
|
|
---
|
|
|
|
## Configuration Surface (`config.yaml`)
|
|
|
|
`config.yaml` is read by Viper with `AutomaticEnv`, so every key has an
|
|
env-var override (`mpc_threshold` → `MPC_THRESHOLD`, etc.). The minimal
|
|
production file:
|
|
|
|
```yaml
|
|
mode: consensus
|
|
environment: mainnet # mainnet | testnet | local
|
|
|
|
# Consensus
|
|
mpc_threshold: 2
|
|
max_concurrent_keygen: 2
|
|
|
|
# Storage
|
|
db_path: "." # SQLite + ZapDB live here
|
|
|
|
# Backup
|
|
backup_enabled: true
|
|
backup_period_seconds: 300
|
|
backup_dir: backups
|
|
|
|
# Event-initiator pubkey (Ed25519, hex). Set when running with an
|
|
# explicit initiator service; leave empty inside trusted clusters where
|
|
# all messages originate via the internal API.
|
|
event_initiator_pubkey: ""
|
|
```
|
|
|
|
Secrets are **never** placed in `config.yaml` in production. Source them
|
|
through the HSM password provider:
|
|
|
|
```bash
|
|
mpcd start ... \
|
|
--hsm-provider aws \
|
|
--hsm-key-id arn:aws:kms:us-east-1:...:key/...
|
|
```
|
|
|
|
In `environment: production`, `mpcd` refuses to start with an
|
|
`env`/`file` provider (see `resolveZapDBPassword` in `cmd/mpcd/main.go`).
|
|
|
|
---
|
|
|
|
## Operational Hardening
|
|
|
|
Before declaring a cluster production-ready, work through the
|
|
[Operator Runbook](RUNBOOK.md) — health-probe spec, key rotation, backup
|
|
and restore, node add/remove, and the deployment smoke test.
|
|
|
|
---
|
|
|
|
## Appendix A: Legacy Transport (NATS + Consul, deprecated)
|
|
|
|
> **Deprecated.** The legacy NATS + Consul transport is retained only
|
|
> for clusters that have not yet migrated. New deployments must use
|
|
> consensus mode (above). The legacy code path will be removed in a
|
|
> future major version. There is no `--mode=legacy` flag on `mpcd` —
|
|
> legacy deployments use the older `lux-mpc` binary out of `cmd/`.
|
|
|
|
The previous version of this document recommended NATS + Consul as
|
|
prerequisites. That guidance was correct for the legacy `lux-mpc` binary
|
|
and still applies if you are operating that path. For posterity:
|
|
|
|
### Legacy prerequisites
|
|
|
|
- **NATS** server (JetStream enabled)
|
|
- **Consul** server (for peer registration and service discovery)
|
|
|
|
### Legacy bring-up
|
|
|
|
```bash
|
|
docker compose -f compose.legacy.yaml up -d # NATS + Consul
|
|
mpc generate-peers -n 3 # peers.json
|
|
mpc register-peers # writes peers into Consul
|
|
mpc generate-identity --node node0 [--encrypt]
|
|
mpc generate-initiator [--encrypt]
|
|
|
|
cd node0 && lux-mpc start -n node0
|
|
```
|
|
|
|
The legacy bring-up script (`scripts/launch-3-nodes.sh`) and the legacy
|
|
status helper (`scripts/cluster-status.sh`) target this path. They will
|
|
not work against `mpcd` consensus deployments. New automation should
|
|
target the consensus-mode endpoints documented above.
|
|
|
|
### Migration
|
|
|
|
A migration path from legacy → consensus is **out of scope** for this
|
|
document; it requires reshare-based share rotation and is tracked in
|
|
`scripts/migration/`. Operators planning a migration should coordinate
|
|
with the maintainers.
|
|
|
|
---
|
|
|
|
## Appendix B: Decrypt an age-encrypted initiator key
|
|
|
|
```
|
|
age --decrypt -o event_initiator.key event_initiator.key.age
|
|
```
|