Foreword¶
The philosophy of DSH is plugin-based, and plugins and model invocations often need to access external HTTP/HTTPS interfaces. In real-world operation, the target address, provider, and plugin may differ; some requests need direct connections, some need to go through proxies; and when direct connections fail, safely falling back to a proxy is necessary.
dsh-system-proxy handles this type of outbound routing at the DSH host plugin layer, avoiding each plugin having to piece together its own proxy logic.
What This Is¶
dsh-system-proxy is a DSH host plugin used to uniformly handle outbound HTTP/HTTPS routing within the DSH main process. The repository owner is khiqwq, and the source repository is at https://github.com/khiqwq/dsh-system-proxy.
The core problems it solves are:
- HTTP, HTTPS, SOCKS4, SOCKS4a, SOCKS5, SOCKS5h proxies;
- Multiple named proxies;
- Routing by
host,provider, andplugin; - Four actions:
direct,proxy,fallback, andblock; - Using a proxy as a fallback when direct connections fail or have high latency;
- Per-target EWMA latency, failure cooldown, and health memory;
- Explicit
provider/plugincontext without spoofing the caller’s identity; - Proxy credential redaction, safe logging, and hot-reload recovery;
- Wrapping
globalThis.fetch; - Wrapping
node:http/node:httpsrequestandgetwhenpatchNodeHttp: true; - Default protection for internal network / cloud metadata endpoints;
- Interactive configuration cards;
passwordRef/credentialsfor saving passwords.
Runtime requires Node >=22.
Core Capabilities¶
Routing Actions¶
The plugin classifies outbound requests into four actions:
direct: Only use direct connections.proxy: Force the use of the specified named proxy.fallback: Try direct connection first, then switch to a proxy when conditions are met.block: Reject the request locally.
Referencing a proxy name that does not exist or is currently unavailable raises UNKNOWN_PROXY and does not silently fall back to direct connections.
fetch and node http/https¶
The plugin wraps globalThis.fetch. When patchNodeHttp: true is enabled in the configuration, it also wraps the request and get methods of node:http / node:https.
This is a process-level global patch. Multiple competing transport-wrapping plugins should not be loaded simultaneously in the same process.
provider / plugin Context¶
The global fetch itself cannot reliably determine which plugin a request comes from. The caller needs to explicitly provide context:
await ctx.networkRoute.run({ provider: 'openai', plugin: 'my-plugin' }, () => fetch('https://api.openai.com/v1/models'))
For streaming responses, the iterable wrapper can be used to wrap lazy async iterators:
const attributed = ctx.networkRoute.iterable({ provider: 'openai' }, response.body)
This preserves provider / plugin attribution while consuming response.body.
Safety and Fallback¶
Automatic fallback is not an ordinary retry. By default, only GET, HEAD, OPTIONS, and TRACE are allowed to automatically switch routes; POST is not replayed by default.
The following cases are not replayed:
- Streaming uploads;
FormData;bodyexceeding the buffer limit;- User-initiated
abort; - Response headers or SSE bytes already received;
- Post-connection failures where it cannot be proven that request bytes have not been written.
Only pre-connection failures safely switch routes. Proactive health probes are disabled by default.
Internal Network and Cloud Metadata Protection¶
Security defaults force localhost, loopback, 169.254.0.0/16, cloud metadata endpoints, RFC1918 internal networks, and IPv6 ULA to always use direct connections. Internal traffic and cloud metadata traffic therefore remain direct and are not handed over to external proxies.
Passwords and Credentials¶
The plugin supports passwordRef / credentials for saving proxy passwords. Passwords are only submitted via the one-way credentials.set() method and are never saved to settings; the password draft is cleared immediately after saving.
trustRouteHeaders is disabled by default; internal routing control headers are automatically stripped before the request is sent.
SOCKS Limitations¶
SOCKS4 / SOCKS4a explicitly throw SOCKS4_IPV6_UNSUPPORTED before the handshake when faced with IPv6 targets, instead of silently falling back to direct connections.
Installation and Enablement¶
Once released, it can be installed in the corresponding profile:
dsh plugin --profile <name> add dsh-system-proxy
The runtime environment requires Node >=22.
Before installation, check the source code and license, and confirm that running the plugin with the current dsh process permissions is appropriate.
Configuration and Overrides¶
The configuration is located in the profile’s cordis.patch.yml. The basic structure is:
proxiesis an object used to declare multiple named proxies;rulesis an array used to describe routing rules;defaultis an object used to declare the default policy.
Loader configuration is a complete replacement: to add a rule, the full config must be restated, not just incremental fields.
Environment variables can be used for overrides or disabling:
DSH_PROXY_URL
DSH_PROXY_MODE
DSH_PROXY_DISABLE
NO_PROXY/no_proxy
Applicable Scenarios¶
Suitable for the following situations:
- Uniformly handling outbound HTTP/HTTPS requests in DSH;
- Choosing direct connections or proxies based on target host, provider, or plugin;
- Prioritizing direct connections and falling back to proxies on pre-connection failures;
- Protecting
localhost,loopback, cloud metadata, and RFC1918 internal network addresses from being forwarded through external proxies; - Needing to use
passwordRef/credentialsfor proxy passwords rather than writing them into settings.
Links¶
Source repository: https://github.com/khiqwq/dsh-system-proxy