Your friendly, blazing-fast, self-hostable AI gateway
See the code
Your friendly, blazing-fast, self-hostable AI gateway.
You use AI coding tools — Claude Code, Codex, Cursor, Cline, or honestly anything that talks to OpenAI, Anthropic or Gemini. And you know the headaches: a drawer full of API keys, rate limits that hit at the worst time, and a token bill that keeps creeping up.
KeiRouter is the smart middleman that makes all of that someone else's problem. Point your tools at one local endpoint and let it handle the boring stuff — routing each request to the right model, failing over the moment a provider taps out, caching repeat questions, and squeezing oversized prompts down before they ever cost you a token.
Oh, and it's written in Go. So it's light on memory, boots instantly, ships as one binary plus a static dashboard, and the dashboard is actually nice to look at. ✨
Heads up: KeiRouter is under active development. Check the CHANGELOG for what landed recently, and peek at Architecture to see how a request flows.
Routing & reliability
Cost control
Safety & governance
Operations & UX
![]() Chains — build fallback routes and preview them live | ![]() Providers — each provider asks for exactly the credentials it needs |
![]() Provider health — uptime, fallbacks and p95 latency per upstream | ![]() Guardrails — scoped policies with a dry-run tester |
![]() Audit log — hash-chained sign-ins and config changes | ![]() Endpoints — base URL, quick-start snippets and API reference |
![]() Token saving — toggle each saver independently | ![]() CLI tools — detects your coding tools and configures them in one click |
Three steps on any OS: install → start → open the dashboard. Prebuilt binaries mean no Go, no Node, and no config file.
| Your machine | Install with | Run in the background with |
|---|---|---|
| 🍎 macOS | Homebrew | brew services start keirouter |
| 🪟 Windows 10/11 | PowerShell one-liner | keirouter tray |
| 🐧 Linux (Ubuntu, Debian, Fedora, …) | Shell one-liner | --service (systemd) |
| 🐳 Server / VPS | Docker Compose | Docker itself |
brew tap mydisha/keirouter https://github.com/mydisha/keirouter
brew install keirouter
keirouter start
Your browser opens the dashboard. Want it running all the time, even after a reboot?
brew services start keirouter # starts now and on every login
Prefer a menu-bar icon you can quit any time? Run keirouter tray instead.
curl -fsSL https://raw.githubusercontent.com/mydisha/keirouter/main/scripts/install.sh | bash
It downloads the right build for Apple Silicon or Intel, checks its checksum, and installs keirouter to /usr/local/bin (it may ask for your password once).
Open PowerShell and run:
irm https://raw.githubusercontent.com/mydisha/keirouter/main/scripts/install.ps1 | iex
Then open a new terminal (so the PATH change applies) and start it:
keirouter start
Want it in the background? keirouter tray puts an icon in the taskbar tray. To start it automatically when you log in, add a shortcut to your Startup folder:
$s = (New-Object -ComObject WScript.Shell).CreateShortcut("$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\KeiRouter.lnk"); $s.TargetPath = (Get-Command keirouter).Source; $s.Arguments = "tray --no-browser"; $s.WindowStyle = 7; $s.Save()
Pin a version or change the location with
$env:KEIROUTER_VERSION/$env:KEIROUTER_DIRbefore running the installer.
curl -fsSL https://raw.githubusercontent.com/mydisha/keirouter/main/scripts/install.sh | bash
keirouter start
Works on amd64 and arm64 (including 64-bit Raspberry Pi OS). All it needs is curl (or wget) and tar, which every Ubuntu already has.
To keep KeiRouter running in the background and start it on boot, install it as a systemd service instead:
curl -fsSL https://raw.githubusercontent.com/mydisha/keirouter/main/scripts/install.sh | bash -s -- --service
systemctl --user status keirouter # is it up?
journalctl --user -u keirouter -f # follow the logs
systemctl --user restart keirouter # restart after a config change
On a desktop with a system tray, keirouter tray works too.
The easiest way to run KeiRouter on a server:
curl -fsSL https://raw.githubusercontent.com/mydisha/keirouter/main/scripts/install.sh | bash -s -- --docker
Or by hand with Docker Compose:
git clone https://github.com/mydisha/keirouter.git
cd keirouter
cp .env.example .env # set KEIROUTER_MASTER_KEY (openssl rand -base64 32)
docker compose up -d --build
Want PostgreSQL instead of SQLite? Layer on compose.postgres.yaml. VPS, PostgreSQL, Coolify and reverse-proxy notes live in deploy/README.md.
Installed the binary on a server instead? It only listens on
127.0.0.1by default, which keeps the admin dashboard private. Reach it through an SSH tunnel:ssh -L 20180:localhost:20180 you@your-server, then open http://localhost:20180 on your own machine.
Open http://localhost:20180 (keirouter start opens it for you) and sign in with the default password keirouter. It'll nudge you to change that password right away (please do 🙏).
Then grab an API key from the API keys page, or mint one from the terminal:
keirouter bootstrap # prints a kr_ key, once
Remote dashboard access: Plain HTTP works for initial LAN/VPS access, but HTTPS is strongly recommended for production. Behind a TLS-terminating reverse proxy, make sure it overwrites client-supplied
X-Forwarded-ProtoandForwardedheaders so KeiRouter can apply theSecuresession-cookie flag correctly. See the deployment guide.
| Command | What it does |
|---|---|
keirouter start | Start the server on :20180 and open the dashboard |
keirouter start --no-browser | Same, without opening a browser |
keirouter tray | Run in the background with a tray / menu-bar icon |
keirouter status | Is it running, and on which URL? |
keirouter bootstrap | Create an API key and print it once |
keirouter version | Print the version |
| Installed with | Update with |
|---|---|
| Homebrew | brew upgrade keirouter (then brew services restart keirouter if you use it) |
| Shell installer (macOS / Linux) | Run the same one-liner again (then systemctl --user restart keirouter if you used --service) |
| PowerShell installer | Run the same one-liner again |
| Docker | Run the --docker one-liner again, or git pull && docker compose up -d --build |
Settings → System also tells you when a new release is out.
Your data (database, master key, settings) lives in ~/.keirouter (%USERPROFILE%\.keirouter on Windows). Uninstalling never touches it; delete that folder yourself if you want a clean slate.
Homebrew
brew services stop keirouter
brew uninstall keirouter
Shell installer (macOS / Linux)
systemctl --user disable --now keirouter 2>/dev/null; rm -f ~/.config/systemd/user/keirouter.service
sudo rm -rf /usr/local/bin/keirouter /usr/local/share/keirouter
Windows (PowerShell)
Remove-Item -Recurse -Force "$env:LOCALAPPDATA\KeiRouter", "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\KeiRouter.lnk" -ErrorAction SilentlyContinue
Then remove %LOCALAPPDATA%\KeiRouter from your user PATH (Settings → System → About → Advanced system settings → Environment Variables).
Only needed if you want to hack on KeiRouter or run the latest main. You'll need Go 1.26+ and Node.js 22 (20.19+ works too):
curl -fsSL https://raw.githubusercontent.com/mydisha/keirouter/main/scripts/quickstart.sh | bash
It clones the repo to ~/keirouter, installs everything, and starts the backend on :20180 and a hot-reloading dashboard on http://localhost:5180. Already cloned it? Just make setup. To build and install a release-style binary from source instead, use install.sh --source. More in Hack on it.
In your AI tool of choice, set:
http://localhost:20180/v1 for OpenAI-style clients, or http://localhost:20180 for Anthropic-style clients like Claude Codekr_ key from the dashboard or keirouter bootstrap (sent as Authorization: Bearer … or x-api-key)openai/gpt-4o, or the name of a fallback chain you cooked upThat's it. You're routing. The Endpoints page has copy-paste snippets for cURL, Python, Node.js, Claude Code and plain environment variables, and the CLI tools page can write the config for you.
Chat is just the start. KeiRouter proxies the whole buffet behind one key:
| Capability | Endpoint | Dialect |
|---|---|---|
| Chat completions | POST /v1/chat/completions | OpenAI |
| Responses API (Codex & friends) | POST /v1/responses | OpenAI |
| Messages | POST /v1/messages | Anthropic |
| Token counting (answered locally) | POST /v1/messages/count_tokens | Anthropic |
| Native Gemini | POST /v1beta/models/{model}:generateContent | Gemini |
| Embeddings | POST /v1/embeddings | OpenAI |
| Image generation | POST /v1/images/generations | OpenAI |
| Image understanding | POST /v1/images/understanding | — |
| Video generation (async) | POST /v1/videos/generations · GET /v1/videos/{id} | — |
| Text-to-speech | POST /v1/audio/speech | OpenAI |
| Speech-to-text | POST /v1/audio/transcriptions | OpenAI |
| Web search | POST /v1/search | — |
| Web fetch | POST /v1/web/fetch | — |
| Model discovery | GET /v1/models · /v1/models/info · /v1/models/{kind} | OpenAI |
| Your own key's usage | GET /v1/keys/me/usage | — |
GET /healthz is the liveness probe, and accidental /v1/v1/... paths are folded back to /v1/... so a misconfigured base URL still works.
Every request runs through a deterministic token-saving pipeline before it gets translated to the provider's format — so the savings work the same no matter which provider you land on. There are five savers, each independently toggleable from Settings → Token saving, and a dashboard that shows you exactly how much you clawed back.
| Saver | Side | The pitch |
|---|---|---|
| RTK / Slimmer | Input | Shrinks chunky tool output (diffs, greps, listings, build logs, source files) locally before it ever leaves your machine. |
| Headroom | Input | Routes request messages through an external Headroom proxy for deeper compression. Fail-open — if the proxy sneezes, your request sails through untouched. Also sniffs out "phantom savings" so only real wins get counted. |
| Terse | Output | Drops a concise-output directive so the model skips the small talk and gives you the goods. |
| Caveman | Output | Terse's stronger cousin (Wenyan / 文言文 levels included) — trims output tokens by 65–75%. |
| Ponytail | Output | Injects a "lazy senior dev" system prompt (lite / full / ultra) that nudges the model toward the smallest possible change. Stacks on top of Terse or Caveman. |
Terse and Caveman both inject a system directive, so they're mutually exclusive — pick one. Ponytail happily layers on top of either.
Pipeline order: normalizer → RTK → Headroom → Skills → Terse / Caveman → Ponytail → provider translation.
The Usage page breaks savings down per rule, and you can export a shareable savings card.
Headroom is its own open-source compression proxy. KeiRouter just calls its /v1/compress endpoint, so you spin it up locally first. One gotcha worth shouting about: the headroom CLI lives in the Python package — the npm package is a library only, so npm install -g headroom-ai will leave you staring at command not found. Don't say we didn't warn you. 😉
# The clean way: pipx isolates the CLI and sorts out your PATH (needs Python 3.10+)
pipx install "headroom-ai[all]"
pipx ensurepath # puts headroom on your PATH — then restart your shell
headroom proxy --port 8787 # start the proxy
headroom doctor # make sure it's actually happy
On Ubuntu and pip is fighting you (PEP 668 blocks global installs)? Go --user and make sure ~/.local/bin is on your PATH:
pip install --user "headroom-ai[all]"
export PATH="$HOME/.local/bin:$PATH" # drop this in ~/.zshrc or ~/.bashrc
Then over in Settings → Token saving → Headroom proxy: flip it on, set the Proxy URL to http://localhost:8787, and hit Test connection to confirm the handshake. Green check? You're golden.
Why bet on one model when you can have a backup plan? Build a chain on the Chains page. Say you name one coding:
openai/gpt-4o — your first pickdeepseek/deepseek-chat — steps in if the first one rate-limits or face-plantsThen just set your app's model to chain:coding (or plain coding) and let KeiRouter sweat the failover.
Each chain picks a routing strategy:
| Strategy | Behaviour |
|---|---|
| Priority | Tries models in the order listed |
| Round robin | Spreads requests across the steps |
| Latency | Fastest measured step first |
| Cost | Cheapest step first |
Add an optional final fallback that's tried after every step fails, and watch the live route preview while you edit. Inside a provider, multiple accounts can also rotate round-robin or fill in priority order (Settings → Routing).
Off by default; turn it on in your config:
cache:
enabled: true
backend: memory # or "redis" (set redis_url) to persist and share across replicas
ttl: 1h
embedding_provider: hash # exact-match, zero dependencies
# embedding_provider: api # true semantic matching via any OpenAI-compatible embeddings API
similarity_threshold: 0.95
With embedding_provider: api, paraphrases like "How do Go channels work?" and "Explain Go channels" hit the same entry. Cache savings show up on the Overview page. See config.example.yaml for every knob.
Copy-pasting API keys gets old fast. On the Providers page, hit Connect provider and pick one from Sign in with a plan — sign in once, and KeiRouter refreshes your tokens in the background (proactively, before they expire).
| Provider | Flow |
|---|---|
| Claude Code | Anthropic OAuth (PKCE) |
| OpenAI Codex | OpenAI OAuth (PKCE, local callback) |
| GitHub Copilot | GitHub device flow |
| Gemini CLI · Antigravity | Google OAuth |
| Qwen Code | Device flow (PKCE) |
| Kimi Coding | Device flow |
| Grok CLI (Grok Build) | Device flow |
| Kiro | AWS Builder ID device flow, IAM Identity Center, refresh-token import, or API key |
| Kilo Code | Device auth |
| Qoder | Device-token flow or Personal Access Token |
| CodeBuddy (Tencent) | Browser-poll flow |
| Kimchi | Browser callback |
| Cline · ClinePass · iFlow | OAuth |
| Cursor · Command Code | Token import |
Several of these are marked Unofficial in the dashboard: they reuse a client's own sign-in rather than a public API, so check each provider's terms before you lean on them.
The Skills page has two halves:
claude-*), or leave it empty to apply everywhere. Skills are prepended as a stable prefix, so provider prompt caches stay warm, and they're never injected twice across retries or fallbacks. Mark a skill optional and clients can opt out with the X-KeiRouter-Skip-Skills: true header.SKILL.md docs in skills/ that teach Claude, Cursor, ChatGPT or any other agent how to call your KeiRouter endpoint (chat, embeddings, images, TTS, STT, web search, web fetch). Copy the URL or the agent prompt straight from the dashboard.Plans are reusable budget policies you can stamp onto any API key — write the rules once, apply them everywhere. Each plan covers:
0 = unlimited)daily, weekly, monthly, or totalclaude-*, gpt-4* (empty = everything's fair game)Every tenant gets a default plan out of the box. Manage them on the Plans & budgets page and assign them on each API key.
Keep your gateway (and your upstream quotas) from getting hammered with per-key RPM, TPM, and concurrency caps. Limits are off by default; here's an example for a single-instance setup:
limits:
enabled: true
backend: memory # one instance; use "redis" for several (see below)
default_rpm: 600
default_tpm: 200000
default_concurrency: 50
window: 1m
cleanup_interval: 1m
Those defaults only apply to keys without a plan. The moment a key has one, the plan's rpm_limit, tpm_limit, and concurrency_limit take over (0 = unlimited). You can also flip enforcement at runtime from Settings → Routing.
TPM is reserved up front and then settled against the usage the provider actually reports (reconcile_tokens), so cache hits, blocked requests and local failures hand their tokens back. output_reserve_cap stops clients that always send a huge max_tokens from draining TPM up front, and concurrency denials include a Retry-After hint.
Separately, per-IP abuse guards (auth_failures_per_minute, login_attempts_per_minute) stay on even when per-key limits are off.
Running several KeiRouter replicas behind a load balancer? Point them at the same Postgres and Redis and set backend: redis. Every replica then enforces one RPM/TPM window, one concurrency pool per key and one budget counter, instead of each replica handing out its own allowance. SQLite is single-instance only, so HA needs Postgres.
database:
driver: postgres
dsn: postgres://keirouter:secret@postgres:5432/keirouter?sslmode=disable
limits:
enabled: true
backend: redis
redis_url: redis://redis:6379/0
fail_mode: local # local | open | closed while redis is unreachable
How it stays fast:
SUM per request. Spend lives in shared Redis counters seeded from the usage table and re-seeded every budget_counter_ttl. Increments are merged in the background, so Redis writes scale with the number of active budgets, not with traffic.lease_ttl instead of leaking them forever.fail_mode, and budgets fall back to the usage table.Budget edits are broadcast over Redis pub/sub, so every replica drops its cached definitions immediately. GET /api/settings/limits/status shows the backend, its health, live leases and fallback counts.
Not shared across replicas yet:
A built-in content-safety layer runs detectors against every request and response. Policies stack global → provider → model → chain → API key and merge at request time, so the most specific rule wins.
| Detector | Catches | Default engine | Optional engine |
|---|---|---|---|
| PII | Email, phone, credit card, IBAN, IP, URL, NIK / NPWP / Indonesian passport, plus KK / BPJS when a context word such as kartu keluarga is nearby, US SSN, API keys, passwords, private keys | Native Go (Presidio-compatible, checksum-validated) | Microsoft Presidio HTTP sidecar |
| Prompt Injection | Instruction overrides, jailbreak personas, smuggled chat-template tokens, prompt leaks, encoded payloads, indirect injection in tool results (id + en) | Signatures + typo-tolerant intent rules | Prompt Guard 2 classifier · embedding match · LLM judge |
| Topics | Allow-list / block-list of topics | Whole-word keyword match with inflections | LLM (context-aware, lets small talk through) · Embedding similarity |
| Toxicity | Profanity, hate, harassment, violence, sexual, self-harm (id + en) | Native weighted lexicon | OpenAI Moderation API |
| Bias (outbound, experimental) | Political, gender, ethnic, religious stereotyping in responses | Native bilingual lexicon | — |
log_only, warn, mask (rewrite it), or block (refuse it). Strictest action across detectors wins.redact, replace, mask, hash, anonymize, or block.1gn0r3), censoring (f*ck) and spaced-out letters don't slip through.keirouter_guardrail_decisions_total and keirouter_guardrail_eval_seconds at /metrics.KEIROUTER_GUARDRAILS__AUDIT_RETENTION_DAYS controls retention; the test endpoint is capped at 10 req/min.Starter templates ship in the Start from template picker (Indonesia PII · Strict safety · Compliance audit · Public chatbot · Coding agent · Alerts-only), and you can export/import policies as a JSON bundle.
Want NER-based PII detection (PERSON, LOCATION, full multilingual)? Spin up the optional Presidio sidecar:
docker compose -f compose.yaml -f compose.postgres.yaml -f compose.presidio.yaml up -d
Then flip any PII policy's engine to presidio in the dashboard.
Want a trained classifier on top of the injection signatures? Run the Prompt Guard sidecar (Llama Prompt Guard 2, multilingual; the model is gated, so set HF_TOKEN after accepting its license, or set PROMPTGUARD_MODEL=protectai/deberta-v3-base-prompt-injection-v2):
docker compose -f compose.yaml -f compose.promptguard.yaml up -d
Then turn on ML classifier in a prompt-injection policy. Any server speaking the Hugging Face text-classification contract works too: point KEIROUTER_GUARDRAILS__INJECTION__CLASSIFIER_URL at it. The LLM judge layer needs no setup: pick any model, alias or chain KeiRouter routes to.
| Page | What you get |
|---|---|
| Overview | Requests, success rate, latency and spend vs. the previous period, top models, provider health, savings |
| Usage | Cost, tokens and latency (p50 / p95 / TTFT) by provider, model, chain and individual request |
| Console | Live gateway activity: one row per request with a timeline of its fallback attempts and rate-limit waits, plus background warnings (OAuth refresh, health, tunnels) in plain language. Filter with key:prod status:5xx ms>5s is:fallback and friends, then download what you found |
| Guardrails | Block / mask / warn rates, detector breakdowns, overhead and judge cost (see Guardrails) |
| Health | Per-provider and per-model uptime, fallbacks, p95 latency and synthetic probes; 24h / 7d history from persisted snapshots |
| Quota | Upstream quota windows and local usage for every account, with auto-refresh |
| System | CPU, memory, disk, goroutines and KeiRouter's own RSS / heap over time |
Prometheus metrics live at /metrics (loopback-only by default): requests, latency, TTFT, tokens, cost, fallbacks, cache hits, upstream errors, saver activity and guardrail decisions.
Every dashboard sign-in and configuration change is recorded on the Audit log page: who did it (a stable per-session id), the action (keys.create, settings.endpoint, auth.login, ...), the resource, the outcome, client IP, request id and W3C trace id. Request bodies are never stored, only the names of the fields that were sent. Reading a full config export or a database backup is audited too.
GET /api/audit/verify) reports rows that were edited, inserted or deleted from the middle of a chain. Rows cut off the newest end of a chain leave no trace in the chain itself, so ship events to an OTLP collector as well if you need that covered.audit.otlp.endpoint (or the standard OTEL_EXPORTER_OTLP_LOGS_ENDPOINT / OTEL_EXPORTER_OTLP_ENDPOINT; OTLP over HTTP) and each event is also shipped as an OTLP log record, using semantic-convention attributes such as user.id, client.address, http.route and event.name=keirouter.audit.<action>. Any OTel Collector, Grafana Loki, Datadog, Elastic or Splunk pipeline can ingest it.GET /api/audit/export) downloads the filtered log as JSON Lines.audit.retention_days (default 365) prunes old rows.audit:
otlp:
endpoint: http://otel-collector:4318
headers: { Authorization: "Bearer <token>" }
include_requests: true # also ship every gateway request (see below)
Admin events cover who changed the gateway. To also see who used it, set audit.otlp.include_requests: true (or KEIROUTER_AUDIT__OTLP__INCLUDE_REQUESTS=true). Every request is then shipped to the same collector as a keirouter.request log record, using the OpenTelemetry GenAI conventions where they exist:
| Attribute | Example |
|---|---|
keirouter.api_key.id, keirouter.client, keirouter.tenant.id | which key and tool made the call |
gen_ai.provider.name, gen_ai.response.model, keirouter.chain.id, keirouter.fallback.count | where it was routed |
gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, keirouter.cost.usd | what it cost |
keirouter.request.status, error.type, keirouter.latency.ms, keirouter.ttft.ms | how it went |
keirouter.guardrail.action, keirouter.guardrail.detector | what guardrails did |
Prompts and responses are never exported. Records use their own scope (github.com/mydisha/keirouter/requests) and their own queue, so a traffic spike can't crowd admin events out; GET /api/audit/status reports both exporters. The same data stays queryable on the Usage page.
Moving in from another router? Settings → Import & export takes:
Pick Merge, Overwrite, or Wipe & replace. You can also download local or passphrase-encrypted portable backups and restore them later.
Rebrand the admin dashboard and the public Usage Portal from Settings → Branding:
kei (default), sage-terra, ocean, midnight, forest, rose, lavender, mono, sunset, arctic, cherryLight, dark, and system themes are a click away in the account menu.
The CLI tools page detects which coding tools are installed, shows whether each one already points at KeiRouter, and can write (or remove) the config in one click — pick an endpoint, an API key and an optional default model. Prefer doing it by hand? Every tool has a manual snippet too.
Claude Code · Codex CLI · Cline / Roo · GitHub Copilot · Factory Droid · OpenClaw · OpenCode · Kilo Code · Hermes Agent · DeepSeek TUI · jcode
Using OpenCode? The @keirouter/opencode-plugin discovers your models straight from GET /v1/models.
A dedicated, no-admin-required view at /portal where teammates keep an eye on their own usage — quota and spend, token usage over time, compression savings, and plan limits. Sign in with an API key, or share a read-only portal ID (key_…) so nobody has to hand out a secret. Signed in with a key, there's also a Playground tab for quick chats. No keys to the kingdom required.
🧠 LLM / Chat
| Category | Providers |
|---|---|
| Major Cloud | OpenAI, Anthropic, Google Gemini, Azure OpenAI (API key or Entra ID), Vercel AI Gateway |
| Enterprise Cloud | AWS Bedrock, AWS SageMaker, Azure AI Foundry, IBM watsonx.ai, Oracle Cloud Generative AI, SAP Generative AI Hub, Databricks, Snowflake Cortex |
| Free / Free Tier | OpenRouter, NVIDIA NIM, Ollama Cloud, Cloudflare Workers AI, BytePlus ModelArk, MiMo Free, Kiro |
| Sign in with a plan | Claude Code, OpenAI Codex, GitHub Copilot, Gemini CLI, Antigravity, Cursor IDE, Cline, ClinePass, Kilo Code, Qwen Code, Qoder, iFlow, Kimi Coding, Kimchi, CodeBuddy (Tencent), Grok CLI, Command Code |
| Model labs | xAI (Grok), Mistral (+ Codestral), Cohere, AI21 Labs, Perplexity (+ Agent / Responses), Meta (Llama API, Model API), Amazon Nova, Inception Labs, v0, Cognition, Sarvam AI, Maritaca AI, GigaChat (Sber) |
| China / Asia | DeepSeek, Alibaba (Model Studio / DashScope, Coding, Intl), Moonshot AI & Kimi, Z.AI & GLM Coding (+ China), MiniMax Coding (+ China), Volcengine Ark, Xiaomi MiMo (+ Token Plan), SiliconFlow, ModelScope, Tencent TokenHub |
| Fast inference | Groq, Cerebras, SambaNova, DeepInfra, FriendliAI |
| Inference clouds | Together AI, Fireworks AI, Nebius AI, Hyperbolic, Featherless, Nscale, Baseten, Nous Research, Scaleway AI, Lambda, Replicate, Hugging Face Inference Providers, OVHcloud, DigitalOcean Gradient AI, Heroku, W&B Inference, Clarifai, Crusoe Cloud, GMI Cloud, Parasail, DataRobot, Venice AI, and more |
| Aggregators & gateways | OpenCode, AgentRouter, GitHub Models, Helicone AI Gateway, CometAPI, AIHubMix, Eden AI, NanoGPT, Poe |
| Emerging | Blackbox AI, Chutes AI, Galadriel, Cortecs, LibertAI, Synthetic, and many more |
| Self-hosted | vLLM, Ollama, LM Studio, llamafile, Docker Model Runner, Xinference, Lemonade, Text Generation WebUI, NVIDIA Triton, RAGFlow |
| Custom | Any OpenAI- or Anthropic-compatible endpoint (self-hosted, proxy, etc.), with your own model registry and prices |
Self-hosted on the same machine or LAN? KeiRouter's SSRF guard blocks loopback and private addresses by default. Set
KEIROUTER_SECURITY__ALLOW_PRIVATE_BASE_URL=true(orsecurity.allow_private_base_url: true) to reach them.
🏢 Enterprise clouds connect with their own credentials, not just an API key. The connect form asks for exactly what each one needs:
| Provider | Authentication |
|---|---|
| AWS Bedrock | Bedrock API key, IAM access key, or the server's environment / instance role, with optional STS assume-role. Converse API for every model family, native Anthropic via invoke/, embeddings, and optional Bedrock Guardrails |
| AWS SageMaker | IAM access key or instance role; the model is your endpoint name (TGI, LMI, vLLM, SGLang), with optional inference components |
| Azure OpenAI · Azure AI Foundry | API key or a Microsoft Entra ID service principal |
| IBM watsonx.ai | IBM Cloud IAM, Cloud Pak for Data, or a bearer token; project or deployment space |
| Oracle Cloud Generative AI | Signed requests (tenancy, user, fingerprint, PEM key); on-demand or dedicated clusters |
| SAP Generative AI Hub | AI Core service key or client credentials; deployment discovered for you |
| Databricks | Personal access token or OAuth machine-to-machine; serving endpoints discovered live |
| Snowflake Cortex | Programmatic access token or key-pair JWT |
Exchanged tokens are cached and refreshed automatically, and every secret field is sealed with the same envelope encryption as API keys.
🎨 Media & Search
| Type | Providers |
|---|---|
| Image Generation | OpenAI, xAI, Venice AI, Recraft |
| Video Generation | xAI |
| Text-to-Speech | OpenAI |
| Speech-to-Text | OpenAI Whisper, Groq Whisper |
| Embeddings | OpenAI, Mistral, Cohere, AWS Bedrock, Azure AI Foundry, Databricks, GigaChat, GitHub Models, Cloudflare Workers AI, Together AI, Fireworks AI, Nebius, NVIDIA NIM, OpenRouter, Voyage AI, Jina AI, Venice AI, vLLM |
| Web Search | Tavily, Exa, Serper, Brave Search |
| Web Fetch | Tavily, Exa, Firecrawl |
Image, speech, transcription, video and embedding requests use the OpenAI wire format (/images/generations, /audio/speech, /audio/transcriptions, …), so any upstream or self-hosted server that speaks it works as a custom provider. The dashboard also lists media services with their own APIs (ElevenLabs, Deepgram, AssemblyAI, Stability AI, Fal.ai, Black Forest Labs, Google PSE, …) marked Coming soon: native adapters for those are on the way, and until then they only work behind an OpenAI-compatible proxy added as a custom provider.
Browse the full catalog (and filter by capability) under Connect provider in the dashboard.
Out of the box, KeiRouter runs on an embedded SQLite database — zero config, zero fuss. Running it for a team? Switch to PostgreSQL: copy config.example.yaml and run with -config, or use environment variables like KEIROUTER_SERVER__PORT=8080 (double underscore = nesting). The master key is KEIROUTER_SECURITY__MASTER_KEY (Compose maps KEIROUTER_MASTER_KEY to it for you). Docker/Coolify examples are in deploy/README.md.
Most day-to-day behaviour — token savers, routing, outbound proxy, branding, backups, update checks — lives in the dashboard under Settings, and changes save automatically.
Curious what happens after you hit send? Here's the life of a request:
/api/*) only answers on loopback by default (security.bind_loopback_only). Exposing it? Put it behind a reverse proxy and set a stable master key.Found a security issue? Please follow SECURITY.md instead of opening a public issue. 🙏
make setup # first time: installs deps + starts backend (:20180) and dashboard (:5180)
make dev # after that: just start the servers
make test # run the backend test suite
make build # build the backend binary + frontend assets
PRs and ideas are always welcome — start with CONTRIBUTING.md.
MIT — see LICENSE. Go build something cool. 🛠️
Your friendly, blazing-fast, self-hostable AI gateway
See the code
Your friendly, blazing-fast, self-hostable AI gateway.
You use AI coding tools — Claude Code, Codex, Cursor, Cline, or honestly anything that talks to OpenAI, Anthropic or Gemini. And you know the headaches: a drawer full of API keys, rate limits that hit at the worst time, and a token bill that keeps creeping up.
KeiRouter is the smart middleman that makes all of that someone else's problem. Point your tools at one local endpoint and let it handle the boring stuff — routing each request to the right model, failing over the moment a provider taps out, caching repeat questions, and squeezing oversized prompts down before they ever cost you a token.
Oh, and it's written in Go. So it's light on memory, boots instantly, ships as one binary plus a static dashboard, and the dashboard is actually nice to look at. ✨
Heads up: KeiRouter is under active development. Check the CHANGELOG for what landed recently, and peek at Architecture to see how a request flows.
Routing & reliability
Cost control
Safety & governance
Operations & UX
![]() Chains — build fallback routes and preview them live | ![]() Providers — each provider asks for exactly the credentials it needs |
![]() Provider health — uptime, fallbacks and p95 latency per upstream | ![]() Guardrails — scoped policies with a dry-run tester |
![]() Audit log — hash-chained sign-ins and config changes | ![]() Endpoints — base URL, quick-start snippets and API reference |
![]() Token saving — toggle each saver independently | ![]() CLI tools — detects your coding tools and configures them in one click |
Three steps on any OS: install → start → open the dashboard. Prebuilt binaries mean no Go, no Node, and no config file.
| Your machine | Install with | Run in the background with |
|---|---|---|
| 🍎 macOS | Homebrew | brew services start keirouter |
| 🪟 Windows 10/11 | PowerShell one-liner | keirouter tray |
| 🐧 Linux (Ubuntu, Debian, Fedora, …) | Shell one-liner | --service (systemd) |
| 🐳 Server / VPS | Docker Compose | Docker itself |
brew tap mydisha/keirouter https://github.com/mydisha/keirouter
brew install keirouter
keirouter start
Your browser opens the dashboard. Want it running all the time, even after a reboot?
brew services start keirouter # starts now and on every login
Prefer a menu-bar icon you can quit any time? Run keirouter tray instead.
curl -fsSL https://raw.githubusercontent.com/mydisha/keirouter/main/scripts/install.sh | bash
It downloads the right build for Apple Silicon or Intel, checks its checksum, and installs keirouter to /usr/local/bin (it may ask for your password once).
Open PowerShell and run:
irm https://raw.githubusercontent.com/mydisha/keirouter/main/scripts/install.ps1 | iex
Then open a new terminal (so the PATH change applies) and start it:
keirouter start
Want it in the background? keirouter tray puts an icon in the taskbar tray. To start it automatically when you log in, add a shortcut to your Startup folder:
$s = (New-Object -ComObject WScript.Shell).CreateShortcut("$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\KeiRouter.lnk"); $s.TargetPath = (Get-Command keirouter).Source; $s.Arguments = "tray --no-browser"; $s.WindowStyle = 7; $s.Save()
Pin a version or change the location with
$env:KEIROUTER_VERSION/$env:KEIROUTER_DIRbefore running the installer.
curl -fsSL https://raw.githubusercontent.com/mydisha/keirouter/main/scripts/install.sh | bash
keirouter start
Works on amd64 and arm64 (including 64-bit Raspberry Pi OS). All it needs is curl (or wget) and tar, which every Ubuntu already has.
To keep KeiRouter running in the background and start it on boot, install it as a systemd service instead:
curl -fsSL https://raw.githubusercontent.com/mydisha/keirouter/main/scripts/install.sh | bash -s -- --service
systemctl --user status keirouter # is it up?
journalctl --user -u keirouter -f # follow the logs
systemctl --user restart keirouter # restart after a config change
On a desktop with a system tray, keirouter tray works too.
The easiest way to run KeiRouter on a server:
curl -fsSL https://raw.githubusercontent.com/mydisha/keirouter/main/scripts/install.sh | bash -s -- --docker
Or by hand with Docker Compose:
git clone https://github.com/mydisha/keirouter.git
cd keirouter
cp .env.example .env # set KEIROUTER_MASTER_KEY (openssl rand -base64 32)
docker compose up -d --build
Want PostgreSQL instead of SQLite? Layer on compose.postgres.yaml. VPS, PostgreSQL, Coolify and reverse-proxy notes live in deploy/README.md.
Installed the binary on a server instead? It only listens on
127.0.0.1by default, which keeps the admin dashboard private. Reach it through an SSH tunnel:ssh -L 20180:localhost:20180 you@your-server, then open http://localhost:20180 on your own machine.
Open http://localhost:20180 (keirouter start opens it for you) and sign in with the default password keirouter. It'll nudge you to change that password right away (please do 🙏).
Then grab an API key from the API keys page, or mint one from the terminal:
keirouter bootstrap # prints a kr_ key, once
Remote dashboard access: Plain HTTP works for initial LAN/VPS access, but HTTPS is strongly recommended for production. Behind a TLS-terminating reverse proxy, make sure it overwrites client-supplied
X-Forwarded-ProtoandForwardedheaders so KeiRouter can apply theSecuresession-cookie flag correctly. See the deployment guide.
| Command | What it does |
|---|---|
keirouter start | Start the server on :20180 and open the dashboard |
keirouter start --no-browser | Same, without opening a browser |
keirouter tray | Run in the background with a tray / menu-bar icon |
keirouter status | Is it running, and on which URL? |
keirouter bootstrap | Create an API key and print it once |
keirouter version | Print the version |
| Installed with | Update with |
|---|---|
| Homebrew | brew upgrade keirouter (then brew services restart keirouter if you use it) |
| Shell installer (macOS / Linux) | Run the same one-liner again (then systemctl --user restart keirouter if you used --service) |
| PowerShell installer | Run the same one-liner again |
| Docker | Run the --docker one-liner again, or git pull && docker compose up -d --build |
Settings → System also tells you when a new release is out.
Your data (database, master key, settings) lives in ~/.keirouter (%USERPROFILE%\.keirouter on Windows). Uninstalling never touches it; delete that folder yourself if you want a clean slate.
Homebrew
brew services stop keirouter
brew uninstall keirouter
Shell installer (macOS / Linux)
systemctl --user disable --now keirouter 2>/dev/null; rm -f ~/.config/systemd/user/keirouter.service
sudo rm -rf /usr/local/bin/keirouter /usr/local/share/keirouter
Windows (PowerShell)
Remove-Item -Recurse -Force "$env:LOCALAPPDATA\KeiRouter", "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\KeiRouter.lnk" -ErrorAction SilentlyContinue
Then remove %LOCALAPPDATA%\KeiRouter from your user PATH (Settings → System → About → Advanced system settings → Environment Variables).
Only needed if you want to hack on KeiRouter or run the latest main. You'll need Go 1.26+ and Node.js 22 (20.19+ works too):
curl -fsSL https://raw.githubusercontent.com/mydisha/keirouter/main/scripts/quickstart.sh | bash
It clones the repo to ~/keirouter, installs everything, and starts the backend on :20180 and a hot-reloading dashboard on http://localhost:5180. Already cloned it? Just make setup. To build and install a release-style binary from source instead, use install.sh --source. More in Hack on it.
In your AI tool of choice, set:
http://localhost:20180/v1 for OpenAI-style clients, or http://localhost:20180 for Anthropic-style clients like Claude Codekr_ key from the dashboard or keirouter bootstrap (sent as Authorization: Bearer … or x-api-key)openai/gpt-4o, or the name of a fallback chain you cooked upThat's it. You're routing. The Endpoints page has copy-paste snippets for cURL, Python, Node.js, Claude Code and plain environment variables, and the CLI tools page can write the config for you.
Chat is just the start. KeiRouter proxies the whole buffet behind one key:
| Capability | Endpoint | Dialect |
|---|---|---|
| Chat completions | POST /v1/chat/completions | OpenAI |
| Responses API (Codex & friends) | POST /v1/responses | OpenAI |
| Messages | POST /v1/messages | Anthropic |
| Token counting (answered locally) | POST /v1/messages/count_tokens | Anthropic |
| Native Gemini | POST /v1beta/models/{model}:generateContent | Gemini |
| Embeddings | POST /v1/embeddings | OpenAI |
| Image generation | POST /v1/images/generations | OpenAI |
| Image understanding | POST /v1/images/understanding | — |
| Video generation (async) | POST /v1/videos/generations · GET /v1/videos/{id} | — |
| Text-to-speech | POST /v1/audio/speech | OpenAI |
| Speech-to-text | POST /v1/audio/transcriptions | OpenAI |
| Web search | POST /v1/search | — |
| Web fetch | POST /v1/web/fetch | — |
| Model discovery | GET /v1/models · /v1/models/info · /v1/models/{kind} | OpenAI |
| Your own key's usage | GET /v1/keys/me/usage | — |
GET /healthz is the liveness probe, and accidental /v1/v1/... paths are folded back to /v1/... so a misconfigured base URL still works.
Every request runs through a deterministic token-saving pipeline before it gets translated to the provider's format — so the savings work the same no matter which provider you land on. There are five savers, each independently toggleable from Settings → Token saving, and a dashboard that shows you exactly how much you clawed back.
| Saver | Side | The pitch |
|---|---|---|
| RTK / Slimmer | Input | Shrinks chunky tool output (diffs, greps, listings, build logs, source files) locally before it ever leaves your machine. |
| Headroom | Input | Routes request messages through an external Headroom proxy for deeper compression. Fail-open — if the proxy sneezes, your request sails through untouched. Also sniffs out "phantom savings" so only real wins get counted. |
| Terse | Output | Drops a concise-output directive so the model skips the small talk and gives you the goods. |
| Caveman | Output | Terse's stronger cousin (Wenyan / 文言文 levels included) — trims output tokens by 65–75%. |
| Ponytail | Output | Injects a "lazy senior dev" system prompt (lite / full / ultra) that nudges the model toward the smallest possible change. Stacks on top of Terse or Caveman. |
Terse and Caveman both inject a system directive, so they're mutually exclusive — pick one. Ponytail happily layers on top of either.
Pipeline order: normalizer → RTK → Headroom → Skills → Terse / Caveman → Ponytail → provider translation.
The Usage page breaks savings down per rule, and you can export a shareable savings card.
Headroom is its own open-source compression proxy. KeiRouter just calls its /v1/compress endpoint, so you spin it up locally first. One gotcha worth shouting about: the headroom CLI lives in the Python package — the npm package is a library only, so npm install -g headroom-ai will leave you staring at command not found. Don't say we didn't warn you. 😉
# The clean way: pipx isolates the CLI and sorts out your PATH (needs Python 3.10+)
pipx install "headroom-ai[all]"
pipx ensurepath # puts headroom on your PATH — then restart your shell
headroom proxy --port 8787 # start the proxy
headroom doctor # make sure it's actually happy
On Ubuntu and pip is fighting you (PEP 668 blocks global installs)? Go --user and make sure ~/.local/bin is on your PATH:
pip install --user "headroom-ai[all]"
export PATH="$HOME/.local/bin:$PATH" # drop this in ~/.zshrc or ~/.bashrc
Then over in Settings → Token saving → Headroom proxy: flip it on, set the Proxy URL to http://localhost:8787, and hit Test connection to confirm the handshake. Green check? You're golden.
Why bet on one model when you can have a backup plan? Build a chain on the Chains page. Say you name one coding:
openai/gpt-4o — your first pickdeepseek/deepseek-chat — steps in if the first one rate-limits or face-plantsThen just set your app's model to chain:coding (or plain coding) and let KeiRouter sweat the failover.
Each chain picks a routing strategy:
| Strategy | Behaviour |
|---|---|
| Priority | Tries models in the order listed |
| Round robin | Spreads requests across the steps |
| Latency | Fastest measured step first |
| Cost | Cheapest step first |
Add an optional final fallback that's tried after every step fails, and watch the live route preview while you edit. Inside a provider, multiple accounts can also rotate round-robin or fill in priority order (Settings → Routing).
Off by default; turn it on in your config:
cache:
enabled: true
backend: memory # or "redis" (set redis_url) to persist and share across replicas
ttl: 1h
embedding_provider: hash # exact-match, zero dependencies
# embedding_provider: api # true semantic matching via any OpenAI-compatible embeddings API
similarity_threshold: 0.95
With embedding_provider: api, paraphrases like "How do Go channels work?" and "Explain Go channels" hit the same entry. Cache savings show up on the Overview page. See config.example.yaml for every knob.
Copy-pasting API keys gets old fast. On the Providers page, hit Connect provider and pick one from Sign in with a plan — sign in once, and KeiRouter refreshes your tokens in the background (proactively, before they expire).
| Provider | Flow |
|---|---|
| Claude Code | Anthropic OAuth (PKCE) |
| OpenAI Codex | OpenAI OAuth (PKCE, local callback) |
| GitHub Copilot | GitHub device flow |
| Gemini CLI · Antigravity | Google OAuth |
| Qwen Code | Device flow (PKCE) |
| Kimi Coding | Device flow |
| Grok CLI (Grok Build) | Device flow |
| Kiro | AWS Builder ID device flow, IAM Identity Center, refresh-token import, or API key |
| Kilo Code | Device auth |
| Qoder | Device-token flow or Personal Access Token |
| CodeBuddy (Tencent) | Browser-poll flow |
| Kimchi | Browser callback |
| Cline · ClinePass · iFlow | OAuth |
| Cursor · Command Code | Token import |
Several of these are marked Unofficial in the dashboard: they reuse a client's own sign-in rather than a public API, so check each provider's terms before you lean on them.
The Skills page has two halves:
claude-*), or leave it empty to apply everywhere. Skills are prepended as a stable prefix, so provider prompt caches stay warm, and they're never injected twice across retries or fallbacks. Mark a skill optional and clients can opt out with the X-KeiRouter-Skip-Skills: true header.SKILL.md docs in skills/ that teach Claude, Cursor, ChatGPT or any other agent how to call your KeiRouter endpoint (chat, embeddings, images, TTS, STT, web search, web fetch). Copy the URL or the agent prompt straight from the dashboard.Plans are reusable budget policies you can stamp onto any API key — write the rules once, apply them everywhere. Each plan covers:
0 = unlimited)daily, weekly, monthly, or totalclaude-*, gpt-4* (empty = everything's fair game)Every tenant gets a default plan out of the box. Manage them on the Plans & budgets page and assign them on each API key.
Keep your gateway (and your upstream quotas) from getting hammered with per-key RPM, TPM, and concurrency caps. Limits are off by default; here's an example for a single-instance setup:
limits:
enabled: true
backend: memory # one instance; use "redis" for several (see below)
default_rpm: 600
default_tpm: 200000
default_concurrency: 50
window: 1m
cleanup_interval: 1m
Those defaults only apply to keys without a plan. The moment a key has one, the plan's rpm_limit, tpm_limit, and concurrency_limit take over (0 = unlimited). You can also flip enforcement at runtime from Settings → Routing.
TPM is reserved up front and then settled against the usage the provider actually reports (reconcile_tokens), so cache hits, blocked requests and local failures hand their tokens back. output_reserve_cap stops clients that always send a huge max_tokens from draining TPM up front, and concurrency denials include a Retry-After hint.
Separately, per-IP abuse guards (auth_failures_per_minute, login_attempts_per_minute) stay on even when per-key limits are off.
Running several KeiRouter replicas behind a load balancer? Point them at the same Postgres and Redis and set backend: redis. Every replica then enforces one RPM/TPM window, one concurrency pool per key and one budget counter, instead of each replica handing out its own allowance. SQLite is single-instance only, so HA needs Postgres.
database:
driver: postgres
dsn: postgres://keirouter:secret@postgres:5432/keirouter?sslmode=disable
limits:
enabled: true
backend: redis
redis_url: redis://redis:6379/0
fail_mode: local # local | open | closed while redis is unreachable
How it stays fast:
SUM per request. Spend lives in shared Redis counters seeded from the usage table and re-seeded every budget_counter_ttl. Increments are merged in the background, so Redis writes scale with the number of active budgets, not with traffic.lease_ttl instead of leaking them forever.fail_mode, and budgets fall back to the usage table.Budget edits are broadcast over Redis pub/sub, so every replica drops its cached definitions immediately. GET /api/settings/limits/status shows the backend, its health, live leases and fallback counts.
Not shared across replicas yet:
A built-in content-safety layer runs detectors against every request and response. Policies stack global → provider → model → chain → API key and merge at request time, so the most specific rule wins.
| Detector | Catches | Default engine | Optional engine |
|---|---|---|---|
| PII | Email, phone, credit card, IBAN, IP, URL, NIK / NPWP / Indonesian passport, plus KK / BPJS when a context word such as kartu keluarga is nearby, US SSN, API keys, passwords, private keys | Native Go (Presidio-compatible, checksum-validated) | Microsoft Presidio HTTP sidecar |
| Prompt Injection | Instruction overrides, jailbreak personas, smuggled chat-template tokens, prompt leaks, encoded payloads, indirect injection in tool results (id + en) | Signatures + typo-tolerant intent rules | Prompt Guard 2 classifier · embedding match · LLM judge |
| Topics | Allow-list / block-list of topics | Whole-word keyword match with inflections | LLM (context-aware, lets small talk through) · Embedding similarity |
| Toxicity | Profanity, hate, harassment, violence, sexual, self-harm (id + en) | Native weighted lexicon | OpenAI Moderation API |
| Bias (outbound, experimental) | Political, gender, ethnic, religious stereotyping in responses | Native bilingual lexicon | — |
log_only, warn, mask (rewrite it), or block (refuse it). Strictest action across detectors wins.redact, replace, mask, hash, anonymize, or block.1gn0r3), censoring (f*ck) and spaced-out letters don't slip through.keirouter_guardrail_decisions_total and keirouter_guardrail_eval_seconds at /metrics.KEIROUTER_GUARDRAILS__AUDIT_RETENTION_DAYS controls retention; the test endpoint is capped at 10 req/min.Starter templates ship in the Start from template picker (Indonesia PII · Strict safety · Compliance audit · Public chatbot · Coding agent · Alerts-only), and you can export/import policies as a JSON bundle.
Want NER-based PII detection (PERSON, LOCATION, full multilingual)? Spin up the optional Presidio sidecar:
docker compose -f compose.yaml -f compose.postgres.yaml -f compose.presidio.yaml up -d
Then flip any PII policy's engine to presidio in the dashboard.
Want a trained classifier on top of the injection signatures? Run the Prompt Guard sidecar (Llama Prompt Guard 2, multilingual; the model is gated, so set HF_TOKEN after accepting its license, or set PROMPTGUARD_MODEL=protectai/deberta-v3-base-prompt-injection-v2):
docker compose -f compose.yaml -f compose.promptguard.yaml up -d
Then turn on ML classifier in a prompt-injection policy. Any server speaking the Hugging Face text-classification contract works too: point KEIROUTER_GUARDRAILS__INJECTION__CLASSIFIER_URL at it. The LLM judge layer needs no setup: pick any model, alias or chain KeiRouter routes to.
| Page | What you get |
|---|---|
| Overview | Requests, success rate, latency and spend vs. the previous period, top models, provider health, savings |
| Usage | Cost, tokens and latency (p50 / p95 / TTFT) by provider, model, chain and individual request |
| Console | Live gateway activity: one row per request with a timeline of its fallback attempts and rate-limit waits, plus background warnings (OAuth refresh, health, tunnels) in plain language. Filter with key:prod status:5xx ms>5s is:fallback and friends, then download what you found |
| Guardrails | Block / mask / warn rates, detector breakdowns, overhead and judge cost (see Guardrails) |
| Health | Per-provider and per-model uptime, fallbacks, p95 latency and synthetic probes; 24h / 7d history from persisted snapshots |
| Quota | Upstream quota windows and local usage for every account, with auto-refresh |
| System | CPU, memory, disk, goroutines and KeiRouter's own RSS / heap over time |
Prometheus metrics live at /metrics (loopback-only by default): requests, latency, TTFT, tokens, cost, fallbacks, cache hits, upstream errors, saver activity and guardrail decisions.
Every dashboard sign-in and configuration change is recorded on the Audit log page: who did it (a stable per-session id), the action (keys.create, settings.endpoint, auth.login, ...), the resource, the outcome, client IP, request id and W3C trace id. Request bodies are never stored, only the names of the fields that were sent. Reading a full config export or a database backup is audited too.
GET /api/audit/verify) reports rows that were edited, inserted or deleted from the middle of a chain. Rows cut off the newest end of a chain leave no trace in the chain itself, so ship events to an OTLP collector as well if you need that covered.audit.otlp.endpoint (or the standard OTEL_EXPORTER_OTLP_LOGS_ENDPOINT / OTEL_EXPORTER_OTLP_ENDPOINT; OTLP over HTTP) and each event is also shipped as an OTLP log record, using semantic-convention attributes such as user.id, client.address, http.route and event.name=keirouter.audit.<action>. Any OTel Collector, Grafana Loki, Datadog, Elastic or Splunk pipeline can ingest it.GET /api/audit/export) downloads the filtered log as JSON Lines.audit.retention_days (default 365) prunes old rows.audit:
otlp:
endpoint: http://otel-collector:4318
headers: { Authorization: "Bearer <token>" }
include_requests: true # also ship every gateway request (see below)
Admin events cover who changed the gateway. To also see who used it, set audit.otlp.include_requests: true (or KEIROUTER_AUDIT__OTLP__INCLUDE_REQUESTS=true). Every request is then shipped to the same collector as a keirouter.request log record, using the OpenTelemetry GenAI conventions where they exist:
| Attribute | Example |
|---|---|
keirouter.api_key.id, keirouter.client, keirouter.tenant.id | which key and tool made the call |
gen_ai.provider.name, gen_ai.response.model, keirouter.chain.id, keirouter.fallback.count | where it was routed |
gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, keirouter.cost.usd | what it cost |
keirouter.request.status, error.type, keirouter.latency.ms, keirouter.ttft.ms | how it went |
keirouter.guardrail.action, keirouter.guardrail.detector | what guardrails did |
Prompts and responses are never exported. Records use their own scope (github.com/mydisha/keirouter/requests) and their own queue, so a traffic spike can't crowd admin events out; GET /api/audit/status reports both exporters. The same data stays queryable on the Usage page.
Moving in from another router? Settings → Import & export takes:
Pick Merge, Overwrite, or Wipe & replace. You can also download local or passphrase-encrypted portable backups and restore them later.
Rebrand the admin dashboard and the public Usage Portal from Settings → Branding:
kei (default), sage-terra, ocean, midnight, forest, rose, lavender, mono, sunset, arctic, cherryLight, dark, and system themes are a click away in the account menu.
The CLI tools page detects which coding tools are installed, shows whether each one already points at KeiRouter, and can write (or remove) the config in one click — pick an endpoint, an API key and an optional default model. Prefer doing it by hand? Every tool has a manual snippet too.
Claude Code · Codex CLI · Cline / Roo · GitHub Copilot · Factory Droid · OpenClaw · OpenCode · Kilo Code · Hermes Agent · DeepSeek TUI · jcode
Using OpenCode? The @keirouter/opencode-plugin discovers your models straight from GET /v1/models.
A dedicated, no-admin-required view at /portal where teammates keep an eye on their own usage — quota and spend, token usage over time, compression savings, and plan limits. Sign in with an API key, or share a read-only portal ID (key_…) so nobody has to hand out a secret. Signed in with a key, there's also a Playground tab for quick chats. No keys to the kingdom required.
🧠 LLM / Chat
| Category | Providers |
|---|---|
| Major Cloud | OpenAI, Anthropic, Google Gemini, Azure OpenAI (API key or Entra ID), Vercel AI Gateway |
| Enterprise Cloud | AWS Bedrock, AWS SageMaker, Azure AI Foundry, IBM watsonx.ai, Oracle Cloud Generative AI, SAP Generative AI Hub, Databricks, Snowflake Cortex |
| Free / Free Tier | OpenRouter, NVIDIA NIM, Ollama Cloud, Cloudflare Workers AI, BytePlus ModelArk, MiMo Free, Kiro |
| Sign in with a plan | Claude Code, OpenAI Codex, GitHub Copilot, Gemini CLI, Antigravity, Cursor IDE, Cline, ClinePass, Kilo Code, Qwen Code, Qoder, iFlow, Kimi Coding, Kimchi, CodeBuddy (Tencent), Grok CLI, Command Code |
| Model labs | xAI (Grok), Mistral (+ Codestral), Cohere, AI21 Labs, Perplexity (+ Agent / Responses), Meta (Llama API, Model API), Amazon Nova, Inception Labs, v0, Cognition, Sarvam AI, Maritaca AI, GigaChat (Sber) |
| China / Asia | DeepSeek, Alibaba (Model Studio / DashScope, Coding, Intl), Moonshot AI & Kimi, Z.AI & GLM Coding (+ China), MiniMax Coding (+ China), Volcengine Ark, Xiaomi MiMo (+ Token Plan), SiliconFlow, ModelScope, Tencent TokenHub |
| Fast inference | Groq, Cerebras, SambaNova, DeepInfra, FriendliAI |
| Inference clouds | Together AI, Fireworks AI, Nebius AI, Hyperbolic, Featherless, Nscale, Baseten, Nous Research, Scaleway AI, Lambda, Replicate, Hugging Face Inference Providers, OVHcloud, DigitalOcean Gradient AI, Heroku, W&B Inference, Clarifai, Crusoe Cloud, GMI Cloud, Parasail, DataRobot, Venice AI, and more |
| Aggregators & gateways | OpenCode, AgentRouter, GitHub Models, Helicone AI Gateway, CometAPI, AIHubMix, Eden AI, NanoGPT, Poe |
| Emerging | Blackbox AI, Chutes AI, Galadriel, Cortecs, LibertAI, Synthetic, and many more |
| Self-hosted | vLLM, Ollama, LM Studio, llamafile, Docker Model Runner, Xinference, Lemonade, Text Generation WebUI, NVIDIA Triton, RAGFlow |
| Custom | Any OpenAI- or Anthropic-compatible endpoint (self-hosted, proxy, etc.), with your own model registry and prices |
Self-hosted on the same machine or LAN? KeiRouter's SSRF guard blocks loopback and private addresses by default. Set
KEIROUTER_SECURITY__ALLOW_PRIVATE_BASE_URL=true(orsecurity.allow_private_base_url: true) to reach them.
🏢 Enterprise clouds connect with their own credentials, not just an API key. The connect form asks for exactly what each one needs:
| Provider | Authentication |
|---|---|
| AWS Bedrock | Bedrock API key, IAM access key, or the server's environment / instance role, with optional STS assume-role. Converse API for every model family, native Anthropic via invoke/, embeddings, and optional Bedrock Guardrails |
| AWS SageMaker | IAM access key or instance role; the model is your endpoint name (TGI, LMI, vLLM, SGLang), with optional inference components |
| Azure OpenAI · Azure AI Foundry | API key or a Microsoft Entra ID service principal |
| IBM watsonx.ai | IBM Cloud IAM, Cloud Pak for Data, or a bearer token; project or deployment space |
| Oracle Cloud Generative AI | Signed requests (tenancy, user, fingerprint, PEM key); on-demand or dedicated clusters |
| SAP Generative AI Hub | AI Core service key or client credentials; deployment discovered for you |
| Databricks | Personal access token or OAuth machine-to-machine; serving endpoints discovered live |
| Snowflake Cortex | Programmatic access token or key-pair JWT |
Exchanged tokens are cached and refreshed automatically, and every secret field is sealed with the same envelope encryption as API keys.
🎨 Media & Search
| Type | Providers |
|---|---|
| Image Generation | OpenAI, xAI, Venice AI, Recraft |
| Video Generation | xAI |
| Text-to-Speech | OpenAI |
| Speech-to-Text | OpenAI Whisper, Groq Whisper |
| Embeddings | OpenAI, Mistral, Cohere, AWS Bedrock, Azure AI Foundry, Databricks, GigaChat, GitHub Models, Cloudflare Workers AI, Together AI, Fireworks AI, Nebius, NVIDIA NIM, OpenRouter, Voyage AI, Jina AI, Venice AI, vLLM |
| Web Search | Tavily, Exa, Serper, Brave Search |
| Web Fetch | Tavily, Exa, Firecrawl |
Image, speech, transcription, video and embedding requests use the OpenAI wire format (/images/generations, /audio/speech, /audio/transcriptions, …), so any upstream or self-hosted server that speaks it works as a custom provider. The dashboard also lists media services with their own APIs (ElevenLabs, Deepgram, AssemblyAI, Stability AI, Fal.ai, Black Forest Labs, Google PSE, …) marked Coming soon: native adapters for those are on the way, and until then they only work behind an OpenAI-compatible proxy added as a custom provider.
Browse the full catalog (and filter by capability) under Connect provider in the dashboard.
Out of the box, KeiRouter runs on an embedded SQLite database — zero config, zero fuss. Running it for a team? Switch to PostgreSQL: copy config.example.yaml and run with -config, or use environment variables like KEIROUTER_SERVER__PORT=8080 (double underscore = nesting). The master key is KEIROUTER_SECURITY__MASTER_KEY (Compose maps KEIROUTER_MASTER_KEY to it for you). Docker/Coolify examples are in deploy/README.md.
Most day-to-day behaviour — token savers, routing, outbound proxy, branding, backups, update checks — lives in the dashboard under Settings, and changes save automatically.
Curious what happens after you hit send? Here's the life of a request:
/api/*) only answers on loopback by default (security.bind_loopback_only). Exposing it? Put it behind a reverse proxy and set a stable master key.Found a security issue? Please follow SECURITY.md instead of opening a public issue. 🙏
make setup # first time: installs deps + starts backend (:20180) and dashboard (:5180)
make dev # after that: just start the servers
make test # run the backend test suite
make build # build the backend binary + frontend assets
PRs and ideas are always welcome — start with CONTRIBUTING.md.
MIT — see LICENSE. Go build something cool. 🛠️
31 followers · starred Jun 2026