Virtual Keys vs API Keys
The word "key" shows up in two completely different places in the Ferro Labs AI Gateway, and conflating them is a common source of confusion. They are unrelated:
- A
virtual_keyis a routing identifier inside your config. It names a provider and its credential set. It is not a secret and is never sent by clients. - An admin API key (
fgw_...) is a client credential. Clients send it as a Bearer token to authenticate to the gateway.
This page keeps the two straight.
virtual_key โ the config target identifierโ
Every entry in targets[] carries a virtual_key (Target.VirtualKey in
config.go). It is the unique identifier the gateway uses to pick which
provider, with which credentials, handles a request.
Resolution happens before routing and plugin execution: the gateway looks up
g.providers[virtual_key] to find the provider instance that was built for that
target. Each provider instance is constructed from a ProviderEntry
(providers/providers_list.go) and loads its credentials either from environment
variables (OSS self-hosted) or from an injected credential map (managed
deployments). So the virtual_key is the name of a credential set โ the actual
secret (for example OPENAI_API_KEY) lives in the environment, never in the
virtual_key itself.
strategy:
mode: fallback
targets:
- virtual_key: openai # selects the OpenAI provider + OPENAI_API_KEY
retry:
attempts: 3
on_status_codes: [429, 502, 503]
- virtual_key: anthropic # selects the Anthropic provider + ANTHROPIC_API_KEY
circuit_breaker:
failure_threshold: 5
timeout: 30s
The same identifier is what routing strategies reference as target_key. For
example, conditional, content-based, and A/B-test strategies all route to a
target_key that must match a virtual_key declared in targets[]:
strategy:
mode: conditional
conditions:
- key: model
value: gpt-4o
target_key: openai # must match a targets[].virtual_key
- key: model
value: claude-3-5-sonnet-20241022
target_key: anthropic
A virtual_key is plaintext configuration. It carries no authentication weight
and is safe to commit. The provider's real API key is resolved separately from
the environment at request time.
A target can also declare models[] alongside its virtual_key โ model IDs
the operator asserts that target serves, additive to whatever the model
catalog and live discovery already report. It's the provider-agnostic way to
route a model a provider doesn't otherwise advertise (a preview ID, a
self-hosted deployment, a provider with no /models endpoint):
targets:
- virtual_key: gemini
models:
- gemini-2.5-flash # joins the routing index and /v1/models
See Configuration for the full targets[]
schema.
Admin API keys (fgw_...) โ client credentialsโ
Admin API keys are the credentials clients use to authenticate to the
gateway. They are minted via POST /admin/keys and returned as a string with
an fgw_ prefix. The admin control plane that issues and validates them is
split across three packages under internal/admin/ โ model (the APIKey
type and scope rules), repository (the key store), and handlers (the HTTP
surface) โ there's no single keys.go file to point to.
A client then presents the key as a Bearer token on inference routes:
curl http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer fgw_8f3c...." \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o","messages":[{"role":"user","content":"Hello"}]}'
Each key is a record with a lifecycle:
- Scopes โ
adminorread_only. When none are supplied at creation, the key defaults toread_onlyโ least privilege, not admin. - Expiry โ an optional
expires_at. A key past its expiry stops authenticating.PUT /admin/keys/{id}can extend it by sending a newexpires_at, or remove it entirely with"clear_expiration": true. - Rotation โ
POST /admin/keys/{id}/rotateswaps the key string in place (stampingrotated_at) while keeping the same ID, scopes, and usage history. - Revocation vs. deletion โ these are not the same operation.
POST /admin/keys/{id}/revokemarks the key inactive so it immediately stops validating, but keeps the record (and its usage history) inGET /admin/keysand in request-log attribution.DELETE /admin/keys/{id}removes the record outright โ it's irreversible, and existing request-log rows keep the now-orphanedapi_key_idbut can no longer resolve it to a name. Prefer revoke when you want the trail; delete when you don't need it. - Usage โ every successful validation bumps
usage_countandlast_used_at.
A key can't delete, revoke, or de-scope itself, and the last remaining admin
key can't be deleted, revoked, or stripped of the admin scope โ both are
refused with 409 to prevent a self-inflicted lockout.
When listed, the stored key string is masked to its first 8 characters
(fgw_...) so the full secret is never echoed back by the admin API.
Dashboard sessions are not API keysโ
POST /admin/session exchanges an API key (or MASTER_KEY) for a short-lived
dashboard session token, prefixed fgws_ โ deliberately distinct from an
API key's fgw_ prefix so the two can never be confused. A session is a
separate record from the APIKey it was minted from: it carries its own ID,
inherits the source key's scopes at mint time, and expires on its own clock
(24h absolute, or 1h after the last request โ whichever comes first). Signing
out (DELETE /admin/session) or an admin revoking one (DELETE /admin/sessions/{id}, or all at once with DELETE /admin/sessions) deletes
the session row rather than marking it, so it stops validating immediately.
Revoking a session never touches the API key it came from.
Side-by-sideโ
virtual_key (config) | Admin API key (fgw_...) | |
|---|---|---|
| Defined in | targets[].virtual_key in config | Issued at runtime via POST /admin/keys |
| Purpose | Names a provider + credential set | Authenticates a client to the gateway |
| Is it a secret? | No โ plaintext, safe to commit | Yes โ a Bearer credential |
| Who uses it | The gateway, internally, before routing | Clients, in the Authorization header |
| Resolved to | A provider instance (g.providers[...]) | A validated APIKey record + scopes |
| Lifecycle ops | Edit config and reload | Create, rotate, revoke, expire, delete |
| Example value | openai, anthropic, vertex-ai | fgw_8f3c... |
Relatedโ
- Authentication โ master key, client request auth, and issuing admin API keys.
- Configuration โ full
targets[]and strategy reference. - Providers configuration โ which env var each
provider's
virtual_keyresolves to.