How RelayKey makes API-key leaks survivable.
RelayKey is built around one security assumption: your API keys will eventually leak somewhere. The real upstream key stays encrypted behind RelayKey; applications, agents, vendors, and people get scoped RelayKeys you can audit, limit, expire, and revoke. For human-held keys, new-IP confirmation can block a stolen RelayKey before the upstream ever sees a request. We don’t claim zero-knowledge. We claim something more useful: a blocking and containment layer between your real keys and the places they would otherwise spread.
Our trust model, in plain English.
What we hold.
Your real upstream API keys, encrypted at rest. We hold these because we have to: to forward a request to OpenAI, Anthropic, Grafana, Stripe, or an internal API, our proxy must decrypt the key in memory at the moment of forwarding.
What we never see.
Your request and response bodies. Your query strings (unless you opt in per integration). Your disposable RelayKeys in plaintext after creation: we store only their SHA-256 hash and verify by hash on every request.
What applications, agents, and people see.
A short-lived, scoped RelayKey (rk_proxy_...). They can call the upstream API only via the methods, paths, and IPs you've allowed. They never see the real upstream key, never receive a refresh token, and lose access the moment you click revoke.
Threats we explicitly defend against, and how.
| Threat | Mitigation |
|---|---|
| Real API key leak from our database | Real upstream API keys are encrypted with AES-256-GCM using a separate encryption key (RELAYKEY_MASTER_KEY) that lives outside the database. In production this key is provided by the platform secrets store (Fly secrets at v0; AWS KMS / GCP KMS at v1). A SQL dump alone is not sufficient to decrypt customer keys. |
| Server compromise (RCE) | RelayKey’s proxy intentionally has minimal attack surface. We do not parse JSON bodies. We do not render user-supplied HTML. We do not execute customer plugins. We pin all dependencies via lockfile and avoid post-install scripts. SAST runs on every change. |
| Stolen audit logs | Audit logs are encrypted at rest using the same envelope encryption pattern. By default we record paths only, not query strings, not request bodies, not response bodies. Customers can opt into query string logging per integration with a written warning that query strings can carry sensitive data. |
| Insider threat (RelayKey employee) | Production database access is break-glass-only and itself audited. For enterprise customers we offer customer-managed encryption keys (BYOK) so even a compromised RelayKey employee cannot decrypt your upstream keys without your KMS approval. |
| Supply chain (malicious dependency) | We use a strict lockfile, ignore post-install scripts, run dependency audits on every commit, and minimize transitive dependencies. The proxy data plane has fewer than 30 direct dependencies. |
| Disposable key theft (rk_proxy_... leaks) | Human-held RelayKeys use new-IP confirmation: a stolen token used from an untrusted network returns 423 Locked before the upstream is reached. Application and service RelayKeys can be pinned to fixed IPs. If the attacker is already inside a trusted environment, method/path scope, expiry, rate policy, audit, and one-click revoke contain the damage without rotating the real upstream key. |
| Side-channel timing attacks on tokens | Token lookups are by SHA-256 hash, not by token equality. We never compare plaintext tokens. |
| Replay attacks | All requests are forwarded with HTTPS to the upstream. The proxy does not store request bodies or response bodies, so there is nothing to replay from our side. Upstream vendors’ replay protections (e.g. Stripe’s idempotency keys) continue to work normally. |
What is actually implemented today.
A snapshot of the controls in production, as of today. We update this list when behavior changes, not when we plan to change it. If something you need is on the “not yet” list below, ask us. We will tell you whether it’s weeks or quarters away.
| Control | Today |
|---|---|
| Encryption at rest | AES-256-GCM via a local-key envelope scheme (web/lib/crypto.ts). The encryption key (RELAYKEY_MASTER_KEY) lives outside the database in the platform secrets store. A KMS-backed envelope scheme stub exists for v1.1. |
| Encryption in transit | TLS 1.2+ at the Fly edge for every public hostname (relaykey.ai, app.relaykey.ai, proxy.relaykey.ai). Internally Caddy talks to Fastify over loopback only. |
| Audit log scope | Every proxy call is logged with method, path, decision, status, duration, and source IP (IP normalized for shared egress). Every admin action is logged with actor, subject, and metadata in a separate admin_audit_events table. |
| Audit log retention | Plan-based: 7 days (Free), 30 days (Builder), 90 days (Team), 1 year (Business). Older rows are pruned by a scheduled job. |
| Authentication (dashboard) | Magic link with a 15-minute TTL and a click-through confirm step to defeat email-scanner prefetch. Sessions last 30 days. |
| Self-hosted deployment (data stays in your VPC) | Available. Same dashboard, audit log, and disposable-RelayKey model. The proxy and database run in your environment. RelayKey's cloud is contacted only for license validation; never for API traffic, vendor API keys, audit logs, paths, or request/response bodies. More on /self-hosted → |
| 2FA for API access (step-up auth on new IPs) | Default-on for human-held RelayKeys. The first IP that calls the proxy is auto-trusted (TOFU, like SSH known_hosts) so setup never friction-blocks; every new IP after that triggers an emailed confirmation link to the key holder. Until they click it, the proxy returns 423 Locked. The upstream is never reached. Confirmed IPs are stored per key holder and admins can revoke individual entries any time. Application and service RelayKeys can use fixed IP allowlists instead. |
| Authentication (management API) | rk_mgmt_... Bearer tokens with optional self-expiry, optional IP allowlist, and optional per-token method/path scope. |
| Role model | admin / viewer per workspace; mutations require admin. Provisioner-role management tokens get a tighter scope subset (mint disposable RelayKeys within fixed bounds, never read the upstream key). |
| Domain claim | Business email domains bind to one organization on first sign-up. Subsequent sign-ups on a claimed domain must be invited by an existing admin. |
| Data residency | Primary in ord (Chicago). Single region today; multi-region is a roadmap item. |
| Sub-processors | Fly.io (hosting), Resend (transactional email), Cloudflare (DNS only), Stripe (billing, when live), Google Analytics 4 (marketing site only). |
| Vulnerability disclosure | /.well-known/security.txt; contact [email protected]. |
Need to fill out a security questionnaire?
We will respond to a CAIQ, SIG, or your own custom security questionnaire within five business days. Send it to [email protected]. We do not currently hold SOC 2, ISO 27001, or HIPAA attestations See the Compliance section below for where we are on that.
What we don’t yet defend against.
- Compromise of the upstream vendor. If ElevenLabs is breached, RelayKey doesn't help. We're a layer in front of the vendor, not a replacement for the vendor's security.
- Compromise of your own client. If your application server is compromised, the attacker has access to whatever credentials that application holds, including the rk_proxy_... token your code is using. RelayKey limits what that token can do and lets you kill it without rotating the real upstream key.
- Inbound webhooks. RelayKey is outbound-only. Webhooks from your vendors continue to hit your endpoints directly. On the roadmap.
What we log.
By default, RelayKey logs request paths but not full query strings, request bodies, or response bodies. You can enable query string logging per integration if needed, with a warning that query strings may contain sensitive data.
For every proxied request, the audit log records:
- timestamp
- organization ID
- integration ID
- RelayKey ID
- method
- path
- allowed/blocked decision
- block reason
- status code
- IP
- user agent
RelayKey is built on RelayKey.
The simplest way to know we take this seriously: RelayKey’s own developer doesn’t use real Cloudflare, Stripe, or Resend production keys day to day. They use scoped RelayKeys.
When we needed to wire up our DNS, our developer received a Cloudflare RelayKey scoped to a single zone, with dns_records paths only, expiring every seven days, IP-pinned to our deploy runner. The real Cloudflare token never left our dashboard. Every API call our developer makes is auditable in our own RelayKey audit log. The disposable key can be revoked at any time without touching Cloudflare.
We use this same pattern for every contractor, agent, and external service we work with. The day we have to break that pattern is the day we have a real bug to fix.
| Service | Real key location | Access pattern for collaborators |
|---|---|---|
| Cloudflare | Founder, in RelayKey | rk_proxy_... to cloudflare/zones/{zone}/dns_records, 7d TTL, IP-pinned |
| Resend | Founder, in RelayKey | rk_proxy_... to resend/emails, 24h TTL, send-only |
| Stripe | Founder, in RelayKey | rk_proxy_... to read-only Stripe endpoints, 24h TTL |
| Production database | KMS-managed, app-only | No human direct access |
Compliance.
RelayKey is a young company. We are not yet SOC 2 Type II certified. We have built RelayKey from day one in line with SOC 2-relevant controls: encrypted at rest, audit logging, access controls, and least-privilege secrets management. We intend to pursue formal certification once we have the operational history to support it.
Customers in regulated industries can request a security questionnaire response and a DPA. If you need a signed DPA, BAA, or SOC 2 questionnaire response, email [email protected].
Report a vulnerability.
Email [email protected]. Standard 90-day disclosure window. We will respond within two business days. We do not currently run a paid bounty.
