9Router adalah local proxy yang sit between AI coding tools (Claude Code, Cursor, Codex) dan provider LLM. Dengan token saver, auto-fallback, multi-account round-robin. Tutorial ini cover full lifecycle dari install sampai production rotation.
Install via npm → jalanin di server pakai 9router -p 20128 --no-browser -t -l → dashboard di localhost:20128/dashboard (password default 123456) → add provider (Kiro, Xiaomi, dll) → setup proxy if needed → connect AI tools ke localhost:20128/v1. Multi-account = sticky session + round-robin via dashboard.
9Router butuh Node.js 18+. Install via npm sebagai global package, jalanin sebagai background daemon biar tetap up.
# Prerequisite: Node.js 18+
$ node --version
v20.10.0
# Install 9router globally
$ npm install -g 9router
# Verify install
$ 9router --version
0.4.71
Jalankan sebagai daemon (headless mode wajib di server, biar gak exit langsung):
# Production: tray mode + log + no browser
$ 9router -p 20128 --no-browser -t -l
# Atau pake daemon mode
$ 9router serve --port 20128 --no-open --daemon
Tanpa flag -t (tray mode), proses 9router akan exit dalam beberapa detik dengan pesan "Exiting...". Di server headless WAJIB pake -t.
Verify running:
$ ss -tlnp | grep 20128
LISTEN 0 511 0.0.0.0:20128 *:* users:(("node",pid=12345))
$ curl http://localhost:20128/api/health
{"ok":true}
Buka dashboard: http://your-server:20128/dashboard (default password: 123456). Ganti password di Settings.
Proxy dipake biar request ke provider LLM lewat IP residential (anti rate-limit, bypass geo-block). Pake hasil dari tutorial T-001.
http://user:pass@gateway:port# Format yang accepted di 9router proxy field
http://user:[email protected]:6060
http://user-session-abc123:[email protected]:6060
socks5://user:[email protected]:7777
Pake sticky session ID per provider biar IP konsisten — less ban risk. Untuk rotation aggressive, ganti session di setiap restart 9router.
9Router support 40+ provider. Setiap provider butuh API key sendiri. Mulai dari satu dulu untuk test.
Add provider via dashboard:
Kalau API key lo prefix tp- (token plan), pake provider type "Xiaomi MiMo (Token Plan)" — BUKAN regular "Xiaomi MiMo". Salah pilih = 401 Invalid API Key.
Kiro punya 2 auth: API Key (ksk_*) untuk CLI saja, dan OAuth Refresh Token untuk 9router. ksk_* TIDAK bisa dipake di 9router — pake AWS Builder ID OAuth flow.
Setelah 9router jalan, configure AI tool (Claude Code, Cursor, Codex, Hermes Agent) buat point ke 9router — BUKAN langsung ke provider.
Get auth token:
$ cat ~/.9router/auth/* | head -c 64
a3f2b1c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1
Hermes Agent (~/.hermes/config.yaml):
model:
default: kr/claude-sonnet-4
provider: 9router
providers:
9router:
api_key: your-9router-auth-token # dari ~/.9router/auth/
api_mode: chat_completions
base_url: http://localhost:20128/v1
default_model: kr/claude-sonnet-4
Claude Code / Cursor / Codex: set environment variable atau config file:
$ export ANTHROPIC_BASE_URL="http://localhost:20128/v1"
$ export ANTHROPIC_API_KEY="your-9router-auth-token"
Setiap provider pake prefix model: kr/ Kiro, xm/ Xiaomi, or/ OpenRouter. Misal kr/claude-opus-4.8, xm/mimo-v2.5-pro. Cek list lengkap di dashboard → Models.
Untuk multi-account ops, lo butuh banyak proxy yang assigned ke setiap connection. Pake proxies.txt dari tutorial T-001.
Cara 1 — Dashboard (recommended): 9router punya import proxy bawaan, gak perlu sentuh DB.
http://user:pass@host:portCara 2 — Inject SQLite (advanced/bulk): kalau butuh ratusan sekaligus, inject langsung ke tabel proxyPools. Tiap row nyimpen konfigurasi sebagai JSON di kolom data.
# WAJIB backup DB dulu sebelum modify
$ cp ~/.9router/db/data.sqlite ~/.9router/db/data.sqlite.bak
$ python3 inject_proxies.py
# inject_proxies.py — schema asli 9router v0.4.71
import sqlite3, json, uuid
from pathlib import Path
from datetime import datetime, timezone
db = sqlite3.connect(Path.home() / ".9router/db/data.sqlite")
proxies = Path("proxies.txt").read_text().strip().split("\n")
now = datetime.now(timezone.utc).isoformat()
for proxy in proxies:
# proxyPools: id, isActive, testStatus, data(JSON), createdAt, updatedAt
data = json.dumps({
"name": f"Imported {proxy.split('@')[-1]}",
"proxyUrl": proxy,
"noProxy": "",
"type": "http",
"strictProxy": False,
})
db.execute(
"INSERT INTO proxyPools (id, isActive, data, createdAt, updatedAt) VALUES (?, 1, ?, ?, ?)",
(str(uuid.uuid4()), data, now, now)
)
db.commit()
print(f"Injected {len(proxies)} proxies")
9router cache config di memory — INSERT ke SQLite gak langsung kebaca. WAJIB restart biar DB di-reload: systemctl restart 9router (atau kill PID lalu start ulang). Verifikasi schema asli dulu sebelum inject: sqlite3 ~/.9router/db/data.sqlite ".schema proxyPools" — kolom bisa beda antar versi.
Penting dipisah dua konsep yang sering ketuker:
providerConnections.combos (id, name, kind, models, ...). Combo gak nyimpen API key/proxy/quota — itu murni model grouping.Step 1 — Add Connection per akun (Dashboard → Providers → [Provider] → Add Connection):
# Multi-akun Xiaomi MiMo: 3 connection di provider yang sama
Connection #1
Name: "luke-account-1"
API Key: tp-xxxxxxxxxxxx # cluster-specific key
Proxy: http://user-s1:pass@gateway:6060 # optional, per-connection
Priority: 1
isActive: true
Connection #2
Name: "luke-account-2"
API Key: tp-yyyyyyyyyyyy
Proxy: http://user-s2:pass@gateway:6060
Priority: 2
Connection #3
Name: "luke-account-3"
API Key: tp-zzzzzzzzzzzz
Proxy: http://user-s3:pass@gateway:6060
Priority: 3
Combo strategy (STEP 07) yang nentuin gimana 3 connection ini diputerin: fallback = primary dulu, drop kalau limit. round-robin = spread rata. sticky = session nempel.
Step 2 — Create Combo (Dashboard → Combos → New Combo): grouping model yang mau dipake klien.
# Schema asli: combos (id, name, kind, models)
# Data real dari DB live:
Combo: "KIRO"
models: [
"kr/claude-opus-4.8",
"kr/claude-opus-4.7",
"xmtp/mimo-v2.5-pro-claude"
]
Combo: "XIAOMI"
models: [
"xmtp/mimo-v2.5-pro",
"xmtp/mimo-v2-pro",
"xmtp/mimo-v2.5"
]
Klien (Claude Code, Cursor, dll) tinggal hit nama combo sebagai model — 9router otomatis pilih connection & model dari list itu sesuai strategy yang lo set.
Banyak yang nyari "field API key di combo" — itu gak ada. API key & proxy di-attach ke connection, bukan combo. Combo cuma nge-list model. Quota tracking pun di-track per connection (ada di providerConnections.data field modelLock_*, backoffLevel, lastUsedAt) — bukan per combo.
9Router punya 3 combo strategy buat distribute traffic antar connection (Settings → Combo Strategy):
# Settings → Combo Strategy (nilai asli di dashboard)
1. fallback → Pake combo utama, pindah ke berikutnya kalau gagal/limit (default)
2. round-robin → Cycle equal antar combo tiap request
3. sticky → Satu session nempel ke satu combo terus, anti-fingerprint
Pattern recommended untuk production:
Per-provider bisa di-override sendiri lewat providerStrategies (tiap provider punya fallbackStrategy masing-masing).
Aktifin di Settings → RTK. 9router bakal auto-compress tool_result output (git diff, grep, ls) sebelum kirim ke provider. Saving 20-40% token — free win.
Monitoring rotation: endpoint /api/* butuh session login (bukan curl polos — bakal 401). Buat health check pake /v1/models yang open, dan liat stats per-combo di dashboard → Usage.
# Health check (open, no auth) — konfirmasi router hidup + list model
$ curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost:20128/v1/models
HTTP 200
# Stats detail (requests per combo, token saved, latency)
# → buka dashboard: http://localhost:20128/dashboard → tab Usage
# /api/usage/stats butuh cookie session (login dulu), gak bisa curl langsung
Mostly aku belajar from production scars. Lo gak perlu repeat.
Tanpa -t flag, 9router exit dalam detik di headless server. Selalu pake: 9router -p 20128 --no-browser -t -l
Auth token yang dipake AI tools BUKAN dashboard password (123456). Token hex ada di ~/.9router/auth/ — panjang ~64 chars.
Connection OAuth (Kiro, etc) GAK persisten di SQLite. Setelah restart, harus re-add via dashboard. API key providers (Xiaomi, OpenRouter) survive restart.
Default port sama persis. Gak bisa run dua-duanya. Uninstall OmniRoute dulu atau pakai port berbeda: 9router -p 20129 ...
Token dari ~/.local/share/kiro-cli/data.sqlite3 GAK akan work di 9router Import Token. CLI pake AWS SSO DeviceCode flow, 9router butuh desktop IDE refresh token. Solusi: pake AWS Builder ID OAuth flow di dashboard.
Multi-account masih kena 429 dari Kiro/provider kalau ramean. Set delay antar request (Settings → Throttle) atau gunakan sticky session lebih lama biar kelihatan natural.