AIRelays
AIRelays is a local OpenAI-compatible HTTP server with provider-scoped runtimes.
- The default runtime uses an AIRelays-owned ChatGPT subscription login.
- The Claude runtime balances your own subscription accounts through AIRelays-owned local
claudeCLI profiles; your personal Claude Code sign-in is never used or changed. - Both serve the same OpenAI-compatible endpoint; see docs/capabilities.md for what works on each.
- AIRelays protects the relay with its own bearer token by default.
- Traffic is logged to JSONL files with automatic rotation and retention (7 days, 1 GiB total by default).
Independence And Intended Use
- AIRelays is an independent third-party project. It is not affiliated with, endorsed by, or sponsored by any provider.
- Provider and product names are used only to describe compatibility targets and upstream behavior.
- AIRelays is designed for a single user running a local relay for personal convenience.
- AIRelays is not presented as a shared, pooled, multi-user, or resale service.
- The Claude runtime is local-only and not presented as a sanctioned provider integration path.
- Personal, local, or noncommercial use does not by itself establish provider permission. You are responsible for checking that your access methods, automation, credential handling, and workloads comply with the applicable terms. AIRelays does not certify compliance; unauthorized use may lead to account suspension or termination, and relay compatibility may stop working without notice.
See DISCLAIMER.md — it links the official Anthropic and OpenAI terms and policy pages to review.
Install
AIRelays ships two ways; both drive the same relay and share the same
config (~/.config/airelays) and data (~/.airelays).
Headless relay and CLI (macOS, Linux)
For servers, SSH sessions, Intel Macs, or terminal-first workflows, install
the airelays command with one line:
curl -fsSL https://raw.githubusercontent.com/lpalbou/AIRelays/main/scripts/install-headless.sh | bash
The script installs the newest PyPI release into an isolated environment
without sudo: with uv when available, otherwise
into a virtualenv from Python 3.11+ (linked as ~/.local/bin/airelays). On a
machine with neither, it installs uv first, which provides Python. Run the
same command again to upgrade. Then continue with the Quick Start.
You can also install the package yourself (Python 3.11+, including Windows):
python -m pip install airelays # or: uv tool install airelays
Or from a source checkout:
python -m pip install .
Desktop app (GUI + system tray)
macOS (Apple Silicon) and Linux (x86_64):
curl -fsSL https://raw.githubusercontent.com/lpalbou/AIRelays/main/scripts/install-desktop.sh | bash
Windows (x64), in PowerShell:
irm https://raw.githubusercontent.com/lpalbou/AIRelays/main/scripts/install-desktop.ps1 | iex
The installers download the newest release's installer from GitHub Releases, verify its SHA-256 digest, install without sudo or admin rights, and start the app:
- macOS:
AIRelays.appin/Applications(~/Applicationswhen/Applicationsis not writable). A running copy is stopped and replaced. - Linux: the AppImage as
~/.local/bin/airelays-desktop, plus an applications-menu entry. Withoutlibfuse2, the launcher uses AppImage's extract-and-run mode. - Windows: the NSIS setup, run silently for the current user.
The app is self-contained: it embeds its own Python and relay, so it needs
neither Python nor Node. Run the same command again to update. Set
AIRELAYS_VERSION=0.16.2 (or $env:AIRELAYS_VERSION on Windows) with any
installer to pin a release, and AIRELAYS_NO_LAUNCH=1 to skip starting the
app. You can also download the DMG, AppImage, deb, or setup .exe from the
Releases page directly; the builds are not notarized or code-signed, see
Troubleshooting.
The tray app (desktop/) provides a dashboard with relay start/stop, auth and network modes, OpenAI and Claude sign-in/sign-out, per-account usage bars, a model list with copy-ready ids, live traffic, and diagnostics. The tray icon shows connection state and pulses on request activity; the app can start at login, starts the relay when it opens, and restarts a crashed relay automatically.
In Overview, Connect Your App brings the endpoint, relay key, authentication, and network access controls together. Clicking outside the dashboard hides it without stopping the relay or discarding edits. On macOS and Windows, left-click the tray icon to reopen it; right-click for the menu. Open Dashboard is also available from the tray menu on every platform.
To build the installers from source (Rust and Node required), see desktop/README.md:
cd desktop
./scripts/bundle_runtime.sh
npm install && npm run build
An earlier native macOS status-bar app remains available under macos/AIRelaysMenuBar:
swift build --package-path macos/AIRelaysMenuBar
swift run --package-path macos/AIRelaysMenuBar AIRelaysMenuBar
Quick Start
OpenAI runtime:
airelays init
airelays login
airelays doctor
airelays serve --port 8080
Headless / server install (SSH, no browser):
airelays init
airelays login --device
airelays doctor
airelays serve --port 8080
Device-code login prints a short code you approve from a browser on any
other device (laptop, phone). On SSH sessions and displayless Linux,
airelays login selects it automatically. Do not paste the browser-flow
URL into a browser on another computer: its sign-in redirect only works on
the machine running the relay (see
docs/troubleshooting.md for the SSH-tunnel
alternative).
OpenAI runtime in open local relay mode:
airelays init --no-auth
airelays login
airelays serve --no-auth --port 8080
This disables only the AIRelays client-token gate. It does not bypass the upstream ChatGPT login.
Claude runtime:
airelays init
airelays claude login # repeat to add another account
airelays serve --port 8080
Claude runtime in headless environments:
# on any machine WITH a browser:
claude setup-token # prints a long-lived token
# on the server:
airelays init
airelays claude set-token # paste the token; stored 0600, survives restarts
airelays serve --port 8080
AIRelays uses only Claude accounts it owns. Each airelays claude login
creates a separate Claude Code profile under ~/.airelays/claude/accounts/.
airelays claude set-token stores an optional setup token 0600 in
~/.airelays/claude-token — unlike a shell export, it keeps working under
systemd, launchd, and docker — and runs it as the account default in its
own relay-owned Claude Code configuration. Your personal Claude Code sign-in
(~/.claude, whatever claude auth login or /login selected), an
inherited CLAUDE_CONFIG_DIR, and an exported CLAUDE_CODE_OAUTH_TOKEN are
never used, so switching or signing out Claude Code never affects the relay.
Until an account is connected, claude:* requests return
503 provider_auth_error and /v1/models lists no Claude models.
airelays claude accounts # list account ids and sign-ins
airelays claude login # add another subscription
airelays claude login --replace ACCOUNT # renew one sign-in
airelays claude logout ACCOUNT # sign out just this account
The desktop Anthropic card offers Sign in / Add account ("In a
browser" or "With a token"), separate usage bars, and sign-out on each row.
Accounts join and leave routing without a relay restart.
The default balanced strategy prefers available execution slots and the
lowest reported weekly usage among eligible accounts. Without fresh usage
information, accounts take turns. Exhausted short or model-specific windows
are skipped; account failures can fail over only before response bytes are sent.
Set [providers.claude].balance to round_robin or ordered to change selection.
Usage bars come from Claude Code itself: every five minutes the relay asks
each profile's CLI for its usage (get_usage, no prompt or quota), which also
keeps idle profiles signed in. This needs a Claude Code version with
get_usage (verified with 2.1.292+). The setup-token account is
inference-only: Claude reports no usage or email for setup tokens, so it has
no bar. See Subscription Status.
Signing out affects only AIRelays: airelays claude logout ACCOUNT signs out
that relay-owned profile and stops routing to it, and
airelays claude logout default deletes the stored setup token. Your personal
Claude Code sign-in is untouched. With multiple accounts,
airelays claude logout requires an account or --all.
Upgrading from 0.15.0 or earlier: if Claude only worked through your personal
Claude Code sign-in, a custom CLAUDE_CONFIG_DIR, or an exported
CLAUDE_CODE_OAUTH_TOKEN, connect it once with airelays claude login (or
airelays claude set-token). Profiles already enrolled keep working.
When the Claude runtime is enabled, AIRelays keeps the same auth behavior as the rest of the relay. The default protected mode requires the AIRelays bearer token; --no-auth starts an open local relay. Claude remains restricted to loopback binding.
Basic Verification
Run setup and upstream probes before starting the server:
airelays doctor
List the model ids the running relay accepts (grouped by provider):
airelays models
Use airelays doctor --skip-response to skip the tiny /responses smoke request.
Public health:
curl http://127.0.0.1:8080/healthz
Protected relay status:
curl http://127.0.0.1:8080/v1/relay/status \
-H 'authorization: Bearer YOUR_AIRELAYS_TOKEN'
OpenAI model listing:
curl http://127.0.0.1:8080/v1/models \
-H 'authorization: Bearer YOUR_AIRELAYS_TOKEN'
OpenAI text request:
curl http://127.0.0.1:8080/v1/chat/completions \
-H 'authorization: Bearer YOUR_AIRELAYS_TOKEN' \
-H 'content-type: application/json' \
-d '{
"model": "gpt-5.5",
"messages": [{"role": "user", "content": "Reply with exactly: OPENAI AIRelays OK"}]
}'
Claude text request:
curl http://127.0.0.1:8080/v1/chat/completions \
-H 'authorization: Bearer YOUR_AIRELAYS_TOKEN' \
-H 'content-type: application/json' \
-d '{
"model": "claude:sonnet",
"messages": [{"role": "user", "content": "Reply with exactly: CLAUDE AIRelays OK"}]
}'
Multiple OpenAI Accounts
One person can enroll several of their own OpenAI subscriptions and let the relay balance across them. Signing in again with a different account adds it (the previous sign-in is kept, never overwritten):
airelays login # first account
airelays login # second account — added alongside the first
airelays accounts # list accounts and the commands to manage them
airelays logout perso@gmail.com # sign one account out
airelays accounts order work@company.com perso@gmail.com # change priority
airelays accounts is the hub: it lists your accounts in balancing order and
prints the exact commands to add, sign out, or reorder them. In the desktop
app, each account row has a sign-out button and the "Add account" button
offers both browser and code (headless) sign-in.
By default AIRelays balances requests by remaining capacity
(balance = "balanced"): the account with the most unused weekly quota
serves next, so consumption equalizes as a percentage of each plan's own
capacity — a small Plus plan and a large Enterprise plan deplete
proportionally instead of the small plan draining many times faster.
Which usage windows a plan reports is upstream policy (some plans report
only a weekly window), so the relay identifies windows by duration and
balances on each account's longest one. The relay probes each account's
usage at launch and refreshes it in the background; an account at a
usage limit is benched until that window resets and rejoins rotation
automatically. Alternatives:
balance = "round_robin" sends strictly equal request counts, and
balance = "ordered" drains the first account before touching the next.
Failed-over requests are logged with the serving account, and
/v1/subscription/status?all_accounts=true reports usage per account.
Multiple accounts exist so one user can use their own subscriptions from one relay; it is not a mechanism for sharing or pooling access between people (see DISCLAIMER.md).
Relay Token
Show the current token:
airelays token show
Rotate the current token:
airelays token rotate
Use the relay token as the client credential when you point an OpenAI-compatible SDK at AIRelays.
Provider Routing
- models starting with
claude:orclaude-use the Claude runtime when it is enabled - other model ids use the OpenAI runtime when it is enabled
- OpenAI model discovery queries the upstream catalog using the installed Codex client version, with a bundled version floor for standalone installations. Optional
[providers.openai] extra_modelsentries extend the catalog; other unlisted ids are rejected locally when the catalog is available. - Claude model discovery queries the installed CLI and exposes both aliases and concrete model ids. The Models tab and
airelays modelsshow each reported alias resolution, such asclaude:fableresolving toclaude-fable-5-1. - The Models tab refreshes automatically every five minutes while reachable. Its Refresh button, or
GET /v1/models?refresh=true, requests fresh provider catalogs. See model discovery for cache and availability limits. - With multiple OpenAI accounts,
/v1/modelsis the union of their catalogs. Balancing, conversation affinity, and failover stay within each model's supporting account subset. The Models tab shows account coverage and labels entries the upstream hides from its own picker. - Subscription bars report upstream allowances, including Claude's separately scoped Fable weekly cap when present. Hover over or activate the ? beside an account name for credit details and snapshot times; percentages are not token counts. See Subscription Status.
- AIRelays rejects requests when the selected runtime is disabled or the route is outside that runtime's published subset
What AIRelays Exposes
GET /v1/modelsGET /v1/models/{model}GET /v1/subscription/status(OpenAI and?provider=claude)GET /v1/account/rate_limitsGET /v1/relay/statusPOST /v1/relay/accounts/refreshPOST /v1/responsesPOST /v1/chat/completionsPOST /v1/completionsPOST /v1/filesGET /v1/filesGET /v1/files/{file_id}GET /v1/files/{file_id}/contentDELETE /v1/files/{file_id}POST /v1/conversationsGET /v1/conversations/{conversation_id}POST /v1/conversations/{conversation_id}DELETE /v1/conversations/{conversation_id}/no-tools/v1/models/no-tools/v1/responses/no-tools/v1/chat/completions/no-tools/v1/completions
What The Relay Changes (Compatibility Layer)
The ChatGPT subscription backend is not the public OpenAI platform API, so
AIRelays adapts some requests instead of letting them fail. Parameter
stripping is always visible: removed parameters are logged as a
compatibility_adaptation record in the traffic logs and reported in the
x-airelays-ignored-parameters response header. Other compatibility
normalizations are documented below and may log dedicated adaptation
records when the request shape itself is repaired.
Sampling parameters are removed. The upstream rejects temperature,
top_p, presence_penalty, and frequency_penalty outright
("Unsupported parameter: temperature"). AIRelays strips them so standard
SDK calls keep working; generation then runs with the upstream's own
sampling defaults, which cannot be overridden. The Claude routes
apply the same adaptation — the local claude CLI has no sampling
controls — so the same SDK calls work against claude:* models too.
Output-token limits are honoured. max_completion_tokens, max_tokens
and max_output_tokens are enforced on both account types: GPT models apply
the limit upstream (minimum 16), and a Claude run stops at the limit. A cut
answer reports finish_reason: "length", as on OpenAI.
Cursor BYOK chat-route compatibility is normalized locally. Some
current Cursor builds send malformed OpenAI-compatible requests to
/v1/chat/completions: either full Responses-style bodies (input,
instructions, flat tools, etc.) or flat Responses-style custom
tools/tool choices/tool calls such as ApplyPatch. AIRelays accepts those
shapes on the chat route, normalizes them to the Responses upstream, and
translates upstream custom_tool_call items back to chat-completions
tool_calls on both streaming and non-streaming responses. Requests that
mix messages and input, or tool outputs that do not reference a
preceding assistant tool call in the same request (or an earlier turn of
its conversation), are rejected loudly.
Unrecognized parameters are disclosed, not silently dropped. On chat
and completions, a parameter AIRelays does not recognize is removed and
listed in x-airelays-ignored-parameters with the stripped ones.
Parameters that would change the output and cannot be honored (stop,
logprobs, logit_bias, web_search_options, moderation, and others
listed in docs/api.md) are rejected with a 422 instead.
Reasoning effort is forwarded, not invented. reasoning_effort (chat
completions) and reasoning: {"effort": ...} (responses) pass through to
the upstream unchanged. Every model's supported modes are published in
/v1/models under airelays.reasoning, using provider catalog metadata
when available. Modes vary by model and may include max or ultra.
Claude modes map to the local CLI's --effort flag; unsupported values
are rejected with the model's supported list. When omitted, the provider
chooses its default; Claude can use an adaptive default. To choose
reasoning depth explicitly:
curl http://127.0.0.1:8317/v1/chat/completions \
-H 'authorization: Bearer YOUR_AIRELAYS_TOKEN' \
-H 'content-type: application/json' \
-d '{
"model": "gpt-5.5",
"reasoning_effort": "medium",
"messages": [{"role": "user", "content": "..."}]
}'
Conversations stick to one account. With multiple OpenAI accounts, a conversation keeps using the account that served its first turn (preserving upstream prompt caching); it only fails over to another account at a turn boundary when the pinned account is at its limit.
Failed calls are retried automatically. Transient upstream failures
(e.g. server_is_overloaded) are retried with exponential backoff — by
default 3 retries waiting 5s/20s/60s, each re-running account failover —
as long as no response byte has reached the client. A retry that succeeds
returns the normal response; a request that keeps failing returns
OpenAI-shaped error JSON with the real HTTP status and the upstream's own
reason. Tune with retry_attempts / retry_backoff_seconds
([providers.openai], desktop Settings → Providers; 0 disables).
Retries appear in the traffic log as retry_backoff records.
Compatibility Boundary
The full side-by-side matrix of what works on GPT models and on Claude models is in docs/capabilities.md.
OpenAI runtime:
- first-class routes:
/v1/responses,/v1/chat/completions,/v1/completions - local files and local conversations are supported
- non-stream responses are reconstructed from streamed upstream events
store=trueis rejected- output-token limits are enforced upstream (minimum 16)
Claude runtime:
- discovered Claude aliases and concrete model ids, plus configured overrides
- supported routes:
/v1/chat/completions,/v1/completionsand/v1/responses - images, PDFs and text files as input; local files and conversations
- function tool calls are returned to your application, which runs them; AIRelays never runs a tool (forced calls are refused; strict tools run non-strict)
- no audio
- structured outputs on chat completions:
response_formatjson_schemamaps to the CLI's native--json-schemaenforcement;json_objectanswers are checked to be one JSON object reasoning_effortmaps to the CLI's--effort(low, medium, high, xhigh, max)- sampling parameters are stripped and disclosed, like on the OpenAI runtime
Security Defaults
- default listener:
127.0.0.1:8080 - protected routes:
/v1/*and/no-tools/v1/* - public routes:
/andGET /healthz - protected diagnostics:
GET /v1/relay/status - default rate limit:
120requests/minute with burst40 - default concurrent request cap:
8per IP - repeated bad tokens trigger a temporary IP block
- the Claude runtime is loopback-only and follows the relay's protected or open local auth mode
Configuration
Traffic logs are bounded by default: up to 7 days, 1 GiB total, and
50 MiB per file, with hourly rotation and oldest-first cleanup. A busy
relay may keep less history because the disk limit takes precedence.
Use airelays logs --retention-days 30 for a month, or the tray app's
Settings → Traffic log retention controls. Changes persist without a
restart; GET/PUT /v1/relay/logging expose the same policy and current usage.
Starting the upgraded relay also applies the limits to existing traffic logs.
See retention configuration.
AIRelays reads configuration in this order:
- CLI flags
AIRELAYS_*environment variables- legacy
OPENAI_ENDPOINT_*migration variables where supported ~/.config/airelays/config.toml- built-in defaults
Important toggles:
AIRELAYS_REQUIRE_BEARER_AUTHAIRELAYS_BEARER_TOKENAIRELAYS_BEARER_TOKEN_FILEAIRELAYS_ENABLE_OPENAIAIRELAYS_OPENAI_MODELS_CACHE_TTL_SECONDSAIRELAYS_ENABLE_CLAUDEAIRELAYS_CLAUDE_BINAIRELAYS_CLAUDE_MODELS
Paths
- config:
~/.config/airelays/config.toml - data dir:
~/.airelays - logs:
~/.airelays/logs - relay token:
~/.airelays/relay-token - Claude accounts:
~/.airelays/claude/accounts/<id>/; setup token:~/.airelays/claude-token - earlier singular AIRelay paths remain compatible for local upgrades
More Docs
Metadata
Release files for airelays 0.17.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| airelays-0.17.0.tar.gz | 6.9 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| airelays-0.17.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 7.1 MB
Release files / airelays-0.17.0.tar.gz
| Download URL | airelays-0.17.0.tar.gz |
|---|---|
| Size | 6.9 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8fc432c6af629d5ae4e7a3f040086ad16511cde350f559ce068610bddb981e32
|
|
BLAKE2b-256 checksum How to use checksums |
355e10bf9bfa43af36797753ab428695816cbe0e4880351d2a7117711e7f85dd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.
Transparency logRelease files / airelays-0.17.0-py3-none-any.whl
| Download URL | airelays-0.17.0-py3-none-any.whl |
|---|---|
| Size | 183.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
02195c455e8d6d7f75dfd7604c4408ad94b2d9c042d7e8c7dbc7fc9f6d48f170
|
|
BLAKE2b-256 checksum How to use checksums |
9b4692f4da08d806da7cb3a6f136330a1614aa33faa217c80bbb61e5848ab006
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.
Transparency log