Metadata-Version: 2.4
Name: airelays
Version: 0.2.1
Summary: AIRelays: an independent OpenAI-compatible local relay for single-user subscription-backed access.
Project-URL: GitHub, https://github.com/lpalbou/AIRelays
Project-URL: Documentation, https://lpalbou.github.io/AIRelays/
Project-URL: Changelog, https://github.com/lpalbou/AIRelays/blob/main/CHANGELOG.md
Author: Laurent-Philippe Albou
License: MIT
License-File: LICENSE
Keywords: ai,gateway,openai-compatible,relay,subscription
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.11
Requires-Dist: fastapi>=0.115.0
Requires-Dist: httpx>=0.28.0
Requires-Dist: keyring>=25.5.0
Requires-Dist: python-multipart>=0.0.20
Requires-Dist: uvicorn>=0.35.0
Provides-Extra: dev
Requires-Dist: build>=1.2.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
Requires-Dist: pytest>=8.3.0; extra == 'dev'
Requires-Dist: twine>=6.1.0; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material>=9.0.0; extra == 'docs'
Requires-Dist: mkdocs>=1.6.0; extra == 'docs'
Requires-Dist: pymdown-extensions>=10.0; extra == 'docs'
Description-Content-Type: text/markdown

# AIRelays

`AIRelays` is an independent local OpenAI-compatible HTTP server backed by a ChatGPT subscription login that AIRelays stores independently. It exposes the verified OpenAI-shaped routes this upstream can support, protects the local relay with its own bearer token, and logs every transit to hourly JSONL files.

`AIRelays` does not require a user-supplied OpenAI platform API key for upstream inference. Instead, it uses the same upstream ChatGPT login protocol that Codex uses while keeping AIRelays auth storage separate from Codex storage. Clients that call `AIRelays` should use the relay bearer token as the local client credential they present to AIRelays.

## Independence And Intended Use

- AIRelays is an independent third-party project. It is not affiliated with, endorsed by, or sponsored by OpenAI.
- 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.
- You are responsible for complying with the terms and usage policies that apply to any upstream account or subscription you use with AIRelays.

See [DISCLAIMER.md](DISCLAIMER.md) for the short project notice.

## Quick Start

```bash
python -m pip install .
airelays init
airelays login
airelays serve --port 8080
```

If you are installing from a published package instead of a source checkout, use `python -m pip install airelays`.

If you want a local relay with no client-side bearer auth, use:

```bash
airelays init --no-auth
airelays login
airelays serve --no-auth --port 8080
```

Smoke test the public and protected surfaces:

```bash
curl http://127.0.0.1:8080/healthz
curl http://127.0.0.1:8080/v1/relay/status \
  -H 'authorization: Bearer YOUR_AIRELAYS_TOKEN'
```

In open local relay mode, the same `GET /v1/relay/status` request works without the `Authorization` header.

Inspect the resolved relay and upstream-auth state at any point:

```bash
airelays status
```

CLI status and setup commands default to readable terminal output. Use `--json` on `airelays init`, `airelays status`, `airelays logout`, `airelays token show`, or `airelays token rotate` when you need machine-readable output for automation.

Point your client at:

```text
http://127.0.0.1:8080/v1
```

Use the token generated by `airelays init` as the client credential when you point an OpenAI-compatible SDK at AIRelays. Standard OpenAI SDKs will then send `Authorization: Bearer <relay-token>` automatically.

If you launch with `--no-auth` or `AIRELAYS_REQUIRE_BEARER_AUTH=false`, clients can call the relay without an `Authorization` header. If an SDK still requires an `api_key` field, any non-empty placeholder string works in that mode.

If you want to provide the relay token yourself instead of using the default token file, launch the server with:

```bash
AIRELAYS_BEARER_TOKEN='YOUR_AIRELAYS_TOKEN' airelays serve --port 8080
```

or point AIRelays at a specific token file:

```bash
airelays serve --bearer-token-file /path/to/relay-token --port 8080
```

## What AIRelays Does

- Uses the same upstream login protocol as Codex browser login and device-code login.
- Stores upstream auth under AIRelays-owned state instead of reusing `~/.codex`.
- Generates and persists a separate relay bearer token for client-to-relay access.
- Can run in open local relay mode with bearer auth disabled.
- Protects `/v1/*` and `/no-tools/v1/*` with bearer auth, per-IP rate limits, concurrent-request caps, and temporary blocks after repeated bad tokens.
- Exposes OpenAI-compatible routes for:
  - `GET /v1/models`
  - `GET /v1/subscription/status`
  - `GET /v1/account/rate_limits`
  - `POST /v1/completions`
  - `POST /v1/responses`
  - `POST /v1/chat/completions`
  - `POST /v1/files`
  - `GET /v1/files`
  - `GET /v1/files/{file_id}`
  - `GET /v1/files/{file_id}/content`
  - `DELETE /v1/files/{file_id}`
  - `POST /v1/conversations`
  - `GET /v1/conversations/{conversation_id}`
  - `POST /v1/conversations/{conversation_id}`
  - `DELETE /v1/conversations/{conversation_id}`
  - `/no-tools/v1/models`
  - `/no-tools/v1/completions`
  - `/no-tools/v1/responses`
  - `/no-tools/v1/chat/completions`
- Logs inbound requests, endpoint rejects, outbound responses, upstream requests, upstream responses, stream lines, and usage summaries to `logs/YYYY/MM/DD-HH.log`.

## First-Run Flow

1. `airelays init`
   - writes `~/.config/airelays/config.toml` if it does not already exist
   - creates `~/.airelays/relay-token` with `0600` permissions if a relay token is missing
   - prints a formatted setup summary and reveals the token only when it was newly created
2. `airelays init --no-auth`
   - writes `~/.config/airelays/config.toml` with bearer auth disabled
   - does not create or require a relay token
3. `airelays login`
   - creates an AIRelays-owned ChatGPT subscription session
4. `airelays serve --port 8080`
   - starts the protected local endpoint
   - fails fast if bearer auth is enabled but no relay token is configured
   - prints the client base URL, token file path, and the required `Authorization` header shape
5. `airelays serve --no-auth --port 8080`
   - starts an open local endpoint with no relay-token check
   - keeps the normal per-IP rate limits and concurrency limits

You can show the current relay token at any time:

```bash
airelays token show
```

You can also rotate the relay token later:

```bash
airelays token rotate
```

## Example Client Usage

Python with the OpenAI SDK:

```python
from openai import OpenAI

client = OpenAI(
    base_url="http://127.0.0.1:8080/v1",
    api_key="YOUR_AIRELAYS_TOKEN",
)

response = client.responses.create(
    model="gpt-5.4-mini",
    input="Summarize the purpose of AIRelays.",
)
print(response.output_text)
```

Raw `curl`:

```bash
curl http://127.0.0.1:8080/v1/responses \
  -H 'authorization: Bearer YOUR_AIRELAYS_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
    "model": "gpt-5.4-mini",
    "input": "Summarize the purpose of AIRelays.",
    "stream": false
  }'
```

Shell example with the OpenAI Python SDK environment variables:

```bash
export OPENAI_BASE_URL='http://127.0.0.1:8080/v1'
export OPENAI_API_KEY="$(tr -d '\n' < ~/.airelays/relay-token)"
```

Open local relay mode with a placeholder key for SDKs that insist on one:

```bash
export OPENAI_BASE_URL='http://127.0.0.1:8080/v1'
export OPENAI_API_KEY='local-open-relay'
```

## Verified Compatibility Boundary

This server is intentionally explicit about what is and is not verified.

- Inference uses `https://chatgpt.com/backend-api/codex`.
- Subscription status uses `https://chatgpt.com/backend-api/wham/usage`.
- The upstream requires `stream=true`, so non-stream OpenAI responses are reconstructed locally from streamed event sequences.
- The upstream requires `store=false`, so requests that try to enable upstream storage are rejected with `422`.
- The upstream requires non-empty `instructions`, so the compatibility layer injects the minimal verified placeholder `"."` only when the caller omitted instructions entirely.
- Image input is supported.
- Text and JSON-like document input is supported by local inlining up to 1 MB.
- Local file uploads are capped at 32 MiB each and 256 MiB total by default.
- Audio input, embeddings, image generation, realtime sessions, and other unverified routes return explicit `501 unsupported_error`.

## Security Defaults

- Listener default: `127.0.0.1:8080`
- Protected routes: `/v1/*` and `/no-tools/v1/*`
- Public routes: `/` and a minimal `GET /healthz`
- Protected diagnostics: `GET /v1/relay/status`
- Relay auth: bearer token required by default
- Token storage: `~/.airelays/relay-token`
- Default rate limit: `120` requests/minute with burst `40`
- Default concurrent request cap: `8` per IP
- Default repeated-auth-failure block: `8` bad attempts in `300` seconds -> `900` second block

See [Security](docs/security.md) for the full behavior.

## Configuration

AIRelays reads configuration in this order:

1. explicit CLI flags such as `--config`, `--port`, or `--auth-storage`
2. `AIRELAYS_*` environment variables
3. legacy `OPENAI_ENDPOINT_*` environment variables where supported as a migration fallback
4. `~/.config/airelays/config.toml`
5. built-in defaults

`auth.storage = "auto"` prefers the AIRelays keyring namespace and falls back to `~/.airelays/auth.json` when keyring access is unavailable.

Important paths:

- config: `~/.config/airelays/config.toml`
- data dir: `~/.airelays`
- upstream auth fallback file: `~/.airelays/auth.json`
- logs: `~/.airelays/logs`
- relay token: `~/.airelays/relay-token`

To override the default token source at launch time:

- `AIRELAYS_BEARER_TOKEN`
- `airelays serve --bearer-token-file /path/to/relay-token`

To launch without relay auth:

- `airelays init --no-auth`
- `airelays serve --no-auth`
- `AIRELAYS_REQUIRE_BEARER_AUTH=false`

See [Configuration](docs/configuration.md) for field details and a sample config.

## Publication Surface

- package name: `airelays`
- CLI command: `airelays`
- Python package: `airelays`

## Documentation

- [Getting Started](docs/getting-started.md)
- [Configuration](docs/configuration.md)
- [Security](docs/security.md)
- [Disclaimer](DISCLAIMER.md)
- [Hosted Docs](https://lpalbou.github.io/AIRelays/)
- [API Notes](docs/api.md)
- [Architecture](docs/architecture.md)
- [Subscription Status](docs/subscription-status.md)
- [FAQ](docs/faq.md)
- [Troubleshooting](docs/troubleshooting.md)
- [ADR Index](docs/adr/README.md)
