How it works
Saving a key
- Your page renders
<ConnectKeyWidget />, which loadsGET /widget?app=...&origin=...from the gateway in an iframe. The iframe is on the gateway's origin, so your page's JavaScript cannot read what the user types. - The user pastes a key. The iframe sends it to
POST /keyson the gateway over TLS. - Inside the enclave the gateway:
- generates a random 256-bit data key and encrypts the API key with AES-GCM,
- splits the data key into two shares: share A (random) and share B (data key XOR share A),
- wraps share B with AWS KMS. KMS only unwraps for an enclave whose image hash matches the key policy.
- The gateway returns a handle,
kh_<keyID>.<shareA>, and stores the sealed record{ keyID, appID, ciphertext, iv, wrappedShareB }, or hands the record back to you in app-owned mode. - The iframe posts the handle to your page, and
ConnectKeyWidgetsaves it to your server throughPOST /api/fiducy/keys.
Using a key
- Your server calls
providerSettings('openai', { userID }). It returns a gatewaybaseURL, your app secret asapiKey, and the handle in thex-key-handleheader. - Your AI SDK sends a normal provider request to the gateway.
- Inside the enclave the gateway checks the app secret, finds the record, unwraps share B through attested KMS, rebuilds the data key from both shares, decrypts the API key, and forwards the request to the provider.
- The provider's response streams straight back. The plaintext key is dropped when the request ends.
Why two shares
Neither side can open a key alone:
| Who has it | What they hold | Can they decrypt? |
|---|---|---|
| Your database | Handle (share A) | No: share B is wrapped by KMS |
| Fiducy storage | Record (ciphertext, wrapped share B) | No: share A is only in your handle |
| AWS account owner | KMS key | No: the key policy only allows the attested enclave |
| The enclave | Code only, no stored state | Only while serving a request that carries both |
No way back out
The gateway proxy is the only way to use a stored key. There is no endpoint that returns a plaintext key to your server. If the proxy does not cover a provider you need, ask us to add it.
The path a key takes, from the widget to the provider.