Configuration¶
Janus reads its configuration from a YAML file at ~/.janus/config.yaml. You can
print the exact path with:
janus config-path
Generate a template with all sections commented out:
janus config-init
DB-driven configuration¶
On first startup, Janus seeds SQLite from your YAML file:
| YAML section | Seeded into |
|---|---|
providers |
providers table |
combos |
combos table |
token_savers |
settings table (saver_* keys) |
pricing |
pricing_overrides table |
server.require_api_key |
settings table (server_require_api_key) |
Seeding is idempotent — if a table already has rows, that section is skipped. After seeding, the database is the source of truth. Editing YAML and restarting will not re-apply providers, combos, savers, or pricing.
To manage runtime config:
- Use the dashboard — changes hot-reload immediately
- Export Config (
GET /dashboard/api/export) — download current DB state as YAML - Reset to Defaults (Settings page) — wipe DB tables and re-seed from YAML
Static api_keys in YAML are not seeded — they remain in the config file and
are checked at auth time alongside DB-managed keys. YAML static keys always have
full access (dashboard login + all models). Scopes (can_login, allowed_models)
apply only to DB-managed sk-janus-* keys — see API Reference
and the Keys dashboard page.
Runtime settings stored in the settings table:
| Key | Description |
|---|---|
server_require_api_key |
true / false — overrides YAML default |
saver_rtk_enabled |
RTK token saver on/off |
saver_caveman_enabled |
Caveman saver on/off |
saver_ponytail_enabled |
Ponytail saver on/off |
saver_ponytail_level |
lite, full, or ultra |
Environment Variable Resolution¶
Any value in the YAML may reference environment variables using the ${VAR_NAME}
syntax. Variables are resolved at startup — if the variable is unset it expands to
an empty string.
providers:
- id: openai
api_key: ${OPENAI_API_KEY}
Set them in your shell or .env:
export OPENAI_API_KEY=sk-...
export ANTHROPIC_API_KEY=sk-ant-...
server¶
Controls the HTTP server.
| Field | Type | Default | Description |
|---|---|---|---|
port |
int |
20128 |
Server port |
host |
str |
"127.0.0.1" |
Bind address |
require_api_key |
bool |
false |
When true, all API endpoints require auth (except /health). Can be toggled at runtime from the dashboard Settings page (stored in DB). |
data_dir |
Path |
~/.janus |
Directory for the SQLite DB (data_dir/janus.db) |
server:
port: 20128
host: 127.0.0.1
require_api_key: false
data_dir: ~/.janus
Binding to all interfaces
Set host: 0.0.0.0 to accept connections from other machines on your network.
Always enable require_api_key when doing so.
providers¶
A list of provider configurations. Each entry registers one upstream account.
| Field | Type | Default | Description |
|---|---|---|---|
id |
str |
(required) | Unique provider/account identifier |
prefix |
str |
(required) | URL routing prefix. Multiple configs can share a prefix for multi-account. |
api_type |
str |
(required) | One of: openai_compat, anthropic, gemini, opencode_free |
base_url |
str |
(required) | Upstream API base URL |
api_key |
str \| None |
None |
API key for the upstream provider |
models |
list[str] |
[] |
Supported model suffixes (used without the prefix) |
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]
Model naming
Clients reference models as {prefix}/{model} — for example openai/gpt-4o.
The models list contains suffixes only (gpt-4o), not the full prefixed name.
See Providers for per-provider setup guides and the full list of
supported api_type values.
combos¶
Named ordered model sequences. A client sends the combo name as the model value
and Janus tries each model in order with all its accounts.
| Field | Type | Default | Description |
|---|---|---|---|
name |
str |
(required) | Combo name (client sends this as the model value) |
models |
list[str] |
(required) | Ordered list of {prefix}/{model} strings to try |
combos:
- name: best-effort
models:
- anthropic/claude-sonnet-4-20250514
- openai/gpt-4o
- gemini/gemini-2.5-pro
In this example, Janus first tries claude-sonnet-4-20250514 on every Anthropic
account. If all are rate-limited or down, it falls back to gpt-4o, then
gemini-2.5-pro.
api_keys¶
A list of static API key strings. These are checked at auth time in addition to DB-managed keys created via the CLI or dashboard.
api_keys:
- sk-janus-my-static-key
- sk-janus-another-key
When server.require_api_key is true, requests must include one of these keys
(or a DB-managed key) in the Authorization: Bearer <key> header. See
API Reference - Authentication for details.
Tip
DB-managed keys (created with janus keys create) are the recommended way to
manage keys — they support per-key usage tracking and budgets. Static keys in
the config are simpler for single-user setups.
token_savers¶
Configures token-saving transformations applied to each request after parsing and before provider routing. Savers stack — all enabled ones run in sequence.
| Saver | Field | Type | Default | Description |
|---|---|---|---|---|
rtk |
enabled |
bool |
true |
Compresses tool output (git diffs, file listings, logs) |
caveman |
enabled |
bool |
false |
Prepends a terse-output system prompt |
ponytail |
enabled |
bool |
false |
Prepends a lazy-dev system prompt |
ponytail |
level |
str |
"full" |
Ponytail verbosity: "lite", "full", or "ultra" |
token_savers:
rtk:
enabled: true # default — compresses tool_result content
caveman:
enabled: false
ponytail:
enabled: false
level: full # lite | full | ultra
Fail-safe
Savers are fail-safe — if a saver throws an exception it is caught and logged, never breaking the request.
pricing¶
User pricing overrides. Keys are model names; values are dicts with per-million-token
rates (in USD). On first startup, this section seeds the pricing_overrides table.
After that, manage overrides via the dashboard Pricing page or CLI — the YAML section
is not re-read on restart.
| Rate Key | Description |
|---|---|
input_per_mtok |
$ per million input tokens |
output_per_mtok |
$ per million output tokens |
cache_creation_per_mtok |
$ per million cache-creation tokens |
cache_read_per_mtok |
$ per million cache-read tokens |
pricing:
my-custom-model:
input_per_mtok: 1.0
output_per_mtok: 3.0
cache_creation_per_mtok: 0.0
cache_read_per_mtok: 0.25
gpt-4o:
input_per_mtok: 2.5
output_per_mtok: 10.0
cache_creation_per_mtok: 0.0
cache_read_per_mtok: 1.25
Unknown models
Models not found in builtin pricing or overrides cost $0.0 — this is not an
error. Add an override if you need cost tracking for an unlisted model.
Pricing uses progressive prefix matching: claude-sonnet-4-20250514 will match
a key claude-sonnet-4 if the full name isn't present.
Full Example¶
server:
port: 20128
host: 127.0.0.1
require_api_key: false
data_dir: ~/.janus
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]
- id: gemini
prefix: gemini
api_type: gemini
base_url: https://generativelanguage.googleapis.com
api_key: ${GEMINI_API_KEY}
models: [gemini-2.5-pro, gemini-2.0-flash]
combos:
- name: best-effort
models:
- anthropic/claude-sonnet-4-20250514
- openai/gpt-4o
- gemini/gemini-2.5-pro
api_keys:
- sk-janus-my-static-key
token_savers:
rtk:
enabled: true
caveman:
enabled: false
ponytail:
enabled: false
level: full
pricing:
my-custom-model:
input_per_mtok: 1.0
output_per_mtok: 3.0
cache_creation_per_mtok: 0.0
cache_read_per_mtok: 0.25