TinyRouter

A self-hosted, OpenAI-compatible LLM gateway small enough to read before you hand it your API keys. Deterministic fallback, two runtime dependencies, one YAML file.

A gateway sits between your provider credentials and every prompt you send. TinyRouter is roughly 2,200 lines of TypeScript with two runtime dependencies, yaml and zod — one person can read all of it in an evening. It has no accounts, billing, database, Redis, or background control plane, and the gateway serves no dashboard.

Install

Standalone executables for Linux and macOS, x64 and arm64. They embed the Bun runtime, so nothing else is needed:

curl -fsSL https://github.com/honzabit/tinyrouter/releases/latest/download/tinyrouter-linux-x64.tar.gz | tar -xz

Or run the container:

docker run --rm -p 8080:8080 --env-file .env \
  -v "$PWD/tinyrouter.yaml:/etc/tinyrouter/tinyrouter.yaml:ro" \
  ghcr.io/honzabit/tinyrouter:latest

Describe your fallbacks in YAML

Serve your own model first and fail over to a hosted one only when it is down or overloaded. Targets are tried in the order you write them — nothing is inferred:

providers:
  local:
    type: openai-compatible
    base_url: http://localhost:11434/v1
  anthropic:
    type: anthropic
    api_key: ${ANTHROPIC_API_KEY}

routes:
  assistant:
    - local/qwen3
    - anthropic/claude-haiku-4-5

Then call the alias exactly as you would call a model:

curl http://localhost:8080/v1/chat/completions \
  -H 'Authorization: Bearer local-secret' \
  -d '{"model":"assistant","messages":[{"role":"user","content":"Hello"}]}'

See what it is doing

A single static HTML file ships in ui/ and reads the gateway's own endpoints. It shows which providers are healthy, which the circuit breaker has demoted, and where your tokens went:

provider circuit models 2xx errors bad bodies
anthropic cooling claude-haiku-4-5 0 3 0
local healthy qwen3 184 0 0

Bad bodies is the column no status code can give you: a provider that answers 200 and then hangs part-way through the stream. The status line went out long before anyone knew.

Download it and open it locally rather than using a copy hosted here. It is a page that reads your gateway, and a page served from someone else's domain is a page whose JavaScript you have to trust with it. That is the whole argument for this project, so it would be odd to make an exception.

When to use it

Use something else when

You needBetter fit
Budgets, virtual keys, per-user spend tracking, an admin UILiteLLM
Guardrails, semantic caching, prompt managementPortkey
A hosted gateway with nothing to operateOpenRouter, Cloudflare AI Gateway
A hundred providers out of the boxLiteLLM or a hosted gateway
Provider switching inside one app whose code you controlAn SDK such as the AI SDK — you may not need a proxy at all

Those are good projects. TinyRouter exists for the case where the feature you want most is being able to understand the whole thing.

Documentation

Reference lives in the repository, versioned alongside the code, so a clone or a source archive describes the commit you actually have rather than whatever is newest: