Skip to content
IQ Routing

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

  1. Open Keys → click the row → click "Reveal".
  2. 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.
  3. 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.
  4. Every reveal writes an api_key_revealed row 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.
  5. Every reveal also enqueues a webhook delivery to any active destination subscribed to the api_key_revealed event 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 version 2, 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_revealed event 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.