API key vault
The vault is a recovery surface, not a permission surface. When an org turns it on, every newly issued API key, personal access token, and provider credential is encrypted at rest with AES-256-GCM alongside the existing one-way hash of the same credential. The hash still gates request authentication; the vault lets an owner or admin recover the plaintext if the human who issued the key lost it.
Reveal authority equals key-create authority. The vault does not introduce a new admin role. An admin who could have issued the key in the first place can also reveal a previously-issued key after stepping up their own credentials.
When to enable the vault
Turn it on when at least one of the following is true:
- You issue keys on behalf of teammates who do not have IQ Routing accounts (consultants, contractors, agencies). Without the vault, a rotated or lost key forces a new issue and a new wiring step in their application; with the vault, you reveal once and resend.
- Your org keeps key plaintext in 1Password / Bitwarden / a password manager and the manager occasionally loses sync. The vault is the recovery path of last resort.
- Your security review requires that key plaintext is recoverable for forensic purposes (incident response, audit trail correlation).
Leave it off when the operational pattern is "the human who created the key keeps the plaintext; if they lose it, they re-issue." That is the default flow and it remains supported.
How to enable
Settings → Key vault → toggle the switch. The change applies to all keys, tokens, and provider credentials issued after the toggle flips. Existing rows stay unvaulted; the next rotation will encrypt them into the vault.
The dashboard surface lives at /settings#key-vault.
How a reveal works
- Open Keys → click the row → click "Reveal".
- The dashboard prompts for step-up authentication. Either enter your account password or complete a WebAuthn challenge if your account has a security key registered.
- The reveal endpoint decrypts the AES-GCM blob server-side, returns the plaintext, and starts a 30-second display window. A countdown ticks down on screen; the plaintext clears from the DOM when it reaches zero.
- Every reveal writes an
api_key_revealedrow to the audit log with the actor email, the key id, and the step-up method. The audit row surfaces in Audit under the actor's filter chip. - Every reveal also enqueues a webhook delivery to any active
destination subscribed to the
api_key_revealedevent so external security tooling (a SIEM, a Slack channel) can react in real time.
Limits and rate limits
- 10 reveals per user per hour. The gateway counts the reveal audit
rows the actor wrote in the trailing hour and rejects further reveals
with
429 Too Many Requests. The window is rolling rather than calendar-aligned, so capacity comes back an hour after each individual reveal rather than all at once on the hour. The count spans all three reveal kinds (API key, personal access token, provider credential). - 30-second TTL on the plaintext. No way to extend; copy the plaintext to your password manager during the window or re-issue.
- One step-up per reveal. Step-up does not grant a session-wide trust window; every reveal re-prompts.
- Vaulted rows only. Keys issued before the vault toggle flipped
are unvaulted; their reveal endpoint returns
404 Not Found. Rotate them to encrypt forward.
Encryption details
- Cipher. AES-256-GCM with a fresh 96-bit random nonce per encrypt. Authenticated; tampering with the ciphertext at rest causes decrypt to fail rather than return garbage.
- Key material. The master key is held in the hosted gateway's secret store, never written alongside the ciphertext, and never emitted to logs. The gateway refuses to encrypt at all rather than fall back to a process-local key, so a vaulted row cannot become undecryptable across a restart.
- Versioning. Each encrypted record carries a version marker, and
the current version is
1. A future master-key rotation writes new records at version2, and the decrypt path dispatches on the marker, so records written on either side of a rotation stay readable throughout. - No client-side decrypt. The encrypted blob never leaves the gateway. The dashboard receives only the plaintext during the bounded reveal window.
Rollback
The Settings → Key vault toggle only ever looks forward. Turning it off stops new keys, tokens, and provider credentials from being vaulted from that point on -- the mirror image of turning it on -- but it does not reach back into anything already vaulted. Rows vaulted before the flip stay encrypted and stay revealable through the normal reveal flow, with the same step-up, rate limit, and IP allowlist checks as always. The toggle is read only at issue time; nothing in the reveal path checks it. Hash-based credential verification is unaffected by the toggle either way, so request authentication keeps working throughout.
See also
- /docs/auth -- how API keys, scopes, and the
gw_live_prefix work end-to-end. - /docs/webhooks -- wiring the
api_key_revealedevent into an external destination. - /audit -- the audit log surface where reveal rows land.
IP allowlist
The reveal endpoint enforces step-up auth, the per-user 10/hour rate limit, the active-org membership check, and a mandatory audit row. A per-org IP allowlist sits on top.
Set the allowlist in Settings → Key vault → IP allowlist. The
textarea accepts one CIDR per line (203.0.113.0/24, 2001:db8::/32,
or a single host like 203.0.113.42/32). An empty list disables the
gate; a non-empty list rejects any reveal whose source IP does not
match. Rejected attempts write an api_key_reveal_ip_blocked audit
row carrying the resolved source IP and the size of the allowlist.
The source IP is resolved in a fixed order. The authoritative value is
the client-IP header the hosted gateway's own edge injects, which the
edge strips from anything the caller sent, so it cannot be forged. Only
if that is absent does resolution fall through, and X-Forwarded-For is
distrusted by default at that point rather than read: a client-supplied
forwarded header does not move you into the allowlist. Last resort is the
raw socket peer. When none of the three yields a parseable address the
reveal is refused with 403 Source IP not resolvable and an
api_key_reveal_ip_blocked audit row, so the gate fails closed rather
than open.
The allowlist is per-org, not per-user. An operator who works from multiple machines lists every CIDR; the allowlist is defence in depth on top of step-up auth, not a per-machine identity.
WebAuthn-required reveals
The default reveal flow accepts the account password step-up or a WebAuthn assertion. A session token plus a phished password is enough to reveal under the default policy.
For orgs whose compliance posture requires hardware-backed step-up,
flip Settings → Key vault → Require passkey for reveals. The
toggle rejects password-only step-up with 403 WebAuthn registration required. The operator must register a passkey under
Settings → Key vault first.
The toggle is independent of the vault enable/disable toggle; an org can enable vaulting and password reveals together during an initial trial, then promote to passkey-required once every operator has enrolled. A passkey loss does not lock the operator out of the dashboard -- only the reveal endpoint gates on the passkey, so the operator can still log in normally, disable the toggle, and rotate keys via the existing revoke-and-reissue flow.