Saltar al contenido
ES EN

Build your own AI model router with Rebel Router

Lab · Local AI

30 min intermediate level validated configuration

Lab architectureComponents: Claude Code, OpenCode, Rebel Router, Budgets, Providers, OpenCode Zen, FinOps ledger.Anthropic APIOpenAI APIruleschosen modelno own keycostClaude Code: step 5Claude CodeANTHROPIC_BASE_URLOpenCode: step 6OpenCoderebel providerBudgets: step 3Budgetsrebel.yamlRebel Router: step 4Rebel Router127.0.0.1:8787Providers: step 2Providersyour keys (.env)OpenCode Zen: step 2OpenCode Zenone key, manyFinOps ledger: step 4FinOps ledgerSQLite + dashboard
  1. Claude Code · step 5 ANTHROPIC_BASE_URL
  2. OpenCode · step 6 rebel provider
  3. Rebel Router · step 4 127.0.0.1:8787
  4. Budgets · step 3 rebel.yaml
  5. Providers · step 2 your keys (.env)
  6. OpenCode Zen · step 2 one key, many
  7. FinOps ledger · step 4 SQLite + dashboard

Install Rebel Router from GitLab, connect it to Claude Code and OpenCode, set budgets, and check which model answered and what it cost.

What you will build and why

Claude Code and OpenCode send almost everything to the same model: the one you picked when the session started. That includes writing a test, summarising a file or updating a README, jobs a much cheaper model handles just as well. Rebel Router is a local proxy that sits in between: it looks at each request, decides what kind of task it is (orchestration, complex code, bulk code, unit tests, QA or documentation), picks among the three models that fit best on quality, price and speed, and writes the cost to a SQLite ledger with budgets.

In this lab you install it from its GitLab repository, bring your own keys (or a single OpenCode Zen key), connect it to Claude Code and OpenCode, set a daily and a per-project budget, and check which model really answered and what it cost. Everything runs on your machine; the router only listens on 127.0.0.1.

One warning before you start: in our 12-task benchmark, letting the router choose cost 82% less than using Claude Opus 5.5 for everything, but it passed 9 of 12 tasks against 11 of 12. It is a first measurement on a small sample. That is why the lab also shows you how to keep on Opus whatever you do not want to risk.

Lab: Build your own AI model router with Rebel Router

Requirements

SystemmacOS or Linux (Windows with WSL)
Node.js22.13
Rebel Router0.1.0
Claude Code2.1.295
OpenCode1.19.0
RAM512 MB free
Disk300 MB

Step by step

  1. Clone and install Rebel Router

    You need Node.js 22.13 or later. The package has a single runtime dependency. npm install builds the code and npm link puts the rebel command on your PATH.

    If npm link asks for permissions, your global npm prefix belongs to the system. Use a Node version manager, or run the router from the clone with npx rebel start.

    git clone https://gitlab.com/larebelion/rebel-router.git
    cd rebel-router
    npm install
    npm link
    rebel models
  2. Bring your own keys (or one OpenCode Zen key)

    Rebel Router ships with no keys: it uses yours, and only for the provider it picks for each request. Copy the example to .env and fill in the ones you have. A provider without a key is skipped.

    If you would rather not have one key per vendor, an OpenCode Zen key in OPENCODE_API_KEY reaches GPT, Grok, DeepSeek, Kimi, GLM and Qwen. By default Zen is only used for vendors without a key of their own, and never for Claude. Gemini is not on Zen and still needs GEMINI_API_KEY. VENICE_API_KEY is optional: it turns on the JEV classifier (beta); without it the router classifies with local rules.

    cp .env.example .env
    chmod 600 .env

    File .env

    ANTHROPIC_API_KEY=CHANGE_ME_ANTHROPIC_API_KEY
    OPENAI_API_KEY=
    XAI_API_KEY=
    GEMINI_API_KEY=CHANGE_ME_GEMINI_API_KEY
    DEEPSEEK_API_KEY=
    OPENCODE_API_KEY=CHANGE_ME_OPENCODE_ZEN_KEY
    VENICE_API_KEY=

    Replace every CHANGE_ME_… value with your own before starting: never keep example passwords or keys.

  3. Set budgets and rules

    The configuration lives in ~/.rebel-router/rebel.yaml (or in ./rebel.yaml in the directory you start the router from). This file caps spend at 10 dollars a day and 3 for the shop project, prefers cheaper models from 80% of a budget, and at 100% drops to the cheapest acceptable model (downgrade) instead of cutting you off.

    Two rules worth knowing from day one: aliases.opus: passthrough sends whatever you ask Opus for to Opus unchanged, and localOnly keeps a project on your machine (Ollama models only). If you would rather have a hard stop, set onLimit to block and the router answers HTTP 402.

    mkdir -p ~/.rebel-router

    File ~/.rebel-router/rebel.yaml

    server:
      host: 127.0.0.1
      port: 8787
    routing:
      excludeProviders: []
      aliases:
        opus: passthrough
        haiku: hint
    budgets:
      daily: 10
      projects:
        shop: 3
      onLimit: downgrade
      downgradeAt: 0.8
    projects:
      secret-client:
        localOnly: true
    opencode:
      gateway: auto
  4. Start the router

    Load .env into the environment and start it. The router listens on http://127.0.0.1:8787, serves its dashboard on /dashboard and writes every request to ~/.rebel-router/ledger.db. Leave it in a terminal of its own or run it as a service.

    The providers line of the start-up banner says which vendors have a key and which go through Zen. Read it: if one you expected is missing, .env was not loaded.

    set -a; . ./.env; set +a
    rebel start

    Expected output

    Rebel Router 0.1.0 listening on http://127.0.0.1:8787
      classifier: local rules (set VENICE_API_KEY to enable JEV)
      dashboard:  http://127.0.0.1:8787/dashboard
      Claude Code: ANTHROPIC_BASE_URL=http://127.0.0.1:8787   OpenCode: baseURL http://127.0.0.1:8787/v1
  5. Connect Claude Code

    rebel init claude --global merges its settings into ~/.claude/settings.json (after saving a settings.json.rebel-backup copy, and touching nothing else). It points ANTHROPIC_BASE_URL at the router, turns on the headers that say whether a call comes from the main session, a subagent or a compaction, and adds the hooks, the status line and the /rebel-spend and /rebel-top commands.

    With --global the CLI, claude -p, subagents, IDE extensions and the Agent SDK with user settings all go through the router. For per-project budgets, also install it in each project with --project, so every request carries the x-rebel-project header. Restart Claude Code afterwards.

    Mind what you will see: Claude Code keeps showing the model it asked for (Opus, say); the one that really answered is on the status line. No hook can change the session model, which is why the proxy does the switching.

    rebel init claude --global
    cd ~/code/shop
    rebel init claude --project shop
  6. Connect OpenCode

    In OpenCode the router is declared as one more provider. rebel init opencode writes opencode.json with the rebel provider and a plugin under .opencode/plugins/. Then pick the model rebel/auto with /models, or rebel/auto:unit_tests to pin the task class.

    The integration targets OpenCode 1.19, the stable release. The 2.0 preview builds changed the plugin format and did not load the provider.

    cd ~/code/shop
    rebel init opencode --project shop
  7. The two cases you configure by hand

    Claude desktop app (Code tab). It does not read ANTHROPIC_BASE_URL from the settings files. Go to Developer → Configure Third-Party Inference and set the base URL to http://127.0.0.1:8787.

    VS Code extension. The Claude Code it spawns does read the settings files, but the extension's own login check does not. Add the variable to your VS Code user settings.

    File settings.json (VS Code user)

    {
      "claudeCode.environmentVariables": [
        { "name": "ANTHROPIC_BASE_URL", "value": "http://127.0.0.1:8787" }
      ]
    }

Check that it works

  1. See how it classifies and what it would pick

    rebel classify shows the class and complexity it gives an instruction, without calling any model. rebel top lists the three models the Broker would pick for that class, with the reason. You can pin a class by writing #rebel:tests, #rebel:docs or #rebel:complex in a prompt.

    rebel classify "Write vitest unit tests for the slugify function"
    rebel top --task tests

    Expected output

    {
      "taskClass": "unit_tests",
      "complexity": 0.15,
      "confidence": 1,
      "source": "rules",
  2. Check that it routes and that the spend adds up

    Work for a while in Claude Code or OpenCode, then read the ledger. rebel spend groups spend by model, task class, project or day; rebel export writes a CSV with every request (requested model, model used, tokens, cost, latency and the reason for the pick). The dashboard at http://127.0.0.1:8787/dashboard shows the same as charts.

    To reconcile, compare one day's total with your provider's billing console. Cost is the token usage each provider reports times the official price in the catalogue, so it should match unless there are discounts or surcharges the catalogue does not know about.

    rebel status
    rebel spend --today --by model
    rebel spend --today --project shop --by task_class
    rebel export --days 1 > spend.csv
If something fails

Claude Code does not go through the router

Check that the variable is in the settings and that the router is running. Inside Claude Code, /status shows the base URL in use: a launcher or a variable exported in your shell can win over the user settings. The desktop app and VS Code need the manual change from step 7.

grep ANTHROPIC_BASE_URL ~/.claude/settings.json
rebel status

Requests fail with HTTP 402

You hit a budget with onLimit: block. See which project spent it, raise the limit in rebel.yaml, or switch to downgrade so the router drops to a cheaper model instead of refusing.

rebel spend --today --by project

HTTP 508 when chained with another gateway

The router detected a loop: a provider URL points back at the router itself. If you use a corporate gateway, put its URL in providers.anthropic.baseUrl in rebel.yaml and keep Claude Code pointed at the router.

OpenCode says the model rebel/auto is unavailable

This is usually an OpenCode 2.0 preview build. Go back to the stable 1.19 and repeat step 6.

Harden the setup

Keep the router local

Leave server.host at 127.0.0.1: the router holds your keys and refuses requests with a non-local Host, a browser Origin from another site or a non-JSON body, so no web page can spend your credit. Do not expose it on the network.

Critical work on the model you choose

The benchmark showed where cheap routing fails: unit tests and QA. Use aliases.opus: passthrough so Opus requests are left alone, classModels to pin a model per task class, and localOnly for projects whose code must not leave your machine.

What JEV sees

If you turn JEV on, Venice only receives a summary: the first 600 characters of your latest instruction, the approximate context size, tool names and file extensions. Never the code. With classifier.jev.summary.maxChars: 0 no text is sent, and without VENICE_API_KEY it is not used at all.

Clean-up: undo the lab

Remove the router and go back to where you were

--uninstall removes only what Rebel Router added and puts back the values it replaced. Then uninstall the command and, if you do not want to keep your spend history, delete ~/.rebel-router (the ledger and your rebel.yaml live there). In OpenCode, remove the rebel provider from opencode.json.

rebel init claude --global --uninstall
npm rm -g rebel-router
rm -r ~/.rebel-router

What is verified

  • .env: no automatic validator, review it by hand
  • ~/.rebel-router/rebel.yaml syntax (YAML parser)
  • settings.json (VS Code user) syntax (JSON parser)
  • Syntax of 12 command block(s) (bash -n in an isolated container, not executed)
  • Versions (4), options (7) and config keys (28) checked against 8 official documentation pages
  • Install from GitLab (git clone, npm install, npm link) and the repository's 90 tests passing
  • Router against a fake provider (scripts/mock-provider.mjs): Anthropic API requests classified and routed, rebel spend, rebel status, rebel export and the /dashboard page
  • Budget with onLimit: block: the request past the limit gets HTTP 402
  • rebel init claude --global and --uninstall on an existing settings.json (what was there is kept) and rebel init opencode
  • Real Claude Code 2.1.295 (claude -p with a subagent) through the router against the fake provider (npm run smoke:claude)
  • 12-task benchmark with 10 models and real keys through a gateway (results in bench/results/latest.json)
  • With your own provider keys, the desktop app and the VS Code extension: not run here, check them on your machine

Automatic checks run on 11 October 2026. Items marked "·" were not executed: check them in your own environment.

Sources

Article generated with AI.larebelion

Kernel

· Head of technology · Spain

“First measure where the money goes; then let the router decide.”

Comentarios

Publicar un comentario