The security checklist for self-hosting an AI router
Self-hosting kRouter puts your OAuth tokens, API keys and prompts on that machine. What it protects by default, and what is still your job.
A self-hosted router holds the most valuable things in your AI setup: OAuth tokens for your subscriptions, API keys for metered providers, and a record of your prompts. Get the exposure wrong and a stranger spends your quota, or reads your traffic.
Here is the checklist, with what kRouter does by default at each step and what is still up to you.
What changed in this post
An earlier version of this checklist included things kRouter does not have: a KROUTER_BIND variable, a brute-force config in ~/.krouter/config.json, OAuth tokens encrypted at rest, a krouter --uninstall-ca command and per-user dashboard views. This version replaces them with what the code actually does, and adds the security releases that shipped since.
1. Know what is listening
kRouter binds to 0.0.0.0 by default, so it is reachable from your network, not just your machine. The CLI says so when it starts:
Network-exposed: reachable at http://192.168.1.20:20128 (bound 0.0.0.0). Use --host 127.0.0.1 for local-only.If nothing outside the machine needs it, bind it to loopback:
krouter --host 127.0.0.1The krouter command ignores the HOSTNAME and PORT environment variables; use --host and --port. Under Docker, publish the port on loopback only with -p 127.0.0.1:20128:20128.
To reach a loopback-bound kRouter on a server from your laptop, use an SSH tunnel: ssh -L 20128:127.0.0.1:20128 user@vps.
2. The dashboard password
The default password, 123456, only works from the machine kRouter runs on. A remote login with it gets a 403. The same rule applies to the example passwords from the docs, such as change-me and your-password. Requests that come through a reverse proxy never count as local.
So for any remote access, set a real password: either INITIAL_PASSWORD on first start, or later in Settings, section Security. Leave Require login on.
Failed logins lock the source IP out for 30 seconds after 5 attempts, then 2, 10 and 30 minutes. If your team already has an identity provider, kRouter can use OIDC sign-in instead of a password.
3. The API: keys for everyone off the machine
Callers on the same machine can use /v1 without a key. Remote callers always need one; without it they get a 401 "API key required for remote API access". Set REQUIRE_API_KEY=true to remove the local exemption too, which is the right setting on a shared or multi-user host.
kRouter generates the keys; you do not pick them. Create them on the dashboard's Endpoint page, one per person or device, so you can switch one off or delete it without touching the others.
4. Brute force is throttled already
You do not need to configure this. A remote caller that presents a key or token that fails validation is locked out after 10 failures, for 30 seconds, then 2, 10 and 30 minutes, and gets a 429 with a Retry-After header while locked. The counter resets an hour after the last failure. It is held in memory, so a restart clears it.
5. SSRF on URLs that arrive in requests
Some endpoints fetch a URL that the caller supplies: the web fetch tool (/v1/web/fetch), base URL overrides on /v1/search, and image URLs inside messages. Those are checked before kRouter fetches anything. The checks reject loopback, private and link-local ranges (including the 169.254.169.254 cloud metadata address), non-HTTP schemes, hostnames that resolve into private space, and redirects that lead there. v0.5.149 closed seven bypasses in the shared SSRF guard.
Base URLs you configure yourself for providers are trusted, which is how a local Ollama works. That makes dashboard access the thing to protect.
6. Credentials at rest
OAuth tokens and API keys live in the SQLite database at $DATA_DIR/db/data.sqlite (~/.krouter by default). They are not encrypted at rest. Anyone who can read that file can use your accounts. Treat the data directory, and any backup of it, like ~/.ssh.
Two things help. Exporting or importing the database from the dashboard asks for your password again, even inside a logged-in session. And the session signing secret is generated for you and stored in $DATA_DIR/jwt-secret with mode 0600; a JWT_SECRET shorter than 32 characters, or one copied from documentation, is ignored.
7. Your prompts are stored, unless you turn that off
Observability is on by default. kRouter records request details -- the request, what was sent upstream, the upstream reply and the response -- so the dashboard can show them. Each field is cut at 5 KB and the latest 1,000 records are kept, in the same database. Callers that are neither local nor logged in get those bodies redacted.
If you do not want prompts on disk, turn off Enable Observability in Settings. MITM debug dumps are separate, and off unless you set DEBUG_MITM=1 or MITM_FILE_LOG=1.
8. Tunnels are public
The Endpoint page can expose kRouter through a Cloudflare Tunnel or Tailscale. The built-in Tailscale option uses Funnel, so both give you a public URL. Requests through them count as remote, so they need a key, but the dashboard login is reachable through the tunnel unless you turn off Allow dashboard access via tunnel on the same page.
9. The MITM root CA
MITM mode, for IDEs that hardcode their endpoint, installs a root certificate authority in your system trust store. That deserves care.
- kRouter generates a 2048-bit RSA key and a self-signed root certificate valid for 10 years, and regenerates it when it is within 30 days of expiry. The files are
$DATA_DIR/mitm/rootCA.keyandrootCA.crt. - The certificate has no name constraints. Anything holding
rootCA.keycan mint certificates your machine trusts for any domain, not only the eight hosts kRouter intercepts. Guard that file. - Only enable MITM on a machine you personally control. It does not work inside containers anyway, and editing the hosts file needs root or administrator rights.
- When you stop using it, remove the certificate with the Uninstall button on the MITM page's root certificate card.
kRouter marks its own outbound requests with an x-request-source: local header so the interceptor passes them through instead of catching them in a loop. v0.5.55 reverted a header scrub that removed it and broke MITM.
10. One login sees everything
There is a single dashboard identity, password or OIDC, and whoever has it sees every request from everyone. Requests themselves are independent -- one caller's context never mixes with another's -- but the dashboard is not multi-tenant. If people must not see each other's prompts, run separate instances.
11. Stay on a current version
Five security releases shipped in the last two months:
| Version | Fixed |
|---|---|
| 0.5.135 | Remote auth bypass: a spoofed Host: localhost header was treated as local |
| 0.5.136 | SSRF on /v1/search; remote login with the default password; request history readable with login off |
| 0.5.149 | Unauthenticated access through the /codex rewrite; seven SSRF bypasses |
| 0.5.157 | Next.js advisory allowing unauthenticated remote code execution on Windows hosts |
| 0.5.162 | Example JWT_SECRET and INITIAL_PASSWORD values from the docs worked |
Update with npm install -g @sifxprime/krouter@latest, or pull the new image under Docker. The changelog lists every release.
12. The final test
From a different network:
# The dashboard should be unreachable, or show the login page
curl -I https://your-router.example.com/dashboard
# No key: 401 "API key required for remote API access"
curl https://your-router.example.com/v1/chat/completions
# Wrong key: 401, and after 10 tries a 429 with Retry-After
curl -H "Authorization: Bearer wrong" https://your-router.example.com/v1/chat/completionsIf all three behave, you are in good shape. The deployment docs and the security docs cover the rest.
Common questions
Is kRouter exposed to my network by default?
Yes. It binds to 0.0.0.0, and the CLI warns about it at start. Remote callers still need an API key for /v1 and a login for the dashboard, but if you do not need remote access, start it with --host 127.0.0.1.
Are my OAuth tokens encrypted?
No. They are stored in the SQLite database in kRouter's data directory without encryption at rest. Protect that directory and its backups the way you protect SSH keys.
Does kRouter store my prompts?
By default, yes: Observability records request and response bodies, cut at 5 KB per field, and keeps the latest 1,000. Turn off Enable Observability in Settings if you do not want that.
Can someone brute-force my API key?
Not quickly. A remote caller is locked out after 10 failed attempts, for 30 seconds at first and up to 30 minutes on repeat, and the counter only resets an hour after the last failure. Keep the number of people and machines holding a key small, and delete keys you no longer use.
How do I remove the MITM certificate?
Use the Uninstall button on the root certificate card of the dashboard's MITM page. There is no CLI command for it.
Related
Published by Kodelyth, the team that builds kRouter. Posts are drafted with AI assistance and reviewed by a person before they go out. kRouter is free and MIT licensed.
Install kRouterRelated posts
- How kRouter worksSelf-host an AI router on a $6/month VPSDeploy kRouter on a small VPS so your team shares one endpoint: systemd, nginx with streaming, Tailscale-only access, Docker, and the caveats.
- How kRouter workskRouter is a multimodal AI gateway, not just an LLM routerOne local endpoint for chat, speech, transcription, images, video, embeddings, web search and fetch across 95+ providers -- and where each one stops.
- How kRouter worksClaude Cowork with other models: third-party inference setupClaude Desktop can run Cowork through any Messages API gateway. Setting it up with kRouter, what the app assumes about your models, and what you give up.
Relevant docs
- Core conceptsProviders, combos, account routing, token savers, MITM mode, quota tracking, the response cache and remote access: the eight ideas behind kRouter.
- The Zenith Routing EngineHow kRouter picks which of your accounts serves a request: Zenith scoring, conversation stickiness, cooldowns, and the round-robin, P2C and random options.
- Token savers: RTK, Caveman, Ponytail, Headroom, PXPIPEFive ways kRouter can shrink a request before it reaches the provider. Only RTK is on by default. What each saver does, what it needs, and its limits.