go build -o dispatch ./cmd/dispatchgo test -count=1 -timeout=60s ./...docker build -t dispatch:rc .mkdir -p ./config
docker run -d --name dispatch \
-p 18087:18087 \
-e OPENROUTER_API_KEY=sk-or-... \
-v ./config:/config \
dispatch:rc(Optional non-root: add --user 65532:65532 and chown 65532:65532 ./config.)
mkdir -p secrets
cp .env.example secrets/dispatch.env
# Edit secrets/dispatch.env with your OpenRouter API key
$EDITOR secrets/dispatch.env
docker compose up -d --buildenv_file: ./secrets/dispatch.env loads the key into the container. The read-only secrets directory mount allows API-key hot reload even when editors replace the file atomically.
OPENROUTER_API_KEY=sk-or-... ./dispatch --config /path/to/router.yaml| Setting | Value |
|---|---|
| Base URL | http://localhost:18087/v1 |
| API Key | placeholder (server-side) |
| Model | dispatch/auto |
# Smoke test (no API key needed)
./scripts/smoke.sh http://localhost:18087
# Health check
curl http://localhost:18087/health
# Debug classification
curl -s -X POST http://localhost:18087/debug/route \
-H "Content-Type: application/json" \
-d '{"model":"dispatch/auto","messages":[{"role":"user","content":"write a function"}]}'
# Stats
curl -s http://localhost:18087/debug/stats | jq| Symptom | Cause | Fix |
|---|---|---|
permission denied on first run |
/config not owned by UID 65532 |
chown 65532:65532 ./config |
OPENROUTER_API_KEY environment variable not set |
Missing API key | Set -e OPENROUTER_API_KEY=... or use secrets/dispatch.env |
| Upstream 401 | Invalid/expired API key | Check key at openrouter.ai/keys |
| Upstream 401 ("Missing Authentication header") | Dispatch not sending Authorization; env var missing or config mismatch | docker inspect env, check api_key_env in router.yaml, check api_key_present in /debug/stats and startup log |
| Upstream 429 | Rate limited | Wait or upgrade plan |
| "Provider returned error" | Provider rate-limited/down, fallbacks disabled | Enable allow_fallbacks: true or empty provider.order: [] |
| 502/503 from OpenRouter | Provider unavailable | Let OpenRouter fallback; check /debug/stats for patterns |
| Port 18087 in use | Conflicting service | Change server.listen and port mapping |
OpenCode shows only dispatch/auto |
Expected behavior | Check X-Dispatch-Model header for actual model |
| OpenRouter shows App = Unknown | Empty http_referer/site_title | Set both in config; see README troubleshooting |