# How it works

A high-level tour of the components and how a signing request flows through them.

## The services

The vault is a cluster of cooperating services. Every service runs inside your own cloud account.

| Service | Role | Count |
|---|---|---|
| Orchestrator | Single REST API entry point. Routes requests, drives state, and coordinates all other components. | 1 |
| Policy Server | Evaluates policy rules independently and signs its decision. | 3 |
| Key Store / MPC Node | Holds one key share. Verifies policy decisions before participating in signing. Runs in AWS Nitro Enclave confidential compute. | 3 |
| Message Queue | Coordinates state between the three Policy Server nodes. | 1 (NATS JetStream) |
| Relay | Routes the MPC round messages between Key Store nodes during a signing or key-generation ceremony. | 1 (hub) |
| Database | Stores persistent state. The Orchestrator has one (hub); each Policy Server and each Key Store shard has its own. | 7 (PostgreSQL) |
| Web app | The browser-based management UI. | 1 (Next.js) |

The Policy Servers and Key Stores form three coupled node pairs (A, B, C). Each pair runs independently. No node is a single point of failure.

## The three-node MPC cluster

Keys are never stored whole. Instead, each Key Store node holds exactly one key share. No node ever has enough material to reconstruct the full key.

This is a 2-of-3 threshold model:

* Any two nodes can cooperate to produce a valid signature.
* Compromising one node gives an attacker only one share, which is cryptographically useless on its own.
* Losing one node does not break signing: the remaining two nodes satisfy the threshold.

Key material is generated in a distributed key generation (DKG) ceremony. The full key never exists in one place, not even briefly.

## Why the Orchestrator is untrusted

The Orchestrator handles routing and state, but it cannot approve or forge a policy decision. Policy Servers and Key Stores treat it as a potentially adversarial component. Even a fully compromised Orchestrator cannot cause a signing operation that policy would otherwise deny.

The trust boundary is:

1. Each Policy Server evaluates rules independently and signs its own verdict.
2. The Orchestrator collects verdicts until it has 2-of-3 signatures.
3. Each Key Store independently verifies those signatures before participating in MPC signing.

No single component can bypass this chain.

## A signing request, end to end

1. Machine User sends `POST /v1/keys/{key_id}/sign` to the Orchestrator.
2. Orchestrator routes the request to all three Policy Server nodes.
3. Each Policy Server independently evaluates the Signing Policy (ordered, first-match rules).
4. Each Policy Server signs its verdict (Approve or Deny).
5. Orchestrator collects 2-of-3 signed Approve verdicts.
6. Orchestrator forwards those signatures to all three Key Store nodes.
7. Each Key Store independently verifies the 2-of-3 policy signatures.
8. The three Key Stores run the MPC signing protocol and produce a signature.
9. The Orchestrator returns the signature to the caller.

The caller never waits longer than the slowest responsive node. If one node is temporarily unavailable, signing continues with the other two.

![Vault architecture: Orchestrator, three Policy Server + Key Store node pairs, NATS, relay, and databases](/img/diagrams/vault-architecture.svg)

```mmd
flowchart TB
    MU["Machine User /\nAPI Client"]
    WebApp["Web App\n(Next.js UI)"]

    subgraph OrchestratorZone["Orchestrator: UNTRUSTED BOUNDARY"]
        ORC["Orchestrator\n(REST API entry point)"]
        NATS["NATS JetStream\n(policy-server bus)"]
        RELAY["Relay\n(MPC round router)"]
    end

    subgraph TrustBoundary["Trust Boundary: Independent Verification"]
        subgraph Policies["Policy Servers (x3)"]
            POL_A["Policy Server A"]
            POL_B["Policy Server B"]
            POL_C["Policy Server C"]
        end
        subgraph KeyStores["Key Stores (x3, Nitro Enclave)"]
            KEY_A["Key Store A"]
            KEY_B["Key Store B"]
            KEY_C["Key Store C"]
        end
    end

    MU -->|"POST /v1/keys/{id}/sign"| ORC
    WebApp -->|"HTTPS (internal)"| ORC

    ORC -->|"evaluate policy (HTTP)"| Policies
    ORC -->|"sign with 2-of-3 policy sigs (HTTP)"| KeyStores
    ORC -->|"create MPC room"| RELAY
    ORC --> NATS

    NATS -.->|"policy events"| Policies
    RELAY -.->|"MPC rounds"| KeyStores

    POL_A --- KEY_A
    POL_B --- KEY_B
    POL_C --- KEY_C
```

*Three coupled node pairs (each a Policy Server + Key Store) sit inside the trust boundary; the Orchestrator routes requests from outside it.*

## Signing request flow

The sequence below shows how a signing request moves from the caller through policy evaluation and MPC signing.

![End-to-end signing request flow: Machine User to Orchestrator to Policy Servers to Key Stores and back](/img/diagrams/signing-request-flow.svg)

```mmd
sequenceDiagram
    participant MU as Machine User
    participant ORC as Orchestrator
    participant POL as Policy Servers (3x)
    participant KS as Key Stores (3x)

    MU->>ORC: POST /v1/keys/{id}/sign

    rect rgb(240, 245, 255)
        Note over ORC,POL: Policy Evaluation
        ORC->>POL: Fan out: evaluate Signing Policy (all 3 nodes)
        POL->>POL: Each node independently evaluates ordered rules
        POL-->>ORC: Signed verdict from each node
    end

    alt Policy denied or blocked
        ORC-->>MU: 403 Forbidden
    else 2-of-3 signed Approve collected
        rect rgb(240, 255, 245)
            Note over ORC,KS: MPC Signing
            ORC->>KS: Forward request with 2-of-3 policy signatures
            KS->>KS: Each node independently verifies 2-of-3 policy signatures
            KS->>KS: MPC threshold-signing protocol (2-of-3 nodes)
            KS-->>ORC: Threshold signature
        end
        ORC-->>MU: 200 OK, signature returned
    end
```

*Policy evaluation and MPC signing are independent verification layers. The Orchestrator cannot short-circuit either.*
