Running Workbenches
Models and connections
The Workbench author chooses the runner, the model, the allowed provider routes, and the native runner configuration. You cannot override them when you run it. You bring the credentials: wb connect saves which provider and sign-in method to use, puts the credential where the runtime reads it, and checks it.
Runners#
The reference engine registers three runners.
| Runner | Runtimes | Steering | Permissions and questions | MCP | Verified versions |
|---|---|---|---|---|---|
OpenCode (opencode) |
local, docker, e2b, daytona | Native, mid-turn | Native | Remote MCP servers | 1.18.22, 1.18.26; 2.2.2 (experimental) |
Pi (pi) |
local, docker, e2b, daytona | At Pi's next legal model boundary | Native extension questions; no permission protocol | Remote HTTP MCP, Pi 0.99 or newer | 0.84.3; 1.5.0 (RPC) |
Claude Code (claude-code) |
local, docker, e2b, daytona | Native, at the next tool result | Native | Remote HTTP MCP servers | 2.1.318 |
| Runner | Sign-in | Credential store | runner_config |
Repository instructions |
|---|---|---|---|---|
| OpenCode | Local uses your own sign-in. wb connect runs opencode auth login on the host for Docker and E2B, and a first foreground run can finish it inside Docker or E2B. None on Daytona. |
Docker volume and E2B host store. Provider variables win over a stored entry. | A file or a directory | AGENTS.md |
| Pi | Local uses Pi's own sign-in (/login inside Pi). No command-line sign-in, so other runtimes take API keys. |
Docker volume and E2B host store. A stored entry wins over provider variables. | A directory | Not read |
| Claude Code | Local uses your Claude Code login (claude auth login). Docker, E2B, and Daytona take ANTHROPIC_API_KEY, or a claude setup-token token as CLAUDE_CODE_OAUTH_TOKEN, from your environment. |
None | A JSON file with permissions.allow, permissions.deny, and max_turns |
CLAUDE.md, local runtime only |
Every runner supports multi-turn context, streaming, image input, follow-up input, cancellation, and tool events. Adapters translate what the runner natively exposes and never invent a capability it lacks.
Claude Code requires version 2.1.318 or newer. OpenRouter usage reports what OpenRouter charged, including cancelled turns, rather than Claude Code's Anthropic list-price estimate.
How a run picks a provider#
A manifest names a model as lab/model and optionally an ordered list of provider routes.
- With no
routes, every provider in the model catalog that serves the model is allowed, the lab's own provider first, then alphabetically. - With
routes, only those providers, in the author's order.
A run uses the first of these that is authenticated:
--connection <provider>onrunorresume.- The default
wb connectsaved for this runner and runtime. - The first allowed route, in manifest order, with a key in the environment.
- The runner's own credential store: its sign-in on
local, and ondockerande2bthe storewb connectfills for runners that keep one. Daytona has no store.
If the first route has no key and a later one does, the later one is used without a warning. After a route is chosen, other providers' keys are removed from the run. wb view <alias> shows each allowed route and which is ready.
The model catalog#
wb reads a digest-verified model catalog from metadata.workbenches.dev and caches it for six hours. It maps each model to the providers that serve it and each provider to its environment variables. First use needs an internet connection; later commands use the cache if the service is unavailable. Without a cache: Model metadata is not cached. Run the command again while connected to the internet. wb init --model <id> rejects a model the catalog does not know.
Provider variables#
| Provider | Variable |
|---|---|
openai |
OPENAI_API_KEY |
anthropic |
ANTHROPIC_API_KEY |
openrouter |
OPENROUTER_API_KEY |
google |
GEMINI_API_KEY, GOOGLE_API_KEY, or GOOGLE_GENERATIVE_AI_API_KEY |
xai |
XAI_API_KEY |
mistral |
MISTRAL_API_KEY |
vercel |
AI_GATEWAY_API_KEY |
Keys come from the environment of the wb process, --env-file, and --env. --env-file ignores names that are not provider keys, credential variables the runner reads itself (such as CLAUDE_CODE_OAUTH_TOKEN), or declared by the Workbench; --env rejects them. A key passed with --env-file works for one run without connecting.
wb connect#
wb connect
# Model provider, runtime, harness, provider, authentication method, credential
# Or the E2B or Daytona runtime, then a masked API key promptThe default flow asks for the execution boundary first. It saves the preferred route for that runner and runtime, puts the credential into the store that runtime reads, and checks it. It ends with:
Ready: <Provider> for <Runner> in <runtime>or with exactly what is missing and the one command that fixes it, exiting 3. On e2b it ends with Saved: instead, because checking would create a billable sandbox; the first run confirms it.
wb connect my-expert --runtime docker
printf '%s' "$OPENROUTER_API_KEY" | wb connect --runtime e2b --harness opencode --provider openrouter --stdin
wb connect --runtime e2b --harness opencode --provider openrouter --remove| Option | Effect |
|---|---|
[workbench] |
Narrow providers to the routes this Workbench allows and check with its runner and runtime. Without --runtime it uses the Workbench's first runtime. |
--runtime <name> |
The runtime to connect: local, docker, e2b, or daytona. |
--harness <runner> |
The runner, when no Workbench is given. |
--provider <provider> |
The provider. |
--method <method> |
The sign-in method when the provider has several, such as api-key, chatgpt for an OpenAI subscription, or the provider's native sign-in. |
--stdin |
Read an API key from standard input. |
--remove |
With --provider, delete that provider's entry from that runtime's store. |
--status |
With --runtime e2b or daytona, check the saved key. |
--worker <slug> <runner> |
Connect a runner on an organization's worker. See Workers. |
smoke, view, and run preflight always name the runtime in their hint, such as wb connect my-expert --runtime docker. Run that command.
Where each runtime reads credentials#
| Runtime | Store | How wb connect fills it |
How it is checked |
|---|---|---|---|
local |
The runner's own sign-in on this machine | It does not. Sign in with the runner (opencode auth login, claude auth login) or set the provider variable. |
The runner lists the provider; without a Workbench only the provider variable counts. |
docker |
A per-runner named volume mounted at /workbench-credentials, for runners with a credential file |
A short-lived helper container from the Workbench image, with no network and a read-only root, receives the file on standard input. | The same inspection wb smoke runs, inside the container. |
e2b |
~/.workbench/runtime-credentials/e2b/<runner>/, synced into each sandbox |
Written on the host, files 0600 in 0700 directories. |
The entry exists on the host, or the provider variable is set. |
daytona |
None | It does not. Set the provider variable or pass --env-file. |
The provider variable is set. |
Docker writes and checks go through a Workbench image, so Docker needs a Workbench reference. A runner without a native credential file (Claude Code) has no store on any runtime: wb connect checks readiness, says what is missing, and stores nothing.
How the credential is given#
--stdin: an API key on standard input, the bare value only. One trailing newline is removed; whitespace or aNAME=prefix is rejected. This is the only unattended way.- An API key method in a terminal: a hidden prompt such as
OpenRouter API key for Docker runs (input hidden). - A subscription method: the runner's command-line sign-in runs fresh against a private temporary data home, and only that provider's entry is kept. It needs a person at a terminal.
wb connect never reads another tool's credential files, so your own runner sign-in is neither read nor copied. Only the selected provider's entry is written, merged beside existing entries. Credential values are never printed, logged, passed in arguments, or placed in a child process environment.
Readiness respects the method: a provider variable counts for an API key or native method but never for a subscription. The default is saved only once the route is ready. A connect that exits 3 or is cancelled leaves the previous default in place. Flags that would have no effect, such as --stdin with a subscription method or on local and daytona, are rejected before anything is read or written.
Defaults, including the method, are stored in ~/.workbench/connections.json. It holds no secrets. Selecting a default never modifies a Workbench package or model.
First-run sign-in#
If the selected credential of a runner that supports first-run sign-in is missing, the first foreground run starts the real runtime, asks the runner for its browser or headless sign-in, shows its URL and instructions, waits, and continues the same run. A detached run refuses to start an invisible sign-in: Authentication is required for <provider>. Start a foreground task run once to finish <provider> sign-in. For API keys, nothing happens during a run; connect the key or pass it with --env-file.
Claude Code credentials#
Claude Code keeps no Workbench store on any runtime.
- On
localit uses your Claude Code login. A Console login (claude auth login --console) reports its metered cost; a claude.ai subscription reports none. - On every runtime it takes
ANTHROPIC_API_KEY, or your token fromclaude setup-tokenasCLAUDE_CODE_OAUTH_TOKEN, through the environment or--env-file. Workbench never stores the token. - An OpenRouter or Vercel key also works.
- On
docker,e2b, anddaytona,wb connect --method api-keychecksANTHROPIC_API_KEYand--method setup-tokenchecksCLAUDE_CODE_OAUTH_TOKEN.
Errors#
| Error | Fix |
|---|---|
No authenticated route is available for <model>. Run wb connect <ref> --runtime <runtime>, or pass the provider key for one run with --env-file. |
Check wb view <ref> for the allowed routes. Pass one provider's key, or run the wb connect command shown. Exit 3. |
Connection X is not authenticated for <model> with <runner> in the <runtime> runtime. |
The --connection provider has no credential in this runtime. Drop the flag, supply that key, or connect it. |
<Workbench> does not allow connection X: <model> is routed through ... |
The Workbench's routes leave out that provider. Drop --connection, or use one of the providers named. |
Pass the <Provider> API key on standard input: ... |
No terminal is attached. Pipe the key with --stdin. |
... in a terminal with OpenCode installed |
A subscription needs a fresh sign-in in a terminal, or one foreground run. |
Docker keeps <Runner> credentials in a volume that is written and checked through a Workbench image |
Rerun with a Workbench: wb connect <alias> --runtime docker. |
<Runner> has no credential store Workbench writes; <Provider> keys reach it only from the environment or --env-file |
Set the provider variable or pass --env-file each run. |
Claude Code on the <runtime> runtime needs ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN |
Pass either, choose an OpenRouter or Vercel route, or run on local. |
First-run authentication for pi is not available inside a Workbench run yet |
Connect the key first with wb connect <alias> --runtime <runtime> --stdin, or pass an environment key. |
DAYTONA_API_KEY is required for the Daytona runtime..., E2B_API_KEY is required... |
Set the variable or run wb connect --runtime daytona (or e2b). |
Never print a key or an env file to check for it. Test by name only: test -n "$OPENAI_API_KEY" && echo set.