Enterprise-grade AI routing gateway ā 40+ LLM providers, PostgreSQL 17, OpenAI-compatible API
2.1K
docker pull rickicode/axonrouter:latest
Every release is published with an immutable sha-<short> tag, a semver tag on
tagged releases, and latest tracking the release branch.
ghcr.io/rmz17x/axonrouter carries the identical image as a mirror.
Connect all your AI coding agents to 40+ LLM providers with pure PostgreSQL 17 concurrency, dedicated Hono API gateway, and zero file-lock bottlenecks.
ā” AxonRouter vs 9Routerā ⢠š Quick Start (Docker)ā ⢠š” Featuresā ⢠š ļø Supported Toolsā ⢠š Providersā ⢠š Setupā
AxonRouter is an enterprise-grade, high-concurrency fork of decolua/9router (9Router)ā . While 9Router targets local single-user developer setups using embedded SQLite (data.sqlite), AxonRouter is purpose-built for heavy multi-agent concurrency, containerized environments, and production deployments.
Fork Lineage:
decolua/9router(local SQLite) ārickicode/9router-Xā AxonRouter (Pure PostgreSQL 17 SSOT + Dedicated Hono Gateway cluster).
| Architectural Component | 9Router Original | AxonRouter (Enterprise Edition) |
|---|---|---|
| Database Engine | SQLite file-based (better-sqlite3 / sql.js) ā locks on parallel writes | Pure PostgreSQL 17 ā connection pooling via postgres.js, ACID transactions, zero file locks |
| API Gateway Layer | Integrated inside Next.js server event loop | Dedicated Hono API Gateway (gateway/server.js) ā multi-worker Node cluster on port 3778 |
| Cache & Cooldowns | In-memory Map (unindexed, wipes on reload) | In-Memory Speed Layer (memoryStore.js) ā TTL keys, owner-token mutexes, zero hot-path DB tax |
| Usage Logging | Monolithic unpartitioned tables | Native Monthly Range Partitioning (usage_history, request_details) with auto-rolling tables |
| Failover & Recovery | Basic sequential fallback | Combo Fusion, Difficulty Scoring & Circuit Breaker ā negative-availability memoization |
| Client Error Isolation | Client 4xx errors can lock provider accounts | Strict 4xx Error Isolation ā client errors (400, 404, 413, 499) never freeze upstream accounts |
| Deployment Model | Local Single-User CLI / Web | Docker Compose Production Stack (Web Dashboard 3777 + Hono Gateway 3778 + PostgreSQL 17) |
The public LLM API (/v1/*) runs in a dedicated Hono gateway process (axonrouter-api, port 3778), fully separated from the dashboard (axonrouter-web, port 3777). Heavy streaming traffic from dozens of concurrent coding agents never contends with the dashboard's event loop.
/v1/chat/completions, /v1/messages, /v1/models, /v1/embeddings, audio/video endpoints.GATEWAY_WORKERS=4) across CPU cores.@/... module alias resolver (gateway/alias-resolver.mjs, registered at boot) with no bundler/webpack pass.3778 for low-latency streamingIncoming Request (Claude Code, Cursor, Codex, OpenClaw, Cline...)
ā
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Port 3778: Dedicated Hono API Gateway (Cluster Mode) ā
ā or Port 3777: Standalone Hono server (Vite SPA build) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā src/sse/services/auth.js ā
ā ⢠Provider / combo credential resolution via mutex locks ā
ā ⢠Negative-availability pre-check (fast-fail on exhaustion)ā
ā ⢠LKG (Last-Known-Good) route memory ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā open-sse/handlers/chatCore.js ā
ā ⢠Format detection (OpenAI / Claude / Gemini / Kiro) ā
ā ⢠Bidirectional wire format normalization & translation ā
ā ⢠Upstream dispatch, retry backoff & stream forwarding ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā
ā¼ ā¼
āāāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāā
ā PostgreSQL 17 ā ā In-Memory Speed ā
ā (Partitioned DB) ā ā Layer (TTL Maps) ā
āāāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāā
AxonRouter ships as a containerized stack: Docker is required (the installer will offer to install it for you if missing). Running without Docker is supported only for manual local development ā see Local Node.js Developmentā .
The production stack orchestrates:
http://localhost:3778http://localhost:3777valkey/valkey:8-alpine image ā realtime speed layer, no persistence)5432 (private to Docker network)Everything lives in one compose file (docker-compose.yml). Three switches pick the topology:
| What | Command |
|---|---|
| Split stack (default) | docker compose up -d |
| Built-in PostgreSQL 17 | COMPOSE_PROFILES=builtin-db docker compose up -d |
| Combined ā API + web in ONE container | docker compose --profile combined up -d axonrouter |
The combined container runs both servers under one supervisor (docker/entrypoint-combined.mjs): a dead web server fails the container so Docker restarts it, a crashed gateway is restarted within a budget, and SIGTERM reaches both.
All published GHCR images are multi-arch (linux/amd64 + linux/arm64) ā the same compose stack works on x86 servers and ARM boxes (Raspberry Pi, Graviton, Ampere).
Every push to main / release (and every v* tag) rebuilds the images and pushes them to GitHub Container Registry:
| Image | What it runs | Ports |
|---|---|---|
rickicode/axonrouter-web:latest | Web Dashboard + Control Plane | 3777 |
rickicode/axonrouter-api:latest | Standalone Hono API Gateway | 3778 |
rickicode/axonrouter:latest | Combined ā both in one container | 3777, 3778 |
Two pipelines publish the same three images, so the registry is correct no matter which remote you push to: GitLab CI (.gitlab-ci.yml, the source of truth) and GitHub Actions (.github/workflows/docker-publish.yml, the mirror). See DOCKER.mdā for the required CI variables and the tag policy.
The installer does everything interactively without needing to clone the git repository:
Install Docker now? (yes/no) and installs it automatically via get.docker.com.docker-compose.yml and .env.example directly from GitHub into ~/AxonRouter. Zero git clone required..env (JWT_SECRET, API_KEY_SECRET, ENCRYPTION_KEY, POSTGRES_PASSWORD, INITIAL_PASSWORD) ā press Enter to accept auto-generated values.Start AxonRouter now? (yes/no) and launches the stack with docker compose up -d using pre-built multi-arch GHCR images.curl -sSL https://raw.githubusercontent.com/rickicode/AxonRouter/main/scripts/install.sh | sh
docker-compose.yml location)The default install path is ~/AxonRouter (your home directory) ā wherever you run the command. Pass an explicit path as the first argument to override it:
| Environment | Command | Resulting compose file |
|---|---|---|
| Default (any OS user) | run the installer | ~/AxonRouter/docker-compose.yml |
| Custom location | curl ⦠| sh -s -- /your/path | /your/path/docker-compose.yml |
Default example (installs to ~/AxonRouter):
curl -sSL https://raw.githubusercontent.com/rickicode/AxonRouter/main/scripts/install.sh | sh
Native PowerShell installer (recommended ā installs Docker Desktop via winget if missing, generates secrets, starts the stack):
irm https://raw.githubusercontent.com/rickicode/AxonRouter/main/scripts/install.ps1 | iex
C:\Users\<you>\AxonRouter\docker-compose.yml& ([scriptblock]::Create((irm https://raw.githubusercontent.com/rickicode/AxonRouter/main/scripts/install.ps1))) -Path D:\AxonRouter\\wsl$\<distro>\home\<user>\AxonRouter\docker-compose.yml| Port | Service | Deskripsi & Contoh URL |
|---|---|---|
3777 | Web Dashboard & Control Plane | http://localhost:3777 (manajemen router, provider API keys, proxy pool, metrics) |
3778 | High-Throughput Hono API Gateway (/v1) | Endpoint cepat multi-worker untuk AI agent (Cursor, Claude Code, Cline): ⢠http://localhost:3778/v1/chat/completions (OpenAI format)⢠http://localhost:3778/v1/messages (Anthropic format)⢠http://localhost:3778/v1/models (Daftar model aktif) |
Base URL Klien: Untuk menghubungkan Cursor, Claude Code, Cline, atau SDK OpenAI, arahkan Base URL ke:
http://localhost:3778/v1
Jika Anda lupa password dashboard, Anda bisa meresetnya kapan saja dengan perintah satu baris:
# Reset ke default (12345677):
curl -sSL https://raw.githubusercontent.com/rickicode/AxonRouter/main/scripts/reset-password.sh | sh
# Atau reset ke password baru pilihan Anda:
curl -sSL https://raw.githubusercontent.com/rickicode/AxonRouter/main/scripts/reset-password.sh | sh -s -- "password_baru_anda"
irm https://raw.githubusercontent.com/rickicode/AxonRouter/main/scripts/reset-password.ps1 | iex
All compose commands run from the install directory:
cd ~/AxonRouter # default install location
docker compose up -d # start
docker compose ps # status
docker compose logs -f # logs
docker compose down # stop
1. Install Docker (skip if the installer already offered and you accepted):
curl -sSL https://get.docker.com | sh
2. One-command install (recommended):
sh install.sh
The installer ships inside the image: it pulls rickicode/axonrouter:latest, extracts docker-compose.yml + .env.example, generates secrets, and starts the stack. No git clone needed.
Configure .env with your secure secrets (e.g. via openssl rand -hex 32):
JWT_SECRET ā Session cookie signing keyINITIAL_PASSWORD ā Initial dashboard admin passwordAPI_KEY_SECRET ā HMAC secret for gateway tokensPOSTGRES_PASSWORD ā PostgreSQL database passwordDATABASE_URL ā PostgreSQL connection string3. Launch Container Stack:
# Run production stack using GHCR images:
docker compose up -d
# Or build locally from source (same compose file):
docker compose build && docker compose up -d
# Or run API + web in ONE container:
docker compose --profile combined up -d axonrouter
http://localhost:3777/dashboardhttp://localhost:3778/v1AxonRouter intentionally separates the web dashboard and the public LLM API into two independent processes:
| Aspect | Dashboard (Port 3777) | API Gateway (Port 3778) |
|---|---|---|
| Process | Hono server serving the Vite SPA (dist/) | Standalone Hono multi-worker cluster |
| Entry | server.js ā src/server/webServer.mjs | gateway/server.js |
| Purpose | Management UI, settings, live observability | High-throughput /v1/* inference routing |
| Concurrency | Single Node event loop | Node cluster across CPU cores (GATEWAY_WORKERS) |
| Failure isolation | Dashboard restart never drops live agent streams | Gateway crash never kills the dashboard |
Advantages of this split:
Local Docker networks and agentic workspaces can reach the gateway via container DNS:
http://axonrouter-api:3778/v1, or from WSL/VM hosts viahttp://host.docker.internal:3778/v1.
AxonRouter supports direct credential rotation and wire translation across 40+ providers:
docker compose up -d
Mode manual ā hanya untuk pengembangan lokal. Produksi berjalan dengan Docker saja. Membutuhkan Node.js 22+ dan PostgreSQL 17 yang sudah berjalan:
# 1. Install dependencies
npm install
# 2. Configure environment
cp .env.example .env
# Set DATABASE_URL=postgres://axonrouter:password123@localhost:5432/axonrouter
# 3. Start development servers
npm run server # Web + API server on port 3777 (run `npm run build` first)
npm run dev # Vite dev UI on port 5173, proxying /api and /v1 to :3777
node gateway/server.js # Standalone Hono Gateway on port 3778
AxonRouter is open-source software licensed under the MIT Licenseā .
src/app/api/credentials/export/route.js). The TypeScript route (added in 7de8177) was invisible to src/server/routeLoader.mjs, which only loads route.js files, so GET /api/credentials/export fell through to the SPA fallback 404 even though the file shipped in the image. This adds the production-served route.js twin. Its auth verifies the live dashboard JWT session (verifyDashboardAuthToken) instead of the .ts version's PASETO check ā the paseto package is not installed and the login issues JWT HS256 cookies, so the PASETO check could never validate a real session. The audit-log line is skipped (audit/settings modules are .ts-only); remote callers always need a valid dashboard cookie.open-sse/services/proxyAutoFetcher.js). last_fetched_at is only bumped on a successful fetch, so a group whose fetch_url answers 503 (the BrightData DC link, dead at the source while its sibling ispxx1 still answers 200) stayed permanently "due": 1440 failed fetches a day, each an HTTP call plus a DB round-trip and a fresh log line ā 50% of all log output, and the source of every EHOSTUNREACH/CONNECT_TIMEOUT on the Postgres host. Failures now back off 1m ā 2m ā 4m ⦠capped at the group's own interval (never below 30m) and log once per distinct error message, with an explicit recovery line when the fetch works again. The group keeps its existing pools while the feed is down; health sweeps still test them.open-sse/services/tokenRefresh.js, tokenRefresh/providers.js, src/sse/services/backgroundTokenRefresh.js). qoder accepts only a long-lived personal access token and exposes no token endpoint, so every 60s tick selected its connection and re-fired refreshAccessToken, which could only answer No refresh URL configured ā 192 warnings in 6 hours for one connection. Selection now requires isRefreshableProvider() (a dedicated handler, or a resolvable refresh URL). The provider's own warning stays for the on-request path, where it is genuinely actionable.request_details retention constant aligned (src/lib/db/repos/requestDetailsRepo.js). The repo's fallback cap said 200 while DEFAULT_SETTINGS.observabilityMaxRecords said 1000, so the effective row window depended on whether a settings row existed ā and a prune that was working correctly looked broken (1001 rows instead of 200). Both are 1000 now, documented as a row window (roughly the last 3ā4 hours at current traffic), not a time window; long-range analytics belong to usage_history.open-sse/services/combo.js, open-sse/services/jevUpstream.js). A chain answered by a judge hop was logged as tier=<x> (jev), hiding which classifier ran, and a judge-only chain logged 0 of 2 upstream(s) usable on every request even while classifying correctly.background-token-refresh gains a regression case for a personal-access-token provider being skipped (qoder vs grok-cli); bg-refresh-disable-dead mock updated for the new selector gate. 47 tests green across the five affected files.Content type
Image
Digest
sha256:802fc8b87ā¦
Size
116.7 MB
Last updated
about 20 hours ago
docker pull rickicode/axonrouter-web