dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DSH web plugin: password-gate authentication (login page + HttpOnly signed cookie) for exposing dsh web behind a reverse proxy
为 DSH Web GUI 增加密码访问认证的插件:所有 HTTP 请求与 WebSocket 升级都必须携带有效会话,未登录一律跳转到登录页。装上它之后,把 dsh 反代到公网(或直接暴露端口)才不至于裸奔。
/logoutSecure 自动跟随 x-forwarded-proto;限速信任本机对端的 X-Forwarded-For127.0.0.1 仅本机、以及每张网卡的 IP),热生效、无需重启进程(REST → host 半区应用 patch → HMR 重绑 webserver)。不再提供 `0.0.0.0`:dsh 0.2.1-alpha.2 起通配符地址会在解析配置阶段直接让启动失败,写进去即硬砖(见下文「通配符与启动失败」)unlockRemoteSettings 组合配置在启动页注入 __DSH_TRANSPORT__.ownsHost 标志,让反代域名下的设置面(模型页、插件配置、常规设置等)完整可用,默认关闭(见下文安全权衡)?token= 链接访问一律重定向回登录页,只有插件自身的服务端 mint 走回环放行通道——既不需要手工引导,也不会再撞上 DSH 的 401 死页面(0.1.7 起)$DSH_HOME/web-auth/password.hash,0600),永不回显、不落明文配置dsh plugin --profile web add @fonlan/dsh-web-auth从 npm 安装:dsh plugin add 会把包写入 profile 的依赖与 bundle 栈(cordis.patch.yml 自动 insert 插件行,host 半区 + 浏览器半区一次挂载)。升级到新版本:
dsh plugin --profile web update @fonlan/dsh-web-auth安装后需要重启 dsh web 进程才生效(当前会话会短暂断连重连)。
宿主兼容性:0.2.1 面向 dsh ^0.2.0-rc.1 || ^0.2.1-alpha.1 || ^0.2.1-alpha.2 || ^0.2.1-rc.2,含两处按宿主演进做的适配:取值域去掉通配符(宿主 0.2.1-alpha.2 起拒绝),设置页不再依赖已被移除的客户端 settingsScope 服务(改走本插件 REST)。在更早的 0.1.x 宿主上本插件同样可用,但请勿把监听地址设成 0.0.0.0——宿主一旦升级到 0.2.1-alpha.2,该值会让 dsh 无法启动(见下文「通配符与启动失败」)。
重启后打开 dsh web,会被重定向到登录页:
改密入口在 设置 → 侧栏「访问认证」(需先登录)。
场景 · 行为 未登录访问页面(GET/HEAD) · 302 → /login?next=原路径,登录后跳回 未登录访问 API(POST 等) · 401 JSON DSH 启动令牌交换(GET /?token=…) · 仅插件服务端的 mint 重入(直连回环对端 + 内部标记头)或已持有插件会话的请求放行并改写为回环来源转发:DSH 自行校验令牌、签发自己的 dsh-auth cookie(303 → 干净 /);令牌无效则收到 DSH 自带的 401 浏览器直接打开打印的令牌 URL(未登录) · 302 → /login?next=/,丢弃 `token` 参数:令牌每次启动重新生成,旧书签/旧链接本就无效,放行只会把用户丢到 DSH 的 401 页面;登录后服务端 mint 会自动补发有效的 dsh-auth cookie DSH 会话 cookie 自动签发 · 插件在放行响应上服务端代理 DSH 的令牌交换(走本机回环 /?token=),把 dsh-auth Set-Cookie 随响应转发给浏览器——新浏览器无需再手工打开打印的 token URL;每分钟最多一次(结果会持续续期 DSH 的 30 天 cookie) 反代域名下已登录访问任意路径(含 /api、插件前缀) · 改写为回环来源放行,网关特权方法与插件围栏不再 403 反代域名下的设置页(模型页等) · 默认报 settings are unavailable in this browser——dsh 客户端按页面 hostname 判定"非本机浏览器",设置镜像保持进程本地。开启 unlockRemoteSettings 后启动页注入 ownsHost,设置面完整可用(读写仍过密码门 + 回环围栏) 跨站请求(即使带 Cookie) · 仍被围栏 403 拒绝 未登录 WebSocket 升级 · 握手直接 401 拒绝 登录失败 · 跳回登录页显示「密码错误」(统一文案) 连续失败 · 按 IP 限速,提示「尝试过于频繁」 会话过期 · 跳回登录页;滑动续期:剩余不足 24h 自动刷新 修改密码 · 校验旧密码 → 轮换签名密钥 → 全员下线,跳转登录页 切换监听地址(设置卡片) · 校验取值(必须是具体 IPv4/IPv6 字面量,通配符被拒绝)→ POST /api/web-auth/listen → host 半区应用 patch(profile 优先,home 兜底)→ HMR 热重载 webserver 重新绑定;WebSocket 短暂断连后自动重连 /logout · 清除 Cookie,跳转登录页
无需再处理 DSH 自己的启动令牌:DSH 的浏览器会话认证(dsh web启动时打印的http://127.0.0.1:3080/?token=…)用 cookie 绑定 Host。插件会把所有已认证请求的 Host 改写为回环地址,而 DSH 的 cookie 必须与「DSH 看到的 Host」一致——0.1.5起插件会在登录/会话期间自动在服务端完成 DSH 令牌交换并把dsh-authcookie 转交给浏览器(含持续续期),所以外部设备、新浏览器都不需要再手工访问带 token 的 URL。0.1.7起这条路径进一步收紧:未登录浏览器带token访问会被重定向到/login并丢弃该参数(旧令牌在 dsh 重启后即失效,放行只会展示 DSH 自己的401页面),登录本身就已经完成 mint,不再需要任何手工引导步骤。
dsh 默认只监听 127.0.0.1。在 设置 → 侧栏「访问认证」 页面底部可以把它切换为本机任意网卡上的具体地址(列表由 host 半区枚举 os.networkInterfaces() 生成),或切回 127.0.0.1 仅本机——无需重启 dsh 进程:
GET /api/web-auth/status 取当前地址与候选列表(allowed),提交时 POST /api/web-auth/listen;host 半区把取值写进当前部署的 patch 层:profile 的 cordis.patch.yml;若 home 层($DSH_HOME/cordis.patch.yml)已声明 webserver 行则写 home 层(home 优先级更高,改在低层会被覆盖)。文件里其余内容(注释、!!js 端口表达式)原样保留。isLocalListenHost:必须是 IPv4/IPv6 字面量、不是通配符、并且确实是本机网卡拥有的地址,否则直接拒绝(400 invalid-host,响应里附上合法候选列表),不会写坏文件。下拉列表即由同一枚举生成,页面不可能提供一个写不进去的值。http://:3080)。想同时保留本机与局域网入口,请让 dsh 继续监听 127.0.0.1,另在本机放一层反代监听局域网/公网并指向它——这也是 dsh 现在推荐的暴露方式。127.0.0.1,切回后只能在本机访问,请改用 127.0.0.1 重新打开页面。该能力依赖 webserver 行位于 profile/home patch 层(默认安装即如此)。若你的 webserver 配置来自自定义 --patch overlay,请直接编辑 overlay 文件。监听地址是组合配置,写在 patch 文件里、由 loader 在启动时读取。因此一个写坏的取值不是「绑定失败」而已——它会让下一次启动整体失败,而本插件自己也在 waiting、无法自救。0.2.0 把所有已知的三类都堵在写入之前:
类别 · 写坏后会看到 · 插件现在如何拦截 通配符 0.0.0.0 / :: · 解析配置阶段报 host "0.0.0.0" is an unspecified (wildcard) address, which is not supported … · 通配符一律拒绝(400 invalid-host / 写盘前抛错) 本机没有的地址(如 macOS 上的 127.0.0.2、过期 DHCP 地址) · listen EADDRNOTAVAIL: address not available … · 只接受本机网卡实际拥有的地址(os.networkInterfaces() 枚举,127.0.0.1 恒定可用) 把行追加到模板的空序列 [] 之后 · failed to parse overlay …: end of the stream or a document separator is expected · 识别模板 [] 并替换它(非空 flow 文档直接拒绝编辑)
三类症状长这样(webserver 未激活,所有依赖它的插件排队等 webServer):
dsh: startup failed: 2 required plugins did not activate
webserver (required) @deepseek-ai/dsh-host-webserver → 上面某一类报错
Plugins waiting for services:
web-runtime / web-auth / better-sidebar / task-kanban / … (missing webServer)恢复方式都是手工改 patch 文件(插件此时不会加载):
# 1) 看当前写死的 host
grep -n -A3 '^- id: webserver' ~/.dsh/profiles/web/cordis.patch.yml
# 2) 改成 127.0.0.1(或本机某张网卡的实际地址)后保存若第一行是 [] 之类的空序列而下面又跟了 - id: 行,把那个 [] 行整行删掉即为合法文档。
DHCP 提醒:绑定具体网卡地址后,地址由 DHCP 变化(或网卡消失)时,下一次启动同样会EADDRNOTAVAIL。长期部署建议让 dsh 监听127.0.0.1,把对外暴露交给本机反代(反代再指向回环),这样地址变化不影响 dsh 启动。
0.2.0 起插件的所有写路径都会在改动任何字节之前校验,因此这三类砖不会再被本插件写出来。(若你仍在用 0.1.10 及更早版本并已中招,同样按上面方式手工修复,再升级到 0.2.0。)
unlockRemoteSettings,默认关闭)现象:通过域名反代访问 dsh 时,设置 → 模型页报「加载提供方目录失败: settings are unavailable in this browser」,常规设置、插件配置等设置面同样不可用。这不是故障,是 dsh 的有意设计:设置文档(含 provider 凭据引用等敏感配置)只对"本机浏览器"开放——客户端在页面加载时按 location.hostname 判定是否回环(localhost / 127.x),反代域名永远不算,设置镜像因此保持进程本地、根本不发起读取。注意这与服务端无关:本插件已把已认证请求改写为回环来源,/api 通道完全正常,只有客户端这道门在拦。
开启:在 profile 的 cordis.patch.yml(~/.dsh/profiles//cordis.patch.yml)加一条 config 覆盖:
- id: web-auth
config:
unlockRemoteSettings: trueHMR 会监听到 patch 变化并热重载 web-auth 行(无需重启进程)。生效后,域名下已登录页面的启动 HTML 会携带一段内联脚本,设置 window.__DSH_TRANSPORT__.ownsHost = true——dsh 客户端把它读作"该页面由宿主直接提供",设置镜像随之正常加载。只在成功渲染的 index 响应上注入(未登录请求在闸门处就被 302/401,根本到不了渲染);已存在的 __DSH_TRANSPORT__ 载体只会合并、不会被覆盖。不需要时删掉该配置即回退(同样热生效)。
安全权衡(请务必理解后再开启):ownsHost 是 dsh 画的"设置仅本机可改"边界;开启等于把这道边界改由本插件的密码门承担——每个通过密码认证的浏览器都能读写 host 设置文档(包括 provider API key 的引用、监听地址等)。若域名暴露在公网,请确保:强密码、反代全程 HTTPS、最好叠加反代层限流/IP 白名单。只在本机/内网使用或设置面不受影响时,保持默认关闭即可。
127.0.0.1 + SSH 端口转发完成首密设置,再开放公网。/logout 只清浏览器 Cookie。密码泄露时请修改密码——这会轮换密钥并让所有会话失效。$DSH_HOME/web-auth/(默认 ~/.dsh/web-auth/),权限 0600;删除 password.hash 即回到未配置状态(认证关闭)。pnpm install
npm run build # host: tsc → lib/;client: esbuild → lib/client.js(ModuleLoader 格式)
npm run typecheck
npm run test # 单元 + 集成测试(node:test,真实 HTTP/WS 全流程)仓库结构:
src/auth-core.ts — 纯逻辑:scrypt 哈希、Cookie 签名/校验、限速器、next 校验src/gate.ts — 把认证闸门包到 node:http 服务器的 request/upgrade 监听器外层src/handlers.ts — 登录页/登录/登出/改密/状态/监听切换 路由处理src/profile-patch.ts — 监听地址切换:webserver 行的 patch 层文本改写(保留注释与 !!js 表达式),并在写入前拒绝通配符src/listen-addresses.ts — 监听地址取值域:字面量/通配符判定、本机网卡枚举(候选列表)、本机对端判定(mint 重入与 XFF 信任)src/remote-unlock.ts — 远程设置解锁:unlockRemoteSettings 开启时经 webServer.tapIndex 注入 ownsHost 启动页脚本src/index.ts — cordis 插件装配(host 半区)src/client/index.tsx — 设置卡片(浏览器半区,settings.section 插槽,id web-auth;读写走本插件 REST)cordis.patch.yml — bundle 补丁层(insert 插件行)MIT