Back to marketplace

dsh-web-startup-auth

Security

GDWhisper/dsh-web-startup-auth

Allows remote web startup of DeepSeek Harness with username/password authentication. Replaces the default startup to permit non-loopback hosts, adds login/registration pages, session cookies, and API protection.

  • dsh-plugin
GitHub Stars
50GitHub
Views
8DSH Plugin Hub
Forks
5GitHub
Open issues
3GitHub Issues
Manifest version
0.1.2dsh-web-startup-auth
Latest push
Oct 7, 2026GitHub
License
MITTypeScript
Plugin type
Host + ClientRuns in both Host and Web Client

Verification and compatibility

This section shows evidence collected by the catalog. Undeclared information is labeled as unknown.

Static checked
01Exact source: github · dsh-web-startup-auth@0.1.202Validated: Aug 23, 202603Verified Harness: 0.1.0-rc.7
Current-version compatibility
Verified on the catalog Harness version
Declared Harness range
Not declared
Declared platforms
Not declared
Profiles
web
Build approval
Unknown
Permissions
Not declared
External services
Not declared
Telemetry
Unknown
No known risk flags found

This is not a security endorsement. Review source, permissions, and configuration before installing.

View evidence and scope

Verification covers only the named source, version, and Harness environment. It does not guarantee future compatibility.

  • github:GDWhisper/dsh-web-startup-auth#3ea0399d0c03ea0f0e0ee2073dc4a6bbd6b998c3
  • The manifest and static structure passed checks; a runtime load check has not completed.

README

View source

dsh-web-startup-auth

中文 | English

DSH(DeepSeek Harness)远程 Web 启动 + 用户名/密码认证插件。

⚠️ 版本跟进声明:本项目只跟进官方 dsh 的正式发布通道(next dist-tag),不跟进 alpha 预览通道(当前基线:dsh 0.2.0-rc.1,依赖 5 个 @deepseek-ai/dsh-* 已 bump 到 ^0.2.0-rc.1;适配审查与观察哨见 docs/upgrade-dsh-0.2.0-playbook.md)。

登录页

原版 @deepseek-ai/dsh-web-app/startup 出于安全考虑硬拒绝 --host 0.0.0.0;本插件替换它,并配一个带登录/注册页的认证插件,让 dsh web 可以在局域网(或任何非回环接口)上安全暴露浏览器界面。

特性

  • 远程启动:--host 0.0.0.0 可用,替代原版启动器的硬性拒绝;--host ::(或任意 IPv6 字面量,如 ::1、fd00::1)同样可用(#35;非规范拼写自动规范化,带 zone 的(%eth0)与 IPv4-mapped(::ffff:a.b.c.d)写法拒绝)。纯 IPv6 网络下 0.0.0.0 只绑 IPv4、界面不可达;:: 在 Linux 默认双栈(net.ipv6.bindv6only=0),同时监听 IPv6 与 IPv4-mapped,比 0.0.0.0 覆盖更全;bindv6only=1 的系统上 :: 仅 IPv6。
  • 登录/注册页:远程访问首次会引导设置管理员账号密码,之后进入登录页;与 DSH 黑白蓝风格一致;颜色跟随系统明暗偏好(prefers-color-scheme)。
  • 免登录的本地访问:判定依据是请求本身而非启动参数——TCP 对端地址与 Host 头都是回环时才免认证。所以本机浏览器直接访问 http://127.0.0.1:<端口>/ 无需注册或登录;局域网客户端、以及反向代理转发来的请求(Host 是公网域名)一律需要会话。免登录不等于已登录:本机没有会话 cookie,认证页显示的是「管理员账号」而不是「当前登录」,也不会给出退出登录入口(没有会话可退)。
  • 可选的「本机登录校验」:默认本机免登录;管理员可在设置面板 → 认证 → 登录要求里把这个开关打开,之后连本机地址也要求会话(多人共享服务器时用它禁止同机其他账号免登录)。开关须先有管理员账号才能启用(未注册时后端会拒绝,设置页会先引导去注册)。
  • 可选的人机验证(滑块拼图):应网友要求加上了没什么用的防人机验证,有兴趣可以开启试试 XD —— 在设置面板 → 认证 → 人机验证里滑动开启,登录时会让你把一块拼图拖进缺口。每张图里画了两个缺口:拼图块和真缺口是同一种形状(凸块方向每张随机:右/左/上/下,也可能没有凸块,共 5 种),旁边的假缺口形状永远不同,拼图块只塞得进形状相同的那个(纯装饰,脚本照样秒过)。 说它没什么用是有实测依据的:拼图的答案必须画在图上(人要看着它拖),所以脚本几十行就能读出来——本插件按常规做法实现的三种变体,用约 30 行脚本攻击的命中率分别是 100%(缺口涂暗)、100%(只描边)、97%(撒噪点,但那一版连人也解不出来)。之前试过的 4 位数字图片题同样被模板匹配打到 96.7%。当个乐子,别当安全边界。 详见 docs/agent/human-verification.md。
  • 登录限流硬化:每 IP 10 分钟内 5 次失败锁 30 秒,外加全局指数退避——所有来源累计失败超过 20 次后按台阶翻倍(5 秒 → 10 秒 → …封顶 5 分钟),用来挡住"一千个 IP 各试几次"的分布式撞库;429 带 Retry-After。本机(真回环且未开启「本机登录校验」)豁免全局退避,避免攻击者借此把管理员锁在自己机器外面。
  • 会话认证:登录后下发签名 cookie(dsh_sid,默认 14 天有效、可在设置面板调节(3–180 天档位),HttpOnly + SameSite=Lax)。
  • API 保护:所有注册路由(/api/* 及第三方插件的 RPC 路由,除 /api/auth/* 与 /login)必须携带有效会话,否则返回 401/拒绝握手。
  • 设置面板「认证」标签页:向 DSH 设置面板注入"认证"页,提供退出登录(仅在持有会话时出现)、修改用户名、修改密码与会话有效期档位选择。标签在导航栏里显示盾牌+勾图标(上游不允许注册方指定图标,由本插件在前端替换默认的齿轮)。界面颜色全部使用 DSH 主题 token,跟随明/暗主题自动切换(#25)。
  • 远程场景修复(局域网 HTTP 访问的坑):
    • crypto.randomUUID polyfill —— 非安全上下文下该 API 缺失,会导致所有 RPC 失败。
    • 原生浏览器认证桥接 —— dsh 0.1.2 起(当前基线 0.2.0-rc.1)上游自带浏览器认证(dsh-auth-* 签名 cookie),/api 与 index.html 一律要求携带、连回环都不豁免(本机也得先换 token URL)。本插件为已通过自己认证(dsh_sid 会话,或 TCP 对端 + Host 双回环的本地请求)的访问者自动补发该 cookie:页面导航经一次 200 跳板(meta refresh 回原路径)即带上、登录响应直接下发(不用 3xx:重定向响应里新设的 cookie 不会带给重定向目标,补发会被逐跳重放成 ERR_TOO_MANY_REDIRECTS),全程无需接触启动打印的 token URL——「账号/密码 + 可撤销会话」仍是唯一认证入口,上游 cookie 只是通过上游闸门的凭据。

安装

本插件是一个 DSH bundle(package.json 的 dsh.bundle.patch 声明了随包分发的 cordis.patch.yml)。用 dsh plugin 安装后,包会被加入 profile 的 dsh.profile.bundles,补丁层自动生效,无需手动编辑任何配置文件。

# 方式一:从源码安装
git clone <仓库地址>
cd dsh-web-startup-auth
npm install        # 安装构建依赖(typescript 等)
npm run build      # 编译 src/ 到 lib/(插件运行时加载 lib/ 下的产物)
dsh plugin --profile web add .

# 方式二:从 npm registry 安装(已装过旧版本时执行同一条命令即可升级)
dsh plugin --profile web add dsh-web-startup-auth@latest

dsh plugin 是 pnpm 转发器,--profile <name> 必填;add . 会把当前目录以 link: 方式装进 profile。

版本范围:本插件声明支持 dsh >=0.1.7-rc.1、不支持 0.3.0 起的版本线(含其预发布版)。运行中的 dsh 超出范围时,dsh 会跳过本 bundle 并打印原因——这是刻意的 fail-loud:宁可不加载,也不在未适配验证过的 dsh minor 版本线上静默运行。注意粒度是 minor 线:同一条线内的后续版本(含 rc 等预发布)会照常加载,每出一条新 minor 线要先完成适配再考虑放宽范围。确需强行启用可走 profile 的 version exemption(插件管理器文档)。

启动:

dsh web --host 0.0.0.0
# 纯 IPv6 网络(或想同时覆盖 IPv6):
dsh web --host :: --port 8080

安装时已自动应用补丁,无需再用 --patch 叠加——重复叠加会把插件再插入一遍,导致重复。

使用

  1. 浏览器访问 http://<主机IP>:<端口>/(在本机则用 http://127.0.0.1:<端口>/,无需登录)。IPv6 地址在 URL 中必须加方括号:http://[<IPv6地址>]:<端口>/。注意:--host :: 启动时终端只打印回环 URL(上游的 LAN 地址推导只枚举 IPv4),局域网 IPv6 地址请用上面的形式手动拼接;打印的回环 URL 固定是 http://127.0.0.1:<端口>/(上游硬编码),bindv6only=1 的系统上 :: 是纯 IPv6 套接字、它不通,本机请用 http://[::1]:<端口>/。
  2. 远程访问首次会跳转到 /login,显示"设置管理员账号密码"注册表单。
  3. 注册成功后自动登录并进入主界面;之后访问需登录。
  4. 退出登录 / 修改用户名 / 修改密码 / 调整会话有效期 / 开关拼图验证:打开主界面设置面板 → 认证标签页(同时也是一个独立入口,/api/auth/logout 清除会话 cookie)。

如果需要在本地设置账号密码(本机默认免登录,界面不会自动跳登录页):

  1. 浏览器打开 http://127.0.0.1:<端口>/login —— 未注册时该页直接显示注册表单。
  2. 或进设置面板 → 认证,页顶「尚未设置管理员账号」处点「前往设置管理员账号」。

注册后本机仍免登录;想让本机也要登录,到认证 → 登录要求打开「本机登录校验」(需先有账号)。开关打开后本机也要走 /login(页面会立即跳过去),登录后认证页才会显示「当前登录」和退出登录入口。

凭据与会话密钥保存在 $DSH_HOME/web-auth.json($DSH_HOME 未设置时即 ~/.dsh/web-auth.json,与 dsh 自身数据同目录;可用环境变量 DSH_WEB_AUTH_FILE 覆盖整个路径):

  • 密码使用 scrypt(随机盐,64 字节)散列存储,不保存明文。
  • 会话 cookie 用随机生成的密钥做 HMAC-SHA256 签名,防伪造。有效期档位(sessionMaxAgeDays,默认 14)也存在这个文件里,可在设置面板调节;调整只对新签发的会话生效。
  • 忘记密码:在服务器本机执行 dsh --profile web auth-reset,交互式设置新密码(或 dsh --profile web auth-reset --password <新密码> 非交互)。重置会轮换会话密钥,所有已登录的浏览器都需要重新登录一次。
  • 修改用户名 / 修复含控制字符的用户名:dsh --profile web auth-reset --username <新用户名>(可与 --password 同用)。同样轮换会话密钥。用户名在注册/登录/修改时统一剥除 C0 控制字符(0x00–0x1F)与 DEL(0x7F)——旧版本若已把含 DEL 的用户名原样存盘,用它即可修复。
  • 兜底方案:删除凭据文件($DSH_HOME/web-auth.json,默认 ~/.dsh/web-auth.json)并重启,即可重新注册。这只影响登录状态——已登录的浏览器需要再登录一次,不会删除任何会话记录或历史数据(它们存在 ~/.dsh/sessions/ 等位置,与本文件无关)。
  • auth-reset 不能用来首次创建账号:它要求已有凭据,否则报「尚未注册管理员账号,无需重置」。首次只能用上面的注册页。

常见问题

Q:怎么在本地设置账号密码?必须代理到公网才能配置吗?

不需要,代理到公网不是前提。本机默认免登录(不会自动跳登录页),所以请在浏览器手动打开 http://127.0.0.1:<端口>/login——未注册时这一页直接显示注册表单;或者进设置面板 → 认证,页顶「尚未设置管理员账号」处点「前往设置管理员账号」。注意命令行 dsh --profile web auth-reset 只能改已有账号的密码/用户名,不能用来首次建号。

Q:本机不想登录,远程才要登录,可以吗?

这就是默认行为。设置面板 → 认证 → 登录要求默认关闭,即本机(真回环)免登录;远程客户端一律需要会话。打开这个开关(需先有管理员账号)则本机也强制登录,适合多人共用一台服务器时禁止同机其他账号免登录。没有「受信主机免登录」这种开关,--trusted-host 也不参与认证判断。

Q:忘记密码 / 想改用户名?

dsh --profile web auth-reset                      # 交互式
dsh --profile web auth-reset --password <新密码>   # 非交互
dsh --profile web auth-reset --username <新用户名>

都会轮换会话密钥,所有已登录的浏览器需要重新登录一次。兜底方案是删掉凭据文件($DSH_HOME/web-auth.json,默认 ~/.dsh/web-auth.json)再重启重新注册——不影响历史会话数据。

Q:访问某个接口返回 403,是本插件拦的吗?

本插件的认证失败只会返回 401 或者跳转到登录页,从不返回 403。所以看到 403 先看响应体的形状来定位来源:

响应体来源怎么办
401,或页面被送到 /login本插件(会话无效)重新登录
纯文本 forbidden上游 dsh 的 Host 信任围栏检查反向代理是否保留了真实 Host,必要时加 --trusted-host <域名>
{"error":"request-not-trusted"}第三方插件自带的回环限制见下一条
transport failure ... HTTP 403第三方插件自带的回环限制见下一条

Q:第三方插件(如 dsh-im、dsh-workbuddy-connect)在局域网返回 403?

这是该插件给它自己的接口加的回环地址限制(只放行 127.0.0.1 / localhost),与本插件和你的服务器都无关——本插件只管 /api/* 的会话闸门,管不到别的插件挂在 webServer 上的路由。

  • dsh-im:提供了开关,在 profile 的 cordis.patch.yml 里加 rpcAuthority: trusted-host;新版已默认放行,一般不用配。
  • 没有开关的插件(如 dsh-workbuddy-connect):只能去该插件的仓库提 issue,请求提供一个可配置的等价开关。

临时绕过办法是在你自己的电脑上做一次 SSH 端口转发,让浏览器通过回环地址访问,限制就不会触发:

ssh -L 3080:127.0.0.1:3080 用户名@<服务器IP>

保持这个窗口不关,浏览器打开 http://127.0.0.1:3080 即可。注意此时所有请求的 Host 都是回环,会被当作本机访问——不要在不受信任的网络上这么做。

Q:设置面板报「加载提供方目录失败 / settings are unavailable in this browser」?

最常见的原因是装到了旧版本。这个症状在 0.1.1 就已修好,0.1.5 起换了实现(远程浏览器下 settings 面板正常渲染)。先确认版本:

dsh plugin --profile web add dsh-web-startup-auth@latest

add 时务必带 @latest:不带的话 pnpm 会一直复用首次写入的版本号,之后用 update 也升不上去(历史上因此排查过多次)。

Q:升级 dsh 之后插件行为异常?

  • 从源码安装的:改完源码要 npm run build,然后重启 dsh web(lib/ 是入库的构建产物,不构建等于没改)。
  • 从 npm 安装的:重跑一次上面那条 add dsh-web-startup-auth@latest。
  • 本插件支持 dsh >=0.1.7-rc.1,不支持 0.3.0 起的版本线。超出范围时 dsh 会跳过本 bundle 并在启动日志里打印原因(刻意的 fail-loud),在启动日志里搜 skipped 就能确认。

Q:登录页和首页来回循环跳转 / Safari 报 ERR_TOO_MANY_REDIRECTS?

  • 首页与 /login 互跳的死循环:0.1.3 起已修复(0.1.2 的认证判定只看启动参数、不看请求本身,反代场景下会误判)。
  • Safari / iOS 上补发原生 cookie 的跳转被反复重放:0.1.11 起已修复。

两者如果仍出现,先确认装的是最新版,清一下该站点的 cookie 再试。

Q:拼图验证能挡住脚本吗?

挡不住,如「特性」一节所说——答案就画在图上,脚本几十行就能读出来。当个乐子,别当安全边界。

索引

如果您在寻找开箱即用的专为 Agent 时代研发的 IDE,推荐您使用 Omniterm

安全说明

  • 本插件提供认证,但不提供传输加密。明文 HTTP 下凭据与流量可被同一网络中的抓包者看到,建议仅在可信内网使用,或在前面部署 HTTPS 反向代理。
  • 会话有效期默认 14 天,可在设置面板「认证」标签页调节(3/7/14/30/60/90/180 天档位,持久化于凭据文件);调整只影响新签发的会话,已登录的会话保持签发时的有效期。
  • 密码散列使用 Node 内置 crypto.scryptSync,无第三方依赖。
  • 会话不可服务端撤销:dsh_sid 是自包含签名 cookie,/api/auth/logout 只清除浏览器一侧的 cookie。cookie 一旦泄露(如明文 HTTP 下被嗅探),在有效期内无法单独吊销(上限即设置面板所选的会话有效期档位)。例外:dsh --profile web auth-reset、设置面板的「修改密码」与「修改用户名」都会轮换会话密钥,一次性让所有已登录的浏览器重新登录(操作者自己的当前会话由服务端重新签发,保持登录)。
  • 首次注册窗口:凭据未设置时任何访问者都可注册为管理员。在把服务暴露到不可信网络之前请先完成首次注册。
  • 登录防护:登录失败按客户端 IP 限速——连续 5 次失败锁定 30 秒(纯内存、无持久化);注册要求密码至少 8 个字符。限速覆盖 /api/auth/login、/api/auth/change-password 与 /api/auth/change-username(旧密码/当前密码错误同样计次)。如需更严格防护请在反向代理层增加通用限速。
  • API 会话闸门双层防护:所有受保护路由在注册层默认拒绝(未认证导航 302 登录页、其余 401);共享 API 另有一道挂在上游官方 connection/request 扩展点上的会话闸门。两层互为保险:上游接口变化导致其中一层失效时,另一层仍保证只有可撤销的会话(或真本机回环)能访问 API——30 天不可单独吊销的上游原生 cookie 永远不能单独作为 API 凭据。
  • 凭据文件权限:$DSH_HOME/web-auth.json(默认 ~/.dsh/web-auth.json;含密码哈希与会话签名密钥)以 0600 保存,目录以 0700 创建;插件启动时会自动修复旧版本遗留的过宽权限。
  • --trusted-host:该参数仅为与原版 CLI 兼容而保留透传,不参与本插件认证判断——远程客户端一律需要有效会话,不存在"受信主机免登录"。
  • 反向代理(nginx 等)部署:可以放心的做法是 dsh 只监听 127.0.0.1,由代理做 SSL 卸载并转发。此时代理必须转发真实 Host(nginx 默认即为 proxy_set_header Host $host;,配上 --trusted-host <域名> 让 DSH 自身的 Host 围栏放行);认证通过后插件按请求的真实 Host(公网域名)补发上游原生浏览器 cookie,上游闸门据此放行。反之,若代理把 Host 写死成 127.0.0.1,插件会认为请求来自本机从而放行全部流量、不做认证——不要这样配置。X-Forwarded-For 不被采信(客户端可伪造),信任判定只看 TCP 对端地址与 Host。
  • 上游兼容层(dsh 0.1.7 基线):dsh rc.8–0.1.1 时代,DSH 前端用浏览器地址栏 hostname 判定 connection.isLoopback,远程浏览器下的 settings mirror 走内存模式、插件配置卡片与 Models 页不可用;本插件当时通过 webServer.tapIndex 注入脚本在 connection 激活瞬间把该标志覆盖为恒 true。0.1.2 上游引入真实 cookie 认证后能进 UI,但 settings mirror 仍按同一标志判定——LAN 浏览器依旧得到从不读 host 的 memory mirror,设置面板的 Models(提供方目录)会报 "settings are unavailable in this browser"。直接恢复旧的 getter 覆盖会破坏 web boot(A/B 实测 26 个前端插件 pending),因此 0.1.2 起改用注入 window.__DSH_TRANSPORT__ = { ownsHost: true }:connection client 构造时据此把 isLoopback 报为 true(api/rpc 字段缺省时安全回退,且不重写 cordis 服务),LAN 与回环浏览器的全部设置面(含 Models)都正常渲染。当前保留的浏览器侧 shim:该 transport hook 与 crypto.randomUUID polyfill(明文 HTTP 非安全上下文所需)。

开发

npm install
npm run typecheck   # tsc --noEmit
npm test            # vitest
npm run build       # tsc -p tsconfig.json + tsdown,产物输出到 lib/
  • tsc 编译 node 侧源码(src/*.ts)与类型声明到 lib/、lib/types/。
  • tsdown 把前端插件(src/client/index.tsx)打包成浏览器 bundle lib/client.js(window.__ModuleLoader__.load 注册格式)。改前端代码后必须重新构建,profile 里 link: 安装会自动加载新产物。
  • 前端插件依赖的 @deepseek-ai/dsh-client-* 包只用于类型与构建,运行时由 DSH 前端模块表提供。

许可证

MIT

Comments

0
Newest first