A local-first AI gateway and LLM proxy for OpenAI, Anthropic, and custom models.
Use one local endpoint to route requests, manage provider authentication, enforce quotas, and inspect usage.
- Quick start
- Connect coding tools
- Configure a client
- Supported providers
- API endpoints
- Development
- Docker Compose
docker run -d \
--name srouter \
--restart unless-stopped \
-p 3000:3000 \
-p 1455:1455 \
-v "$HOME/.srouter:/root/.srouter" \
ghcr.io/seaavey/srouter:latestSRouter stores its SQLite database and provider credentials in ~/.srouter on the host.
Open http://localhost:3000 and configure a provider from the dashboard. Then create a virtual API key from API Keys and test a model from Playground.
Requirements: Node.js 22 or later and pnpm 11.
git clone https://github.com/seaavey/SRouter.git
cd SRouter
pnpm install
pnpm build
pnpm startThe dashboard is available at http://localhost:3000.
Install and configure the CLI from npm:
npx @srouter/cli setup
npx @srouter/cli doctor
npx @srouter/cli link claude --model claude-3-7-sonnet
npx @srouter/cli link opencode --model antigravity/gemini-3.7-flash-highRun a coding tool with SRouter's proxy environment:
npx @srouter/cli run claudeUse --dry-run to preview configuration changes without writing files:
npx @srouter/cli link claude --dry-runThe CLI supports Claude Code and OpenCode. Configuration changes are backed up and can be restored with unlink.
SRouter exposes OpenAI-compatible and Anthropic-compatible endpoints.
| Setting | Value |
|---|---|
| Base URL | http://localhost:3000/v1 |
| API key | sr-live-your_virtual_key |
| Models | GET http://localhost:3000/v1/models |
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:3000/v1",
api_key="sr-live-your_virtual_key"
)
response = client.chat.completions.create(
model="antigravity/gemini-3.7-flash-high",
messages=[{"role": "user", "content": "Ping!"}],
stream=True
)
for chunk in response:
print(chunk.choices[0].delta.content or "", end="", flush=True)import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
baseURL: "http://localhost:3000/v1",
apiKey: "sr-live-your_virtual_key"
});
const message = await client.messages.create({
model: "anthropic/claude-3-7-sonnet",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello from SRouter!" }]
});
console.log(message.content[0].text);curl -N http://localhost:3000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sr-live-your_virtual_key" \
-d '{
"model": "antigravity/gemini-3.7-flash-high",
"messages": [{"role": "user", "content": "Ping!"}],
"stream": true
}'SRouter normalizes authentication, model routing, streaming, quotas, and protocol differences across providers.
| Provider | Model prefix | Authentication | Live quota |
|---|---|---|---|
| Google Antigravity | antigravity/* |
OAuth 2.0 PKCE | Yes |
| OpenAI Codex / ChatGPT | openai_codex/* |
OAuth 2.0 PKCE | Yes |
| Anthropic Claude | anthropic/* |
API key / OAuth | Yes |
| OpenCode Zen | opencode_zen/* |
Free / access token | Yes |
| Amazon Q / Kiro | kiro/* |
SigV4 / API key | Yes |
| Qoder | qoder/* |
OAuth / device token | Yes |
| GoRouter | gorouter/* |
API key | Yes |
| BluesMinds | bluesminds/* |
API key | Yes |
| SeekAI / TabiToken | seekai/*, tabitoken/* |
API key | Yes |
| Custom endpoints | custom/* |
Custom headers | Configurable |
- OpenAI
chat/completionsand Anthropicmessagesprotocol translation - OAuth token refresh for supported providers
- Fallback chains for rate limits and provider failures
- Virtual API keys with rate limits, token quotas, and expiration
- Cloudflare Tunnel management from the dashboard
- Request logs, token usage, quota data, and estimated costs
- Token Saver prompt processing
Most gateway endpoints use the /v1 prefix. The health check is available at /health.
| Method | Endpoint | Purpose |
|---|---|---|
GET |
/health |
Server health check |
POST |
/v1/chat/completions |
OpenAI-compatible chat completion |
POST |
/v1/messages |
Anthropic-compatible messages |
GET |
/v1/models |
List available models |
GET |
/v1/models/:model |
Inspect a model |
GET / POST |
/v1/providers |
Manage provider connections |
GET / POST |
/v1/keys |
Manage virtual API keys |
GET |
/v1/quota |
Read provider quota data |
GET |
/v1/logs |
Read request logs and telemetry |
GET / POST |
/v1/tunnel/* |
Manage Cloudflare Tunnel state |
Copy .env.example to .env for local development. The main settings are:
| Variable | Default | Purpose |
|---|---|---|
PORT |
3000 |
Main API and dashboard port |
OAUTH_PORT |
1455 |
Local OAuth callback listener |
DATABASE_PATH |
~/.srouter/srouter.db |
SQLite database path |
DATABASE_URL |
Not set | PostgreSQL connection string |
WEB_DIST_PATH |
apps/web/dist |
Built dashboard path |
SROUTER_PUBLIC_URL |
Not set | Public URL for OAuth callbacks on the main port |
NODE_ENV |
development |
Runtime environment |
When SROUTER_PUBLIC_URL is set, OAuth callbacks use the main PORT instead of the secondary OAUTH_PORT listener.
This repository is a pnpm workspace managed by Turborepo. It contains the API, web dashboard, CLI, and shared packages.
pnpm install
pnpm devThe development servers use these ports:
| Service | URL |
|---|---|
| API | http://localhost:3000 |
| Web dashboard | http://localhost:5173 |
| OAuth listener | http://localhost:1455 |
Run focused checks for the app or package you changed:
pnpm --filter <app-or-package> build
pnpm --filter web lint
pnpm exec prettier --check <changed-files>
git diff --checkRun one test file with the package's test setup:
cd apps/api
pnpm exec tsx --test --test-concurrency=1 --import ./tests/setup.ts tests/<focused-file>.test.tsDo not run root pnpm build, pnpm test, or broad lint commands on resource-constrained development machines. CI runs the full build and test workflow.
services:
srouter:
image: ghcr.io/seaavey/srouter:latest
container_name: srouter
restart: unless-stopped
ports:
- "3000:3000"
- "1455:1455"
volumes:
- ${HOME}/.srouter:/root/.srouter
environment:
PORT: 3000
NODE_ENV: productionSRouter is distributed under the MIT License.