Gateway provider secrets
Store a third-party provider API key (OpenAI / Anthropic / custom) on the server, encrypted at rest, used only server-side via the gateway. A deployed agent routes its LLM calls through the AuditTrail gateway and references a stored secret by name — so the plaintext never leaves the box.
This is distinct from BYOK keys, which the Operations Assistant chat uses directly. A gateway secret exists so an agent running anywhere can keep its provider credential server-side.
Warning — secrets are tied to your instance secret key
Secrets are encrypted with a key derived from your instance's
AUDITTRAIL_SECRET_KEY(HKDF-SHA256). RotatingAUDITTRAIL_SECRET_KEYinvalidates every stored gateway secret — you will need to re-add them.
What is and isn't stored
- The plaintext value is write-only: it is accepted on create / rotate, encrypted immediately, and never returned by any endpoint, never logged, and never persisted except as a Fernet token.
last4(the last 4 characters of the plaintext) is the only plaintext-derived value persisted — it powers the••••abcddisplay so you can tell which key is configured without exposing it.- Decryption happens only in the gateway forward path, in a local
variable, at the moment the upstream
Authorizationheader is set, then the value is discarded.
How a request selects a secret
Point any OpenAI-compatible client at the gateway proxy with your
AuditTrail virtual key, and add the X-AuditTrail-Secret header naming a
stored secret (by its name or id):
curl https://your-instance/api/v1/gateway/proxy/v1/chat/completions \
-H "Authorization: Bearer at-gw-…" \
-H "X-AuditTrail-Secret: prod-openai" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o","messages":[{"role":"user","content":"hi"}]}'The gateway resolves prod-openai scoped to the owner of the virtual
key, decrypts it in memory, and uses it as the upstream provider
credential. The secret is never visible to the client, never echoed in the
response, and never logged.
- Header absent → the gateway uses the instance
AUDITTRAIL_GATEWAY_*env key (existing behaviour, unchanged). - Header names a secret you don't own →
404(existence is never leaked across tenants).
Endpoints
All routes are user-scoped; cross-tenant access returns 404.
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/agent-secrets | List secrets (last4 only — never plaintext) |
POST | /api/v1/agent-secrets | Store a new secret (409 on duplicate name) |
PUT | /api/v1/agent-secrets/{id} | Rotate (re-encrypt with a new value) |
DELETE | /api/v1/agent-secrets/{id} | Delete (204) |
POST / PUT carry the plaintext value in the body. No response — list,
create, rotate, or otherwise — ever returns it.
CLI — audittrail secrets
The Python SDK's console script (pip install audittrail) manages the same
surface from a terminal. It reads AUDITTRAIL_API and AUDITTRAIL_API_KEY
(an sk-at-… API key from Settings → API Keys) from the environment;
--api / --api-key override per invocation.
audittrail secrets list # metadata + last4 only
audittrail secrets add prod-openai --provider openai
audittrail secrets rotate prod-openai
audittrail secrets rm prod-openai # type the name to confirmaddandrotateread the value from a hidden interactive prompt. There is deliberately no--valueflag — argv is visible to every other process on the machine. For scripts, pipe it instead:printf '%s' "$KEY" | audittrail secrets add prod-openai --provider openai --value-stdinrmasks you to type the secret's name back before deleting (--yesskips the confirmation, for scripts).- Like the API itself, the CLI never prints a stored value — after
add, the only recovery from a lost key isrotate.
UI — /settings?tab=keys
- Add secret — name it, pick a provider, paste the value. It is shown once and never again.
- Rotate — paste a new value; the stored ciphertext is replaced.
- Delete — typed-confirm (tier 2) so a production secret can't be fat-fingered offline.
Supported providers
| Provider | Notes |
|---|---|
openai | Used as the Authorization: Bearer … credential to api.openai.com |
anthropic | Used as the x-api-key credential to api.anthropic.com |
custom | Any OpenAI-compatible upstream you wire up |