Vulos llmux
part of Vulos · open source · the sovereign LLM gateway

Every model.
Every language.
On your box.

A single Go binary that speaks the OpenAI API and routes every request to the provider behind it. Point your existing client at it — in any language — and get routing, fallbacks, budgets, caching and live cost. Inference runs on your box by default, and a request is never silently sent off the box.

or install it yourself in under a minute
100+ providers OpenAI-compatible 0 dependencies MIT licensed
Multiplexer routing
openai anthropic gemini bedrock + 100 more MUX your app
6 native adapters 688+ priced models 1 OpenAI API

install

One binary. Running in under a minute.

Go 1.25+ to build; Node 18+ only if you rebuild the embedded web UI. No database, no cache, no sidecar — for a single replica this is the whole install.

# 1. clone & build — embeds the web UI
git clone https://github.com/vul-os/llmux
cd llmux
make build

# 2. configure a provider (or skip — auto-detected from env)
export OPENAI_API_KEY=sk-...

# 3. run — gateway on :4000, dashboard at /ui
cp llmux.example.json llmux.json
./dist/llmux -config llmux.json# the repo ships a Dockerfile — no local Go toolchain needed
docker build -t llmux .
docker run -p 4000:4000 \
  -e LLMUX_MASTER_KEY=$(openssl rand -hex 32) \
  -e OPENAI_API_KEY=sk-... \
  -v $PWD/llmux.json:/etc/llmux/llmux.json \
  llmux -config /etc/llmux/llmux.json

Then confirm it's alive — GET /health needs no auth:

terminal
curl http://localhost:4000/health
# {"status":"ok"}

open http://localhost:4000/ui   # embedded dashboard

Prerequisites, scaling to multiple replicas, and every flag: Install & Run →

drop-in

Change one line. Keep your stack.

No SDK to learn. The OpenAI client you already use just points at llmux — and the model string selects the route: an alias, a provider/model prefix, a wildcard, or a least-cost pick like cheapest.

from openai import OpenAI

client = OpenAI(base_url="http://localhost:4000/v1", api_key="sk-team-a")

resp = client.chat.completions.create(
    model="cheapest",                  # least-cost live route
    messages=[{"role": "user", "content": "hi"}],
)
print(resp.usage)  # includes per-request cost_usdimport OpenAI from "openai";

const client = new OpenAI({ baseURL: "http://localhost:4000/v1" });

const r = await client.chat.completions.create({
  model: "anthropic/claude-3-5-sonnet",  // provider/model prefix
  messages: [{ role: "user", content: "hi" }],
});curl http://localhost:4000/v1/chat/completions \
  -H "Authorization: Bearer sk-team-a" \
  -H "Content-Type: application/json" \
  -d '{"model":"claude-*","messages":[{"role":"user","content":"hi"}]}'
# streaming ("stream":true) returns byte-identical OpenAI SSE{
  "providers": [
    { "name": "local",  "base_url": "http://127.0.0.1:11434/v1" },  // on-box: always allowed
    { "name": "openai", "api_key_env": "OPENAI_API_KEY",
      "allow_egress": true }        // explicit off-box opt-in
  ],
  "routes": [
    { "model": "*", "provider": "local", "fallbacks": ["openai"] }
  ]
}

where your AI runs

Sovereign by default. Off-box only when you say so.

A default-deny egress gate runs before every dispatch. A loopback endpoint is local and always allowed; any off-box endpoint is blocked until you opt it in, per provider. The gate fails closed, never opens a socket to a blocked target, and logs every permitted off-box call with its tier — so egress is always observable, never silent.

local

always on

Inference on THIS box — a loopback or unix-socket endpoint (Ollama, llama.cpp, vLLM). Always allowed. This is the default.

sovereign

you vouch for it

An off-box endpoint you personally run and vouch for. Allowed on your explicit per-provider declaration.

brokered

allow_brokered

A named third party under a claimed no-train agreement. Blocked until you opt it in per provider.

external

blocked by default

Any other off-box provider that may mine or train on you. Blocked until you set allow_egress. The gate fails closed.

the gateway

A control plane for every call.

One OpenAI-compatible endpoint

Speak the OpenAI HTTP API — chat, completions, embeddings, responses, rerank, moderations, images, audio. Any OpenAI SDK, any language, works unchanged. Just swap base_url.

Multi-provider routing

Native adapters for Anthropic, Gemini, Cohere, Bedrock and Azure, plus passthrough for any OpenAI-shaped upstream. Tool-calling, vision and streaming translated per provider.

Fallbacks & retries

Aliases, provider/model prefixes, wildcards and catch-all routes. Fallback chains retry the next provider on 429/5xx with exponential backoff — a blocked or down primary never takes you down.

Per-key budgets

Every app holds its own virtual key with a USD budget, an RPM limit and a model allow-list. Over budget → 402, over rate → 429. Spend persists to Postgres; tokens are hashed at rest.

Exact + semantic caching

Identical requests answer from a per-key LRU+TTL cache; semantic caching matches on embedding similarity. In-memory, or shared across replicas via Redis. Cache hits cost nothing upstream.

Live cost accounting

A built-in price catalog auto-syncs from OpenRouter + LiteLLM. Real per-request cost lands in every response’s usage block — streaming included — and rolls up in the embedded dashboard.

getting started

From a running binary to your first tracked call.

Already followed Install? Three moves and you have a routed, budgeted, cost-tracked call flowing through llmux.

STEP 01

Configure a provider key

Export a key and llmux auto-detects the provider — no config file needed to start:

export OPENAI_API_KEY=sk-... export ANTHROPIC_API_KEY=sk-ant-...

A loopback local provider (Ollama, llama.cpp, vLLM) is always allowed; any off-box provider needs an explicit allow_egress before llmux will ever dial it.

STEP 02

Make your first call

Point any OpenAI client at localhost:4000/v1 — the rest of your code is untouched:

curl http://localhost:4000/v1/chat/completions \ -H "Authorization: Bearer sk-team-a" \ -d '{"model":"cheapest","messages":[{"role":"user","content":"hi"}]}'

The model string picks the route — an alias, a provider/model prefix, a wildcard, or cheapest. Full snippets in 17+ languages: Quickstart →

STEP 03

Watch it in the dashboard

Every response carries live usage + cost, and shows up instantly at /ui — shipped inside the binary, no separate service:

llmux dashboard — usage by model with request counts, tokens and live cost

built in

See spend, keys and models — live.

An admin dashboard ships inside the binary at /ui — usage by model, virtual-key budgets, and the live price catalog. No separate service, no extra deploy.

localhost:4000/ui
llmux dashboard — usage by model with request counts, tokens and live cost llmux dashboard — the live model catalog with pricing and context windows llmux dashboard — virtual keys with per-key budgets and rate limits

Usage · served from /ui

100+
providers, one API
688+
live-priced models
1
static Go binary
$0
self-hosted, MIT

Self-host free. Forever.

One binary, your keys, your infra — and your prompts stay on your box. Point your OpenAI SDK at it and go.