packages/dsh-remote-web-ui
zhu1090093659/dsh-web手机/PC 远程操控 dsh web 工作区:扫码配对、令牌门控通道、SSE 实时同步,提供移动端与完整桌面 GUI 两种远程形态。
将DSH Web GUI代理到3301端口供局域网访问。口令可选,无明文保存,初始3080端口保存回环不暴露。
Formerly published as
dsh-proxy; the project and package are nowdsh-3301.
http://<this-machine-ip>:3301 with added security, so you can access DSH from anywhere on the LAN — including your phone.
⚠️ DSH 1.1 only. DeepSeek Harness 1.2 added a local token verification that breaks this reverse-proxy approach (the proxy can no longer pass through to the GUI). This project targets DSH 1.1. If you're on 1.2+, a different access method is required.✅ Updated: that limitation is solved. The project now also ships as a DSH plugin (identity
dsh-3301) that works on current DSH — see Plugin form below. The standalone proxy remains supported.
English · 中文
Password-protected reverse proxy (HTTP + WebSocket) — and now also a DSH plugin (dsh-3301) — in front of a
DSH Web GUI.
DSH stays bound to 127.0.0.1 (loopback only, safe). This proxy binds to
0.0.0.0 so a phone on the LAN can reach it — behind a password. The plugin form starts with no password: set one, or leave the entry open. The plugin form starts with no password: set one, or leave the entry open. The plugin form starts with no password: set one, or leave the entry open.
Zero-install dependencies — the standalone CLI is pure Node built-ins and the plugin reuses packages DSH already ships. Cross-platform (macOS / Linux / Windows × x86 / ARM) — verified on Windows; macOS/Linux follow from the platform branches in the code, but have not been tested on real hardware yet.
A password-protected reverse proxy in front of DSH. DSH stays loopback-only;
this binds 0.0.0.0:3301 and forwards HTTP + WebSocket to it. Auth is a
signed cookie (30-day session), optional HTTPS, rate-limited. To run:
DSH_PROXY_PASS=<pw> node lib/cli.js. To stop on macOS:
launchctl bootout gui/$(id -u)/com.dsh.lan-proxy. See below for details.
Plugin form — preferred on current DSH:
node tools/deploy.mjs dsh-3301(ordsh plugin --profile web add dsh-3301), restart DSH, then openhttp://<host-ip>:3301. There is no default password, and the settings card lives at Settings → Plugins → Plugin configuration → dsh-3301. Details below.
dsh-3301)The standalone proxy below still works, but the supported way to run this on current DSH is as a
plugin, and the package/plugin identity is dsh-3301 (the npm name dsh-proxy is taken by
an unrelated LLM-gateway plugin).
127.0.0.1 — it never listens on the LAN.0.0.0.0:3301 by default) inside the DSH process: no
separate daemon, no scheduled task, it starts and stops with DSH.$DSH_HOME/dsh-3301/auth.json —
no plaintext is written — and changing or clearing it is loopback-only.ctx.connection.authenticatedUrl() API — the plugin no longer
reads your credential file — and the client bootstrap is injected with
ctx.webServer.tapIndex(), i.e. before compression, so no HTML rewriting.⚠️ Until you set a password, anyone who can reach port 3301 gets into DSH. Set one before exposing the machine: Settings → Plugins → Plugin configuration → dsh-3301, or
http://127.0.0.1:3301/__gate/setupfrom the host itself.
# Today: clone and deploy (there is deliberately no npm package yet — see the note below)
git clone https://github.com/Aztech-Lab/dsh-3301
cd dsh-3301
node tools/deploy.mjs dsh-3301
The first line people reach for —
dsh plugin --profile web add dsh-3301— needs an npm package, and none is published (by choice: the repository is the install source, and it has no build step). Use the clone-and-deploy path above until that changes.
The deploy target is your DSH profile (
$DSH_HOME/profiles/<profile>), which normally lives outside this repository. If the write is refused (EACCES: permission denied), grant that directory write access, or run the command as the user that owns$DSH_HOME.
Restart DSH afterwards, then open Settings → Plugins → Plugin configuration → dsh-3301:
| Field | Meaning |
|---|---|
| Enabled | turning it off releases the listening port |
| Bind host | 0.0.0.0 (LAN reachable) / 127.0.0.1 (host only) |
| Port | default 3301 |
| Username | login name on the gate form, default dsh |
| Session lifetime | session-cookie validity, default 30 days |
| Failure limit / lockout | per-IP lockout after repeated failed logins |
| Inject client bootstrap | adds __DSH_TRANSPORT__.ownsHost so a LAN page keeps host-backed settings |
| Password | write-only input: empty = clear; changing it requires the current password |
For first-run setup you can also open http://127.0.0.1:3301/__gate/setup on the host, and
http://127.0.0.1:3301/__gate/health reports the entry's state without authentication.
The card follows the shipped DSH plugin cards; its styling uses the declarations and theme
variables of DSH's own dsh-client-ui-settings-plugins package (BSD-3-Clause). All implementation
code here is original.
The standalone CLI (
node lib/cli.js,DSH_PROXY_*environment variables) is unchanged and still supported for running a proxy outside DSH.
This section describes the standalone CLI form; the plugin form above differs in auth (form login + scrypt) and needs no TLS of its own.
Running it as a DSH plugin is the path above.
DSH_PROXY_PASS=yourpassword node lib/cli.js
# or after installing the bin
DSH_PROXY_PASS=yourpassword dsh-3301
Then open http://<this-machine-ip>:3301 from any device on the LAN.
| var | default | meaning |
|---|---|---|
DSH_PROXY_PORT | 3301 | listen port |
DSH_PROXY_HOST | 0.0.0.0 | listen host |
DSH_PROXY_USER | dsh | basic-auth username |
DSH_PROXY_PASS | (required) | basic-auth password |
DSH_UPSTREAM | 127.0.0.1:3080 | upstream DSH host:port |
DSH_PROXY_SECRET_FILE | os.tmpdir()/dsh-3301-secret | session-signing secret file |
DSH_PROXY_CERT | /tmp/dsh-3301-cert.pem | HTTPS cert (enables TLS if present) |
DSH_PROXY_KEY | /tmp/dsh-3301-key.pem | HTTPS key |
The plugin's
3301entry is plain HTTP — put TLS or a tunnel in front of it if the network is untrusted.
Generate a self-signed cert once, then the proxy serves HTTPS:
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout /tmp/dsh-3301-key.pem -out /tmp/dsh-3301-cert.pem \
-days 365 -subj "/CN=dsh-3301"
Browsers will show a one-time self-signed warning — accept it once. The password and session are then encrypted on the wire.
The plugin starts and stops with DSH, so no launchd entry is needed.
See com.dsh.lan-proxy.plist (template). Load it with:
launchctl bootstrap gui/$(id -u) /path/to/com.dsh.lan-proxy.plist
# stop (turn the proxy off)
launchctl bootout gui/$(id -u)/com.dsh.lan-proxy
# start again
launchctl bootstrap gui/$(id -u) /path/to/Library/LaunchAgents/com.dsh.lan-proxy.plist
# check status
launchctl list | grep dsh.lan-proxy
What's hardened:
HttpOnly — tamper-proof and resistant to timing attacks.Known risks / things to be aware of:
0.0.0.0; anyone on the LAN can
attempt access. The password is the only gate. On an untrusted network this
is not sufficient.Recommended deployment:
Contributions are welcome! Ideas that would help:
Open an issue or PR — see the GitHub repo.
This work is conducted by Aztech Labs with DeepSeek Harness (Deepseek-V4.1-Flash).
MIT