nonmean/dsh-lan-access
为DeepSeek Harness网页插件添加局域网访问功能
Project Overview项目介绍
dsh-lan-access is a DeepSeek Harness web plugin that adds a LAN access toggle in Settings → General. When enabled, the GUI binds to 0.0.0.0 so machines on the same network can open http://<LAN-IP>:3080, with the /api trust fence updated live for full chat, tools, and workspace use. Install directly from GitHub via dsh plugin --profile web add — no build step required. Caveats: default is off (loopback) for safety; enabling exposes the agent's tools to the network, so use only on a trusted LAN. Verified against DeepSeek Harness v0.1.1-rc.1. Disabling from a remote machine cuts that machine off, and native dialogs plus model discovery remain loopback-only.
dsh-lan-access 是 DeepSeek Harness 的 Web 插件,在设置→通用中加入“局域网访问”开关。开启后将 Web GUI 绑定到 0.0.0.0,同网设备可通过 http://<LAN-IP>:3080 访问,并同步更新 /api 受信边界。安装无需构建,直接从 GitHub 用 dsh plugin --profile web add 加载即可。注意事项:默认关闭(仅回环),仅在受信网络下开启;该绑定使代理工具暴露于网络;远程关闭会立即切断该机器访问;与 v0.1.1-rc.1 配合验证,其它版本未测试;远程仍受限于本地回环的原生对话框和模型发现。
请帮我了解并安装插件:【dsh-lan-access】【https://github.com/nonmean/dsh-lan-access】
Send this message to DSH in your current session. CLI install commands may not be accurate across systems — DSH will figure it out for you.把上面这条消息直接发给当前会话里的 DSH,让它帮你了解并安装。安装命令不一定准确,发给 DSH 更稳。
Or use CLI install (for developers)或使用命令行安装(适合开发者)
CLI Install命令行安装
dsh plugin --profile web add github:nonmean/dsh-lan-access
把 nonmean/dsh-lan-access 加入你的 DSH 配置(web profile)即可启用。
READMEREADME
dsh-lan-access
A DeepSeek Harness web plugin that adds a LAN access toggle to the DSH
Settings shell (Settings → General). It replaces the manual cordis.patch.yml
webserver override:
Tested with DeepSeek Harness
v0.1.5-rc.2— this plugin is verified runnable against that harness version.
- On — the web GUI binds
0.0.0.0, so other machines on the same network can open it athttp://<LAN-IP>:3080/?token=….dsh webprints the full URL (with the per-process?token=launch token) for the LAN address when it starts — a fresh LAN browser needs that token in the URL to authenticate. The /api trust fence is updated live, so the browser on a LAN machine works fully (chat, tools, workspace). - Off — the GUI binds
127.0.0.1again (loopback only — the safe default).
Screenshots
The DSH web GUI opened from another machine on the same network
(http://192.168.0.101:3080):

The LAN access toggle in Settings → General, showing the address other devices can open:

How it works
| Half | File | Role |
|---|---|---|
| Host | src/index.ts |
Registers the persisted lan-access settings namespace, the fenced /lan-access JSON route (GET state / POST set), the bind controller, and the lanAccess bind-host service. The webserver row's composed host expression reads that service, so every webserver (re)start — boot, toggle, or a post-boot user-patch re-apply — converges to the persisted setting; the controller only restarts the row when the bind actually differs. |
| Client | src/client/ |
Registers the General-settings row (settings.general.item, order 15) with a native checkbox switch, the LAN URLs (primary first, all live NIC addresses shown, copy button), zh/en copy, and restart-tolerant polling. |
The route fence accepts loopback or the deployment's trusted authorities, read live from the connection row's resolved config — the same boundary the /api gateway uses. Cross-site requests are refused.
Install from GitHub
The built artifacts (lib/) are committed, so installation needs no build
step and no modification of the DeepSeek Harness checkout:
# From GitHub (replace <owner>/<repo>)
dsh plugin --profile web add git+https://github.com/<owner>/<repo>.git
# ...or clone and install the local checkout (link: keeps your rebuilds live)
git clone https://github.com/<owner>/<repo>.git
dsh plugin --profile web add link:/path/to/dsh-lan-access
# Restart the GUI
dsh web
The install appends dsh-lan-access to dsh.profile.bundles; its
dsh.bundle.patch inserts the host row and overrides the webserver row's
host with the lanAccess service expression. The client half is picked up
by the client-modules scanner automatically. No harness change is required
for the core feature — the toggle, the LAN bind, and the live /api trust
fence all ship inside the plugin.
Local development — rebuild with
pnpm build(ornpm run build) after changingsrc/, then reinstall/restart. The repo'snode_modulesmirrors the DSH profile's package farm (TypeScript/tsdown come from the harness checkout).
Migrating from a manual patch — remove any
webserverhost: 0.0.0.0override from the profile'scordis.patch.yml(and the bundle patch layers) so the plugin is the single owner of the bind host.
Use
Open the GUI, go to Settings (sidebar footer) → General.
Flip 局域网访问 / LAN access.
- Enabling shows the ONE address other devices can open — the IPv4 of the
interface that owns the default route (
http://192.168.x.x:3080) — with a copy button. - That address must include the browser-session
?token=…when opened from another machine.dsh webprints the full URL (with the token) for both the loopback and the LAN address on startup — copy the LAN one, e.g.http://192.168.0.101:3080/?token=ICKD2317KYP…. The token is a per-process launch token that exchanges for a session cookie; a fresh LAN browser cannot authenticate without it. - The web server restarts to rebind; the row waits for it and re-reads the state (a network error mid-restart is not reported as failure).
- The plugin also installs a
crypto.randomUUIDpolyfill on plain-HTTP LAN origins (that Web API only exists in secure contexts, and the DSH API client mints every RPC id with it — without the polyfill a remote browser fails with "crypto.randomUUID is not a function").
- Enabling shows the ONE address other devices can open — the IPv4 of the
interface that owns the default route (
The choice is persisted in
~/.dsh/settings.yaml:lan-access: enabled: true
Remote Settings pages and workspace — no harness change needed
Everything the plugin serves works from a LAN browser with zero modification of the DSH checkout:
- The current
/apigateway trusts the served LAN authority, so the configuration plane (settings.*,credentials.*) reaches the host directly — the Host/Origin fence admits the LAN host and the ordinary browser-session auth authenticates it. The Models page provider directory, the Plugins configuration cards, and the Language/Appearance rows therefore work remotely with no extra hop. - The client
settingsScopedegrades to memory mode on non-loopback origins (surfaces render empty). The browser bundle widensconnection.isLoopbackto "loopback OR served LAN authority" at runtime. The client entry injectsconnectionand is markeddsh.client.immediately, so its bundle is prefetched and itsapplyruns right after the connection row provides the handle — before any settings surface (which waits onremote/settingsScope) readsremote.$host.isLoopback. Thatinject-ordered widening is what keeps the scope in host mode on a LAN page: without it, a surface that binds early sees the unpatchedisLoopbackand its scope stays memory-mode — the plugin configuration cards render nothing. crypto.randomUUIDdoes not exist on plain-HTTP LAN origins. The bundle installs agetRandomValues-based polyfill (same CSPRNG).
Harness API changes this plugin tracks
The harness evolved the settings/connection APIs between 0.1.0-rc.5 and
0.1.5-alpha.1; the plugin was updated accordingly:
@deepseek-ai/dsh-settingsdropped thesettingsNamespace(ns)helper —settings.register/.update/.replace/.mutatenow take the raw namespace string (validated at runtime and by a compile-time guard).- The client
ConnectionHandleno longer carries anapimember — remote methods go throughconnection.rpc.call('/api', '<ns>/<method>', …)— so the plugin no longer patchesconnection.api.settings.*/connection.api.credentials.*. - The
/apigateway stopped pinning the configuration plane to loopback: it now trusts the served LAN authority, so the settings/credentials RPCs reach the host directly. The plugin therefore widensconnection.isLoopbackearly (viainject: ['connection']plus a synchronous patch) to keepsettingsScopein host mode on a LAN page; the fenced/lan-access/rpcproxy is no longer required for the remote Settings surfaces.
Remaining loopback-only (hardcoded in the harness, not patchable from a
plugin): host.pickDirectory / host.openPath (native dialogs and host
file opens) and llm.discoverModels (the Models page "discover" button).
The workspace's own add/browse flow does not need them, and chat file
opens route into the sidebar editor.
Debug aids
The host exposes GET /lan-access/diag (fenced like the other routes) with
the latest browser boot reports: slot-registration counts, whether the
connection patch is active, and a settingsScope probe bound to the
shell namespace (status ready proves the host-mode settings read works
end to end). During the first minute after boot the browser also posts a
2-second poll of the Plugins cards' own injected snapshots (available
flags), the slot ledger view, and the declared spec — the exact data that
separates "cards gone", "cards abdicated", and "cards present but rendering
null" when a Settings page misbehaves on a remote machine.
Optional: dsh-better-sidebar compatibility patch
dsh-better-sidebar's trust fence matched the connection row by the wrong
name and read the raw !!js config, so its panels (explorer / editor /
terminal / git) only ever accepted loopback. The repo ships the fix as a
profile-level pnpm patch (no harness change):
./scripts/install-patches.sh web
This copies patches/dsh-better-sidebar.patch into the profile's
patches/ directory, registers it under patchedDependencies in
pnpm-workspace.yaml, and runs pnpm install.
Security notes
- Default is off (loopback). The DSH launcher itself refuses
--host 0.0.0.0for the same reason: binding all interfaces exposes the agent's tools to the network. Only enable it on a trusted network. - The toggle is only reachable through the fenced route, and disabling from a remote machine cuts that machine off (expected — re-enable locally).
- The bind survives plugin reloads and patch re-applies; a full process restart re-applies the persisted value at boot.
Development
pnpm build # tsdown: lib/index.js (host) + lib/client.js (browser bundle)
pnpm typecheck # tsc --noEmit
The client bundle is a __ModuleLoader__.load closure-factory artifact (same
format as the DSH monorepo's tsdown client preset); only the frozen
platform-module table words stay external. After changing client code, rebuild
and restart dsh web (the client-modules package metadata cache expires only
on restart).
xmanrui/dsh-im
tencent-connect/dsh-qqbot
flymysql/dsh-remote
whiteguo233/dsh-openbiliclaw
hanshanyike/dsh-yolo
omdsh-dev/dsh-lark
AX1202/ax-feishu-bridge