AndyYJF
dsh-lan-web-auth
DeepSeek Harness (dsh) host plugin: crypto.randomUUID polyfill + password auth for running the web GUI over LAN / ZeroTier
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 14, 2026
- Updated
- Aug 14, 2026
Introduction
dsh-lan-web-auth
English | 简体中文
Host plugin for DeepSeek Harness (dsh --profile web) that makes the browser GUI usable over the LAN and protects it with a password:
-
crypto.randomUUIDpolyfill — the GUI's RPC client mints request ids withcrypto.randomUUID(), which browsers only expose in secure contexts (HTTPS or loopback). Plain-HTTP LAN origins (192.168.x.x, hostnames, ZeroTier IPs) are not secure contexts, so every/apicall throws client-side (crypto.randomUUID is not a function), the session list never loads, and the GUI falls into the workspace-picker state. The polyfill is based oncrypto.getRandomValues, which IS available on insecure origins. -
Password authentication — wraps the page fallback, the
/apiprefix route, and the two WebSocket downlink upgrades (/api/events.mux,/api/events.host) with a cookie check. Unauthenticated visitors get a built-in login page;POST /api/auth/loginwith the correct password sets an HttpOnly cookie (30 days). Loopback hosts (127.0.0.1/localhost/::1) are exempt by default (config.loopbackExempt).
Security & Risks
Read this before exposing the GUI beyond your own machine. dsh web is a remote-control surface: the harness can execute shell commands, read/write files, and manage credentials and settings, so successful (or bypassed) authentication is effectively remote code execution as your user.
- Plaintext HTTP, no TLS. The password, the session cookie, and all conversation content travel unencrypted. Anyone who can sniff the network segment (e.g. open Wi-Fi, a compromised AP/router) can capture the password or hijack a session. On untrusted networks, put dsh behind a TLS-terminating reverse proxy, or reach it only over an encrypted tunnel (ZeroTier / Tailscale / WireGuard).
- No brute-force protection. The login endpoint has no rate limiting or lockout; a weak password can be guessed. Use a long, random, unique password.
- Password stored in plaintext.
config.passwordlives in~/.dsh/profiles/web/cordis.patch.yml; theDSH_WEB_PASSWORDalternative is visible in the process environment. Anyone with local read access can recover it. Never commit a real password (the example patch uses a placeholder). - Loopback exemption is a real hole. With
loopbackExempt: true(the default), any local process that can reach127.0.0.1:3080— not just your browser — bypasses the password entirely, because it can send a loopbackHostheader and the DNS-rebinding fence does not constrain non-browser clients. SetloopbackExempt: falseto enforce the password on loopback too. - Stateless token, no revocation. The session token is a SHA-256 of the password; changing the password is the only way to invalidate outstanding cookies. Cookies are valid for 30 days.
0.0.0.0bind widens exposure. The example patch binds all interfaces, so the GUI is reachable on every network the machine is attached to (Wi-Fi, ZeroTier, TUN adapters, ...). On networks you do not fully trust, bind a specific interface instead (e.g.host: '192.168.1.13').- Auth is additive, not a security boundary. This plugin's password gate sits on top of the built-in browser-trust fence (Host/Origin checks). It is a convenience layer for home/LAN use, not a substitute for HTTPS plus real authentication. If your threat model matters, front dsh with a proper reverse proxy.
- Workaround, may conflict with upstream. This plugin works around upstream limitations (missing
crypto.randomUUIDon non-secure contexts; no built-in auth). If a future dsh release fixes either, review whether this plugin is still needed before upgrading.
Install
-
Copy the package into your web profile's node_modules:
~/.dsh/profiles/web/node_modules/dsh-lan-web-auth/(Or install it as a plugin: add
"dsh-lan-web-auth": "file:<path>"to~/.dsh/profiles/web/package.jsondependencies and rundsh plugin --profile web install.) -
Add the row to your user patch layer
~/.dsh/profiles/web/cordis.patch.yml— see patch.example.yml for the full LAN setup (bind0.0.0.0, trusted hostname, plugin row). The patch layer is a user-owned extension point: it survivesnpm i -g @deepseek-ai/dshupgrades. -
Restart
dsh --profile web(or let the user-patch watcher hot-apply it).
Config
| Field | Meaning |
|---|---|
password | Login password. Falls back to the DSH_WEB_PASSWORD environment variable. If neither is set, auth stays disabled (polyfill only) and a warning is logged. |
loopbackExempt | When true (default), loopback origins skip the password. |
Changing config.password in the patch hot-applies via HMR; LAN clients must log in again.
Notes
- The token is a SHA-256 of the password — stateless, survives restarts.
- Auth is in addition to the built-in DNS-rebinding / browser-trust fence, which still applies.
- Plugin code changes need a dsh restart (the loader caches modules per process); config changes hot-apply.
- The webserver row in patch.example.yml binds all interfaces (
0.0.0.0) so LAN/ZeroTier devices can reach the GUI; theconnectionrow adds a hostname to the trust fence.