dsh-model-proxy
English | 中文
Route DeepSeek Harness model API requests through a proxy so third-party overseas model providers work from mainland China, while requests outside the domains you list stay direct — web search, page fetching, MCP servers and tool calls are not proxied unless you add their domains.
- Does not patch any harness file; uninstalling restores the original behaviour exactly
- System proxy, custom proxy (
http/https/socks4/socks4a/socks5/socks5h), or env-based - Global or per-domain routing; domain rules accept exact hosts, wildcards and suffixes
- Three safety guarantees that configuration cannot loosen (below)
Install
dsh plugin --profile <your-profile> add github:recyvan/dsh-model-proxy
Restart DSH afterwards — the host half loads inside the harness process.
Requires DSH 0.2.1-rc.2 or newer. The settings page is generated by the host from the
plugin's Config schema (0.2.x removed the settings service and the client-side
settingsScope service), so older harnesses are rejected at install time.
Configure
Open Settings — Model Proxy (its own page in the settings sidebar).
| Field | Meaning |
|---|---|
| Mode | rule (recommended) / system / custom / off |
| Proxy protocol | http / https / socks5 / socks5h / socks4 |
| Proxy address | socks5h://127.0.0.1:7890, or just 127.0.0.1:7890 (scheme filled in from the protocol field) |
| Proxy only these domains | empty = built-in preset; filled = proxy only these (comma separated, *.openai.com, .aiplatform.googleapis.com) |
| Exclude domains | always direct, highest priority |
Changes take effect on the next request — no restart.
Three common setups
A. Clash / V2Ray on 127.0.0.1:7890, overseas APIs only
Mode = rule, protocol = http, address = 127.0.0.1:7890, leave the domain list empty.
The preset sends OpenAI / Anthropic / Gemini / xAI / Groq / OpenRouter and similar through
the proxy, and keeps DeepSeek, DashScope, Zhipu, Kimi and other mainland providers direct.
B. Follow the Windows system proxy
Mode = system. The plugin re-reads
HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings every 30 s and follows
changes made in Windows.
C. Everything through the proxy, a few exceptions
Mode = custom, fill the address, list the exceptions under "Exclude domains".
Notes for overseas APIs from mainland China
- DNS poisoning: prefer
socks5h(DNS resolved by the proxy), or let your proxy client take over DNS. - Plaintext
httpis refused by default — an API key must not travel over a proxy in clear text. Tick the option on the settings page if you really need it. - The proxy must listen on loopback; enable the local/LAN listener in your proxy client.
Verify
- The settings page shows live status:
path A · exit route · N rules · proxied x / direct y. http://127.0.0.1:<port>/dsh-model-proxy/statusreturns the same snapshot as JSON (loopback / same-origin only).- In a source checkout:
npm test(=node test/run-all.mjs). The suite ships with the repository, not with the npm package, and needs no install — it resolves the host's ownundiciand@deepseek-ai/schemasteryfrom your DSH profile (orDSH_HOME).
Safety guarantees
These hold regardless of configuration, and are what the rules engine checks first:
- Forced direct — loopback, private ranges (
10/8,172.16/12,192.168/16,169.254/16,fc00::/7—localhostand.local/.lan/.internalstyle names never go through a proxy. User rules can add entries but cannot remove these. - Plaintext gate — a model request that is not
httpsis rejected before any socket is opened, unless the option is explicitly enabled (the page then shows a standing warning). - Data minimisation — the plugin passes request and response bodies through as streams;
it never parses them, never writes them to disk, and never reads
Authorizationor any provider API key. Default logging records the exit id and error class only — no hostnames, no paths, no bodies.
A failed exit falls back to direct, never to another exit: silently moving traffic to a different exit would change the outbound identity without the user noticing. The fallback is logged and shown on the settings page, so a degraded state is visible rather than silent.
Development
lib/index.js Cordis host entry — the only file that touches the harness API
lib/client.js settings page (`__ModuleLoader__` browser entry)
lib/core/* pure logic, no harness dependency, independently testable
test/run-all.mjs test suite: pure-logic groups, then entry-file smoke tests
undici and @deepseek-ai/schemastery are both peers — the harness already ships them,
and installing a second copy would mean two dispatcher/Schema instances. The devDependencies
entries exist only so npm test can run outside DSH; the suite itself prefers the host's copies.
Limitations
- Covers requests issued through
fetch— in practice every model request, plus any otherfetchtraffic whose domain you add to the proxy list.transport: websocketstreaming and provider native-SDK channels (e.g. Amazon Bedrock) do not pass throughfetchand cannot be proxied here — use the harness's built-in environment-variable proxy for those. Web page fetching is in the same category: the harness's fetch tool builds its ownundiciagent, so domain rules never see it. - Routing is by domain. A domain that serves both a model API and other traffic sends all of it through the proxy; list such domains under "Exclude domains".
- Proxy credentials can only be embedded in the proxy address (
http://user:pass@host:port), which stores them in plain text in settings. Prefer letting the proxy accept loopback connections without authentication. - Only a single-hop proxy is fully wired; the multi-hop chain interface exists in
lib/core/dispatcher.jsbut only the first hop is handed to undici.
Uninstall
dsh plugin --profile <your-profile> remove dsh-model-proxy
dsh --profile <your-profile>
After removal DSH is fully direct again — no configuration residue, no listening port.
License
MIT
No comments yet. Be the first to write one.