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.
Watch the short walkthrough to see the dashboard, provider controls, routing surface, quotas, logs, and supporting features in context.
- 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 |
| 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.