The two-faced AI routing gateway
Project description
Janus
The two-faced gateway for AI coding tools. Janus sits at the threshold of every AI call — facing the developer on one side and every provider on the other.
Janus is a local-first, single-user AI routing gateway. It exposes OpenAI/Anthropic/Gemini-compatible HTTP endpoints that your coding tools (Claude Code, Codex, Cursor, Cline, ...) talk to, then translates and routes each request to 40+ AI providers — without either side needing to know the other exists.
First-time setup
Janus needs Python 3.11+. Everything lives under ~/.janus/ — a seed
config.yaml and a SQLite database (janus.db) that becomes the source of truth
after the first startup.
1. Install
From PyPI (recommended):
pip install janus-ai
From source (development):
git clone https://github.com/amanverasia/Janus.git
cd Janus
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
2. Create config
janus config-init
This writes ~/.janus/config.yaml. Open it and add at least one provider with
your API keys. Environment variables in ${VAR} form are resolved at startup:
export OPENAI_API_KEY=sk-...
export ANTHROPIC_API_KEY=sk-ant-...
Example provider block:
providers:
- id: openai
prefix: openai
api_type: openai_compat
base_url: https://api.openai.com/v1
api_key: ${OPENAI_API_KEY}
models: [gpt-4o, gpt-4o-mini]
You can also add providers later from the dashboard — no restart required.
3. Start the server
janus serve --port 20128
For access from other machines on your LAN or Tailscale:
janus serve --host 0.0.0.0 --port 20128
Janus serves plain HTTP only. Use http://, not https://, unless you put
a reverse proxy with TLS in front.
4. Verify
curl http://localhost:20128/v1/health
# {"status":"ok"}
Open the dashboard at http://localhost:20128/dashboard.
The root URL / redirects there.
5. Configure via dashboard
On first startup, Janus imports providers, combos, token_savers, and
pricing from YAML into SQLite. After that, the database is authoritative —
editing YAML and restarting will not re-apply changes. Use the dashboard instead.
| Step | Where | What |
|---|---|---|
| Add providers | Providers | Pick from the catalog or add custom; fetch models, test connection |
| Create a client key | API Keys | sk-janus-... shown once — save it |
| Enable auth | Settings | Toggle Require API key (recommended for remote access) |
| Set dashboard login | Settings → Dashboard Login | Username + password for remote browser sign-in |
| Connect your tools | Tool Setup | Copy-paste env vars for Claude Code, Codex, Cursor, Cline |
Dashboard access rules:
- localhost — no sign-in required
- Remote (LAN, Tailscale, Docker on
0.0.0.0) — sign in at/dashboard/loginwith your dashboard username/password or a Janus API key
Create a key from the CLI instead:
janus keys create --name "my-laptop"
6. Send a test request
curl http://localhost:20128/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o",
"messages": [{"role": "user", "content": "Hello!"}],
"max_tokens": 50
}'
List registered models:
curl http://localhost:20128/v1/models
7. Point your coding tool at Janus
Claude Code / Anthropic tools:
export ANTHROPIC_BASE_URL=http://localhost:20128/v1
export ANTHROPIC_API_KEY=sk-janus-yourkey # if require_api_key is on
OpenAI-compatible tools (Codex, Cursor, Cline, etc.):
export OPENAI_BASE_URL=http://localhost:20128/v1
export OPENAI_API_KEY=sk-janus-yourkey # if require_api_key is on
Use prefix/model in requests (e.g. openai/gpt-4o,
anthropic/claude-sonnet-4-20250514) or a combo name like best-effort.
📚 Documentation · Contributing · Changelog
Docker
mkdir -p janus-data
janus config-init --path janus-data/config.yaml
# Edit janus-data/config.yaml — add providers and ${ENV_VAR} keys
# Optional: pass API keys via .env in the repo root
echo 'OPENAI_API_KEY=sk-...' >> .env
docker compose up -d
The image binds to 0.0.0.0:20128. SQLite and config persist in ./janus-data/.
After first startup, manage providers and settings from the dashboard — not by
editing YAML alone.
Remote dashboard: enable Require API key in Settings, create a Janus API key, and set a dashboard username/password under Dashboard Login.
curl http://localhost:20128/v1/health
open http://localhost:20128/dashboard # macOS; or visit in your browser
Configuration
Janus reads YAML from ~/.janus/config.yaml (or --config) with ${ENV_VAR}
token resolution. Generate a template with janus config-init.
On first startup only, YAML seeds the SQLite database. Subsequent changes should be made via the dashboard or Export Config / Reset to Defaults on the Settings page.
server:
port: 20128
host: 127.0.0.1
require_api_key: false
providers:
- id: openai
prefix: openai
api_type: openai_compat
base_url: https://api.openai.com/v1
api_key: ${OPENAI_API_KEY}
models: [gpt-4o, gpt-4o-mini, o3, o4-mini]
- id: anthropic
prefix: anthropic
api_type: anthropic
base_url: https://api.anthropic.com
api_key: ${ANTHROPIC_API_KEY}
models: [claude-sonnet-4-20250514, claude-opus-4-20250514]
combos:
- name: best-effort
models: [anthropic/claude-sonnet-4-20250514, openai/gpt-4o]
Supported Provider Types
api_type |
Use For |
|---|---|
openai_compat |
Any OpenAI-compatible API (OpenAI, Groq, Together, DeepSeek, OpenRouter, Mistral, Fireworks, Perplexity, xAI, ...) |
anthropic |
Direct Anthropic API |
gemini |
Direct Google Gemini API |
opencode_free |
OpenCode Zen free tier |
Known Provider Base URLs
| Provider | base_url |
|---|---|
| OpenAI | https://api.openai.com/v1 |
| Groq | https://api.groq.com/openai/v1 |
| Together AI | https://api.together.xyz/v1 |
| DeepSeek | https://api.deepseek.com/v1 |
| OpenRouter | https://openrouter.ai/api/v1 |
| Mistral | https://api.mistral.ai/v1 |
| Fireworks | https://api.fireworks.ai/inference/v1 |
| Perplexity | https://api.perplexity.ai |
| xAI (Grok) | https://api.x.ai/v1 |
| Qwen/DashScope | https://dashscope.aliyuncs.com/compatible-mode/v1 |
Client Setup
See step 7 in First-time setup for the basics. The dashboard
Tool Setup page (/dashboard/tools) generates copy-paste env vars for your
exact server URL and auth settings.
Claude Code / Anthropic tools:
export ANTHROPIC_BASE_URL=http://localhost:20128/v1
OpenAI tools (Codex, Cursor, etc.):
export OPENAI_BASE_URL=http://localhost:20128/v1
export OPENAI_API_KEY=sk-janus-yourkey # if require_api_key is on
Features
- Fallback routing — multi-account rotation with cooldowns (429->60s, 5xx->30s, auth->300s, network->15s)
- Combos — named ordered model sequences (e.g.,
"model": "best-effort") - Token savers — RTK compression (default ON), Caveman terse prompt, Ponytail lazy-dev prompt
- Budgets — daily spending limits per API key or global, with warn/block thresholds
- Analytics — cost tracking, spend trends, success rates, per-model/provider/key breakdowns
- Pricing — 28 builtin model prices, YAML-overridable, cache token rates
- Dashboard — HTMX UI at
/dashboardwith charts, budget management, usage stats, and remote username/password login - Upstream key inventory — validate, monitor, and route through a multi-key pool for 29 providers (
/dashboard/inventory)
Upstream Key Inventory
Built-in dashboard for upstream provider API keys: health checks, credit tracking, and automatic routing through the best available key.
Dashboard: http://127.0.0.1:20128/dashboard/inventory
- Overview stats, paginated/sortable keys table, key detail modal, best-keys widget
- Add keys, bulk submit, import from Dashboard export JSON, re-identify misclassified keys
- Encryption at rest; routable keys wired into gateway fallback rotation
- Background recheck scheduler (twice daily by default)
| Variable | Purpose |
|---|---|
INVENTORY_ENCRYPTION_KEY |
Fernet key for encrypting upstream keys at rest |
INVENTORY_PUSH_TOKEN |
Auth token for POST /dashboard/api/inventory/push |
INVENTORY_SCHEDULER_ENABLED |
Set to false to disable background rechecks (default: true) |
janus inventory generate-encryption-key # create Fernet key
janus inventory migrate export.json --verify # import Dashboard export + summary
janus inventory verify # cutover verification summary
janus inventory encrypt-keys # encrypt plaintext keys in DB
CLI Reference
| Command | Description |
|---|---|
janus serve |
Start the gateway server |
janus config-init |
Generate default config YAML |
janus config-path |
Print config file path |
janus keys create/list/revoke |
Manage API keys |
janus usage stats/cost/by-key |
Usage and cost reports |
janus budgets list/set/delete |
Manage spending budgets |
janus pricing list/show |
View model pricing |
janus inventory migrate/verify/encrypt-keys/generate-encryption-key |
Upstream key inventory and cutover |
Development
git clone https://github.com/amanverasia/Janus.git
cd Janus
python -m venv .venv
pip install -e ".[dev]"
# Run tests
.venv/bin/python -m pytest
# Lint + typecheck
.venv/bin/ruff check src/janus/ tests/
.venv/bin/mypy src/janus/
# Start dev server
.venv/bin/janus serve --port 20128 --reload
Tech Stack
Python 3.11+ / FastAPI / httpx / Pydantic v2 / aiosqlite / Jinja2 / HTMX / Chart.js
License
GPL-3.0 © 2026 Aman Verasia
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file janus_ai-1.2.0.tar.gz.
File metadata
- Download URL: janus_ai-1.2.0.tar.gz
- Upload date:
- Size: 320.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
558d2b744eeda990ee65bab89f19317c5ca87eb33e0ce7a4734b652f2d7a419e
|
|
| MD5 |
3c2ad4c69ff235437b677c0bab6d7799
|
|
| BLAKE2b-256 |
b65bec7fa9c1bff2be81e32287d5cd54620e02ef1e90ec5886176ce86ff960cd
|
Provenance
The following attestation bundles were made for janus_ai-1.2.0.tar.gz:
Publisher:
publish.yml on amanverasia/Janus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
janus_ai-1.2.0.tar.gz -
Subject digest:
558d2b744eeda990ee65bab89f19317c5ca87eb33e0ce7a4734b652f2d7a419e - Sigstore transparency entry: 2082145377
- Sigstore integration time:
-
Permalink:
amanverasia/Janus@6036a05b557931e6b30659074a7e18e691cd165f -
Branch / Tag:
refs/tags/v1.2.0 - Owner: https://github.com/amanverasia
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6036a05b557931e6b30659074a7e18e691cd165f -
Trigger Event:
push
-
Statement type:
File details
Details for the file janus_ai-1.2.0-py3-none-any.whl.
File metadata
- Download URL: janus_ai-1.2.0-py3-none-any.whl
- Upload date:
- Size: 188.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7994b264f2879efee2fa9a402a62b1f5ce3dbd73529f134e776cc590e84c35cc
|
|
| MD5 |
a3ef1e53dce311a9e201bbde3aa3d2f3
|
|
| BLAKE2b-256 |
03884d89d02e91d81b8d9d0679946c1e0eb2622527241dec3396cebe417ab4e6
|
Provenance
The following attestation bundles were made for janus_ai-1.2.0-py3-none-any.whl:
Publisher:
publish.yml on amanverasia/Janus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
janus_ai-1.2.0-py3-none-any.whl -
Subject digest:
7994b264f2879efee2fa9a402a62b1f5ce3dbd73529f134e776cc590e84c35cc - Sigstore transparency entry: 2082145381
- Sigstore integration time:
-
Permalink:
amanverasia/Janus@6036a05b557931e6b30659074a7e18e691cd165f -
Branch / Tag:
refs/tags/v1.2.0 - Owner: https://github.com/amanverasia
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6036a05b557931e6b30659074a7e18e691cd165f -
Trigger Event:
push
-
Statement type: