Web UI 局域网/远程访问:为纯 HTTP 非安全上下文注入 crypto.randomUUID polyfill,局域网/Tailscale IP 直连时前端不再崩溃。
安装
# npm 包(预构建)
dsh plugin --profile web add dsh-web-lan-access
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:AcidGr/dsh-web-lan-access
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 或 ERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yaml 的 allowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。
README
English | 简体中文
DeepSeek Harness Web UI 的局域网/远程访问支持插件。
一条命令绑定所有网口,从局域网 IP、Tailscale IP 或 VPN 地址访问界面,同时保持远程页面上的宿主设置可用。
兼容性
| dsh | 插件 | 说明 |
|---|---|---|
| 0.2.2-rc.1 | 1.4.x | 当前版本,已端到端验证 |
| 0.2.1-rc.x | 1.4.x | 已端到端验证 |
| 0.2.0-rc.x | 1.3.x | 当时的 webserver 仍接受 0.0.0.0 |
| ≤ 0.1.7 | 1.3.x | 尚未加入通配地址限制 |
1.4.0 是针对 dsh 0.2.1 收紧 webserver 的版本。从 1.3.x 升级请先看为什么 1.4.0 必须改架构。
问题
远程浏览器要正常工作,中间隔着三件事:
1. 监听只绑回环。 dsh web 默认绑 127.0.0.1,而从 0.2.1 起 CLI 直接拒绝通配地址:
error: --host 0.0.0.0 is an unspecified (wildcard) address, which is not supported:
binding every interface would expose remote code execution to the network
2. /api 围栏拒绝未知权威。 每个请求的 Host 必须是回环、监听器自身的绑定字面量,或已声明的 trustedHosts 条目。通配绑定没有自己的字面量——0.0.0.0 匹配不上任何请求 Host——于是页面壳能加载,但所有 /api 请求返回 403,会话永远出不来。
3. 宿主设置受 isLoopback 门控。 client-connection 由 isLoopback: transport?.ownsHost === true 推导该标志,客户端插件据此决定能否访问宿主面。从局域网或 Tailscale 地址打开的页面按主机名不是回环,所以即使认证成功,模型页和插件设置页仍然是空的。
原理
两块小改动,都可逆。
替换 webserver 行
lib/webserver.js 继承 @deepseek-ai/dsh-host-webserver,只改两处:
Config委托给原版 schema,仅豁免通配地址那一条拒绝。非字面量 host、端口越界、缺端口仍然照原样失败——放宽范围只有一条错误信息那么宽。getOrigin()绝不返回通配 origin。父类用绑定地址拼 URL,0.0.0.0会拼出http://0.0.0.0:3080——浏览器根本拨不通。通配监听改为报告回环地址,而--public-url仍会在上层覆盖对外宣告的根地址。
其余全部原样继承:服务名、路由、index tap、TLS、gzip、监听生命周期。
从实时网卡列表扩宽 /api 围栏
bundle patch 从本机当前所有非内部 IPv4 推导 trustedHosts,局域网、Tailscale(100.x)、VPN 地址全部接受,无需逐 IP 配置。
注入宿主所有权 bootstrap
通过 webserver 官方 index-tap 扩展点(webServer.tapIndex),在 <head> 的第一个脚本位置注入,早于启动清单和 shell 入口。它声明 ownsHost: true 并保留普通 HTTP fetch transport;如果其他 shell 已提供 transport,则不会替换。
1.4.0 移除了什么:
crypto.randomUUIDpolyfill。从 0.2.1 起 DSH 通过@deepseek-ai/dsh-util-crypto生成 UUID,它基于crypto.getRandomValues(非安全上下文可用),所以纯 HTTP 的局域网页面已无需补丁即可渲染。旧 polyfill 所绕过的 bug 已不存在。
为什么 1.4.0 必须改架构
1.3.x 及更早版本的做法是把 host: '0.0.0.0' 写进原版 webserver 行的 config。0.2.1 让这个做法因两个独立原因失效:
- webserver 自己的 schema 现在拒绝通配地址,该行校验失败,启动直接中止并报
3 required plugins did not activate。 - patch 无法改目标的
name——app-boot 会跳过任何name与目标不符的补丁——所以无法原地把该行换成自定义模块。
因此 1.4.0 禁用原版行(disabled: true,启动审计会跳过),并用新 id 挂载子类。同一轮升级还带来第二处损坏:web-app 不再提供 webRuntime 服务,旧表达式 ctx.webRuntime?.trustedHosts 抛 cannot get property "webRuntime" without inject。现在围栏列表直接由 os.networkInterfaces() 推导。
安装
方式一:直接发给你的 DSH(最省心 🤖)
直接在 DSH 网页对话框中把本仓库链接发给 AI,并附上指令:
DSH Agent 会自动在后台执行安装命令并完成配置。
方式二:CLI 命令行安装(推荐)
从 npm 安装:
dsh plugin --profile web add dsh-web-lan-access
不走 npm / 本地开发,用仓库地址:
dsh plugin --profile web add github:AcidGr/dsh-web-lan-access
重启 dsh web,浏览器硬刷新。
方式三:手动安装(无 pnpm / 离线)
PROFILE="$DSH_HOME/profiles/web" # 按实际修改 DSH_HOME 和 profile 名
mkdir -p "$PROFILE/plugins"
cp -r dsh-web-lan-access "$PROFILE/plugins/lan-access"
ln -sfn ../../plugins/lan-access "$PROFILE/node_modules/dsh-web-lan-access"
然后在 profile 的 package.json 里把 dsh-web-lan-access 加入 dsh.profile.bundles。bundle patch 会自行注册所需行,无需手改 cordis.patch.yml。
使用
插件是自包含的:bundle patch 已提供绑定 host,所以你不要传 --host(也不能传——CLI 依然拒绝 0.0.0.0)。--port、--tls-cert、--tls-key、--public-url 全部照常可用。
安装插件后正常启动即可:
dsh --profile web --port 3080patch 会从本机当前所有非内部 IPv4(局域网
192.168.x、Tailscale100.x、VPN 接口)推导/api信任围栏。只要远程接口在dsh web启动时已就绪,局域网 / Tailscale IP 访问零额外配置:打开http://<服务器IP>:3080或http://<tailscaleIP>:3080即可正常加载会话和模型。如果你不想让插件接管绑定(例如想保持 127.0.0.1 + 端口转发),把
dsh-web-lan-access从 bundles 里去掉,改用 socat / rinetd /tailscale serve从127.0.0.1:3080转发端口,并把转发入口地址手动加入trustedHosts。MagicDNS 主机名(如
xxx.tailXXXX.ts.net)——围栏只能自动发现 IP 字面量,无法发现主机名。想按名字访问时用可重复的 CLI 参数补上:dsh --profile web --trusted-host myhost --trusted-host myhost.tailXXXX.ts.net或者把
connection行的trustedHosts换成字面量列表,并保留你需要的网卡地址:- id: connection config: trustedHosts: - <短名> # 如 myhost —— 必须单独列出! - <名称>.tailXXXX.ts.net # 完整域名 - 192.168.1.10 # 保留你的 IP:字面量列表会完全替换 - 100.64.0.5 # 掉推导表达式⚠️ 围栏逐字比对 Host 头:MagicDNS 短名(
http://myhost:3080)≠ 完整域名,短名必须单独列一行,否则所有/api请求返回 403(页面壳能开、会话/模型全无)。⚠️ 往
connection.config.trustedHosts写纯列表是替换而非合并 bundle 的推导表达式。要么保留推导表达式并用--trusted-host追加名字,要么把你需要的每个 IP 都写全。不要混用。
宿主所有权范围
该 transport 信号会启用 DSH 当前归入“拥有宿主”的全部客户端界面,并非只启用模型页。其中包括宿主持久化设置,以及打开生成文件等宿主原生操作。仅应在已认证的远程浏览器确实用于操作 agent 主机时使用本插件。DSH 的 Host/Origin 围栏和浏览器认证仍然生效;此信号改变的是客户端能力投影,不是请求认证。
旧版 harness 如果仍在服务端把特权方法限制为仅回环,这些方法仍会返回 403。客户端 bootstrap 不会削弱服务端围栏。
验证
# 1. bootstrap 已注入
curl -s http://127.0.0.1:3080/ -b <cookie> | grep lan-access-bootstrap
# 2. 所有网口都在监听(地址列为 0.0.0.0)
ss -ltnp | grep 3080
# 3. 围栏放行你的网卡 IP、拒绝其他
curl -s -o /dev/null -w '%{http_code}\n' -H "Host: 100.64.0.5:3080" http://127.0.0.1:3080/api # 401
curl -s -o /dev/null -w '%{http_code}\n' -H "Host: evil.example:3080" http://127.0.0.1:3080/api # 403
401 表示围栏放行了该权威、只缺会话 cookie——对没有浏览器会话的可信 IP 而言这是预期结果。403 表示权威被拒绝。再从另一台设备打开 http://<服务器IP>:3080——会话和模型应正常加载。
安全警告
绑定 0.0.0.0 会把 DSH 认证入口暴露到所有可达接口,包括你未必考虑过的地址——容器网桥、VPN 隧道,以及主机连接的任何其他网络。trustedHosts 是 Origin/Host 围栏而非身份认证;DSH 会另外认证浏览器。
本插件是在有意放宽 DSH 维护者刻意加入的限制:
binding every interface would expose remote code execution to the network
请只在可信网络使用;用防火墙限制网段(如 ufw allow from 192.168.0.0/16),或走 Tailscale / 带鉴权的反向代理。如果单个网口就够用,优先用原生的 dsh web --host <ip>——它不需要任何插件,而且围栏会自动信任绑定字面量。
由于本插件依赖 webserver 的 schema 与 URL 构造内部实现,未来 DSH 版本可能再次破坏它。升级前请核对兼容性表。
回滚
- bundle 安装:
dsh plugin --profile web remove dsh-web-lan-access - 手动安装:从
dsh.profile.bundles移除dsh-web-lan-access;bundle patch 的行随之消失,原版仅回环的webserver重新接管。
开发
node --test # 9 项测试,除已链接的 dsh 包外无其他依赖
lib/webserver.js 需要能从包根解析到 @deepseek-ai/dsh-host-webserver。在 profile 外开发时链接一次:
mkdir -p node_modules/@deepseek-ai
ln -sfn "$(npm root -g)/@deepseek-ai/dsh/node_modules/@deepseek-ai/dsh-host-webserver" \
node_modules/@deepseek-ai/dsh-host-webserver
许可证
MIT
链接
同类插件
zhu1090093659/dsh-web#packages/dsh-remote-web-ui★ 9959
手机/PC 远程操控 dsh web 工作区:扫码配对、令牌门控通道、SSE 实时同步,提供移动端与完整桌面 GUI 两种远程形态。
zhu1090093659/dsh-web#packages/dsh-ssh★ 9959
SSH 远程运维面板:Web 终端、SFTP 传输、本地端口转发与一条命令并发集群执行,Agent 与面板共用同一份主机配置。
liguobao/ds-harness-remote★ 1319
通过端到端加密通道远程访问 DeepSeek Harness 及兼容的编码工作区,支持自适应 Relay/WebRTC 传输、设备授权、工作区文件、只读预览、终端和可选的 Codex Remote。
saya-ch/dsh-mobile★ 728
通过 Android App 或手机浏览器访问 DeepSeek Harness,支持安全局域网连接、远程访问、持久设备配对和可自定义移动界面。
uumit-ai/dsh-uumit-remote★ 381
用 UUMit 账号在手机或其他电脑上打开这台电脑的 dsh web,不用自建服务器、不装其他程序:插件内置通道客户端连接 UUMit 托管的通道,侧栏里的面板负责配对和选择谁能用(仅自己、通过邀请链接或手机号授权的人、所有人),可随时撤销。需 dsh web 且 DSH >=0.2.1-rc.2。
ZSeven-W/dsh-ios★ 355
在对话里直接操作 iOS 模拟器或 USB 连接的 iPhone:22 个 Agent 工具用于启动、构建、按无障碍标识或 OCR 文本驱动 UI、列表行操作与 SwiftUI 预览热重载,并附带可点击拖拽的流式侧边栏面板。
社区评论
评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。