dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Cloud relay for DeepSeek Harness: expose your home dsh instance to any device with full real-time sync (out-of-tree plugin + wire-trunk architecture)
English | 中文
Map a home-run DeepSeek Harness (dsh) instance to the public internet — get the exact same dsh experience as sitting at your home machine from any device, anywhere: live session sync, streaming output, tool cards, approval prompts, and workspace browsing.
Phone / remote PC ──HTTPS──→ Cloud relay (dsh-relay-cloud, one VPS)
│ outbound trunk (dialed by the home plugin)
▼
Home dsh (stays bound to 127.0.0.1, zero exposed ports)
└─ dsh-relay-host plugin: an "invisible browser" inside
the dsh process, replaying every request from loopback
/api/* + two downlink WebSockets) against the local 127.0.0.1:3080 and replays them verbatim over one outbound trunk to the cloud.~/.dsh/profiles/web/ user patch layer; git pull, auto-updates, and uninstall never interfere with each other.Three roles: cloud (a public server), home (the machine running dsh), and remote (phone / any browser). About 15 minutes end to end.
| Role | Requirement |
|---|---|
| Cloud | Linux server with a public IP (1 vCPU / 1 GB is enough), SSH access, one open TCP port (8443 below) |
| Home | A working dsh web (default 127.0.0.1:3080), Node ≥ 22 |
| Remote | Any modern browser |
# Trunk token (home ↔ cloud identity, 32 hex chars)
node -e "console.log(require('crypto').randomBytes(24).toString('hex'))"
# → note as <TOKEN>
# Pairing code (entered once per browser, 8 hex chars)
node -e "console.log(require('crypto').randomBytes(4).toString('hex'))"
# → note as <CODE>
⚠️ These two values are the keys to the whole system. Store them safely; never commit them.
SSH into your server:
ssh root@<SERVER_IP>
2.1 Install Node 22 (skip if node -v shows ≥ 22):
ARCH=$(uname -m); case "$ARCH" in x86_64) NARCH=x64;; aarch64) NARCH=arm64;; esac
curl -fsSL https://nodejs.org/dist/v22.14.0/node-v22.14.0-linux-$NARCH.tar.xz -o /tmp/node.tar.xz
tar -xJf /tmp/node.tar.xz -C /opt
ln -sfn /opt/node-v22.14.0-linux-$NARCH /opt/node
ln -sf /opt/node/bin/node /usr/local/bin/node
ln -sf /opt/node/bin/npm /usr/local/bin/npm
node -v # v22.14.0
2.2 Fetch the code and install dependencies:
git clone https://github.com/SunNull/dsh-relay.git /opt/dsh-relay
cd /opt/dsh-relay/cloud
npm install
2.3 Create a systemd service (replace <TOKEN>/<CODE>):
cat > /etc/systemd/system/dsh-relay.service <<EOF
[Unit]
Description=dsh-relay cloud
After=network-online.target
[Service]
Environment=DSH_RELAY_BIND=0.0.0.0
Environment=DSH_RELAY_PORT=8443
Environment=DSH_RELAY_TOKEN=<TOKEN>
Environment=DSH_RELAY_PAIRING_CODE=<CODE>
WorkingDirectory=/opt/dsh-relay/cloud
ExecStart=/usr/local/bin/node server.mjs
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now dsh-relay
systemctl status dsh-relay # active (running)
curl -s http://127.0.0.1:8443/healthz # {"ok":true,...,"trunkReady":false}
trunkReady:falseis expected — the home side is not connected yet.
2.4 Open the port (TCP 8443 inbound, source 0.0.0.0/0):
ufw allow 8443/tcpVerify from the home machine: open http://<SERVER_IP>:8443/healthz — you should see JSON.
On the home machine, pick one of two ways. For the admin panel UI use Option A; Option B is functionally equivalent but the sidebar entry will not appear (see its note).
Option A: standard bundle install (the repo declares dsh.bundle; one command mounts it; the panel UI relies on this option's dsh.client discovery):
dsh plugin --profile web add "github:SunNull/dsh-relay"
Then append the trunk credentials to ~/.dsh/profiles/web/cordis.patch.yml (the bundle layer carries no credentials; the user layer overrides it):
- id: dsh-relay-host
config:
relayUrl: ws://<SERVER_IP>:8443/trunk
token: <TOKEN>
Option B: installer one-liner (clones the repo and writes the config for you):
git clone https://github.com/SunNull/dsh-relay.git
cd dsh-relay
node install.mjs --relay-url ws://<SERVER_IP>:8443/trunk --token <TOKEN>
The installer:
index/admin/client) flat into ~/.dsh/profiles/web/plugins/ (index.mjs imports ./admin.mjs by relative path, so all three must sit side by side)~/.dsh/profiles/web/cordis.patch.yml (dsh hot-reloads this patch layer)Option B note: the relay and the admin route work fully, but the ⚙ Relay admin sidebar entry will not appear — the client panel rides the bundle's
dsh.clientdiscovery (Option A). Switch to Option A if you want the panel UI.
Both ways need one dsh web restart (module code needs process loading; config-only changes later need no restart). Uninstall: Option A dsh plugin --profile web remove dsh-relay-host, Option B node install.mjs --uninstall:
# restart your dsh web, e.g.
pnpm dsh web
Verify the trunk — on the server:
curl -s http://127.0.0.1:8443/healthz
# "trunkReady":true means connected; false → check plugin config and token
Open this on a phone (or any device):
http://<SERVER_IP>:8443
<CODE> → "配对"Repeat on more devices as needed (default cap: 10, see env vars).
By default dsh opens a native OS directory dialog on the host desktop, invisible to remote browsers. Append this patch so everyone uses the in-page directory browser:
Edit ~/.dsh/profiles/web/cordis.patch.yml and append:
- id: directory-picker
disabled: true
- insert:
- id: directory-picker-browse-host
name: '@deepseek-ai/dsh-host-directory-picker-browse'
- id: directory-picker-browse-ui
name: '@deepseek-ai/dsh-client-ui-directory-picker-browse'
Saves hot-apply (this is dsh's officially documented composition for remote-serving deployments). "Add workspace" now opens a web directory browser any device can use.
Plain HTTP risks interception. With a domain name you get automatic HTTPS:
# Install Caddy (Debian/Ubuntu official repo)
apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update && apt install caddy
Point a DNS A record at the server, then:
cp /opt/dsh-relay/cloud/Caddyfile.template /etc/caddy/Caddyfile
# edit the file: replace dsh.example.com with your domain
nano /etc/caddy/Caddyfile
systemctl reload caddy
Use https://<YOUR_DOMAIN> from now on; repoint the home side (hot-applies, no dsh restart):
cd dsh-relay
node install.mjs --relay-url wss://<YOUR_DOMAIN>/trunk --token <TOKEN>
The ⚙ Relay admin entry in the dsh web sidebar opens the built-in admin panel — it works both locally and from remote paired browsers, so you can manage everything from your phone on the couch.
Four areas:
The security model in one line: management follows dsh-web reachability — whoever can open your dsh web can manage the relay; if you lose a phone, revoke it from the home machine or any other paired device.
Upgrade requirement: the panel needs both the cloud and the home plugin on 0.2.0+ (at home, re-run
dsh plugin --profile web add "github:SunNull/dsh-relay"ornode install.mjs, then restart dsh web).
| Task | How |
|---|---|
| Cloud status & audit | Visit http://<SERVER>:8443/__relay (JSON) |
| Health check | curl http://<SERVER>:8443/healthz (no auth) |
| Cloud logs | journalctl -u dsh-relay -f |
| Revoke one / all paired devices | Use the "Relay admin" panel in the dsh web sidebar (no SSH needed; see Admin panel) |
| Update cloud code | cd /opt/dsh-relay && git pull && systemctl restart dsh-relay (per-release changes: CHANGELOG) |
| Uninstall home plugin | node install.mjs --uninstall (delete the repo dir for a full cleanup) |
| After a dsh restart | Nothing to do — the plugin reconnects automatically with backoff |
| Variable | Default | Meaning |
|---|---|---|
DSH_RELAY_PORT | 3081 | Listen port |
DSH_RELAY_BIND | 127.0.0.1 | Bind address; 0.0.0.0 for public deployments |
DSH_RELAY_TOKEN | dev-token | Trunk token (must match plugin config.token) |
DSH_RELAY_PAIRING_CODE | random, printed | Browser pairing code; set explicitly in production |
DSH_RELAY_STATE | ./relay-state.json | Device tokens + audit tail persistence |
DSH_RELAY_MAX_TOKENS | 10 | Max paired devices |
DSH_RELAY_MAX_WS | 16 | Max concurrent browser WebSockets |
DSH_RELAY_BLOCK_PRIVILEGED | 0 | 1 = block settings/credentials methods at the cloud |
DSH_RELAY_HEARTBEAT_MS | 20000 | Keepalive cycle for the trunk and every browser downlink (ms). A browser that misses a full cycle of pongs is treated as dead and dropped (the stock client auto-reconnects and replays the baseline); the ping traffic also keeps NAT mappings from expiring on quiet links |
Run these after deployment to verify the full chain:
# 1) Trunk essentials (static/RPC/WS bridge) — edit BASE at the top first:
node probe/e2e-probe.mjs
# 2) Browser end-to-end (pairing → UI → WS); needs pip install playwright && playwright install chromium:
DSH_RELAY_BASE=http://<SERVER>:8443 DSH_RELAY_PAIRING_CODE=<CODE> python probe/browser-acceptance-m1.py
DSH_RELAY_BLOCK_PRIVILEGED=1 for a more conservative posture (chat/files unaffected).relay-state.json holds only device tokens and audit counters — never session content.<TOKEN> in the cloud systemd unit and re-run install.mjs --token at home.| Symptom | Cause & fix |
|---|---|
| Remote shows "home offline" page | Home dsh not running / plugin not connected. Check trunkReady via healthz; verify token & network |
| Pairing says device limit reached | Test pairings filled the 10 slots. Use the panel's revoke-all, then re-pair |
| Forgot the pairing code | Click-to-reveal it in the "Relay admin" panel; pre-0.2.0 deployments can read it from the cloud's relay-state.json (or the systemd unit / boot log) |
| Rotate the pairing code | Use the panel's regenerate on the target code (or delete + recreate). Note: editing DSH_RELAY_PAIRING_CODE in systemd and restarting does not rotate existing codes — once the state file exists the env var only seeds a missing table and old codes keep working; revoke a code via the panel's regenerate/disable/delete instead |
| First load is slow (10-20 s) | Normal: cold cache fetches static assets one by one; subsequent loads hit the cache |
| Remote "add workspace" does nothing | Step 5 patch missing (the native dialog opened on the home desktop) |
| Blank page / infinite spinner | Hard-refresh / clear cache first; then run the probes to localize (static / RPC / WS) |
DeepSeek Harness 0.1.0-rc.5 (2026-08). dsh is in developer preview; treat the probes as the acceptance contract when the wire changes.