Host-Binding Encryption: The Architecture Behind KeyVault Edge
The KeyVault Edge security model rests on a single property: a sanitized token should be useless from origins it wasn't issued for. This post walks through how the token is built, what the edge proxy checks, and where the limits are.
The problem we're solving
API keys as issued by providers (OpenAI, Stripe, AWS, etc.) have one catastrophic property: they are origin-independent. A key issued to your account can be used from any IP address, any network, any country - as long as the key string is known.
This means that the security of your API access reduces entirely to the security of the key string itself. If the string leaks through any channel - source code, CI logs, a developer's email, a stolen laptop - the attacker has full access to your API account with no further barrier.
Host-binding adds that barrier at the proxy layer, without provider cooperation. The proxy only injects the real key into requests whose origin matches the token's allowlist, and the real key is never returned to the caller.
Sanitized token design
A sanitized token is a self-contained, signed envelope. It starts with kvt_, followed by a base64url body and an HMAC-SHA256 signature over that body.
kvt_<body>.<signature>
body (base64url), in order:
version 1 byte
DEK nonce 12 bytes
wrapped DEK 32-byte data key + 16-byte GCM tag,
encrypted with the edge master key (KEK)
payload nonce 12 bytes
payload AES-256-GCM ciphertext under the DEK
signature HMAC-SHA256(body), base64urlThe encrypted payload holds the real provider key plus the token's policy: the allowed hosts, rate limits, an optional expiry and monthly cost cap, and the endpoint patterns it may call. Each token gets its own data key (DEK), and the DEK is wrapped by a key-encryption key that only the edge proxy holds. Without that key, the token is ciphertext.
Host-binding: the cryptographic constraint
The allowed hosts travel inside the encrypted, signed payload, so whoever holds the token can't edit them. The proxy enforces them on every request.
When the proxy receives a request with a token in the X-KV-Token header, it:
- 1.Verifies the HMAC signature; a tampered or forged token is rejected with 403
- 2.Unwraps the token's data key with the edge master key and decrypts the payload in Worker memory
- 3.Takes the request's Origin header (falling back to Referer, then Host) and compares its hostname with the token's allowed hosts, exact or *.wildcard
- 4.On a mismatch: returns 403, discards the decrypted payload and records a breach event
- 5.Checks expiry, revocation, rate limits and the monthly cost cap
- 6.Injects the real key into the provider's auth header and forwards the request
- 7.Strips its own X-KV-* headers and Cloudflare client headers from the upstream request
- 8.Streams the provider response back to the caller
Cloudflare Workers: why V8 isolates matter
The proxy runs on Cloudflare Workers. The choice is architectural, not operational: V8 isolates provide a fundamentally different security boundary than traditional server processes.
No shared memory between requests
Workers run in V8 isolates, which keep separate Workers' memory apart. The decrypted key exists only in the request's working memory and is not written to logs.
No file system
Workers have no persistent file system, so decrypted key material can't be written to disk or a temp file.
Runs at the edge
Requests are handled at a Cloudflare location near the caller, so the decrypt-and-forward step doesn't add a trip to a central server.
No database lookup for the key
Because the encrypted key travels inside the token, the proxy doesn't fetch it from a database on each request; it only needs its own master key.
Request flow: end-to-end
// ─── What happens at the network layer ───────────────────────────────────
// 1. Your app sends: POST <proxy>/v1/chat/completions
// X-KV-Token: kvt_...
// X-KV-Target: https://api.openai.com
// Origin: https://yourdomain.com
//
// 2. Worker verifies the HMAC, decrypts the token in memory,
// and checks yourdomain.com against the token's allowed hosts
// 3. Forwards: POST https://api.openai.com/v1/chat/completions
// Authorization: Bearer <real key from the token>
// (X-KV-* and Cloudflare client headers removed)
//
// 4. Streams the response back to your app
// ─────────────────────────────────────────────────────────────────────────For streaming responses the Worker forwards chunks as they arrive; the decrypt-and-check step happens once, before the request is forwarded.
Breach detection and alerting
When a token fails validation (bad signature, wrong origin, expired or malformed), the proxy records a breach event in the background and rejects the request. The event contains:
{
token_prefix: "kvt_AbCdEfGh",
event_type: "domain_mismatch",
attacker_ip: "203.0.113.42",
attempted_host: "evil.example",
user_agent: "...",
geo_location: "City, Country"
}The event appears in your dashboard and is sent to the alert channels you configure:
- Slack
- Your own webhook endpoint
Request bodies are not logged.
Threat model
Token leaked in source code
From any origin not on the allowlist, the proxy returns 403 and records a breach event. The token only works for origins you listed.
Token leaked in CI logs
Same as above. Rotate the token anyway; it costs nothing.
Token used from a compromised authorized domain
Requests succeed (the domain is authorized). Anomalous usage patterns trigger alerts. Revoke token from dashboard.
KeyVault Edge infrastructure compromised
Tokens are only ciphertext without the edge master key. Someone who obtained that key and your tokens could decrypt them, so in that case rotate the real keys at the provider. Enterprise plans can wrap keys with your own KMS.
Header forgery from a server
Browsers set Origin and Referer, but a server-side attacker can send any value. If they know an allowed origin, host-binding alone won't stop them; rate limits, cost caps and anomaly alerts are the backstop. Keep allowlists tight.
Token with no allowed hosts
An empty allowlist skips the host check. Always set at least one allowed host.
Request body interception
Both legs use TLS: your app to the edge, and the edge to the provider (the target must be https). Request bodies are not logged.
Try the architecture in your project
Two environment variable changes. No SDK changes. Plans from $9/month.
Get started