Introduction¶
DeepSeek Harness (DSH)’s dsh web listens only on 127.0.0.1 for security reasons and explicitly rejects --host 0.0.0.0. Its session cookie has SameSite=Strict, so a mobile browser opening it via an external link will only receive HTTP 401 dsh web authentication required. If you want to control DSH with a tablet or phone in the same room, the native way won’t work.
dsh-lan-pair is maintained by FORGE-131. It consists of a DSH plugin and a 45 KB Android shell (named DSH LAN). The plugin opens a separate LAN port inside the DSH process and holds the DSH session cookie itself; the phone only talks to the plugin and never gets the DSH startup token. Below is an introduction to its installation, pairing, and remote access.
Its Structure¶
It consists of two parts, both essential:
- Plugin Side (
dsh-lan-pair): Starts a LAN listener inside the DSH process, reverse proxies to localhost, automatically completes the DSH token exchange, and holds the session cookie itself. - App Side (DSH LAN): Automatically discovers DSH computers on the LAN, enters a 4-digit pairing code once, and connects immediately after opening, displaying the interface in a full-screen system WebView.
- There is also a deep-link solution for browsers; the phone doesn’t need root, USB debugging, or manual URL construction.
Installing the Plugin¶
Prerequisites: DSH is installed (this plugin has been verified on 0.1.7-rc.2), and you have a web profile. <profile> is your profile name; when using the official desktop launcher, it is usually the DSH version number (e.g., 0.1.7-rc.2), while using dsh web yourself is usually web.
- Enter the profile directory and add dependencies. Windows example
cd %USERPROFILE%\.dsh\profiles\0.1.7-rc.2, then:
pnpm add github:FORGE-131/dsh-lan-pair#v0.2.3
To debug by modifying code, use pnpm add link:/path/to/dsh-lan-pair to install from a local directory.
- Register the package name in
dsh.profile.bundlesof$DSH_HOME/profiles/<profile>/package.json:
{
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-lan-pair"
]
}
}
}
This package comes with cordis.patch.yml, which takes effect automatically after registration; no need to manually modify the patch file.
- Restart DSH. The startup logs should appear:
[dsh-lan-pair] listening on 0.0.0.0:8099 -> 127.0.0.1:10723
[dsh-lan-pair] pairing is off until you open a window (DSH settings -> Mobile Device)
Broadcasting is off by default: before clicking “Enable Pairing”, the phone cannot scan for this computer. If a Windows Firewall prompt appears, please allow it, or manually allow this single TCP port (subnet only):
netsh advfirewall firewall add rule name="DSH LAN Pair" dir=in action=allow protocol=TCP localport=8099 profile=private,domain remoteip=LocalSubnet
Pairing: Four Steps, 4-Digit Code¶
- On the computer, open DSH “Settings -> Mobile Device” and click “Enable Pairing”.
- A window pops up on the computer displaying 4 digits, and broadcasting on the LAN starts simultaneously.
- Enter these 4 digits on the phone.
- After successful pairing, the window closes automatically, broadcasting stops, and the App obtains long-term credentials, connecting immediately upon opening in the future.
The pairing code is valid for two minutes, can only be used once, and becomes invalid after 5 incorrect attempts. Since the code is only 4 digits, without limits, ten thousand combinations could be tried in a few seconds. If the App malfunctions and manual entry is needed, the long-term key is stored in $DSH_HOME/lan-pair.json (e.g., C:\Users\<you>\.dsh\lan-pair.json), with content like:
{ "secret": "bfrVhwqHdzue50xo", "createdAt": "2026-11-01T21:40:07.737Z" }
Deleting the file and restarting DSH will generate a new one. Environment variables take priority; common ones are:
| Variable | Purpose |
|---|---|
DSH_LAN_PAIR_SECRET |
Overwrites long-term key |
DSH_LAN_PAIR_PORT |
Overwrites port (default 8099) |
DSH_LAN_PAIR_PUBLIC_URL |
Fixed public address (for naming tunnels / reverse proxy) |
DSH_LAN_PAIR_TUNNEL |
Command to automatically start tunnel; plugin extracts public address from output |
DSH_LAN_PAIR_TTL_MS |
Validity of pairing code (default 120000) |
DSH_LAN_PAIR_DEBUG |
Output mDNS packet diagnostic logs |
When the pairing window is not open, you can also manually visit http://127.0.0.1:<port>/lanpair/open to enable it.
When Not on the Same LAN¶
Pairing is permanent; the long-term key does not expire, and changing networks does not require re-pairing. However, the App remembers “addresses,” and when going out, you need an externally reachable address. Two ways:
Way 1, let the plugin host the tunnel:
$env:DSH_LAN_PAIR_TUNNEL = "cloudflared tunnel --url http://127.0.0.1:8099 --no-autoupdate"
Way 2, fill in a fixed address (naming tunnel / reverse proxy):
$env:DSH_LAN_PAIR_PUBLIC_URL = "https://dsh.example.com"
The plugin provides GET /lanpair/info on the DSH source to check current available addresses, returning something like:
{
"ok": true,
"port": 8099,
"lan": ["http://192.168.1.14:8099"],
"publicUrl": "https://xxx.trycloudflare.com",
"pairLink": "https://xxx.trycloudflare.com/__pair?k=...",
"lanPairLink": "http://192.168.1.14:8099/__pair?k=...",
"tunnel": { "running": true, "url": "https://xxx.trycloudflare.com" }
}
The plugin writes publicUrl into the mDNS TXT record (remote=). When the App discovers the computer, it gets both the LAN address and the remote address, saves them as an endpoint list, and probes sequentially with a 3-second timeout during connection: it tries the LAN address first (instant connection at home, no public route), and if that fails, tries the remote address (activates automatically when out), using the first successful one without manual switching.
Installing the App¶
Download dsh-lan-pair-<version>.apk from Releases and sideload it (requires allowing “Unknown sources”). Minimum Android 8.0 (API 26), no CPU architecture restrictions (no native libraries included), renders using system WebView, supports https:// remote addresses and deep links, and does not bundle a browser kernel. The app signature is a self-signed certificate (CN=DSH LAN Pair), not a store signature.
Deep Links and Typical Usage¶
You can create a QR code or shortcut for the connection address; scanning it connects immediately:
dshlanpair://connect?base=<address>&remote=<backup address>&fallback=<further backup>&k=<key>
For example, a link containing both LAN and tunnel addresses:
dshlanpair://connect?base=http%3A%2F%2F192.168.1.14%3A8099&remote=https%3A%2F%2Fxxx.trycloudflare.com&k=<key>
Daily usage workflow:
- On the computer, “Settings -> Mobile Device -> Enable Pairing”.
- Open DSH LAN on the phone; the computer appears in the list (e.g.,
DESKTOP-I9IRUS8 (win32) 192.168.1.14:8099). - Enter the 4 digits popping up on the computer and click “Connect”.
- Afterward, every time the App is opened it will connect automatically; to switch computers, press the back key to bring up the connection panel.
If it cannot be scanned, confirm that “Enable Pairing” has been clicked on the computer (it won’t broadcast if not clicked), or manually fill in IP:port in the address bar.
Scope and Notes¶
Suitable for people wanting to control DSH with a phone on the same Wi-Fi, and scenarios where you need to connect back to the home machine after traveling.
A few things you must know:
- The plugin runs with the current DSH process permissions; you should check the source code and license (MIT) before installing.
- The plugin depends on the internal DSH service names
connectionandwebServer, as well asconnection.authenticatedUrl(). These are not public, stable APIs and may change after DSH updates. Please lock the DSH version you have verified inpeerDependenciesinpackage.json. - The pairing key is only for preventing accidental connections, not strong encryption: inside the LAN it is plaintext HTTP, and anyone capable of packet sniffing can get the session. It prevents accidental exposure like a neighbor nearby opening it and commanding your computer; for security, please wrap it in a TLS reverse proxy yourself.
- Cloudflare quick tunnel domains (
trycloudflare.com) change every restart, and multiple tunnels built concurrently on the same exit IP will be rate-limited. For long-term use, please configure a named tunnel or useDSH_LAN_PAIR_PUBLIC_URLto point to a fixed domain. - App sideloading requires allowing “Install unknown sources”, and the first connection will prompt the Windows Firewall to allow that specific TCP port.
Conclusion¶
Its value lies in not touching DSH’s security boundaries: it doesn’t modify the listening of dsh web and doesn’t hand the token to the phone. It uses the plugin’s own port and 4-digit pairing code as an intermediate layer, while using mDNS to merge LAN and tunnel addresses into an automatically switchable endpoint list.
- Directory: https://www.skillhub.cn/plugins/FORGE-131/dsh-lan-pair
- GitHub: https://github.com/FORGE-131/dsh-lan-pair