TUTORIAL T-002 · AI ROUTER · INFRA

Cara Memakai 9Router

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.

PORT: 20128 VERSION: 0.4.71 STEPS: 07 LEVEL: Intermediate
— TL;DR

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.

STEP 01

Setup & Install di Server

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
— PITFALL

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.

STEP 02

Setup Proxy di 9Router

Proxy dipake biar request ke provider LLM lewat IP residential (anti rate-limit, bypass geo-block). Pake hasil dari tutorial T-001.

  1. Dashboard → Settings → Proxy
  2. Enable "Use proxy for outbound requests"
  3. Pilih protocol: HTTP atau SOCKS5
  4. Paste proxy URL format: http://user:pass@gateway:port
  5. Test connection → Save
# Format yang accepted di 9router proxy field http://user:[email protected]:6060 http://user-session-abc123:[email protected]:6060 socks5://user:[email protected]:7777
— TIPS

Pake sticky session ID per provider biar IP konsisten — less ban risk. Untuk rotation aggressive, ganti session di setiap restart 9router.

STEP 03

Import API Key (Single Provider)

9Router support 40+ provider. Setiap provider butuh API key sendiri. Mulai dari satu dulu untuk test.

Add provider via dashboard:

  1. Dashboard → Providers+ Add Provider
  2. Pilih provider (e.g. Xiaomi MiMo, Kiro AI, OpenRouter, dll)
  3. Pilih connection type: API Key atau OAuth
  4. Paste API key, beri name (label), save
— PITFALL: Xiaomi MiMo

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.

— PITFALL: Kiro AI

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.

STEP 04

Bikin Command Setup untuk AI Agent

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"
— MODEL PREFIX

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.

STEP 05

Import Bulk Proxy

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.

  1. Dashboard → Proxy PoolsImport
  2. Paste list proxy (satu URL per baris): http://user:pass@host:port
  3. Save → test connection per proxy (status kelihatan di list)

Cara 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")
— PITFALL: Direct write di-ignore tanpa restart

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.

STEP 06

Setup Connections & Combos

Penting dipisah dua konsep yang sering ketuker:

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.

— PITFALL: Combo ≠ Connection

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.

STEP 07

Rotate Proxy & Round-Robin Strategy

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:

  1. fallback sebagai primary — subscription dulu, drop ke cheap/free kalau kena limit
  2. round-robin kalau punya banyak akun setara & mau spread rata (anti-rate-limit)
  3. sticky per chat session biar context konsisten dari satu provider

Per-provider bisa di-override sendiri lewat providerStrategies (tiap provider punya fallbackStrategy masing-masing).

— RTK TOKEN SAVER

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
— COMMON PITFALLS

Things That Break

Mostly aku belajar from production scars. Lo gak perlu repeat.

Process exit langsung

Tanpa -t flag, 9router exit dalam detik di headless server. Selalu pake: 9router -p 20128 --no-browser -t -l

Auth token salah

Auth token yang dipake AI tools BUKAN dashboard password (123456). Token hex ada di ~/.9router/auth/ — panjang ~64 chars.

OAuth provider lost on restart

Connection OAuth (Kiro, etc) GAK persisten di SQLite. Setelah restart, harus re-add via dashboard. API key providers (Xiaomi, OpenRouter) survive restart.

Port 20128 conflict dengan OmniRoute

Default port sama persis. Gak bisa run dua-duanya. Uninstall OmniRoute dulu atau pakai port berbeda: 9router -p 20129 ...

Kiro CLI refresh_token ≠ IDE refresh_token

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.

429 rate-limit dari upstream

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.