DeepSeek Harness (dsh) Web plugin
- Language
- JavaScript
- License
- MIT
- Branch
- main
Install
$ dsh plugin --profile web add dsh-web-lan-accessRun the command above in your terminal to install this plugin via the dsh CLI. You can switch Profile in the top-right corner. New to dsh? Read the beginner tutorial
Install via your agent
Install the DeepSeek Harness plugin AcidGr/dsh-web-lan-access for me: review the repository at https://github.com/AcidGr/dsh-web-lan-access first, then run the install command and verify the plugin loads successfully.
Paste this instruction to the DSH Web GUI assistant — it will install and verify for you.
一句话定位
让 DSH Web UI 在纯 HTTP 的局域网 / Tailscale / VPN 环境下也能正常加载会话和模型。
核心能力
- 自动让 Web UI 在非安全上下文(纯 HTTP、LAN/Tailscale IP)下能正常启动
- 把 Web 服务默认绑定到 0.0.0.0,免去手动加 --host 参数
- 自动把当前所有非内部 IPv4(局域网、Tailscale 100.x、VPN 接口)加入 API 可信主机
- 在返回的 HTML 顶部注入一段 randomUUID polyfill 脚本,修复浏览器侧的 RPC 报错
- polyfill 在 HTTPS / localhost 下自动成为空操作,不影响正常使用
- 完全可逆:卸载插件即可恢复原状,不修改任何产品源码
技术实现
- 语言: JavaScript(ES Modules,
"type": "module") - 关键依赖: 无 npm 运行时依赖;仅依赖宿主服务的
webServer.tapIndex官方扩展点 - 架构模式: Cordis bundle patch(YAML 配置覆盖 + 极简 JS 插件体),通过
ctx.effect监听 webServer 行激活后注入 HTML 钩子 - 入口文件:
lib/index.js
适用场景
当你想从家里的局域网、公司内网,或通过 Tailscale 从另一台设备访问 DSH Web UI 时,本插件消除"会话和模型加载不出来"这个最常见的拦路虎。 如果你只在 127.0.0.1 本机使用 Web UI,或已经部署了 TLS 反向代理,则不需要这个插件。
前置依赖与兼容性
| 依赖 | 最低版本 | 说明 |
|---|---|---|
| DSH | 未声明 | 需使用 web profile;通过 webServer.tapIndex 与宿主交互 |
| Node.js | >= 22.3.0 | cordis.patch.yml 使用 process.getBuiltinModule("os"),该 API 从 Node 22.3 起可用 |
| 平台 | 跨平台 | Linux / macOS / Windows / Android 均可运行 |
| 原生模块 | 无 | 不引入任何原生依赖 |
安装方式
dsh plugin --profile web add github:AcidGr/dsh-web-lan-access
配置项
本插件无需额外配置。安装后直接 dsh --profile web --port <端口> 启动即可,绑定主机(0.0.0.0)和可信主机(自动枚举所有非内部 IPv4)由 bundle patch 自动接管。
仅在以下场景需要手动配置 connection.trustedHosts:
| 场景 | 写法 | 说明 |
|---|---|---|
用 MagicDNS 短名(如 http://myhost:3080) | - myhost | 短名必须单独列一行 |
| 用 MagicDNS 完整域名 | - myhost.tailXXXX.ts.net | 与短名分开写,Host 头逐字比对 |
| 用 Tailscale IP(100.x) | 无需配置 | 启动时已自动并入可信主机 |
常见问题
Q: 安装后页面还是会话/模型空白怎么办?
A: 先用 curl 抓首页 HTML 搜 lan-access-polyfill,确认 polyfill 真的注入了;如果注入了仍空白,说明 /api 被信任围栏挡了,确认 Tailscale/VPN 在 dsh 启动时已就绪,或把对应 IP 加到 trustedHosts。
Q: 需要自己加 --host 0.0.0.0 启动参数吗?
A: 不需要。插件的 bundle patch 会直接把 webserver 的 bind host 改成 0.0.0.0;新版本 harness 的 CLI 已硬性拒绝 --host 0.0.0.0,但 webserver 配置层仍接受该值。--port 参数照常可用。
Q: 局域网、Tailscale 都能直接用吗?
A: 可以。插件启动时枚举所有非内部 IPv4(局域网 192.168.x、Tailscale 100.x、VPN 接口)自动并入 API 信任围栏。前提是这些接口在 dsh web 启动时已就绪——Tailscale 通常开机自启即可。
Q: 想用 MagicDNS 短名(如 http://myhost:3080)访问为什么不行?
A: 信任围栏只能自动发现 IP 字面量,发现不了主机名。需要把短名(如 myhost)和完整域名(如 myhost.tailXXXX.ts.net)都加入 connection.trustedHosts,Host 头是逐字比对的——只加完整域名不够。
Q: 为什么从远程 IP 进设置页/凭据页是空的?
A: 这是上游 harness 的产品侧策略:少数敏感 API(settings.、credentials.、llm.discoverModels)被钉死为"仅 loopback 可信",与 trustedHosts 无关。需在 127.0.0.1 上访问这些页面,或本地改一行源码。
Q: 绑到 0.0.0.0 安不安全?
A: 不安全。0.0.0.0 等于对整个网络开放,且 /api 信任围栏只是浏览器来源检查,不是登录鉴权。公网 IP 暴露等于把 agent 开放给整个互联网。请用防火墙、Tailscale 或带鉴权/TLS 的反向代理。
Q: 怎么验证 polyfill 真的生效?
A: 在服务器终端执行 curl http://127.0.0.1:3080/ | grep lan-access-polyfill,应能看到包含 polyfill 注释和脚本的 HTML 输出。
Q: 如何卸载?
A: 执行 dsh plugin --profile web remove dsh-web-lan-access 后重启 dsh web 即可,也可手动删除 cordis.patch.yml 里的 lan-access insert 块。
上手难度
入门 — 装好即生效,无需编写配置文件或代码。
已知问题与限制
- 少数特权 API(settings.、credentials.、llm.discoverModels)在原始 harness 中被硬编码为 loopback-only,远程 IP 访问会 403;聊天 / 会话 / 模型不受影响,但设置页(含插件配置卡片)和凭据界面会显示为空(README.md:78-80)
- 信任围栏在启动时一次性快照网卡接口,Tailscale / VPN 必须早于
dsh web启动,否则需重启服务(README.md:62 / cordis.patch.yml:11-13) - MagicDNS 主机名需要手动加入
trustedHosts,且短名与完整域名必须分行列出,Host 头是逐字比对(README.md:67-76) - 绑定到 0.0.0.0 后网络内任意设备均可无鉴权访问,必须配合防火墙 / Tailscale / 反向代理(README.md:90-92)
English | 简体中文
LAN / remote access support for the DeepSeek Harness Web UI.
The problem
The Web UI calls crypto.randomUUID() in boot-critical paths (RPC id minting, message ids, draft attachments). That Web API exists only in secure contexts (HTTPS, or http://localhost / http://127.0.0.1). When the UI is served over plain HTTP from a non-loopback address — a LAN IP, a Tailscale IP, or a hostname — crypto.randomUUID is undefined, every RPC throws, and sessions and models never render.
The fix
A host-side plugin that uses the webserver's official index-tap extension point (webServer.tapIndex) to inject a small polyfill (RFC 4122 v4 built on crypto.getRandomValues, which is available on insecure origins) as the first script in <head>, before the boot manifest and the shell entry. On secure origins the polyfill is a no-op.
- No product source modified; fully reversible
- Version-independent (it only transforms the served
index.html) - Platform-independent (Linux / macOS / Windows / Android)
Install
Bundle install (recommended)
Installed from npm:
dsh plugin --profile web add dsh-web-lan-access
(No npm / local development — point pnpm at the repo instead:
dsh plugin --profile web add github:AcidGr/dsh-web-lan-access
)
Restart dsh web, then hard-refresh the browser.
Manual install (no pnpm / offline)
PROFILE="$DSH_HOME/profiles/web" # adjust DSH_HOME and profile name
mkdir -p "$PROFILE/plugins" "$PROFILE/node_modules/@dsh-profile"
cp -r dsh-web-lan-access "$PROFILE/plugins/lan-access"
ln -sfn ../../plugins/lan-access "$PROFILE/node_modules/@dsh-profile/lan-access"
# append to $PROFILE/cordis.patch.yml:
# - insert:
# - id: lan-access
# name: '@dsh-profile/lan-access'
Usage
The plugin is self-contained: its bundle patch sets the webserver bind host to 0.0.0.0 directly (the CLI flag --host 0.0.0.0 is hard-rejected for safety on newer harness versions, but the webserver config still accepts it — so no source changes and no --host flag are needed; the CLI --port flag still works).
-
Install the plugin, then start normally — without
--host:dsh --profile web --port 3080When bound to
0.0.0.0, the harness automatically adds every local non-internal IPv4 to the/apitrust fence (resolveLanTrust) — LAN IP access needs no extra config.If you prefer NOT to let the plugin take over the bind host (e.g. you want loopback + a port forward), keep the
webserverrow override out of your tree and instead forward a port (socat / rinetd / Tailscale serve) from127.0.0.1:3080, adding the forwarded address totrustedHostsmanually. -
Domains / remote (e.g. Tailscale) — add your own authorities to
trustedHosts:- id: web-runtime config: trustedHosts: - <short-name> # e.g. myhost — MUST be listed separately! - <name>.tailXXXX.ts.net # full domain - 100.x.x.x # tailnet IP⚠️ The fence compares the
Hostheader literally: a MagicDNS short name (http://myhost:3080) is not the full domain — list the short name on its own line, or every/apicall returns 403 (page shell loads, sessions/models absent).
Known limitation: privileged API methods
On unmodified harness builds, a small set of sensitive API methods (settings.*, credentials.*, llm.discoverModels) is pinned to loopback regardless of trustedHosts (isTrustedApiRequest(request, []) in packages/client/connection/src/index.ts). From a remote origin those calls return 403: chat/sessions/models still work, but the Settings pages (including the plugin-config cards) and credentials UI show empty/errors. The polyfill cannot change that — it is a product-side policy. A one-line upstream change (isTrustedApiRequest(request, trustedHosts)) makes them follow the deployment's trusted hosts; until then, edit that line locally or manage those settings from http://127.0.0.1:3080.
Verify
curl http://127.0.0.1:3080/ | grep lan-access-polyfill # must match
Then open http://<server-ip>:3080 from another device — sessions and models must load.
Security warning
Binding 0.0.0.0 makes the agent reachable without authentication by anyone on the same network (/api is an origin fence, not a login). On a server with a public IP this means the whole internet. Use only on trusted networks, restrict with a firewall (e.g. ufw allow from 192.168.0.0/16), or expose through Tailscale / an authenticated reverse proxy instead. A TLS reverse proxy also removes the need for this polyfill entirely.
Rollback
- Bundle install:
dsh plugin --profile web remove dsh-web-lan-access - Manual install: delete the
lan-accessinsert block fromcordis.patch.yml; optionally start without--host 0.0.0.0
License
MIT
Read the usage guide →
Install steps, key points, FAQ and compatibility for this plugin — auto-derived from indexed fields.
Listing badge
[](https://deepseek-plugin.org/plugins/AcidGr/dsh-web-lan-access)Paste this markdown into your GitHub README to link back to this listing. The badge only states the listing — not a security endorsement.