# Architecture & topology

The vault runs across three Kubernetes clusters. Place each cluster in a separate AWS account or GCP
project so a single cloud-identity compromise exposes at most one MPC key share and one Policy Server,
below the signing threshold.

For GCP production, that boundary includes administration: each party owns its own Terraform root and
backend, deployment credentials, project, GKE cluster, Cloud SQL databases, Workload Identities, and
HSM key. The combined three-cluster Terraform example uses one state and caller across the topology
and is intended for integration and proof-of-concept deployments.

## Cluster layout

![Three Kubernetes clusters across isolated cloud environments: two spoke clusters connect privately to one hub cluster](/img/diagrams/cluster-layout.svg)

```mmd
flowchart TB
    subgraph A1["Cloud environment 1: Spoke"]
        direction TB
        KS1["Key Store\n(Confidential compute)"]
        PS1["Policy Server"]
        DB1[("Managed PostgreSQL\nmpc_ks_db · policy_db_0")]
    end

    subgraph A2["Cloud environment 2: Hub"]
        direction TB
        ORC["Orchestrator\n(API entry point)"]
        UI["Vault UI"]
        RELAY["Relay"]
        NATS["NATS JetStream\n(3-node)"]
        KS2["Key Store\n(Confidential compute)"]
        PS2["Policy Server"]
        DB2[("Managed PostgreSQL\nmpc_ks_db · policy_db_1 · orchestrator_db")]
    end

    subgraph A3["Cloud environment 3: Spoke"]
        direction TB
        KS3["Key Store\n(Confidential compute)"]
        PS3["Policy Server"]
        DB3[("Managed PostgreSQL\nmpc_ks_db · policy_db_2")]
    end

    A1 <-->|"Private service endpoints"| A2
    A3 <-->|"Private service endpoints"| A2
```

*Both spokes run a Key Store shard, Policy Server, and managed PostgreSQL. The hub adds the Orchestrator, Vault UI, relay, and NATS.*

One cluster is the **hub**. The other two are **spokes**. All three run a Key Store shard and Policy
Server. The hub additionally runs the Orchestrator, UI, NATS, and relay.

![Deployment topology: three Kubernetes clusters connected through private service endpoints](/img/diagrams/deployment-topology.svg)

```mmd
flowchart TB
    Users["Corporate Users\n(VPN / zero-trust)"]

    subgraph Account2["Cloud environment 2: Hub cluster"]
        direction TB
        ORC2["Orchestrator\n(API entry point)"]
        UI2["Vault UI\n(Next.js)"]
        NATS2["NATS JetStream\n(3-node cluster)"]
        RELAY["Relay\n(MPC round router)"]
        KS2["Key Store\n(Confidential compute)"]
        POL2["Policy Server"]
        RDS2[("Managed PostgreSQL\nmpc_ks_db\npolicy_db_1\norchestrator_db")]
        KMS2["KMS Key"]
    end

    subgraph Account1["Cloud environment 1: Spoke cluster"]
        direction TB
        KS1["Key Store\n(Confidential compute)"]
        POL1["Policy Server"]
        RDS1[("Managed PostgreSQL\nmpc_ks_db\npolicy_db_0")]
        KMS1["KMS Key"]
    end

    subgraph Account3["Cloud environment 3: Spoke cluster"]
        direction TB
        KS3["Key Store\n(Confidential compute)"]
        POL3["Policy Server"]
        RDS3[("Managed PostgreSQL\nmpc_ks_db\npolicy_db_2")]
        KMS3["KMS Key"]
    end

    Users -->|"HTTPS :443"| ORC2
    Users -->|"HTTPS :443"| UI2

    ORC2 -->|":3001"| POL1
    ORC2 -->|":3001"| POL2
    ORC2 -->|":3001"| POL3
    ORC2 -->|":8080"| KS1
    ORC2 -->|":8080"| KS2
    ORC2 -->|":8080"| KS3

    ORC2 <-->|":4222"| NATS2
    ORC2 -->|":80 create MPC room"| RELAY

    KS1 <-->|"Private endpoint :80"| RELAY
    KS3 <-->|"Private endpoint :80"| RELAY
    KS2 <--> RELAY

    POL1 <-->|"Private endpoint :4222"| NATS2
    POL3 <-->|"Private endpoint :4222"| NATS2
    POL2 <--> NATS2

    KS1 --- RDS1
    POL1 --- RDS1
    KS1 --- KMS1
    KS2 --- RDS2
    POL2 --- RDS2
    ORC2 --- RDS2
    KS2 --- KMS2
    KS3 --- RDS3
    POL3 --- RDS3
    KS3 --- KMS3
```

*AWS uses PrivateLink for service endpoints. GCP uses Private Service Connect, with VPC Network Peering for the supporting private routes.*

## Services

### Key Store (every cluster)

The Key Store holds one MPC key share. It runs on the selected cloud's confidential-compute substrate
and encrypts stored key material with a KMS key from the same AWS account or GCP project.

| AWS | GCP |
|---|---|
| Measured AWS Nitro Enclave | Pod on an N2D GKE Confidential Node using SEV-SNP |
| AWS KMS through IRSA | Cloud KMS through Workload Identity |
| KMS access can be bound to enclave measurements | KMS access is bound to the workload identity |

Only the Orchestrator communicates with the Key Store's private HTTP service.

### Policy Server (every cluster)

The Policy Server holds Setup Admins, passkey public keys, user groups, Administrative Policy rules,
and Signing Policy rules. The Orchestrator reads and writes policy state across all three clusters. The
Policy Server has no public HTTP surface.

### Orchestrator (hub only)

The Orchestrator accepts API and UI requests, coordinates the three Key Stores and Policy Servers, and
drives each MPC ceremony. Expose it only through your approved private network, VPN, or zero-trust
gateway unless your security design requires a public endpoint.

### NATS (hub only)

A three-node JetStream cluster carries policy coordination and approval/completion events between the
Orchestrator and Policy Servers.

### Relay (hub only)

The relay routes MPC round messages between Key Store nodes during signing and key generation. Spoke
Key Stores reach it over private service endpoints.

### Vault UI (hub only)

The Next.js web application uses Google OAuth and WebAuthn passkeys. Expose it through the hub front
door: an AWS NLB or GKE Ingress.

## Data stores

| Store | AWS | GCP |
|---|---|---|
| Key Store database (`mpc_ks_db`) | RDS, one per account | Cloud SQL, one per project |
| Policy Server database (`policy_db_N`) | RDS, one per account | Cloud SQL, one per project |
| Orchestrator database (`orchestrator_db`) | RDS in the hub account | Cloud SQL in the hub project |
| Storage-encryption key | AWS KMS key per account | Cloud KMS key per project |

PostgreSQL is not part of the Helm chart. The chart accepts the database connection settings as
values. On GCP, the chart injects a Cloud SQL Auth Proxy sidecar into each Key Store, Policy Server
reader, Policy Server writer, and Orchestrator pod that uses a database. Each workload and its sidecar
share a distinct Kubernetes and Google service-account identity, and the application connects over
pod-local port 5432.

## Required network paths

| From | To | Port |
|---|---|---|
| Corporate users and integrations | Hub UI and Orchestrator front doors | 443 |
| Orchestrator | Policy Server on all three clusters | 3001 |
| Orchestrator | Key Store on all three clusters | 8080 |
| Orchestrator | NATS and relay on the hub | 4222 / 80 |
| Policy Server on all clusters | NATS on the hub | 4222 |
| Key Store on all clusters | Relay on the hub | 80 |
| Application pods | Local managed PostgreSQL, or the pod's Cloud SQL Auth Proxy on GCP | 5432 |

The AWS example wires these paths with PrivateLink. GCP production service paths use Private Service
Connect. The combined GCP example also creates VPC Network Peering for supporting private routes,
but peering is an integration and proof-of-concept option rather than the production cross-project
service boundary.

GKE-managed internal LoadBalancer Services use reserved VIPs, `externalTrafficPolicy: Local`, and
source ranges aligned with each role's NetworkPolicy CIDRs. Each PSC producer admits only the service
attachment's dedicated NAT subnet plus health-check ranges, while hub egress is limited to the exact
spoke endpoint CIDRs. PSC endpoints must be in the same region as their target attachments, so the
example keeps all three clusters and endpoints in one region. PSC provides private connectivity, not
mTLS; application traffic across these paths remains HTTP and NATS. If you provide your own network,
keep the Key Store, Policy Server, NATS, and relay private and preserve the same role-specific
boundaries.

## Confidential-compute choice

The Key Store holds the highest-value material, so it always runs on confidential compute.

* AWS Nitro Enclaves isolate the Key Store from the worker-node operating system and support
  measurement-based KMS policies.
* GKE Confidential Nodes encrypt the whole worker VM's memory from the infrastructure host. Kubernetes
  scheduling and Workload Identity control which pod reaches Cloud KMS.

Policy Server, Orchestrator, NATS, relay, and UI do not hold MPC key shares and run on standard nodes.
