Bring Your Own Keys (BYOK)
An org can bring its own upstream provider keys. When a model routes through
the org credential, the gateway authenticates against the provider with the
org's key instead of the platform credential. Clients keep using the same
gateway key (yb_…) — no client change is needed.
Who can add keys
An org admin, in the admin console: Settings → Provider keys. Every key belongs to the org that added it and is used only for that org's traffic.
Supported providers
| Provider | Registry type | base_url | Note |
|---|---|---|---|
| OpenAI | openai | https://api.openai.com/v1 | |
| Anthropic | anthropic | https://api.anthropic.com | Messages API |
| Z.AI (Coding Plan) | zai-coding | https://api.z.ai/api/coding/paas/v4 | Endpoint is pinned by the adapter; enable YABANIN_ZAI_CODING_ENABLED (see providers) |
| vLLM | openai-compat | http://<host>:8000/v1 | OpenAI-compatible server |
| Ollama | openai-compat | http://<host>:11434/v1 | Ollama's OpenAI-compatible endpoint |
| Custom | openai-compat | any HTTPS OpenAI-compatible base_url | llama.cpp, LM Studio, gateways, … |
Adding a key
- Sign in to the admin console as an org admin.
- Go to Settings → Provider keys and click Add key.
- Fill in Name (e.g.
prod-openai), Provider, Base URL, and the provider API key. - Save. The console immediately shows only a last-4 mask; the plaintext is never displayed again.
- Optional: Test connection fires a guarded probe from the console to the registered base URL (rate-limited per org; the server keeps no test history).
- Route a request to a model of that provider — the gateway now authenticates upstream with the org key.
Ollama example
base_url http://127.0.0.1:11434/v1, provider openai-compat. If the gateway
runs in a container (quickstart compose), point at the host loopback as
http://host.docker.internal:11434/v1 instead.
Credential resolution
When a request needs an upstream credential, the gateway resolves in order:
| Order | Tier | Used when |
|---|---|---|
| 1 | Org key (BYOK) | The requesting org has an enabled key in Settings → Provider keys matching the provider and upstream base URL (the exact URL or its /v1-stripped form); newest match wins. |
| 2 | Per-model | No org key matched: the credential pinned on the model registry entry. |
| 3 | Global | Neither of the above: the platform-wide credential configured on the gateway. |
Routing, budgets, lineage, and audit are identical in all three tiers; only the upstream authentication differs.
What is stored, what is visible
- Stored: the encrypted ciphertext (
api_key_ct) and the last-4 mask — nothing else. No plaintext copy exists at rest. - Visible in the console and admin API: name, provider, base_url, last-4 mask. Never the key.
- The plaintext is used in memory at request time only. It is never logged and never returned by the API after creation.
- Org isolation (RLS): key rows are scoped to their org under row-level security. Another org cannot see or resolve them.
Troubleshooting
httpbase_url rejected. Remote providers must usehttps://. Plainhttp://is accepted only for loopback hosts (127.0.0.1,localhost) so local Ollama and vLLM work. On-prem deployments that register plain-httpliteral private-IP endpoints (vLLM, TGI) can setYABANIN_ORG_CRED_ALLOW_HTTP_PRIVATE=1on the admin server; hostname targets stayhttps-only in every mode, and reserved ranges (loopback, link-local/metadata) stay forbidden at dial time. Put TLS in front of anything remote — see the DNS & TLS runbook.- Key does not resolve. Walk the resolution order: (1) does the org have
an enabled, matching key in Settings → Provider keys — provider matches,
and the base URL equals the model's upstream URL (or its
/v1-stripped form)? (2) does a per-model credential on the model registry entry shadow it? (3) does the global credential exist? Fix the first tier that is wrong. - Another org cannot see the key. By design — RLS scopes every key row to its org. Confirm you are signed in to the org that owns the key.
Verify on the stand
make quickstart-up && make quickstart-seed— stand up; store the printed admin password and API key.- Open
http://127.0.0.1:8086and sign in asadmin@localhost. - Settings → Provider keys → Add key: name
local-ollama, provideropenai-compat, base_urlhttp://host.docker.internal:11434/v1, keyollama. The saved row shows name, base_url, and a last-4 mask — not the key. - Chat through the gateway with the seeded key:
curl -sS -H "Authorization: Bearer yb_…" -H "Content-Type: application/json" -d '{"model":"<ollama-backed registry model>","messages":[{"role":"user","content":"ping"}]}' http://127.0.0.1:8082/v1/chat/completions→200, and Ollama logs the request (proof the org key authenticated upstream). - Negative: try adding a key with base_url
http://10.0.0.1:8000/v1→ rejected by default (plain http beyond loopback; unless the deployment setsYABANIN_ORG_CRED_ALLOW_HTTP_PRIVATE=1). - Negative: sign in as a second org → Provider keys is empty (RLS).
- Delete the org key and repeat step 4: the request still resolves (via per-model / global tier), confirming step 4 used the org key.
Limits
- BYOK changes upstream authentication only. Gateway client keys (
yb_…), budgets, policies, and audit are unchanged. - Key material cannot be exported, re-downloaded, or moved between orgs.
- Last-4 is a display mask only — it is not proof of key validity.