Architecture: Two Planes, Zero Custody
The canonical explanation of Token Vault's webhook-sovereign design — control plane vs data plane, signed-ticket credential paths, encryption custody, and the kill switch.
This page is the one full telling of how Token Vault works. Every other page links here instead of re-explaining it.
Token Vault is a webhook-sovereign architecture split into two planes with strictly separated responsibilities:
| Plane | Where | Holds | Touches credentials? |
|---|---|---|---|
| Control plane | Token Vault (Cloud Run, api.tokenvault.uk) | identities, grants, policies, audit metadata, webhook URL + HMAC secret | No |
| Data plane | Your webhook (Cloudflare Workers / Cloud Run / Lambda / Deno) | credentials, encryption key, OAuth refresh | Yes — this is the custodian |
Token Vault's security property is absence: credential bytes never cross it. It does not receive credentials, does not store them, does not encrypt or decrypt them, and holds no key that could ever do so. In every path below, its transcript is the same: validate → sign ticket → step aside.
The three credential paths
1. Agent retrieval (307 redirect). The agent sends GET /api/agents/credentials?service=github with its tvagent_… key. Token Vault validates the key, checks the grant, and evaluates ABAC policies — then signs an HMAC-SHA256 ticket and answers with a 307 redirect to your webhook's /v1/credential. The agent's HTTP client follows the redirect and receives the credential directly from your webhook. Full request/response detail: API Reference → Agent Credentials.
2. Storing (browser-direct). When you add a token in the dashboard, Token Vault issues a signed store ticket and the browser sends the credential straight to your webhook's /v1/store. The ticket proves authorization; the bytes go browser → webhook.
3. MCP proxy (server-side injection). The agent calls the proxy with an mcp_… key. Token Vault authenticates, policy-checks, and forwards the request to your webhook's /v1/proxy with a signed ticket. Your webhook injects the credential into the upstream request and calls the upstream itself. Token Vault relays the upstream response but never the credential. Detail: API Reference → MCP Proxy.
Refresh follows the same custody rules — by default your webhook refreshes OAuth tokens autonomously and no credential material crosses Token Vault. The one narrow opt-in exception is documented in API Reference → Token Refresh.
Custody and encryption
Encryption at rest is your webhook's decision, not Token Vault's guarantee. Token Vault cannot see — let alone enforce — what your webhook does with a credential after it arrives. The reference implementations encrypt every sensitive field with AES-256-GCM (authenticated encryption, tamper detected on decrypt); metadata stays plaintext so the dashboard can list tokens without a decrypt call.
{
"v": 1,
"alg": "AES-256-GCM",
"fields": {
"accessToken": "<base64(12B-iv || ciphertext || 16B-tag)>",
"refreshToken": "<base64(12B-iv || ciphertext || 16B-tag)>"
},
"meta": {
"serviceName": "github",
"tokenType": "JWT",
"createdAt": "2026-02-01T10:00:00Z",
"expiryTime": 1720003600000,
"hasRefreshToken": true
}
}| Parameter | Value |
|---|---|
| Key | 32 bytes (256-bit), generated and held by the webhook |
| IV | 12 bytes, random per encryption, prepended to ciphertext |
| Tag | 16 bytes, appended by AES-GCM |
| AAD | None |
Your webhook, your rules
A webhook that stores credentials in plaintext is still a conformant webhook — Token Vault can't tell the difference and never claims otherwise. Encrypting at rest is strongly recommended, and every reference implementation does it.
The dashboard shows a trust badge on each token card: TV Zero (green) for tokens Token Vault never touches in any flow, and TV Refresh (blue) for tokens where the webhook has opted in to TV-mediated refresh (in transit only, never stored).
The kill switch
Taking your webhook offline instantly locks the vault — Token Vault cannot hand out anything, because the credentials exist only on your webhook:
- All agent redirect requests fail (webhook unreachable).
- All MCP proxy requests return 503.
- Token refresh stops.
- Bring the webhook back online and everything resumes.
This is the emergency revocation mechanism — no action needed from Token Vault, and nothing Token Vault (or an attacker who compromised it) could do to bypass it. You can go further and firewall Token Vault's static egress IP; see Webhook Security.
Next Step
Bind your webhook → — the one-time handshake that pairs a deployed webhook with your Token Vault account. Or jump straight to the Quickstart.
Ready to try it?
Sign up free with Google — your credentials stay on your own webhook, and the quickstart gets an agent fetching its first credential in about ten minutes.
TOTP / 2FA Codes
Add TOTP secrets through Token Vault's dashboard — they route straight to your webhook, which generates one-time codes for agents. Token Vault never sees the secret.
Binding Your Webhook
The one-time handshake that pairs your deployed webhook with your Token Vault account — bind page, one-time code exchange, HMAC establishment, and troubleshooting.