Which variable each provider reads
This table is generated from the model registry, so it is exactly what the
shipped binary looks for. Adding a provider is
a registry edit.
When keys are read
Once, when the client is constructed. Building a client sweeps the environment for every variable the registry names and keeps the providers it can authenticate. Each request then looks up the key for the provider its model string routed to. Three consequences worth planning around:1
You only set the keys you use
Construction never requires a key. A client built in an environment with
no keys at all is valid; each request then says which variable it wanted.
Routing only to
openai/… models means OPENAI_API_KEY is the only
variable that has to exist.2
An empty value counts as absent
A variable set to the empty string is treated as no key, rather than
sending
Authorization: Bearer upstream and reporting whatever the
provider makes of it.3
A missing key fails before any network call
warpllm raises immediately, naming the variable to set, rather than sending
an unauthenticated request and surfacing the provider’s 401. It arrives as
a 401
AuthenticationError with code: "invalid_api_key" — see
Errors.Two providers may never share a variable — the registry’s tests reject it.
A shared key would silently authenticate one provider with another’s
credentials.
Setting the variables
warpllm reads the process environment directly and loads no.env file itself,
so a .env on disk is inert until something sources it.
examples/.env.example
template to copy.
Narrowing a client to the providers it serves
By default a client serves the whole roster and reads every variable above. Declaring the providers you mean narrows both at once — and lets you supply a key directly, for callers keeping theirs somewhere the process environment cannot reach.Narrows what is read
Only the declared providers’ variables are consulted, so a key exported for
something else is not quietly adopted.
Narrows what is routable
A model under a provider you did not declare is refused at admission with
provider_not_declared, before any socket opens — so a client serving one
provider cannot be talked into billing another by a model string.os.environ.get("OPENAI_API_KEY", "") would
otherwise disable a provider whose key is sitting right there.
A declaration selects from the roster; it does not extend it. There is no
models to write here — adding a provider warpllm does not ship is a registry
change, not a configuration one, and a name the roster does not hold fails when
the client is built. An entry can also carry a base_url (baseUrl in
TypeScript) that points that provider at a proxy or test double; see the
SDK reference.
Inline keys never reach a log.
ProviderConfig’s Debug prints
<redacted>, so a tracing::debug!(?config) in caller code cannot leak one.Gateway authentication
The self-hosted gateway works the same way: it holds the provider keys in its own environment and authenticates upstream with them.Key handling
- Keys are read from the environment and used to sign the upstream request. warpllm persists nothing to disk.
- The gateway never logs request headers or bodies; they carry credentials and prompts. It logs the model name and whether the request streamed.
- Error messages surface the name of a missing variable, never the value of one that is set.
Next
Model routing
How a
provider/model string picks the provider whose key gets used.