> ## Documentation Index
> Fetch the complete documentation index at: https://docs.quantumapi.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Security model

> How Qustody separates duties, where post-quantum keys live, and what the platform protects against.

Qustody is a **non-custodial** orchestration platform. The platform never holds your post-quantum private keys. Signing happens in a separate signing service that you control or that runs alongside Qustody under a strict cryptographic boundary.

## Trust boundaries

```mermaid theme={null}
flowchart LR
    Caller[API caller] -->|HTTPS + bearer / OIDC| API[Qustody API]
    API -->|PostgreSQL TLS| DB[(PostgreSQL<br/>encrypted at rest)]
    API -->|JSON-RPC| Node[Quantum Chain<br/>node pool]
    API -->|HTTPS / gRPC mTLS| Signer[External signing service<br/>holds post-quantum keys]
    API -.->|HMAC-SHA256| Webhook[Your webhook endpoint]

    subgraph "Qustody boundary"
        API
        DB
    end

    subgraph "Signer boundary"
        Signer
    end

    subgraph "Chain boundary"
        Node
    end
```

Three independent boundaries:

| Boundary               | What it holds                                                                           | What it does NOT hold                                                                      |
| ---------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| **Qustody API**        | Wallet metadata, public keys, transaction state, audit trail, encrypted webhook secrets | Post-quantum private keys, raw transaction signatures (only after the signer returns them) |
| **Signing service**    | Post-quantum private keys, signing logic                                                | Transaction state, policy data, customer identifiers                                       |
| **Quantum Chain node** | Public ledger, mempool                                                                  | Anything customer-specific                                                                 |

## Separation of duties

Qustody enforces separation of duties through the [RBAC system](/qustody/user-roles):

* **Operator** can create transactions but cannot approve them.
* **Approver** can approve and reject transactions but cannot create them.
* **Compliance officer** can read screening reports and audit logs but cannot create or approve.
* **Admin** can manage users, roles, and credentials but is not required to be on the signing path.

Any high-risk action (rotate credentials, change a system role, change a policy) is recorded to the immutable audit log with a chained hash.

## Key handling boundaries

* The Qustody API **never sees** post-quantum private keys.
* For any transaction in `PENDING_SIGNATURE`, the API exposes a `signing_payload` (the digest to be signed). Your external signer reads it, produces a post-quantum signature using its own keys, and submits the signature back via `POST /v1/transactions/{id}/signature`.
* The API **verifies** the submitted signature against the public key registered to the wallet before accepting it. A wrong key or an invalid signature transitions the transaction to `FAILED`.
* Three signer integration modes are supported:
  * **Callback** (`CUSTODY_SIGNER_TYPE=callback`) — fully external; the signer pushes signatures to Qustody.
  * **Remote** (`CUSTODY_SIGNER_TYPE=remote`) — Qustody calls the signer over HTTPS.
  * **gRPC** (`CUSTODY_SIGNER_TYPE=grpc`) — Qustody calls the signer over gRPC with mTLS (recommended for production).

## Authentication

Qustody supports two authentication modes:

| Mode                  | Use case                       | Configuration                                                                   |
| --------------------- | ------------------------------ | ------------------------------------------------------------------------------- |
| **API key** (default) | Service-to-service integration | `Authorization: Bearer <key>` header                                            |
| **OIDC / SAML SSO**   | Human users in dashboards      | Enabled with `CUSTODY_SSO_ENABLED=true`; requires `CUSTODY_SSO_JWT_SIGNING_KEY` |

Both can be active at the same time. Webhook signing uses HMAC-SHA256 with a per-endpoint secret (returned once on creation).

## Encryption

| Surface            | At rest                                                                                                | In transit                                                            |
| ------------------ | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- |
| PostgreSQL         | TLS to disk; column-level AES-256-GCM for webhook secrets when `CUSTODY_WEBHOOK_ENCRYPTION_KEY` is set | `sslmode=verify-full` enforced in production                          |
| Signer connection  | mTLS (gRPC mode); bearer token over TLS (remote mode)                                                  | TLS 1.2+                                                              |
| Webhook deliveries | n/a                                                                                                    | TLS 1.2+ to your endpoint, HMAC-SHA256 body signature                 |
| API requests       | n/a                                                                                                    | HTTPS only when `CUSTODY_ENV=production`; HTTP allowed in development |

## Audit trail

Every state-changing API call writes an audit record. Records are chained — each entry includes the hash of the previous entry — so any tampering is detectable.

```bash theme={null}
# List audit records
curl "$BASE_URL/v1/audit" \
  -H "Authorization: Bearer $API_KEY"

# Verify the audit chain
curl "$BASE_URL/v1/audit/verify" \
  -H "Authorization: Bearer $API_KEY"
```

The audit log records: actor (user or API key), action, resource ID, request ID, IP, user-agent, prior-record hash.

## Replay and idempotency

* Every mutating endpoint accepts an `Idempotency-Key` header. Duplicate requests with the same key within 24 hours return the original response without re-executing.
* Webhook deliveries include `X-Webhook-ID`, `X-Webhook-Timestamp`, and `X-Webhook-Signature`. Reject deliveries whose timestamp is more than 5 minutes from your clock to prevent replay.

## What Qustody does NOT protect against

Qustody is one layer of a defense-in-depth strategy. It does **not** protect you from:

* Compromise of your signing service or its keys. Use HSMs, mTLS, and least-privilege access on the signing service.
* A malicious or compromised approver. Use multi-approver policies and rotate roles.
* A malicious tenant administrator with `admin` role. Limit `admin` role assignment and review the audit log.
* Compromise of your webhook endpoint. Always verify the HMAC signature.
* On-chain reorganizations beyond the configured `CUSTODY_CONFIRMATION_DEPTH`. Increase the depth for high-value transfers.

## Related

* [User roles and permissions](/qustody/user-roles)
* [Deployment](/qustody/deployment)
* [Quantum safety](/concepts/quantum-safety)
* [Transaction state machine](/concepts/state-machine)
