DeepSeek Harness plugin

dsh-gateway-thinkmoo

DSH plugin: authenticated reverse proxy for the web GUI. Starts with `dsh web` (default 0.0.0.0:8642) and forwards every request (HTTP streams and WebSocket upgrades) to the loopback DSH server with

Jump to install

Source facts

Repository
thinkmoon/dsh-gateway
Latest update
Aug 21, 2026
Category
Models & Providers
GitHub stars
1
Format
plugin
Catalog evidence
Upstream dsh.bundle evidence
Evidence path
package.json#dsh.bundle
Checked against
0.1.0-rc.8
Upstream check date
2026-08-20

This evidence comes from the upstream catalog. This site has not installed, run, or security-reviewed the plugin.

Install

Start with a prompt that asks an agent to review the GitHub repository and source. Switch to the command if you want to install it yourself.

Copy this prompt into DSH, Codex, or another agent and ask it to review the GitHub repository and source first.

Do not install or run any commands yet. Read this plugin's GitHub repository, README, and relevant source code. Then answer the questions below clearly and directly so I can decide whether it fits my needs:

1. What is this plugin, and what problem does it solve?
2. Who is it for, and what are its typical use cases?
3. How is it used after installation? Include one minimal example.
4. What known limitations or privacy, security, compatibility, or maintenance risks does it have?
5. Give a clear recommendation: recommend, conditionally recommend, or do not recommend, with reasons.

Distinguish statements documented by the repository, inferences from source code, and unknowns. If evidence is insufficient, say so explicitly. Do not guess or simply repeat the README.

GitHub: https://github.com/thinkmoon/dsh-gateway
Plugin: dsh-gateway-thinkmoo
Author: thinkmoon

Check the source files

Read the README and other files from this plugin directory before installing.

File explorer4 files
README.mdSource · read only
README language

dsh-gateway

English | 中文

An authenticated reverse proxy for the DSH Web GUI, packaged as a DSH plugin. It turns remote access into plain local traffic: remote sessions behave exactly like sitting at the machine.

dsh plugin --profile web add dsh-gateway   # or link:<local checkout>
dsh web                                     # gateway auto-listens on 0.0.0.0:8642

Why

DSH's web server intentionally refuses to bind anything but 127.0.0.1dsh web --host 0.0.0.0 is rejected, because binding the harness (which can run arbitrary shell commands through its agent) to a network interface equals exposing remote code execution. On top of that, the /api layer runs a loopback trust fence that validates Host and Origin headers.

Both are good defaults. But sometimes you legitimately want to reach your own harness from your phone, another room, or another continent — over real TLS, with real authentication.

dsh-gateway sits in front of both layers:

remote browser ──HTTPS──> dsh-gateway (0.0.0.0:8642, password auth)
                               │  Host   ← 127.0.0.1:<dshPort>
                               │  Origin ← stripped
                               ▼
                        DSH web (127.0.0.1:<dshPort>)

Every forwarded request gets Host rewritten to the local target and Origin stripped, so DSH's fence treats each remote request as a plain local one — indistinguishable from a browser opened on the machine itself.

Features

  • Plugin-native — starts and stops with dsh web; no extra daemon, no supervisor, nothing to babysit. Follows dynamic ports (--port 0) automatically.
  • Password gate in front of everything — built-in login page, HMAC-SHA256-signed HttpOnly; SameSite=Lax session cookie (default 7d), Authorization: Bearer <password> for programmatic access. WebSocket upgrades are gated by the same session (browsers attach the cookie automatically).
  • Static asset caching — the upstream DSH web server sends no cache headers at all, so browsers re-download the entire JS/CSS bundle on every visit, which makes remote sessions feel sluggish. The gateway fills the gap with a safe, content-aware Cache-Control policy (see below). Repeat visits load from the browser cache: near-instant.
  • Zero runtime dependencies — plain Node core (node:http, node:crypto, node:net, node:tls); nothing to audit, nothing to break.
  • True streaming — request/response bodies are piped end to end, never buffered (DSH accepts request bodies up to 160 MiB; large uploads/downloads and SSE pass through untouched).
  • WebSocket tunneling — upgrades (/api/events.mux, /api/events.host) are bridged as raw bidirectional socket tunnels, so the live UI works exactly as locally.
  • Hardened by default — per-IP login rate limiting (10 attempts / 10 min), timing-safe password comparison, open-redirect guard on next, gateway session cookie never forwarded upstream, secrets stored 0600.

Cache policy

ResponsePolicy
HTMLno-store — never cached; login state and UI freshness always correct
Static assets addressed by content hash — hash-named paths (/assets/, /dist/, /static/, /vendor/, /favicon) or hash query params (?rev=<hash>, ?v=<hash>, …)public, max-age=31536000, immutable — cached for a year; the hash is part of the URL, so an upstream update ships a new URL and takes effect immediately (upstream no-cache on such URLs is overridden)
Other static files (JS/CSS/images/fonts/wasm…, by content-type or extension)public, max-age=300 — short window, then revalidate
Everything else (API, streams)untouched

Applied only to successful (2xx/3xx) GET/HEAD responses; hash-addressed assets override an upstream no-cache (the hash makes it safe), everything else never overrides an upstream policy or ETag.

Install

dsh plugin --profile web add link:/path/to/dsh-gateway
dsh web

The plugin injects the webServer service and starts the gateway once DSH is listening. If the gateway port is busy it disables itself with a log line and leaves DSH running.

Password resolution order: plugin config password$DSH_GATEWAY_PASSWORD~/.dsh-gateway/secret (auto-generated random password on first run, logged once, file mode 0600).

Configuration

In the profile's cordis.patch.yml:

- id: web-gateway
  config:
    enabled: true
    host: 0.0.0.0
    port: 8642
    password: ""            # empty → resolution order above
    sessionTtlMs: 604800000 # 7 days

Deployment shapes

  • Cloudflare Tunnel (recommended) — point the cloudflared service URL at http://127.0.0.1:8642; TLS terminates at the edge. Works with any reverse proxy (Caddy, nginx, Traefik) the same way.
  • LAN / VPS direct — always put TLS in front (Caddy/nginx or a tunnel). Over plaintext HTTP the password and session cookie travel in cleartext.

Verified end-to-end

The test suite (18 checks) covers the login flow, header rewrites (asserted upstream: Host rewritten, Origin stripped, gateway cookie withheld), the cache policy (immutable / hash-query override / revalidate / no-store), streaming, the WebSocket tunnel handshake and echo round-trip, and the auth gate on upgrades:

pnpm install
pnpm typecheck
pnpm test

How it works

ConcernApproach
Making DSH accept remote trafficProxy, not a bind change: DSH stays on loopback; Host is rewritten to 127.0.0.1:<port> and Origin is dropped, so the loopback fence sees plain local requests
Authscrypt-derived key signs expiring session tokens (<exp>.<hmac>), stored in an HttpOnly cookie; Bearer password for scripts
WebSocketsUpgrades tunnel at the socket level — the gateway replays the handshake upstream and then pipes bytes both directions
CachingContent-aware Cache-Control injection: hash-path assets immutable for a year, other static files revalidate after 5 min, HTML always no-store; upstream cache headers and ETags pass through untouched
LifetimeA cordis plugin keyed to the webServer service: listener registered as an effect, closed on fiber disposal; a busy port disables the gateway without touching DSH

Relation to dsh-remote-web-ui

@linxin666/dsh-remote-web-ui is an application-layer solution (pairing/device management inside the DSH plugin system). dsh-gateway is a network-layer one: a transport-level reverse proxy that remotizes the whole web GUI as-is, agnostic to DSH internals. The two can coexist; pick per scenario.

Security notes

  • The gateway grants everything a local DSH browser session has — including agent shell tools. Use a strong password (the auto-generated one is fine) and keep TLS in front.
  • Changing the password invalidates all sessions instantly (the signing key is scrypt-derived from it).
  • Never expose a plaintext gateway port to the public internet.

Requirements

  • Node.js >= 20
  • DSH (@deepseek-ai/dsh) with the web profile

License

MIT