Expose the DSH Web GUI over IPv6 or IPv4 from WSL2 through a reverse proxy (Lucky): /wan up sets up the socat relay, Windows portproxy, firewall, and trusted-host fence, with a Settings card plus commands to configure the domain and ports.
Install
# from npm (prebuilt)
dsh plugin --profile web add dsh-wsl-expose
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:jack-ranbo/dsh-wsl-expose
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time. Only install sources you trust, and pin a commit (github:owner/repo#sha).
README
English | 中文
What problem it solves
DSH's Web GUI binds to 127.0.0.1 only (--host 0.0.0.0 is intentionally refused), and every /api request must pass a browser-trust fence keyed to the request Host. Reaching it from a phone over IPv6 — through WSL2's NAT plus a LAN reverse proxy like Lucky — normally takes a chain of fragile manual steps.
This plugin automates everything that runs on your machine:
public IPv6 user ──> your domain (AAAA) ──> Lucky (reverse proxy)
└─> [Windows global IPv6]:3082 (portproxy v6tov4)
└─> WSL eth0:3082 (socat relay)
└─> 127.0.0.1:<web port> (DSH Web GUI)
What it automates
- Detects WSL2, the WSL
eth0IPv4, and Windows global IPv6 (vianetsh.exe/ipconfig.exe) - Starts the
socatloopback relay (TCP-LISTEN:<relayPort> → 127.0.0.1:<webPort>), detached so it survives restarts - Adds the Windows
netsh interface portproxy v6tov4rule ([WindowsIPv6]:relayPort → WSL:relayPort) - Adds the Windows firewall inbound rule for
relayPort - Writes the
connectiontrusted-host override into the profile'scordis.patch.yml, so the 403 fence accepts your public domain automatically (no--trusted-hostflag needed)
What it cannot automate (it prints exact instructions instead): Lucky reverse-proxy config, router IPv6 inbound firewall, and DDNS AAAA records — those live on other machines.
Install
dsh plugin --profile web add dsh-wsl-expose
Restart dsh web, then in any conversation run:
/wan up dsh.your-domain.cn
Commands
| Command | Action |
|---|---|
/wan up [domain] |
Set up relay + portproxy + firewall + trusted-host. The domain is optional — reads the saved one |
/wan set-domain <d> |
Save the domain once; then /wan up needs no argument |
/wan get-domain |
Show the saved domain |
/wan set-port <p> |
Save the relay port (1-65535); tears down the old port's artifacts when it changes |
/wan get-port |
Show the saved relay port (and the effective one) |
/wan set-web-port <p> |
Save the forward port — the DSH web server's port on 127.0.0.1 (default 3080) |
/wan get-web-port |
Show the saved forward port (and the effective one) |
/wan down |
Tear it all down (removes the managed trusted-host block too) |
/wan status |
Show current state |
/wan doctor |
Diagnose the chain (including the classic Lucky io timeout) |
Set the domain and ports — UI or commands
Both surfaces read and write the same persisted settings (the wsl-expose namespace in settings.yaml).
UI: after restarting dsh web, open Settings → Plugins → dsh-wsl-expose — edit the domain, relay port, forward port, and the trusted-host fence switch; Save commits them.
Commands:
/wan set-domain dsh.your-domain.cn
/wan set-port 3082 # optional — relay listen port (default 3082)
/wan set-web-port 3080 # optional — forward target (default 3080)
Then just /wan up (no argument). The values are persisted in the plugin's settings namespace (wsl-expose), so they survive restarts. Resolution order — domain: CLI arg → saved setting → config domain; ports: saved setting → config → defaults (3082 / 3080).
Configuration (file / profile)
Lower-level options live in the profile's cordis.patch.yml (the user patch layer):
- id: dsh-wsl-expose
config:
mode: ipv6 # 'ipv6' (v6tov4, default) or 'ipv4' (v4tov4)
relayPort: 3082 # port Lucky points at (default 3082)
domain: dsh.your-domain.cn # fallback default when neither CLI arg nor UI setting is set
windowsAddress: '' # pin the Windows listen address if auto-detect picks the wrong one
mode: 'ipv4' listens on the Windows LAN IPv4 (Lucky upstream http://<WindowsIPv4>:3082); mode: 'ipv6' listens on the Windows global IPv6 (upstream http://[<WindowsIPv6>]:3082).
webPort is auto-detected from the running webserver; the config webPort is only a fallback.
Prerequisites
- socat in WSL:
sudo apt install socat - Windows IP Helper service running (
netsh portproxydepends oniphlpsvc) - A global IPv6 prefix (not just
fe80::) assigned to Windows and the Lucky host - Lucky (or any reverse proxy) on another LAN machine
Manual steps the plugin prints (Lucky / router / DDNS)
- Lucky: add a reverse-proxy rule — upstream
http://[<WindowsIPv6>]:3082, listen on80/443, and pass the original Host header through (do not rewrite to the upstream address). - Router: allow inbound IPv6 to the Lucky host on
80/443(IPv6 has no NAT, so this is a firewall allow, not port forwarding). - DDNS: point an
AAAArecord at your home IPv6 prefix (Lucky has a built-in DDNS module).
After /wan up, restart dsh web once so the trusted-host fence picks up the new domain.
Security
--trusted-host / the trusted-host fence is not authentication — it only defeats DNS-rebinding and cross-site requests. Exposing DSH to the internet exposes an agent that can run commands. Add real auth at the Lucky layer (password / Basic Auth / IP allowlist) before opening it publicly.
Troubleshooting
- Lucky reports
io timeout→ run/wan doctor. Almost always one of: the Windows IPv6 changed,iphlpsvcis stopped, or the firewall rule is missing. Test from the Lucky host withcurl -v --connect-timeout 5 "http://[<WindowsIPv6>]:3082"(do not useping— Windows drops ICMPv6 echo by default). - 403 on the workspace → the domain isn't in the trusted-host fence yet. Confirm
/wan upwrote it and restartdsh web. socatwon't start → install it, orrelayPortis already in use.
License
MIT
Links
More in this category
saya-ch/dsh-mobile★ 33
Use DeepSeek Harness from your phone over a protected LAN: a dedicated mobile UI, secure HTTPS pairing, and /mobile conversation customization.
mexiaosqwq/dsh-web-mobile★ 31
Mobile-adaptive layout for the DSH Web UI: the sidebar becomes a content-hugging overlay drawer, the conversation gets the full width, and the settings panel becomes a near-full-width sheet.
liguobao/deepseek-harness-remote★ 23
Secure multi-device remote access for DeepSeek Harness: connect from your phone or browser to your local Harness over an end-to-end encrypted channel (Noise IK with a WebRTC-ready adaptive transport), with device authorization and ApiProxy-only remote capabilities, no shell or arbitrary file access.
AcidGr/dsh-web-lan-access★ 21
LAN/remote access for the Web UI: injects a crypto.randomUUID polyfill on plain-HTTP origins so the frontend survives LAN or Tailscale IP direct links.
JUANWANG-BUAA/dsh-full-remote★ 15
Remote DeepSeek Harness with full server-side API access (`settings.*` / `credentials.*` / `host.listDirectory`). Token-gated reverse proxy, per-device sessions, phone invite QR, fence self-check, optional approval / CIDR / idle timeout / local TLS, WebSocket/SSE.
Blank-not-black/dsh-Remote#plugin★ 10
Mobile remote-control suite: bundle plugin with native sidebar entry and admin drawer, self-healing bearer-token gateway over LAN/Tailscale, Android app (sessions/approvals/questions/goals), /fs/* file transfer (Range resume, 2GB upload), latency-based multi-server auto-switch, offline chat cache.