# 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:

1. `--connection <provider>` on `run` or `resume`.
2. The default `wb connect` saved for this runner and runtime.
3. The first allowed route, in manifest order, with a key in the environment.
4. The runner's own credential store: its sign-in on `local`, and on `docker` and `e2b` the store `wb connect` fills 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

```sh
wb connect
# Model provider, runtime, harness, provider, authentication method, credential
# Or the E2B or Daytona runtime, then a masked API key prompt
```

The 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:

```text
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.

```sh
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](/docs/workers/operations#member-sign-ins).                                                 |

`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

1. `--stdin`: an API key on standard input, the bare value only. One trailing newline is removed; whitespace or a `NAME=` prefix is rejected. This is the only unattended way.
2. An API key method in a terminal: a hidden prompt such as `OpenRouter API key for Docker runs (input hidden)`.
3. 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 `local` it 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 from `claude setup-token` as `CLAUDE_CODE_OAUTH_TOKEN`, through the environment or `--env-file`. Workbench never stores the token.
- An OpenRouter or Vercel key also works.
- On `docker`, `e2b`, and `daytona`, `wb connect --method api-key` checks `ANTHROPIC_API_KEY` and `--method setup-token` checks `CLAUDE_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`.
