Docs/Desktop/Agentic Coding/Gateway

Gateway

Point Lanes sessions at a model provider you host or buy elsewhere -- local Ollama or LM Studio, a vLLM box on your network, or a provider serving the same wire format.

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:

  1. Switch Auth mode to Bearer. There is no approval gate on that path, and it is what the provider most likely wants anyway.
  2. 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:

  1. 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.
  2. 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 / adaptive 400. 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_reference blocks 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 /logout if 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 /compact is 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:

Code
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.