Trust boundaries
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:- 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.
Key handling boundaries
- The Qustody API never sees post-quantum private keys.
- For any transaction in
PENDING_SIGNATURE, the API exposes asigning_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 viaPOST /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).
- Callback (
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 |
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.Replay and idempotency
- Every mutating endpoint accepts an
Idempotency-Keyheader. 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, andX-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
adminrole. Limitadminrole 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.