@yueker/dsh-lan-access
v1.5.7
Published
DSH web profile bundle: makes the Web GUI work over plain-http LAN IPs (polyfills crypto.randomUUID), optionally re-targets authenticated LAN API traffic to loopback authority (allowPrivilegedFromLan), and adds an opt-in password gate for LAN peers with f
Maintainers
Readme
dsh-lan-access
English | 简体中文
一个 DeepSeek Harness Web 配置(profile)bundle,让 Web GUI 能通过 纯 HTTP 局域网 IP(非安全上下文)正常使用,并可选择性地让完整的已认证 Web API 在受信任局域网设备上可用。
功能
crypto.randomUUIDpolyfill(默认启用)—— 修复局域网 IP 访问时 Web GUI 卡死的问题。allowPrivilegedFromLan(可选开启)—— 将已认证局域网客户端的全部/api/*HTTP 与 WebSocket 请求按 DSH 回环权限处理,不再维护容易漂移的方法白名单。authEnabled(可选开启)—— 为整个/api平面(HTTP + WebSocket)加密码门禁,支持首次使用设置密码和网页修改密码;DSH 本身没有 Web 认证。
问题
DSH Web GUI 在客户端连接握手中使用 crypto.randomUUID()(Chrome 中仅安全上下文可用)生成 RPC id。当通过纯 HTTP 的局域网 IP(如 http://192.168.2.102:3080)访问 GUI 时,该源不是安全上下文,于是:
crypto.randomUUID()未定义并抛出TypeError- 连接握手(
host.describe)在发出任何请求之前就失败 - WebSocket 被关闭(关闭码 1006),界面无限循环:
[web-runtime] connection lost, retry #N - 页面一直卡在欢迎页——看不到会话,也看不到工作区
http://localhost 正常,因为 localhost 属于安全上下文——这就是为什么在服务器本机正常、但从其他局域网设备访问却坏掉的原因。
修复
本 bundle 通过 webServer 的 index-tap 向每个被服务的 index.html 注入一段小 <script>,用 crypto.getRandomValues() 定义 crypto.randomUUID()——该 API 在非安全上下文也可用。polyfill 是符合规范的 RFC 4122 v4 UUID 生成器,仅在原生方法缺失时安装。
安装
前置条件
- DSH 的
webprofile 至少启动过一次(profile 位于~/.dsh/profiles/web/,若设置了DSH_HOME则在$DSH_HOME/profiles/web/)。 - 装有
git且能访问 GitHub(用于获取插件)。 - 能操作运行 DSH 的服务器终端。
以下命令适用于 Linux / macOS。Windows(PowerShell)用户请把 ln -s <目标> <链接> 换成 New-Item -ItemType SymbolicLink -Path <链接> -Target <目标>(需管理员或开启开发者模式),路径中的 ~/.dsh 换成 %USERPROFILE%\.dsh。
第 1 步 — 获取插件
克隆仓库(或在 GitHub 页面 Code → Download ZIP 下载并解压):
git clone https://github.com/yueker/dsh-lan-access.git ~/dsh-lan-access没有
git?在 GitHub 页面下载 ZIP 并解压,解压出的目录名是dsh-lan-access-main,第 2 步用它作为目标路径即可。
第 2 步 — 把插件放到 web profile 可加载的位置
mkdir -p ~/.dsh/profiles/web/node_modules
ln -s ~/dsh-lan-access ~/.dsh/profiles/web/node_modules/dsh-lan-access确认链接已创建:
ls -l ~/.dsh/profiles/web/node_modules/
# dsh-lan-access -> /home/<你>/dsh-lan-access如果用 ZIP 解压,目标目录是
dsh-lan-access-main:ln -s ~/dsh-lan-access-main ~/.dsh/profiles/web/node_modules/dsh-lan-access
第 3 步 — 把插件注册为 profile bundle
编辑 ~/.dsh/profiles/web/package.json:
- 在
dependencies中加入"dsh-lan-access": "file:./node_modules/dsh-lan-access"; - 在
dsh.profile.bundles数组末尾(@deepseek-ai/dsh-web-app之后)追加"dsh-lan-access"。
完整示例(保留你 profile 里已有的条目):
{
"name": "dsh-profile-web",
"private": true,
"dependencies": {
"dsh-lan-access": "file:./node_modules/dsh-lan-access"
},
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-lan-access"
]
}
}
}第 4 步 — 配置 profile patch
编辑 ~/.dsh/profiles/web/cordis.patch.yml:
# 1) 把 Web 服务器绑定到所有网卡,让局域网设备能访问
- id: webserver
config:
host: '0.0.0.0'
port: 3080
# 2) 启用插件的局域网修复
- id: secure-context-polyfill
config:
allowPrivilegedFromLan: true # 可选:让完整已认证 API 在局域网可用
authEnabled: true # 可选:密码门禁(首次使用设置密码)host: '0.0.0.0'是局域网访问所必需的。port选一个空闲端口即可。allowPrivilegedFromLan和authEnabled都是可选的——启用前请先阅读下方局域网使用完整 API和密码鉴权两节。
第 5 步 — 重启 web 应用
在运行 dsh web 的终端按 Ctrl+C 停止,然后重新启动:
dsh web第 6 步 — 验证
在服务器上:
curl -s http://127.0.0.1:3080/ | grep -c randomUUID # ≥ 2 表示 polyfill 已注入从同一局域网的其他设备打开 http://<服务器局域网IP>:3080 并强制刷新(Ctrl+Shift+R)。如果开启了 authEnabled: true,第一个访问者会看到"设置访问密码"界面。
备选安装方式:dsh plugin(需要 pnpm)
如果安装了 pnpm,dsh plugin 可以一步完成安装和注册:
# 包发布到 npm 仓库后:
dsh plugin --profile web add dsh-lan-access
# 或直接从本仓库的本地目录安装:
dsh plugin --profile web add /path/to/dsh-lan-access
dsh web更新
cd ~/dsh-lan-access && git pull
# 然后重启 dsh web(Ctrl+C,再 dsh web)(适用于 symlink 安装方式;如果是复制安装,把更新后的目录重新复制覆盖即可。)
局域网使用完整 API(可选开启)
DSH 会把特权 API 操作限制在回环权限。实践表明,在插件里复制并维护一份方法白名单很容易随 DSH 新增接口而失效,例如模型提供方目录、Agent 预设列表、目录浏览以及会话事件流。
开启 allowPrivilegedFromLan: true 后,插件会把局域网客户端的所有 /api/* 请求按回环权限处理,同时覆盖普通 HTTP 调用和 WebSocket 升级(/api/events.mux、/api/events.host):
# ~/.dsh/profiles/web/cordis.patch.yml
- id: secure-context-polyfill
config:
allowPrivilegedFromLan: true
authEnabled: true强烈建议同时开启 authEnabled: true:非回环客户端若未登录,会在回环重定向之前被拒绝。如果关闭鉴权,此选项将信任所有能访问 DSH 监听端口的客户端,因此只能用于受信任局域网。
由于整个 API 采用统一处理,DSH 后续新增接口无需更新插件即可使用,会话与工作区事件也能通过 WebSocket 正常传递。
密码鉴权(可选开启)
DSH 没有内置 Web 认证(官方注释:"until a real authentication layer exists"——在真正认证层出现之前)。当 GUI 可以被其他设备访问时,网络上任何人都能使用。要加密码门禁,设置 authEnabled:
# ~/.dsh/profiles/web/cordis.patch.yml
- id: secure-context-polyfill
config:
authEnabled: true首次使用:第一个访问者会看到"设置访问密码"界面。密码以加盐(scrypt)形式存储在 $DSH_HOME/lan-access-password.json(权限 0600),重启后依然有效。
之后每次局域网访问:显示登录界面。GUI 有意不再显示右下角悬浮的“修改密码”按钮;要修改首次设置的密码,可删除 $DSH_HOME/lan-access-password.json 并重启以重新进入首次设置流程,或在 profile 配置中用 authPassword: '...' 固定新密码。
回环访问(127.0.0.1、::1 或 IPv4 映射回环地址)根据实际 TCP 对端地址免密码。插件绝不信任转发头;这样保留 DSH 本机特权权限语义,同时局域网对端仍需认证。
鉴权覆盖范围:
- 所有来自非回环对端的
/api请求(HTTP 和 WebSocket,包括事件流)都需要会话 cookie;未认证请求返回401/ WebSocket 升级被拒绝 - 会话使用
HttpOnly+SameSite=Strictcookie,token 存储在$DSH_HOME/lan-access-sessions.json(12 小时有效期,仅保存 SHA-256 哈希,重启后仍然有效) - 接口:
POST /api/__lan_auth.status、POST /api/__lan_auth.setup(仅首次)、POST /api/__lan_auth.login、POST /api/__lan_auth.logout、POST /api/__lan_auth.changePassword(需已登录)
⚠️ 安全提示:这是面向受信任局域网的便利性门禁,不是加固的安全边界。密码和会话 cookie 通过纯 HTTP 明文传输(无 TLS)、密码为所有被授权者共享、对暴力破解只有很轻的限制。需要更强保护时,请在 DSH 前面加 TLS 反向代理并配合真正的认证方案。
验证
重启后:
curl -s http://127.0.0.1:3080/ | grep -c randomUUID # ≥ 2 表示 polyfill 已注入然后从其他设备打开 http://<你的局域网IP>:3080 并强制刷新(Ctrl+Shift+R)。
工作原理
| 组件 | 作用 |
|---|---|
| cordis.patch.yml | bundle patch —— 插入一个 host 行(secure-context-polyfill) |
| lib/index.mjs | 插件本体 —— index-tap(polyfill + 鉴权客户端)和 /api HTTP/WebSocket 包装(鉴权门禁 + 受信局域网回环重定向) |
插件声明 inject: [webServer],用 ctx.webServer.tapIndex() 转换每个被服务的 index.html,在 <head> 之后(任何应用 bundle 运行之前)插入 polyfill。可选的 allowPrivilegedFromLan 和 authEnabled 功能会包装已注册的 /api 路由 handler 和 WebSocket 升级路由,让鉴权门禁和回环重定向位于 DSH 自身处理链路之前。
安全提示
将 DSH 绑定到 0.0.0.0 会把 GUI(包含 agent/工作区工具)暴露给整个局域网。当 allowPrivilegedFromLan: false 时,本 bundle 只修复非安全源客户端;开启后,已认证的 /api/* HTTP 与 WebSocket 流量会被有意按回环权限处理。请仅在受信任的网络上使用。
许可证
MIT
