dsh-remote-gateway
Source-filtered remote gateway for DeepSeek Harness behind an authenticated reverse proxy
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 28, 2026
- Updated
- Aug 28, 2026
Introduction
DSH Remote Gateway
A source-filtered gateway for using DeepSeek Harness through an authenticated reverse proxy.
简体中文 · Security · Compatibility

Why
DSH intentionally keeps privileged browser capabilities on loopback. A normal reverse proxy can render the page remotely while settings, plugin bundles, WebSockets, or host directory selection still fail. This plugin provides a narrow compatibility gateway without exposing the internal DSH web server directly.
Features
- Proxies HTTP, SSE, and WebSocket traffic.
- Accepts requests only from explicitly listed source IP addresses.
- Keeps the internal DSH web server bound to
127.0.0.1. - Rewrites the upstream
HostandOriginto the internal loopback authority. - Strips edge authentication and forwarded-client identity headers before requests enter DSH.
- Patches only the exact
dsh-client-connectioncapability probe; every other plugin bundle is passed through byte-for-byte. - Replaces the host-native directory dialog with DSH's in-app filesystem browser, so remote users can choose a workspace on the DSH host.
- Starts and stops with the DSH plugin lifecycle, so an existing systemd, launchd, or other DSH supervisor also supervises the gateway.
Architecture
The reverse proxy is the public security boundary. It must provide TLS and authentication. The gateway's source allowlist is defense in depth, not user authentication.
Install
Requirements:
- DSH web profile
- Node.js 22.19 or newer
- A reverse proxy that supports WebSocket forwarding and authentication
Install from GitHub:
dsh plugin --profile web add github:Jiachi5533/dsh-remote-gateway
Then edit the generated remote-gateway entry in ~/.dsh/profiles/web/cordis.patch.yml:
- id: remote-gateway
name: '@jiachi/dsh-remote-gateway'
config:
host: 0.0.0.0
port: 3080
allowedSources:
- 192.0.2.10 # exact LAN address of the reverse proxy
- 127.0.0.1 # optional local health checks
Restart the DSH web process after the first installation. Configure the reverse proxy to send HTTP and WebSocket traffic to http://DSH_HOST_LAN_IP:3080, and enable HTTPS plus authentication at that proxy.
Never expose port 3080 directly to the internet. Do not add broad network ranges to
allowedSources; the current implementation accepts exact IP addresses only.
Configuration
| Option | Default | Description |
|---|---|---|
host | 0.0.0.0 | Gateway listen address. |
port | 3080 | Gateway listen port. Use 0 only in tests. |
allowedSources | ['127.0.0.1'] | Exact IPv4 or IPv6 source addresses allowed to connect. |
IPv4-mapped addresses are canonicalized, and IPv6 loopback ::1 is treated as 127.0.0.1. The upstream host and port are obtained from DSH itself and cannot be redirected through plugin configuration. Startup fails if the internal DSH web server is not bound to 127.0.0.1.
Allowlisted monitoring systems can check GET /_dsh-remote-gateway/health. It returns 200 ok without contacting the DSH upstream, so it verifies the gateway process and source policy rather than DSH readiness.
Security model
- TLS and user authentication terminate at your reverse proxy.
- Only the reverse proxy's LAN address should be allowed at the gateway.
- The internal DSH web server stays loopback-only.
Authorization,Proxy-Authorization,Forwarded,X-Forwarded-*, andX-Real-IPare removed before proxying upstream.- A source-IP allowlist does not protect against a compromised reverse proxy or another device that can use the same source address.
- DSH plugins execute with the DSH process permissions. Review every third-party plugin before installation.
See SECURITY.md for reporting and operational guidance.
Compatibility
| Component | Status |
|---|---|
DSH 0.1.1-rc.2 | Verified on 2026-08-28 |
| HTTP and SSE | Verified |
| WebSocket upgrade | Verified |
| Settings API | Verified |
| In-app host directory browser | Verified |
| Existing and future standard Host/Client bundles | Passed through unchanged, except the exact connection module path |
The connection compatibility patch is deliberately fail-closed. If a DSH update changes the expected probe, that module returns 502 with an explicit log message instead of silently loading a partly broken remote UI.
Most well-behaved plugins continue to work because their client assets and same-origin requests are transparently forwarded. A plugin may still need its own remote adaptation if it hard-codes localhost, opens a native host dialog, or independently rejects non-loopback browser locations.
Upgrade and removal
Update by re-adding the GitHub source from dsh-market or the CLI, then restart DSH:
dsh plugin --profile web add github:Jiachi5533/dsh-remote-gateway
Remove it with:
dsh plugin --profile web remove @jiachi/dsh-remote-gateway
After removal, delete any remaining remote-gateway, directory-picker-browse, and ui-directory-picker-browse rows that were added by this plugin from the profile patch, then restart DSH.
Troubleshooting
403 forbidden: the TCP source address is not present inallowedSources. Add the reverse proxy's LAN IP, not the remote browser's public IP.502 ... compatibility probe matched 0 times: the installed DSH client connection module changed. Pin a verified DSH version or open an issue with both version numbers.- Page loads but WebSocket fails: enable WebSocket upgrade forwarding in the reverse proxy.
- Native folder dialog appears: confirm both browse picker rows in
cordis.patch.ymlare enabled and the nativedirectory-pickerrow is disabled. - New UI plugin is missing: restart DSH after installing the plugin, then hard-refresh the browser.
Development
npm test
The test suite covers source filtering, IPv4-mapped addresses, trust-header rewriting, fail-closed module patching, and byte-preserving proxy behavior for unrelated plugin bundles.