> ## Documentation index
> Fetch the complete documentation index at: https://haarness.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Run the router yourself

The Haarness Router puts one OpenAI-compatible endpoint in front of your own provider accounts, on your own machine
or inside your network. It is licensed under MIT ([Open source](https://haarness.ai/open-source)), and the managed router in Haarness
Cloud is built on the same code. You do not need a Cloud account.

> **The router's source is not public yet.** Its repository opens soon. Until then, write to
> [admin@haarness.ai](mailto:admin@haarness.ai) if you want to run it.


## Managed router or your own?

| | Managed router (Cloud) | Your own router |
| --- | --- | --- |
| Provider accounts | None needed, or your own keys per router | Your own |
| Who bills you | Haarness, in credits; or your provider, with your keys | Each provider, directly |
| Usage history, spending caps, per-key model lists | Built in | Not included |
| Runs inside your own network | No | Yes |
| Price | Credits, at published rates | Free software; you pay your providers |

Both speak the same API, so switching later only means changing the base URL and the key.

If you only want to use your own provider keys, you may not need to self-host: a Cloud router can use **Your
provider keys** instead of credits. See [Models and routers](https://haarness.ai/docs/cloud/models-and-routers.md).

## Get a first router running

You need the router's source, a current Rust toolchain (install it with [rustup](https://rustup.rs)) and a C
compiler.

1. Build it, in the router's source folder:

   ```sh
   cargo build --release
   ```

2. Write a `config.toml` that names the providers you have keys for, and the environment variables that hold
   the keys:

   ```toml
   [providers.openai]
   kind = "openai"
   api_key_env = "OPENAI_API_KEY"

   [providers.anthropic]
   kind = "anthropic"
   api_key_env = "ANTHROPIC_API_KEY"
   ```

   List only providers whose key you have: if a variable named here is not set, the router does not start.

3. Start it with your keys in the environment and a key of its own for your clients:

   ```sh
   export OPENAI_API_KEY=…  ANTHROPIC_API_KEY=…
   export HAARNESS_ROUTER_KEY=$(openssl rand -hex 32)
   ./target/release/haarness-router --config config.toml --require-key
   ```

4. In the same shell, send it a request:

   ```sh
   curl http://127.0.0.1:8080/v1/chat/completions \
     -H "Authorization: Bearer $HAARNESS_ROUTER_KEY" \
     -H 'Content-Type: application/json' \
     -d '{"model":"openai/gpt-5-nano","messages":[{"role":"user","content":"Hello"}]}'
   ```

The router listens on `http://127.0.0.1:8080`, so only this machine can reach it. Models are named
`provider/model`, and `GET /v1/models` lists the ones your keys can use. Point any OpenAI-compatible client at
`http://127.0.0.1:8080/v1` with `HAARNESS_ROUTER_KEY` as the API key. Its dashboard is at
`http://127.0.0.1:8080/ui`: models, routes, live metrics and a playground.

To use its models in Desktop, the terminal UI or the CLI, [connect the router](https://haarness.ai/docs/providers/router.md).

## Add providers, aliases and fallback

Each `[providers.<name>]` section takes a `kind`:

| `kind` | Notes |
| --- | --- |
| `openai`, `anthropic`, `gemini` | Native adapters |
| `groq`, `mistral`, `xai`, `deepseek`, `together`, `fireworks` | Presets for OpenAI-compatible providers |
| `amazon-bedrock` | A Bedrock API key; set `base_url` for a region other than `us-east-1` |
| `openai-compatible` | Any other OpenAI-compatible server, such as a local model server: set `base_url` |

```toml
[providers.inhouse]
kind = "openai-compatible"
base_url = "https://llm.example.internal/v1"
api_key_env = "INHOUSE_API_KEY"
```

A plain `http://` address is refused unless the section also sets `allow_insecure_http = true`.

An alias gives clients one model name that tries several models in order:

```toml
[aliases.fast]
targets = ["openai/gpt-5-nano", "anthropic/claude-haiku-4-5-20251001"]
```

Clients ask for the model `fast`. The router moves to the next target only when one cannot be reached, or answers
`408`, `429` or a `5xx` before it has sent anything.

`config.all-providers.example.toml` in the router's source shows every provider type. Its README and `docs/`
folder cover model capabilities, `--set` overrides and the dashboard.

## Run it on a server

Before other machines can reach the router:

1. **Require a key** with `--require-key`, as above.
2. **Protect the dashboard, or turn it off.** By default `/ui` has no password, and it can create router keys: anyone
   who can open it can make a key for themselves, even with `--require-key`. Give it a username and password from
   two environment variables:

   ```toml
   [web_ui]
   basic_auth_username_env = "HAARNESS_UI_USERNAME"
   basic_auth_password_env = "HAARNESS_UI_PASSWORD"
   ```

   Or start the router with `--set web_ui.enabled=false` to serve the API only.
3. **Put HTTPS in front** with a reverse proxy that terminates TLS. Haarness connects to a router on another
   machine only over HTTPS, and the dashboard's password is only safe over HTTPS.
4. **Listen on another address** with `--host 0.0.0.0`, `--port 9090` or `--bind 10.0.0.5:8080`. Keep the default
   `127.0.0.1` when the proxy runs on the same machine.

## What it does with your keys

- **Provider keys come only from environment variables.** The config file and `--set` name the variables; there is
  no setting for a key's value. The router reads them once at startup and never returns or logs them.
- **Router keys stay with the router.** The key a client sends is checked and never forwarded to a provider.
  Keys created in the dashboard live in memory and are gone after a restart.
- **Callers cannot bring their own provider key** unless you set `allow_upstream_key_header = true` under
  `[auth]`. Then a request may carry one in `X-Upstream-API-Key`, and only for an exact `provider/model`.
- **It keeps nothing.** No prompts, no usage history, no billing. It logs one line per request, without the
  prompt, headers or keys.
- **The config is strict.** An unknown setting, a typo or a missing environment variable stops the router at
  startup, rather than failing a request later.

## Flags

| Flag | Environment variable | Effect |
| --- | --- | --- |
| `--config FILE` | `HAARNESS_ROUTER_CONFIG` | The config file, `config.toml` by default; `-` reads it from standard input |
| `--require-key` | | Require the key in `HAARNESS_ROUTER_KEY` |
| `--no-auth` | | Accept requests without a key, whatever the config says |
| `--host`, `--port`, `--bind` | `HAARNESS_ROUTER_HOST`, `_PORT`, `_BIND` | Listen address, `127.0.0.1:8080` by default |
| `--set PATH=VALUE` | | Change one config value; repeatable |
| `--unset PATH` | | Remove one config value or section; repeatable |

| Path | What it serves |
| --- | --- |
| `/v1/chat/completions`, `/v1/models` | The OpenAI-compatible API |
| `/ui` | The dashboard |
| `/metrics` | Prometheus metrics; they need a router key when keys are required |
| `/healthz`, `/readyz` | Health checks, always open |

## Next

Connect it to Haarness: [Haarness Router](https://haarness.ai/docs/providers/router.md).
