Inja LLM Gateway
Get started

Claude Code with ChatGPT, Claude, SuperGrok & Gemini

Use ChatGPT Plus/Pro, Claude Pro/Max, SuperGrok, and Gemini/Antigravity consumer OAuth through Claude Code — login, PATH wrappers, live model lists.

Last updated: 2026-09-04

Run Claude Code against the gateway and route work to your own consumer subscriptions:

Product How you authenticate Typical use
ChatGPT Plus / Pro / Team Codex OAuth (llm-gateway auth login chatgpt) GPT models via translation
Claude Pro / Max setup-token / Claude Code login Native Anthropic passthrough
SuperGrok / X Premium+ Grok CLI import or xAI device OAuth Grok 4.5 / 4.6, Composer 2.5
Gemini / Antigravity Import ~/.gemini/jetski-standalone-oauth-token (auth login gemini) Gemini via Google dialect

You can use any combination: GPT only, Grok only, Gemini only, GPT+Grok, Claude+GPT, all four, and so on.

  Claude Code
       │  Anthropic Messages API
       ▼
  llm-gateway  ──► Claude subscription   (passthrough)
               ──► ChatGPT subscription  (translate)
               ──► SuperGrok / xAI       (translate)
               ──► Gemini / Antigravity  (translate)

Related: Any app · Claude app · Codex · Cursor · Claude Code checklist · OAuth · xAI


Before you start

Requirements

  • Gateway binary: release or go build -o llm-gateway ./cmd/gateway
  • Claude Code CLI (claude on PATH)
  • At least one subscription you own (ChatGPT, Claude, SuperGrok, and/or Gemini / Antigravity)

Security & terms of service

  • Log in only with accounts you own. Tokens stay on your machine.
  • Default store: ~/.config/inja-gateway/credentials.json (mode 0600). On macOS this may live under ~/Library/Application Support/inja-gateway/. Override with INJA_GATEWAY_AUTH_FILE.
  • Never commit credential files or paste tokens into tickets/chat.
  • OpenAI Codex OAuth is the ChatGPT subscription path used by the open-source Codex CLI and tools that integrate with it. Tokens talk to the Codex/ChatGPT backend — not a classic Platform sk- API key.
  • Anthropic restricts Free/Pro/Max OAuth for many third-party products. Prefer claude setup-token / official Claude Code flows and re-read Anthropic’s terms and Claude Code authentication. Do not resell multi-tenant access to consumer OAuth.
  • xAI may gate which SuperGrok tiers receive OAuth API access; login success does not guarantee every model id works.
  • Google Gemini / Antigravity consumer OAuth is for personal accounts you own. The Jetski token file has access/refresh tokens, not the OAuth client secret. Refresh needs INJA_GATEWAY_GEMINI_CLIENT_SECRET (see below). Never commit that secret.

This guide is for personal / single-operator use of subscriptions you already pay for.


End-to-end quick start

1. Install binary + helpers

Release binary (no git checkout needed):

# install llm-gateway from GitHub Releases onto PATH, then:
llm-gateway helpers install    # alias: llm-gateway load-helpers
# writes ~/.config/inja-gateway/shell/*.sh + claude-code-subscriptions.yaml

eval "$(llm-gateway helpers source)"   # or paste the source lines into ~/.zshrc

From source:

git clone https://github.com/inja-online/llm-gateway.git
cd llm-gateway
go build -o llm-gateway ./cmd/gateway
source examples/shell/claude-code-helpers.sh   # same scripts as embedded in the binary
Command Purpose
llm-gateway helpers install Extract shell helpers + subscription YAML from the binary
llm-gateway load-helpers Same as helpers install
llm-gateway helpers source Print source … lines for your shell rc
llm-gateway helpers list List embedded files

2. Log in (once per provider you need)

llm-gateway auth login chatgpt    # browser → auth.openai.com (Codex / ChatGPT)
llm-gateway auth login claude     # setup-token / paste Claude OAuth token
llm-gateway auth login grok       # imports ~/.grok/auth.json if present, else device code
llm-gateway auth login gemini     # aliases: google, antigravity — imports Jetski token

llm-gateway auth status

Import shortcuts (if you already use official CLIs):

codex login && llm-gateway auth import chatgpt
llm-gateway auth import claude     # Linux credentials file; macOS prefer setup-token
llm-gateway auth import grok       # recommended: ~/.grok/auth.json from Grok CLI
llm-gateway auth import gemini     # ~/.gemini/jetski-standalone-oauth-token (or GEMINI_AUTH_FILE)

3. HTTPS localhost + background gateway

Claude Code needs a live API. Helpers start the gateway in the background on HTTPS so you do not get ConnectionRefused:

# helpers already sourced (from step 1)
export KEY=local-dev

cc-gateway-up          # gen certs if needed, nohup llm-gateway, wait for /healthz
# → https://127.0.0.1:8787  (+ NODE_EXTRA_CA_CERTS / ANTHROPIC_BASE_URL)

cc-gateway-status
cc-gateway-logs -f     # http + usage lines (model, tokens, latency)
# cc-gateway-logs --usage
# cc-gateway-down

Log file: ~/.local/state/inja-gateway/gateway.log. After a real request you should see:

http status=200 method=POST path=/v1/messages lat_ms=…
usage status=ok provider=… model=… tokens_in=… tokens_out=…

Manual TLS (git checkout):

./examples/scripts/gen-localhost-tls.sh   # prefers mkcert
export GATEWAY_TLS_CERT=$PWD/examples/certs/localhost.pem
export GATEWAY_TLS_KEY=$PWD/examples/certs/localhost-key.pem
export GATEWAY_LISTEN=127.0.0.1:8787
./llm-gateway -config examples/configs/claude-code-subscriptions.yaml
# installed layout uses ~/.config/inja-gateway/claude-code-subscriptions.yaml by default

That config wires each upstream with:

auth: oauth2
oauth:
  credentials: chatgpt   # or claude | grok | gemini

The gateway loads tokens from the auth store and refreshes ChatGPT / Grok / Gemini access tokens before expiry (Gemini also needs the client secret — next section).

4. Launch Claude Code

export KEY=local-dev

cc-gpt                 # auto cc-gateway-up, then Claude Code (GPT only)
cc-grok                # Grok 4.5 + Composer 2.5
cc-gemini              # Gemini / Antigravity (alias: ccgm)
cc-gpt-grok            # GPT + Grok
cc-multi               # Claude + GPT + Grok

PATH wrappers (forwards every arg to claude; --help / --version skip the gateway):

ln -sf ~/.config/inja-gateway/scripts/claude-grok ~/.local/bin/claude-grok
ln -sf ~/.config/inja-gateway/scripts/claude-gemini ~/.local/bin/claude-gemini
ln -sf ~/.config/inja-gateway/scripts/claude-codex ~/.local/bin/claude-codex
claude-grok --help          # same as claude --help (no gateway)
claude-grok -p "ping"       # Grok 4.6, effort xhigh
claude-gemini               # Gemini — live model from GET /v1/models?live=1
claude-codex                # ChatGPT / Codex subscription

claude-gemini / claude-codex set CC_SKIP_GATEWAY_UP=1 (they expect a running gateway) and pick a live upstream id when CC_MODEL is unset (google/… vs chatgpt/…). Fallback aliases: gemini, gpt.

Or:

KEY=local-dev ./examples/claude-code-multi.sh gpt
KEY=local-dev ./examples/claude-code-multi.sh grok
KEY=local-dev ./examples/claude-code-multi.sh gpt+grok
KEY=local-dev ./examples/claude-code-multi.sh multi

KEY is a client credential presented to the gateway (any non-empty string when edge_auth is off; a real edge key when edge auth is on). Subscription secrets never leave the gateway process.

In a session:

/model grok-4.5
/model composer-2.5
/model gpt
/model sonnet

Auth CLI reference

Command What it does
llm-gateway auth login chatgpt [--no-browser] ChatGPT subscription OAuth (PKCE; Codex client)
llm-gateway auth login claude Claude setup-token / paste
llm-gateway auth login grok [--device] Prefer Grok CLI import; --device forces xAI device code
llm-gateway auth login gemini Import Antigravity / Gemini CLI token (google / antigravity aliases)
llm-gateway auth import chatgpt From ~/.codex/auth.json after codex login
llm-gateway auth import claude From Claude Code credentials file (when present on disk)
llm-gateway auth import grok From ~/.grok/auth.json (Grok CLI), Hermes, or OpenClaw paths
llm-gateway auth import gemini From ~/.gemini/jetski-standalone-oauth-token (override: GEMINI_AUTH_FILE)
llm-gateway auth status Which providers are logged in (no secrets printed)
llm-gateway auth logout [provider|all] Remove stored credentials
llm-gateway auth env [provider] Print export lines (sensitive — local debug only)
./llm-gateway auth help
export INJA_GATEWAY_AUTH_FILE=$HOME/.config/inja-gateway/credentials.json

Grok “Invalid action” in the browser

xAI’s prefilled page https://accounts.x.ai/oauth2/device?user_code=… often shows Invalid action when the browser session is wrong.

Prefer:

# Log into Grok CLI first (if you use it), then:
./llm-gateway auth import grok
# or:
./llm-gateway auth login grok   # auto-imports ~/.grok/auth.json when present

Device login opens the base URL and prints the code separately. Force device flow with auth login grok --device.

Gemini / Antigravity client secret (required to refresh)

auth import gemini copies tokens from ~/.gemini/jetski-standalone-oauth-token. That file does not contain Google’s OAuth client secret. Access tokens expire in ~1 hour; refresh POSTs to https://oauth2.googleapis.com/token and Google rejects the grant without the secret.

Do not reuse a random GOCSPX-… from gcloud ADC or Cloud Code — those belong to a different OAuth client. The Gemini CLI / Antigravity (Jetski) client id is:

1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com

Export the secret (personal machine only):

  1. Open Google Cloud Console → APIs & Services → Credentials.
  2. Open the OAuth 2.0 Client ID whose client id matches the string above (Desktop / installed app used by Gemini CLI or Antigravity).
  3. Reveal Client secret (GOCSPX-…). Copy it locally — never paste it into git, tickets, or chat.
  4. Put it in your shell rc (mode 0600) or let import write it into the 0600 store:
# Option A — env (gateway process must inherit this)
export INJA_GATEWAY_GEMINI_CLIENT_SECRET='GOCSPX-…'   # your secret

# Option B — persist into credentials.json on next import
export INJA_GATEWAY_GEMINI_CLIENT_SECRET='GOCSPX-…'
llm-gateway auth import gemini
unset INJA_GATEWAY_GEMINI_CLIENT_SECRET   # optional if store now has it

llm-gateway auth status   # gemini row should show a refresh token; secret is never printed

If you cannot find that client in Console, you do not own the OAuth app (Google’s first-party client). Then use a Gemini API key instead (auth: api_key / x-goog-api-key) — not consumer OAuth.

Never commit GOCSPX-… into YAML, examples, or this repo.


Provider combinations

Claude Code always exposes three “slots” that map to Anthropic-style aliases: opus, sonnet, and haiku (plus small-fast). The launcher fills those slots from the providers you enable.

Named profiles and combos

Separators: + · , · -

Profile Providers Default opus Default sonnet Default haiku / small-fast
gpt ChatGPT only sol gpt (terra) luna
grok SuperGrok only grok-4.5 grok-4.5 composer-2.5
gpt+grok GPT + Grok grok-4.5 gpt composer-2.5
claude Claude only opus sonnet haiku
claude+gpt Claude + GPT opus gpt luna
claude+grok Claude + Grok opus grok-4.5 composer-2.5
gemini Gemini / Antigravity only gemini-pro gemini gemini-flash
multi Claude + GPT + Grok opus gpt composer-2.5
./examples/claude-code-multi.sh list
./examples/claude-code-multi.sh gpt
./examples/claude-code-multi.sh grok
./examples/claude-code-multi.sh gpt+grok
./examples/claude-code-multi.sh claude+gpt
./examples/claude-code-multi.sh claude+grok
./examples/claude-code-multi.sh multi

Shell helpers

source examples/shell/claude-code-helpers.sh
export KEY=local-dev

cc-gateway-up
cc-gpt            # GPT only (Sol/Terra/Luna)
cc-grok           # Grok 4.5 + grok-build (Composer-class)
cc-gemini         # Gemini / Antigravity (ccgm)
cc-gpt-grok
cc-claude
cc-multi
cc-run gpt+grok
cc-list

Environment overrides

Variable Effect
GATEWAY Gateway base URL (default http://localhost:8787)
KEY / GATEWAY_EDGE_KEY Client credential to the gateway
CC_MODEL Main model for the session
CC_OPUS_MODEL / CC_SONNET_MODEL / CC_HAIKU_MODEL Slot pins
CC_GROK_HEAVY Default grok-4.5
CC_GROK_FAST Default composer-2.5
CC_GPT_HEAVY / CC_GPT_MID / CC_GPT_FAST Default o3 / gpt / gpt-mini
CC_GEMINI_MID Gemini-only main model (default gemini)
CC_PROVIDERS Same as profile string (gpt,grok)
CC_MODEL=composer-2.5 KEY=local-dev ./examples/claude-code-multi.sh grok
CC_GROK_HEAVY=grok-4.5 CC_GROK_FAST=composer-2.5 ./examples/claude-code-multi.sh gpt+grok

Permanent Claude Code settings

Merge env into ~/.claude/settings.json (or project .claude/settings.json). Prefer the launcher when you switch combos often.

Example for GPT + Grok (no Claude models in the default slots):

{
  "env": {
    "ANTHROPIC_BASE_URL": "https://127.0.0.1:8787",
    "ANTHROPIC_API_KEY": "local-dev",
    "ANTHROPIC_AUTH_TOKEN": "local-dev",
    "ANTHROPIC_MODEL": "gpt",
    "ANTHROPIC_DEFAULT_OPUS_MODEL": "grok-4.5",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "gpt",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "composer-2.5",
    "ANTHROPIC_SMALL_FAST_MODEL": "composer-2.5"
  }
}

Template with more variants: examples/claude-code-settings.json.example.

Behind a custom ANTHROPIC_BASE_URL, Claude Code does not validate model names against Anthropic’s catalog — gateway aliases and provider/model ids pass through.

Those unknown ids also get no thinking / effort / ultracode unless advertised. Helpers inject:

  • ANTHROPIC_DEFAULT_{OPUS,SONNET,HAIKU}_MODEL_SUPPORTED_CAPABILITIES and ANTHROPIC_CUSTOM_MODEL_OPTION_SUPPORTED_CAPABILITIES = effort,xhigh_effort,thinking,adaptive_thinking,interleaved_thinking
  • session --settings with alwaysThinkingEnabled, modelPicker.replaceBuiltInOptions: true, and modelPicker.options using behavesAs: "claude-fable-5" for gateway aliases (grok-4.6, gemini, sol, sonnet, …)
  • no default --effort / CLAUDE_CODE_EFFORT_LEVEL / effortLevel — those pin /effort until restart. Set CLAUDE_CODE_EFFORT_LEVEL yourself if you want a launch pin.

Ultracode is session-scoped — opt in with CC_ULTRACODE=1. It does not persist in ~/.claude/settings.json. Path override: CC_ULTRACODE_SETTINGS. Your own --settings wins. CLAUDE_CODE_DISABLE_WORKFLOWS disables it.


Model aliases (gateway config)

From examples/configs/claude-code-subscriptions.yaml:

Alias Routes to (Jul 2026)
sonnet / claude anthropic/claude-sonnet-5
opus anthropic/claude-opus-4-8
haiku anthropic/claude-haiku-4-5
fable anthropic/claude-fable-5
gpt / terra chatgpt/gpt-5.6-terra
sol chatgpt/gpt-5.6-sol
luna / gpt-mini chatgpt/gpt-5.6-luna
grok / grok-4.5 xai/grok-4.5
grok-4.6 xai/grok-4.6 (reasoning_effort: xhigh is 4.6-only)
composer-2.5 / composer xai/grok-build-0.1 (Grok Build / Composer-class)
gemini / gemini-flash google/gemini-2.5-flash (fallback; prefer live list)
gemini-pro google/gemini-2.5-pro

YAML aliases are fallbacks. Wrappers and ?live=1 pull current ids from each provider’s /models (Gemini names like models/gemini-… are stripped to gemini-…).

Sources: Claude models · GPT-5.6 · xAI models · Gemini models.

Edit alias targets if your plan 404s (keep short names).

# Short aliases from YAML only
curl -sk https://127.0.0.1:8787/v1/models | jq -r '.data[].id'

# Live: aliases + every provider’s current /models (needs credentials)
curl -sk https://127.0.0.1:8787/v1/models?live=1 | jq -r '.data[].id' | sort

./examples/scripts/refresh-model-catalog.sh

Agents/maintainers: see repo root AGENTS.md (“Model aliases must stay current”).

What “translate” means

Path Behavior
Claude Code → Claude Passthrough — highest fidelity (tools, betas, streaming)
Claude Code → ChatGPT / Grok Translate Anthropic Messages ↔ OpenAI-compatible chat
Claude Code → Gemini Translate Anthropic Messages ↔ Google generateContent
Stream usage on translate input_tokens may be 0 until the final event
Vendor-only fields May drop on translate — see Deprecation policy

How subscription OAuth is applied

Store id (oauth.credentials) Upstream credential Typical base_url
chatgpt Bearer access token (auto-refresh) https://chatgpt.com/backend-api/codex
claude Bearer setup/OAuth token https://api.anthropic.com/v1
grok Bearer access token (auto-refresh) https://api.x.ai/v1
gemini Bearer access token (auto-refresh; needs client secret) https://generativelanguage.googleapis.com/v1beta
providers:
  chatgpt:
    kind: openai_compat
    base_url: "https://chatgpt.com/backend-api/codex"
    auth: oauth2
    oauth:
      credentials: chatgpt

  anthropic:
    kind: anthropic
    base_url: "https://api.anthropic.com/v1"
    auth: oauth2
    oauth:
      credentials: claude

  xai:
    kind: openai_compat
    base_url: "https://api.x.ai/v1"
    auth: oauth2
    oauth:
      credentials: grok

  google:
    kind: google
    base_url: "https://generativelanguage.googleapis.com/v1beta"
    auth: oauth2
    oauth:
      credentials: gemini

auth: oauth2 with a TokenSource always sends Authorization: Bearer, which is what subscription OAuth tokens expect (Claude OAuth is not x-api-key).

Optional edge auth so only your machine can call the gateway:

edge_auth:
  enabled: true
  keys: ["local-dev"]
export KEY=local-dev
./examples/claude-code-multi.sh gpt+grok

API keys instead of subscriptions

If you prefer Platform API keys (not consumer OAuth):

export OPENAI_API_KEY ANTHROPIC_API_KEY XAI_API_KEY
./llm-gateway -config examples/configs/claude-code-multi.yaml
KEY=local-dev ./examples/claude-code-multi.sh multi

Same combo launcher and shell helpers work. See Getting started for generic key setup.


Troubleshooting

Symptom What to try
ConnectionRefused / Unable to connect to API Gateway not running → cc-gateway-up or ./examples/claude-code-multi.sh gpt (auto-starts HTTPS)
TLS / certificate errors ./examples/scripts/gen-localhost-tls.sh; prefer mkcert -install; helpers set NODE_EXTRA_CA_CERTS
missing …/examples/scripts/gen-localhost-tls.sh Re-run llm-gateway helpers install (script is $HOME/.config/inja-gateway/scripts/). Or generate into the install certs dir: bash $REPO/examples/scripts/gen-localhost-tls.sh ~/.config/inja-gateway/certs
cc-list: command not found: _inja_cc_list_profiles Re-source helpers (eval "$(llm-gateway helpers source)" or examples/shell/…)
Empty cc-gateway-logs Rebuild/restart gateway after upgrade; only gateway-routed traffic logs (not Cursor built-in models)
no credentials for chatgpt (or claude/grok/gemini) llm-gateway auth login <provider> or auth import …
auth status shows expired Re-login; Grok/ChatGPT/Gemini refresh if a refresh token is present (Gemini also needs INJA_GATEWAY_GEMINI_CLIENT_SECRET)
Gemini refresh invalid_client / missing secret Export the matching desktop client secret (see above). Do not use a gcloud ADC secret.
Gemini 401 on generativelanguage.googleapis.com Confirm auth status gemini is not expired; restart gateway after adding the google provider to YAML
Grok browser Invalid action auth import grok from Grok CLI; avoid prefilled device URLs
Grok login works, inference 403 xAI tier / entitlement — check SuperGrok plan or API key path
Claude 401 after import on macOS Keychain isn’t read by import — use auth login claude / setup-token
ChatGPT 401 via gateway Re-login Codex OAuth; confirm auth status has refresh; check base_url
Gateway healthz fails Start ./llm-gateway -config examples/configs/claude-code-subscriptions.yaml
Model not found / 404 upstream Fix alias target in YAML to an id your account can call
Translate stream looks empty early Expected: usage often arrives on the final event only
./llm-gateway auth status
./llm-gateway auth logout grok && ./llm-gateway auth import grok
curl -s http://localhost:8787/healthz

Architecture recap

  1. Claude Code always uses the Anthropic Messages wire format (POST /v1/messages).
  2. Gateway resolves model via aliases → provider/model.
  3. Same family as Anthropic → passthrough. Otherwise → canonical IR translate.
  4. Upstream auth comes from the subscription store (oauth.credentials), not from Claude Code’s env (except the edge/client key).
  5. Subscription headers — Claude OAuth betas + X-App: cli; ChatGPT/Codex UA / Originator / account id.
  6. TLS + body fidelity — Chrome-like TLS to Anthropic/ChatGPT; Claude OAuth tool rename, cloaking/cch, multi-account pool on 429.
  7. GET /v1/models — credential-gated aliases + catalog (optional remote refresh).
  8. Usage can be metered with hooks.jsonl (default stdout in the example config).
  claude CLI  ──ANTHROPIC_BASE_URL──►  :8787
       KEY (edge)                         │
                                          ├─ anthropic + Bearer + OAuth betas + utls + cloak
                                          ├─ chatgpt + Bearer + Codex headers + utls
                                          ├─ xai + Bearer (grok creds)
                                          └─ google + Bearer (gemini creds)

File map

Path Role
examples/configs/claude-code-subscriptions.yaml Subscription OAuth providers + aliases
examples/configs/claude-code-multi.yaml API-key multi-provider recipe
examples/claude-code-multi.sh Combo launcher for Claude Code
examples/shell/claude-code-helpers.sh Same scripts embedded in the binary (llm-gateway helpers install)
examples/scripts/claude-grok · claude-gemini · claude-codex PATH wrappers (helpers install → scripts/)
~/.config/inja-gateway/shell/ Install target after helpers install / load-helpers
examples/shell/claude-code-profiles.sh Profile/combo resolver
examples/claude-code-settings.json.example settings.json snippets
subauth/ OAuth login + credential store
cmd/gateway/auth_cmd.go llm-gateway auth CLI

Type to search…

↑↓ navigate↵ selectEsc close