LLMRouter holds upstream provider credentials and exposes a model-routing data plane plus an administrative control plane. Its supported deployment boundary is one trusted host or a small trusted network behind a firewall or same-origin HTTPS reverse proxy. Do not expose the backend directly to the public internet.
Security fixes target the latest main branch and the newest published release.
Older snapshots may not receive backports unless a release notice says so.
Use the repository's GitHub Security Advisories → Report a vulnerability flow when available. Include:
- affected version or commit;
- prerequisites and a minimal reproduction;
- the data, permission, network, or availability impact;
- whether the issue is reachable from the data plane, dashboard, or a trusted administrator action;
- only redacted logs and synthetic credentials.
If private reporting is unavailable, contact the repository owner through a
private channel listed on their GitHub profile and ask to open a private channel.
Do not place exploit details, provider keys, session tokens, backups, or a full
.env in a public issue.
- Provider API keys and any local router token.
- Dashboard session cookies and password hashes.
- Dashboard-issued API keys; only hashes are retained after creation.
- PostgreSQL/JSON control state, which includes identity, policy, usage, IP, audit, and credential-variable metadata.
- CLI backups, which contain password and API-key hashes and can restore state.
- Prompts and provider responses, even though LLMRouter does not intentionally persist complete request/response bodies in its usage log.
/v1/*,/metrics, and detailed diagnostics require a router or dashboard- issued API key. For a shared deployment, create control-plane keys and setLLMROUTER_REQUIRE_CONTROL_API_KEYS=1so the unrestricted legacy token is not accepted.- Dashboard users authenticate separately. Passwords use Argon2 hashes. Hash work runs outside the auth-state mutex on blocking workers, with at most four concurrent login hashes; waiting longer than five seconds returns HTTP 429. Unknown and disabled users still perform an equivalently expensive hash-class operation to reduce timing enumeration. Five failures for a username lock it for 15 minutes. Lockout counters, the worker gate, and sessions are process-local and reset on restart.
- Session cookies are HttpOnly and SameSite=Lax. Set
LLMROUTER_ADMIN_COOKIE_SECURE=1whenever the dashboard is served over HTTPS. - Dashboard writes require a session,
X-LLMRouter-CSRF, and an allowed Origin/Referer when present.LLMROUTER_ALLOWED_ORIGINSextends that write check; it does not enable browser CORS. - The backend has no general CORS response policy. Serve dashboard and API from one trusted origin.
- Keep
LLMROUTER_BINDand published Docker ports on loopback unless a trusted network or reverse proxy needs them. - Configure
LLMROUTER_TRUSTED_PROXIESwith exact proxy IPs/CIDRs. Forwarded client-IP headers are security inputs for IP policy and rate limiting. LLMRouter walks XFF from the connected peer right-to-left and removes only explicitly trusted hops; a single-hop proxy should overwrite XFF with its observed client address. Preserve the original Host authority including its port so browser Origin/Host write checks remain aligned. - Provider URL validation rejects non-HTTP schemes, userinfo, query strings, fragments, and literal private/link-local/metadata addresses by default, including private IPv4 addresses encoded as IPv4-mapped IPv6 literals. Local/custom providers have explicit loopback allowances. Credentials must come from environment-backed header configuration, never URL userinfo or a query parameter.
- Non-local/non-custom Providers require HTTPS by default. Plain HTTP exposes
the Provider API key and prompt/response content to every network hop. Use
LLMROUTER_ALLOW_INSECURE_PROVIDER_HTTP=1only for an explicitly trusted internal upstream; local/custom runtimes retain HTTP support for controlled local integration. The HTTP override does not disable private/metadata-IP protection. - Hostnames are not currently pinned or revalidated after DNS resolution. A hostname that resolves to an internal address is outside the current SSRF guard. Use outbound firewall rules or an allowlist when administrators are not fully trusted.
- Upstream HTTP redirects are disabled and every GET/POST/SSE handshake requires
2xx. A 3xx is treated as an upstream failure and mapped to client-facing 502,
not followed or exposed as a client redirect. Upstream non-stream bodies and
raw SSE bytes are bounded, and streams have an idle timeout. SSE additionally
rejects 204 and any missing/non-
text/event-streammedia type before local headers. Non-2xx/non-SSE error-body reads have total and idle timeouts, and a live stream must reach its protocol termination event rather than treating EOF as successful completion. - PostgreSQL connections use SQLx with rustls. Development mode defaults to
prefer, which can fall back to an unencrypted connection when the server does not offer TLS; the default development Compose database therefore stays on its private bridge. Remote and production databases should useverify-fullwith a trusted root and a hostname that matches the server certificate. Enterprise mode requiresverify-fulland refuses to start withoutLLMROUTER_DATABASE_URL. Compose's constructed URL also requires a URL-safe password or an explicitly percent-encodedLLMROUTER_DATABASE_URLoverride.
LLMROUTER_ALLOW_PRIVATE_PROVIDER_URLS=1 deliberately weakens the URL boundary
and should only be used for a trusted internal runtime.
The usage log stores request ID, identity/team labels, model/provider, token and cost estimates, status, latency, retry/fallback, client IP, and a category-only error summary. Durable usage, request/attempt ledger, and Provider-health error fields remove request values, Tool validation paths, Provider bodies, URLs, and storage diagnostics. Startup migration also rewrites older retained error detail. LLMRouter does not intentionally persist prompts, complete messages, raw provider bodies, authorization headers, or plaintext keys.
The current authenticated caller can receive a bounded upstream error whose common credential fields and token patterns are redacted. That live response redaction remains best-effort and is not proof that arbitrary third-party text is safe to publish. It is deliberately separate from the stricter category-only durable audit record. Review client-captured errors before sharing them.
Common secret-bearing configuration, auth, control-store, login, password-input,
and API-key creation types use custom or redacted Debug output, with regression
tests for the sensitive fields. This reduces accidental tracing and diagnostic
leaks; it does not remove secrets from process memory, crash/core dumps, or
every third-party value. Keep dumps and unrestricted debug access disabled or
tightly controlled.
LLMROUTER_USAGE_LOG_LIMIT controls retained usage records. Dashboard
diagnostic snapshots are redacted but contain personal/usage data. CLI backups
are complete restore artifacts and must be encrypted, access-controlled, and
deleted under a defined retention policy.
Diagnostic snapshot export is a CSRF-protected POST /admin/backup operation
and creates an audit event; no safe-method GET alias is exposed. Provider model
discovery is likewise a mutating POST because it stores provider-test and audit
state.
CLI validation/restore deeply deserializes both documents and enforces critical auth invariants, including unique identities, valid role/status/password hashes, and retention of an active admin. This reduces corrupt-restore risk but does not make the sequential auth/control replacement transactional; stop writers and retain the automatically saved previous values plus a storage-native backup.
- Replace every placeholder and use a long unique admin password and router token.
- Run
scripts/config-validate.sh; it fails closed on enterprise database/TLS policy, malformed PostgreSQL URLs and pool bounds, invalid lease timing, trusted-proxy CIDRs, and allowed origins as well as application settings. It does not test database reachability or the certificate chain. Never setLLMROUTER_ALLOW_NO_AUTH=1on a shared host. - Bind to loopback or place the service behind a firewall and same-origin HTTPS reverse proxy.
- Set secure cookies, exact trusted proxies, and the expected dashboard origin.
- Require dashboard-issued API keys for shared data-plane traffic.
- Protect
.env, PostgreSQL/JSON state, journal logs, and complete backups. - Keep request/response byte limits, stream timeouts, and concurrent-stream permits finite.
- Run the acceptance checks appropriate to auth, routing, or deployment changes.
- The optional OIDC console sign-in preview is single-host and process-local; it is not complete enterprise IAM, distributed SSO/session coordination, SCIM, service-account lifecycle, or public multi-tenant isolation.
- No complete DNS-rebinding protection for provider hostnames.
- The default development Compose PostgreSQL service does not provision a
server certificate; its
preferconnection can remain plaintext inside the private bridge. Use a TLS-configured PostgreSQL endpoint withverify-fullfor an enterprise deployment. - Rate limits, concurrent-stream permits, and login/session state are not shared across instances. Stream permits remain occupied until body completion/drop.
- Origin validation allows non-browser requests without Origin/Referer; it is not an authorization mechanism.
- Secret redaction cannot recognize every credential format.
- Compatibility user/API-key/team quota pre-check/update is not a transactional reservation under concurrency. The tenant budget ledger has a separate transactional reservation and settlement path in PostgreSQL mode.
See Architecture, Configuration, and Operations for the corresponding implementation limits.