A provider profile points a CLI session at a model provider that is not the CLI's own provider. The harness stays the same; only where its requests go changes. That covers three shapes:
- Local -- Ollama or LM Studio on your own machine, no marginal cost.
- Your own hardware -- a vLLM box on the network.
- A provider -- OpenRouter, z.ai GLM, or anything else serving the same wire format, billed per token.
A profile is a session-scoped environment override, not a per-CLI feature -- it
doesn't name a CLI at all. The session already chose one, so Lanes derives the
right environment variable names from it at launch, which means one profile
("my Ollama box") serves whichever harness you point at it. Lanes applies it as
real process environment when the session starts, so credentials never reach the
terminal, the command line, or ps. Claude Code is the harness Lanes knows the
variable names for today; other CLIs work through the profile's free-form
environment table.
Creating a profile
Open Settings, then Agentic Coding, then Gateway. Pick a preset to prefill the base URL and auth mode, or add a blank profile.
Adding a profile selects it, so there is nothing else to switch on: fill in the fields and it applies at the next session start. To switch between profiles later, or to go back to the CLI's own provider, use the Gateway row in the session settings picker. A profile with no base URL does nothing at all, so the half-filled one you are still typing into can't affect a session in the meantime.
The presets are Ollama, LM Studio, vLLM, OpenRouter, and z.ai GLM. You still have to choose a model -- see below -- because Lanes cannot know which models your provider serves.
Hit Test connection once you have a base URL. It sends a single one-token request and reports back. An "unknown model" result counts as success: the probe uses a placeholder model name on purpose, and the provider authenticated the request before rejecting it, which is exactly what the probe is checking.
The Claude Code advanced section is collapsed by default and you can usually leave it alone: the two settings that matter most are already on.
Bearer or API key
The two auth modes differ only in which header carries the credential:
Authorization: Bearer or x-api-key. Prefer Bearer. Most providers,
OpenRouter and z.ai included, only read the Authorization header -- even though
they call the credential an "API key", which is the trap in the name.
API key mode has a second problem that Test connection cannot catch. Claude
Code asks you to approve an API-key variable once per machine, and the prompt's
default answer is No. Decline it and the key is ignored from then on without
asking again, so the session falls back to your saved claude.ai login and sends
that to your provider, which rejects it with a 401. Meanwhile Test connection
still passes, because Lanes sends the credential directly and never hits the
approval gate.
If you see a session 401 on a profile that tested fine:
- Switch Auth mode to Bearer. There is no approval gate on that path, and it is what the provider most likely wants anyway.
- Or re-enable the key in the session with
/config→ "Use custom API key".
Why the token field is never empty
Local providers ignore authentication entirely, so a credential looks
unnecessary. It is not, and the local presets prefill the placeholder local
for two reasons:
- A base URL on its own is not a credential. With the token empty, the harness keeps using its own saved login as the active credential -- which means sending that credential to your provider. On a LAN box or a third-party provider that is a real disclosure.
- Sessions can stall at a login prompt. With no saved login, a reachable base URL is not enough. The harness shows its login screen instead of starting, and a session Lanes spawned for you sits there waiting.
Ollama and vLLM discard the value, so any string works. Lanes warns inline if you clear the field rather than blocking you.
Choosing the model
The profile deliberately has no model setting. Lanes already has one: the Model row in the session settings picker. Open it and type your provider's model id into the custom field.
While a profile is active, that row stops offering the built-in model ids. That is deliberate -- an explicit first-party id is forwarded to your provider verbatim and rejected there.
Default is not a safe pick either. With no model flag and no alias variables
set, the harness sends its own default model id, which your provider won't serve
any more than the others. Name the model.
Two related things you may want, both through the profile's Extra environment table:
ANTHROPIC_DEFAULT_HAIKU_MODEL-- Claude Code uses Haiku for background work like conversation titling. Without this, those calls go out under a model name your provider doesn't serve and fail quietly.ANTHROPIC_MODEL-- sets the model for every session on this profile, instead of picking it per session in the Model row.
What you give up
Routing a harness away from its own provider costs some features. For Claude Code specifically:
- The
thinking/adaptive400. The most common failure. Claude Code treats any model name it does not recognise -- which is every model name your provider serves -- as one that accepts adaptive reasoning, and providers that do not accept it return a 400. The fix is Disable adaptive thinking under Claude Code advanced, on by default. Note this is not covered by the betas toggle: adaptive reasoning is selected by model, not by beta, so the two are separate levers. - Pre-release request fields. Context management and beta tool fields can be
rejected with
Extra inputs are not permitted. Disable experimental betas covers those, also on by default. - Web search. The Anthropic web-search server tool is unavailable.
- Prompt caching. Entirely dependent on the provider.
- MCP tool search and fine-grained tool streaming. Both off by default on a
non-first-party host. MCP itself still works, including the Lanes MCP server.
There is a toggle for tool search under Claude Code advanced if your
provider passes
tool_referenceblocks through unchanged. - Remote Control and voice dictation. Both require a claude.ai identity and are unavailable while a profile is active.
- A startup warning. Claude Code notes that two credential sources are
active whenever you are also logged in to claude.ai. It uses the profile's
credential. Run
/logoutif you want the warning gone. - Context and output limits. If your provider enforces a smaller window than
the model's own, set Auto-compact window to its limit. The harness clamps
that to at least 100,000 tokens, so a lower limit cannot be matched and
/compactis the recovery. Set Max output tokens below the provider's output limit too.
Anthropic does not support routing Claude Code to non-Claude models through a gateway. It works; it is not a supported configuration.
Providers that speak a different wire format
The profile targets providers serving the Anthropic Messages format
(POST /v1/messages), which Ollama, LM Studio, vLLM, OpenRouter, and z.ai all
do natively.
For a backend that only speaks OpenAI's format, put a translating proxy in front
of it and point the profile at the proxy. LiteLLM
exposes /v1/messages for any backend it supports:
uvx --from 'litellm[proxy]' litellm --config ~/litellm.yaml --port 4000
Then use http://localhost:4000 as the base URL. Lanes does not manage the
proxy for you.
Other CLIs
Every profile has an Extra environment table, applied after everything else, so it wins over the mapped fields. That is how you drive a harness Lanes has no built-in mapping for: set its own base-URL and credential variables there directly. Because the profile doesn't name a CLI, the same one can carry Claude Code's variables and another harness's side by side.
The Gateway row in the session picker shows for every CLI for that reason. Note
that a CLI reading its provider config from a file rather than the environment --
Codex reads ~/.codex/config.toml -- cannot be driven this way. Test
connection always probes the Anthropic Messages format, since that is the one
wire format Lanes knows.
When a change takes effect
At the next session start, matching how the terminal program and debug-mode settings behave. A running session keeps the environment it launched with.
One consequence of sessions running through a login shell: anything your
.zshrc or .bashrc exports overrides what the profile sets. If a profile
looks like it is being ignored, check your shell profile for the same variable.
Verifying from inside a session
Run /status in a Claude Code session. An Anthropic base URL line
confirms requests are routing to your provider, and an Auth token line
confirms the profile's credential is active rather than your saved login. If
neither line appears, the profile was not applied -- start a fresh session,
since the change only lands at launch.