Providers and models
The providers bingo ships with, how a credential gets in, named instances for extra endpoints, and how a model is chosen.
What ships
Three provider plugins are compiled in.
| plugin | provider ids | credential |
|---|---|---|
bingo-provider-anthropic | anthropic | an API key |
bingo-provider-openai | openai, codex | an API key; codex takes a ChatGPT subscription through OAuth |
bingo-provider-fake | fake | none — a scripted harness for tests |
The fake provider registers only when BINGO_FAKE_SCRIPT names a script, so a
packaged binary never lists it and never falls back to it.
--provider <id> picks one for a run; without it bingo takes the settings’
provider, and failing that the first one registered. --model <id> picks the
model; without it the settings’ model, and failing that the provider’s
default.
Signing in
bingo login codex # opens a browser
bingo login codex --device
bingo login anthropic # paste a key
bingo logout codex
Three flows, and a provider offers the ones it can honour (ADR-0012, ADR-0017):
- Browser — PKCE over a loopback callback. bingo binds
127.0.0.1:1455, climbing up to twenty ports on a conflict, opens your browser, checks thestateand exchanges the code.BINGO_NO_BROWSERstops it opening one, so you can paste the url somewhere else. - Device (
--device) — a code to type into a browser on another machine. bingo polls for it, for at most fifteen minutes. - Paste (
--paste) — a credential minted elsewhere, read from stdin. This is the flow every key-based provider uses; browser and device are refused for them, because there is no issuer to talk to.
Whatever the flow, the credential lands in ~/.bingo/data/auth.json at mode
0600, one entry per provider id. A settings file never holds a secret: the
project layer is committed, and a committed key is a leaked key.
The same commands exist inside a session as /login <provider> [browser|device|paste]
and /logout <provider>.
Where a key is looked for
For the built-in openai and anthropic providers, in this order:
- the
auth.jsonentry a paste login wrote under that provider’s id; OPENAI_API_KEY/ANTHROPIC_API_KEY;- the provider’s own
apiKeysetting.
auth.json comes first so that /login and /logout mean something in a
shell that already exports a key. OPENAI_BASE_URL and ANTHROPIC_BASE_URL
move the endpoint the same way the baseUrl setting does.
Tokens refresh themselves: a subscription’s access token is renewed 300 seconds before it expires, or on a 401, once, under one lock — so two turns running at the same time make one refresh. An issuer that says the refresh token is gone removes the entry rather than retrying, and the provider reads as expired until you sign in again.
Named instances
One settings key per wire shape holds one endpoint, which is not enough for a
person with two proxies or two subscriptions. instances adds more, each
registered under its own name (ADR-0017):
{
"openai": {
"instances": {
"work": { "baseUrl": "https://proxy.internal/v1" }
}
},
"codex": {
"instances": {
"personal": {}
}
}
}
A name is an identity: --provider work, /model work/gpt-5.4 and
bingo login work all mean that instance, and its credential is keyed by that
name in auth.json — so two ChatGPT subscriptions sit side by side. A name
that collides with a built-in id, or with another instance, is refused at boot
rather than half-registered.
Environment variables feed the default instances only. OPENAI_API_KEY
belongs to openai and never to work: one ambient key must not silently
feed every proxy.
bingo provider add writes the entry a person would otherwise edit in. It asks
for a name, a wire shape (openai or anthropic), a base url and — hidden,
and optional — a key; the instance goes into your user settings layer, the key
into auth.json, and it closes by telling you the bingo --provider <name>
that now works. Registration happens at boot, so the instance is live on the
next run.
The model catalog
bingo carries a snapshot of models.dev, pruned to the facts it reads: context window, maximum output, whether the model reasons, and whether it takes images (ADR-0004). Those facts are resolved once per session — declared setting, then a window learned from an overflow, then the catalogue, then a default that fails closed on what a wrong guess would reject.
/model completes from that catalogue without a network call, and
/model anthropic/claude-x names provider and model in one string. There is no
/provider: a login names the provider, and /model names the model.
A model can read the catalog too. The ListModels tool renders every
registered provider with its auth state and each provider’s models with their
facts, so an orchestrator can pick a vision-capable or long-context model
deliberately instead of guessing an id (ADR-0026).
Declaring what the catalogue got wrong
The kernel’s models settings key overrides a model’s facts by name:
{
"models": {
"openai/gpt-5.4": { "contextWindow": 400000, "maxOutput": 64000 }
}
}
Every field is optional. A window bingo learns from a provider’s own overflow
message is remembered in ~/.bingo/data/learned-windows.json and clamps the
catalogue on the next session opened against that model.