One local address for every AI plan you already pay for: how omp’s auth broker and gateway work, and how to run Claude Code through them.
If you’re like many people, you pay for more than one AI plan: ChatGPT Plus for work, Claude Pro, Max or Ultra for writing, GitHub Copilot or Cursor through your job, maybe a Z.AI GLM Coding Plan on the side. Each one works only inside its vendor’s own app.
Then you find a tool you’d like to try: an editor plugin, a notes app with an "AI" button, a script a friend shared, a different coding agent. It asks for an API key. That’s a separate product, billed per token, on top of the subscription you already pay for.
There’s a way around that. omp is an open-source coding agent, and it ships a small server that most people never notice: the auth gateway. You log in to your subscriptions once, and it gives every app on your machine one address to talk to. It speaks the same "API language" as OpenAI and Anthropic, which most AI tools already understand.
What that gets you:
http://127.0.0.1:4000, and the model name in each request decides which plan answers.curl, Claude Code, Codex CLI.Here is the whole idea, start to finish:
omp auth-broker login openai-codex # 1. log in once, in your browser omp auth-broker serve # 2. the vault (leave it running) OMP_AUTH_BROKER_URL=http://127.0.0.1:8765 \ omp auth-gateway serve # 3. the gateway, in a second terminal # 4. in any app: base URL http://127.0.0.1:4000/v1, API key = the output of `omp auth-gateway token`
The rest of this article covers the CLI, the setup in five phases, and then a worked example: setting up Claude Code to run through the gateway. Before you connect a consumer plan, read the note on subscription terms at the end.
Written against omp 18.18.1 (November 2026). The gateway is moving fast; check
omp auth-gateway --helpon your version.
any client (curl, SDKs, Claude Code, Codex CLI, editor plugins…)
│ Authorization: Bearer <gateway token>
▼
omp auth-gateway serve 127.0.0.1:4000 /v1/messages, /v1/chat/completions, /v1/responses, …
│ broker token (the gateway only)
▼
omp auth-broker serve 127.0.0.1:8765 SQLite vault, background OAuth refresher
│
▼
api.anthropic.com · chatgpt.com (Codex) · api.githubcopilot.com · opencode.ai · …
Two command families, each with a handful of verbs.
omp auth-broker: the vaultomp auth-broker serve [--bind=host:port] [--trust-proxy-headers] # run the vault (default 127.0.0.1:8765) omp auth-broker token [--regenerate] [--json] # print or rotate the broker token omp auth-broker list # every provider omp can log in to omp auth-broker login [<provider>] [--via=user@host] [--dry-run] # log in (no provider: pick from a menu) omp auth-broker logout [<provider>] omp auth-broker import <file|dir> [--provider=<id>] [--dry-run] # bring in exported credentials omp auth-broker migrate --from-local [--include-oauth] [--include-env] [--dry-run] omp auth-broker status [--json]
omp auth-broker list is worth running once. On 18.18.1 it lists about sixty providers. The big ones:
| Provider id | What it is |
|---|---|
anthropic |
Claude Pro, Max and Ultra plans |
openai-codex |
ChatGPT Plus/Pro (Codex subscription); openai-codex-device for headless logins |
github-copilot |
GitHub Copilot |
cursor |
Cursor (browser login; serves Claude, GPT and more) |
zai-coding-plan, zai |
Z.AI GLM Coding Plan: browser sign-in, or an API key (ZAI_API_KEY) |
opencode-go, opencode-zen |
OpenCode plans (API key, OPENCODE_API_KEY) |
deepseek |
DeepSeek (API key, DEEPSEEK_API_KEY) |
openrouter, kimi-code, google-gemini-cli, xai-oauth, … |
the long tail |
omp auth-gateway: the HTTP front endomp auth-gateway serve [--bind=host:port] [--no-auth] [--clients=<file>] [--trust-proxy-headers] omp auth-gateway token [--regenerate] [--json] # the token clients send (created on first run) omp auth-gateway status [--json] # local token file + broker reachability omp auth-gateway check [--strict] [--json] # probe every credential; --strict spends a little quota omp auth-gateway stdio # same routes as JSON lines, for one parent process
serve needs to know where the broker is, through OMP_AUTH_BROKER_URL (or auth.broker.url in config.yml). The default bind is 127.0.0.1:4000.
Do every phase as the same OS user, with the same omp config. The broker reads the credential store that your logins wrote, and a different user or profile means a different, empty store.
omp auth-broker login anthropic # Claude Pro/Max/Ultra omp auth-broker login openai-codex # ChatGPT Plus/Pro (Codex) omp auth-broker login github-copilot # Copilot omp auth-broker login cursor # Cursor omp auth-broker login zai-coding-plan # Z.AI GLM Coding Plan
Each one prints a URL to open in a browser, and some ask you to paste a code back. API-key providers such as OpenCode and DeepSeek go through omp’s interactive login instead: run omp, type /login, and pick the provider. For OpenCode it points you to https://opencode.ai/auth and stores the key as OPENCODE_API_KEY; DeepSeek works the same way with DEEPSEEK_API_KEY.
Check each login before going further:
omp models openai-codex omp -p --no-session --model openai-codex/gpt-5.6-sol "Reply with exactly: PONG"
omp auth-broker serve
On first start it writes its token to <config-dir>/auth-broker.token (mode 0600). Leave it running in its own terminal, screen or tmux window, or service.
In a second terminal:
OMP_AUTH_BROKER_URL=http://127.0.0.1:8765 omp auth-gateway serve
On the same host and user, the gateway finds the broker token file by itself. A gateway on another host or user needs the broker token passed explicitly.
The gateway rebuilds its model catalog every 15 minutes and checks for credential changes every 10 seconds. Still, restart the gateway after adding or removing a login. It is the quickest way to be sure /v1/models reflects the change.
By default every route except /healthz requires the gateway token:
export OMP_GATEWAY_TOKEN="$(omp auth-gateway token)"
There are two alternatives.
--no-auth turns the bearer check off. Any process that can reach the listener can spend your subscriptions, so use it only on loopback, and keep --bind explicit, because the flag doesn’t force a loopback bind:
OMP_AUTH_BROKER_URL=http://127.0.0.1:8765 \ omp auth-gateway serve --bind=127.0.0.1:4000 --no-auth
--clients=<file> admits extra, scoped tokens. Each one is limited to a list of models and accounts. The file stores only SHA-256 digests and is re-read every 10 seconds while the gateway runs:
{
"ci": {
"tokenSha256": "<printf %s \"$TOKEN\" | shasum -a 256>",
"models": ["openai-codex/*", "anthropic/claude-haiku-5-5"]
}
}
A model outside the list answers 404 Unknown model, and that client’s /v1/models shows only what it was granted.
G=http://127.0.0.1:4000
AUTH="Authorization: Bearer $OMP_GATEWAY_TOKEN"
curl -s $G/healthz # {"ok":true,"version":"18.18.1"}
curl -s $G/v1/models -H "$AUTH" | jq -r '.data[].id'
/v1/models is the authoritative list of what you can call: the catalog, filtered to the providers you hold credentials for. Each row carries more than an ID:
{
"id": "anthropic/claude-fable-5",
"owned_by": "anthropic",
"api": "anthropic-messages",
"display_name": "Claude Fable 5",
"input_modalities": ["text", "image"],
"context_length": 1000000,
"max_output_tokens": 128000
}
Then one request per chat format:
# Anthropic Messages
curl -s $G/v1/messages -H "$AUTH" -H 'anthropic-version: 2023-06-01' -H 'Content-Type: application/json' \
-d '{"model":"anthropic/claude-haiku-5-5","max_tokens":100,
"messages":[{"role":"user","content":"What is 17*3?"}]}'
# OpenAI Chat Completions
curl -s $G/v1/chat/completions -H "$AUTH" -H 'Content-Type: application/json' \
-d '{"model":"openai-codex/gpt-5.6-sol","messages":[{"role":"user","content":"What is 17*3?"}]}'
# OpenAI Responses
curl -s $G/v1/responses -H "$AUTH" -H 'Content-Type: application/json' \
-d '{"model":"openai-codex/gpt-6.1-sol","input":"What is 17*3?"}'
Add "stream": true to get SSE back in the same wire format.
model picks the backend| Path | Wire format |
|---|---|
POST /v1/messages |
Anthropic Messages |
POST /v1/chat/completions |
OpenAI Chat Completions |
POST /v1/responses |
OpenAI Responses |
POST /v1/pi/stream |
omp’s native stream format |
The gateway never guesses the format from the body. The backend comes only from the model field, as provider/model-id, so the two are independent. Here is an Anthropic-format request answered by a Codex subscription, a real reply:
{
"type": "message",
"role": "assistant",
"model": "openai-codex/gpt-5.6-sol",
"content": [{ "type": "text", "text": "17 × 3 is 51." }],
"stop_reason": "end_turn",
"usage": { "input_tokens": 18, "output_tokens": 12, "cache_read_input_tokens": 0, "cache_creation_input_tokens": 0 }
}
Bare IDs work too. gpt-5.6-sol with no prefix goes to the highest-ranked provider that serves it. Native accounts (openai-codex, anthropic, openai) rank ahead of aggregators, and modelProviderOrder in config.yml overrides the ranking. Use the prefixed form when it matters which account pays.
The gateway has more routes than chat. They are real, but omp doesn’t generate audio or images itself. Each route forwards to a provider that offers that kind of model, so it only works once the broker holds a login for such a provider:
| Route | What | Who serves it (omp 18.18.1) |
|---|---|---|
POST /v1/images/generations, /v1/images/edits |
image generation and edits (OpenAI Images wire) | Codex subscription (gpt-image-2), OpenAI API key, OpenRouter |
POST /v1/audio/speech |
text-to-speech, returns raw audio | xAI (grok-tts: API key, or the SuperGrok / X Premium+ login), OpenRouter, DeepInfra |
POST /v1/audio/transcriptions |
speech-to-text (multipart file, 25 MiB cap) |
OpenAI API key, OpenRouter |
POST /v1/embeddings |
embeddings (OpenAI wire) | OpenAI API key, OpenRouter |
POST /v1/rerank |
rerank (OpenRouter wire) | OpenRouter |
POST /v1/videos, GET /v1/videos/:id[/content] |
video jobs: submit, poll, download | OpenRouter |
GET /v1/usage |
your subscription usage windows, across every account | every login |
GET /v1/credentials/check |
per-credential health, handy when something starts returning 401 | every login |
Of the big subscriptions, only Codex adds anything here: image generation. Claude, Copilot, Cursor, OpenCode, Z.AI and DeepSeek are chat-only through the gateway. An OpenRouter key turns on almost every route, and the "OpenAI API key" rows mean a direct OPENAI_API_KEY, not the Codex login.
/v1/models marks non-chat rows with a kind field (image, tts, stt, embedding, rerank, video, …), so check there what your logins actually unlock. With no provider for a modality, its route has no model to call and the request is refused. Sending a chat route a non-chat model fails with a 400 that names the route to use instead.
omp auth-gateway stdio serves all of these to a single parent process over stdin/stdout, with no port and no token. It suits editor plugins and scripts:
echo '{"id":1,"path":"/v1/chat/completions","body":{"model":"@smol","messages":[{"role":"user","content":"hi"}]}}' \
| omp auth-gateway stdio
Unlike serve, stdio uses your normal omp setup, including models.yml, extension providers and role selectors like @smol.
Claude Code speaks Anthropic Messages, and it lets you replace its endpoint and credential through environment variables.
export ANTHROPIC_BASE_URL=http://127.0.0.1:4000 # no /v1: Claude Code appends it export ANTHROPIC_AUTH_TOKEN="$OMP_GATEWAY_TOKEN" # sent as Authorization: Bearer unset ANTHROPIC_API_KEY export CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1 # let /model list the gateway's models claude
Use ANTHROPIC_AUTH_TOKEN, not ANTHROPIC_API_KEY. The gateway accepts only Authorization: Bearer, and ANTHROPIC_API_KEY sends x-api-key, which gets a 401. With --no-auth the token is ignored, but set it to a placeholder such as unused if a client insists on one.
Inside Claude Code, run /status to confirm the base URL, and /model to pick from what the gateway serves. A gateway ID that’s missing from the picker can be added with ANTHROPIC_CUSTOM_MODEL_OPTION=anthropic/claude-sonnet-5-5.
Claude Code doesn’t use one model for everything. Background tasks, the "fast" model and subagents each pick their own. If you don’t set them, they ask the gateway for model names it may not serve. Pin them all:
M=anthropic/claude-sonnet-5-5 export ANTHROPIC_MODEL=$M export ANTHROPIC_DEFAULT_OPUS_MODEL=$M export ANTHROPIC_DEFAULT_SONNET_MODEL=$M export ANTHROPIC_DEFAULT_HAIKU_MODEL=anthropic/claude-haiku-5-5 # cheaper model for background work export ANTHROPIC_DEFAULT_FABLE_MODEL=$M export CLAUDE_CODE_SUBAGENT_MODEL=$M
Because the backend is just the model field, you can point the same Claude Code at your Codex subscription:
M=gpt-5.6-sol # bare ID; resolves to openai-codex/gpt-5.6-sol
export ANTHROPIC_MODEL=$M ANTHROPIC_DEFAULT_OPUS_MODEL=$M ANTHROPIC_DEFAULT_SONNET_MODEL=$M \
ANTHROPIC_DEFAULT_HAIKU_MODEL=$M ANTHROPIC_DEFAULT_FABLE_MODEL=$M CLAUDE_CODE_SUBAGENT_MODEL=$M
export CLAUDE_CODE_EFFORT_LEVEL=high
claude
This works day to day. The tool calls translate, streaming works and the agent loop runs. Anthropic doesn’t support Claude Code on non-Claude models, though, and features that exist on only one side (prompt caching details, some thinking controls, server-side tools) don’t translate into each other.
Put the block in a small launcher script (claude-omp) and keep Claude Code’s state separate from your normal setup with CLAUDE_CONFIG_DIR=~/.claude-omp. Then your regular claude stays on its own login, and claude-omp runs on the gateway.
base_url="http://127.0.0.1:4000/v1" with the gateway token as the API key.http://127.0.0.1:4000/v1, the responses wire API, the gateway token, and an exact model ID from /v1/models. Its own ChatGPT login has nothing to do with omp’s Codex login.| Token | File | Grants | Give it to |
|---|---|---|---|
| Gateway token | <config-dir>/auth-gateway.token |
inference through the gateway, never the upstream credentials | your clients |
| Broker token | <config-dir>/auth-broker.token |
credential snapshots: real OAuth access tokens and stored API keys | the gateway, and nothing else |
"Clients need no API keys" means clients never receive an upstream credential. It doesn’t mean the gateway is open. The gateway token is a real secret, and anyone holding it can spend your quota. Transport security is up to you: the gateway speaks plain HTTP, so if it leaves loopback, put TLS or a private network in front of it. A request that carries the gateway token in its URL, or in a logged or forwarded header, is rejected before any credential lookup, which keeps the token out of logs.
models.yml is ignored by serve. Your custom providers, local base URLs and overrides are deliberately left out, so local config can’t redirect broker-backed traffic. (stdio honours them.)/v1/models lists what your credentials can reach in principle, and your plan decides the rest.The gateway makes it easy to use a consumer subscription from any client. Whether your subscription allows that is a separate question, and the answer differs by vendor and changes over time. Claude Pro/Max/Ultra in particular is meant for Anthropic’s own apps and Claude Code, and re-exposing it to other clients can get an account flagged. omp offering a login doesn’t make every use of it allowed. Read the current terms for each plan you wire in. Where it matters, use the provider’s API-key option, which the gateway routes the same way.
Originally published in TheBigPromptLibrary on GitHub (view source).
⭐ Star the library on GitHub for more system prompts and research.