solrouter
v0.5.0
Published
Local privacy router for coding tools: pseudonymises every request before it leaves your machine, and sends work a frontier model would refuse to an uncensored open-weight model you control.
Maintainers
Readme
solrouter
A local routing proxy for your coding tools. It gives you the strongest model that will do your work, without handing that model anything it doesn't need to know about who the work is for.
Two things happen to every request, on your machine, before anything leaves:
- Pseudonymise. Names, emails, IBANs, cards, API keys, private keys, internal
hostnames, case references are swapped for realistic stand-ins (not
[REDACTED]— that makes the model dumber). The mapping table never leaves your machine. The provider does the thinking on a version of your problem with the identities changed; you get the answer back with the real values restored, locally. - Route. Most requests go to a frontier model. Work a frontier model won't do doesn't dead-end at a refusal — it routes to an open-weight model you control. Not by tricking the frontier model or disguising the request (that doesn't work and gets your account banned) — by sending it, openly, to a different model.
Every call leaves a receipt: a hash-chained record of what was masked, which model ran it, and a hash of exactly what the provider received. No PII in it.
P0 (this version). Prose masking, reactive refusal detection (send to frontier, detect a refusal, retry on the private leg), fuzzy + streaming-safe restore, receipts. Not yet: tool-call-argument masking, TEE-side classifier, task decomposition. See
SPEC.md.
Bring your own key — it's a drop-in, not a gateway
By default the frontier leg uses your own credentials, passed straight through.
The proxy inherits whatever your tool already uses — an ANTHROPIC_API_KEY, an
OPENAI_API_KEY, or a Claude Code subscription login — and needs no key of its own.
It's not reselling you frontier tokens; it sits in the middle, masks and routes, and
forwards your request under your own account.
A SolRouter account is only needed for the private/uncensored leg (the model you don't otherwise have access to) — so you can try masking + routing with your existing setup and no signup.
frontier leg → your own key / subscription (auth: "passthrough", the default)
private leg → SolRouter uncensored model (auth: "solrouter", needs login)Install & run
npx solrouter login # opens the browser — sign in with your subscription
npx solrouter code # starts the proxy AND launches Claude Code through it, one tabsolrouter code runs the proxy in-process and launches Claude Code already pointed
at it (same tab); when you quit Claude Code the proxy stops. Pass Claude Code args
straight through: npx solrouter code --resume. To run the proxy on its own instead
(point another tool at http://127.0.0.1:8787), use npx solrouter start.
login opens solrouter.com/cli/auth, you sign in with your SolRouter
subscription, and a key is minted and handed back to the CLI automatically — no
paste. (login --token sk_solrouter_... still works for CI / headless.) The private
leg then calls the backend's /agent route (open-weight model qwen3.8:27b).
Then point any OpenAI- or Anthropic-compatible tool at it — no code change:
| Tool | Setting |
|---|---|
| OpenAI SDK / most tools | base URL → http://127.0.0.1:8787/v1 |
| Anthropic SDK / Claude Code | base URL → http://127.0.0.1:8787 |
| Cursor / Cline / Continue (VS Code) | set the custom OpenAI base URL to http://127.0.0.1:8787/v1 |
Example:
export OPENAI_BASE_URL=http://127.0.0.1:8787/v1
# ...run your tool as usualPer-request control (headers)
You keep the agency — override the router per call:
| Header | Effect |
|---|---|
| x-route: frontier | force the frontier model |
| x-route: private | force the open model you control |
| x-route: auto (default) | frontier, falling back to the open model on a refusal |
| x-mode: private | private leg for everything this request |
| x-intent: <text> | attach a one-line intent note to the frontier leg only — for legitimately-scoped work (e.g. an authorized pentest) that a frontier model will do once it has context. Trades some privacy for the stronger model; never sent to the private leg. |
Commands
solrouter login [--token <key>] verify account (browser), or paste a key
solrouter start [--port N] [--mode auto|frontier|private]
solrouter status config + login state
solrouter verify check the receipt hash-chain
solrouter logoutConfig
Copy config.example.json to ~/.solrouter/config.json or
./solrouter.config.json. Set frontier / private base URLs, models, and
your gazetteer of names. With viaSolrouter: true the legs go through the
SolRouter backend (which holds provider keys and does x402 billing); otherwise set
provider keys via ANTHROPIC_API_KEY / OPENAI_API_KEY / SOLROUTER_API_KEY.
What this will not do
It never reformulates, splits, or disguises a request to make a frontier model
comply with something it would otherwise refuse. Refused work goes to a different
model, openly. That's the honest and durable answer — and it's what keeps your
account, and everyone else's on the same backend, from being banned. See SPEC.md §7.
Develop
node --test 'test/*.test.js'Note
src/pseudonymise.js is vendored from routerlabs/lib/pseudonymise.js — keep both
in sync until it's published as a shared package. Any dependency added to the
masking path is a deployment blocker (it has to run inside the user's perimeter).
