dsh-cookie-guard:修复 DSH 登录 Cookie 累积导致 Web UI 打不开

前言

DeepSeek Harness(DSH)桌面端通过浏览器 Cookie 做 Web UI 认证。每次启动往往会换一个新端口,Cookie 名会随 host:port 变化而变,旧 Cookie 在浏览器里会保留约 30 天,而 DSH 本身不会主动清理。时间一长,同一 127.0.0.1 下会堆出大量 dsh-auth-*,浏览器把它们全部塞进 Cookie 请求头;当累计超过 Node 默认的 16 KiB maxHeaderSize 时,服务器在进入业务逻辑前就返回 431 Request Header Fields Too Large,连清理代码都跑不到,表现为 Web UI「永久打不开」。手动清浏览器 Cookie 能救一次,但无法从根上阻止再次累积。下面介绍社区插件 dsh-cookie-guard(维护者 bigfatdsh,MIT),它作为客户端类 DSH 插件,在 Web 服务器最前面接管大请求头并下发删除指令,把问题压回可控范围。

这是什么

dsh-cookie-guard 是安装在 DSH profile 上的宿主机插件,不修改 DSH 源码,也不依赖第三方 npm 包。插件 id 为 cookie-guard,bundle 名为 dsh-cookie-guard。它针对的是:DSH 浏览器认证 Cookie 名形如 dsh-auth-<sha256(host:port) 的 base64url>,只增不清,最终撑爆请求头。仓库与说明见 GitHub,社区目录页:SkillHub 插件页。

问题是怎么发生的

DSH 每次桌面启动常用新端口,浏览器会多写一条新的 dsh-auth-*;旧条目在有效期内一直存在。浏览器对同一 host 发送全部 Cookie,Node 侧默认 maxHeaderSize 为 16 KiB。超过后任意请求(包括首页)都会在监听器之前被 431 拒绝。README 中修复前本机实测示例:

GET /  Cookie: big=<20 KB>  ->  431 Request Header Fields Too Large

此时即便插件逻辑写在 DSH 里,也进不了 JS 处理器,所以单靠「等 DSH 自己删 Cookie」无法从已爆头的状态恢复。

核心功能

安装到当前 desktop profile 后,在同一台正在运行的 GUI 上,README 给出了修复前后对比(节选):

场景 修复前 修复后
20 KB 无效 Cookie 431 401(正常进入认证流程)
约 20 KB Cookie、90 条旧登录 Cookie 431 200,响应中含 87 条删除指令
约 34 KB Cookie、150 条旧登录 Cookie 431 200,响应中含 147 条删除指令
当前实例的有效 Cookie 200 200,且不会被误删

34 KB 场景下,当前有效 Cookie 保留,其余旧 Cookie 通过 Set-Cookie 带 Max-Age=0 删除;请求头可从约 34 KB 降到约 1 KB。

原理上,插件在 webServer 请求链最前面 prependListener,大致四步:

  1. 把监听器的 maxHeaderSize 提高到可配置上限(默认 256 KiB),让已超过 16 KiB 的请求能进入 JS。
  2. 用与 DSH 相同的算法算出当前 authority 的 Cookie 名,从 req.headers.cookie 里剥掉其它 dsh-auth-*,认证代码只看到当前实例的 Cookie。
  3. 对过期、签名无效、或超出「当前 + 保留条数」预算的 Cookie,在响应里追加删除头(HttpOnly、SameSite=Strict 等与 DSH 一致)。
  4. 注入方式兼容 writeHead、setHeader、隐式 end(),不覆盖 DSH 刚签发的 Cookie。

默认除当前实例外最多保留 3 条其它端口的有效 Cookie,便于多开;只跑单实例时可把保留数设为 0,代价是其它窗口下次请求可能掉登录,需重新走 token URL。

安装与启用

从 GitHub 安装(命令来自仓库 README):

dsh plugin add https://github.com/bigfatdsh/dsh-cookie-guard

README 说明:desktop profile 可通过插件管理器安装后,依赖形如 dsh-cookie-guard: link:...,并出现在 dsh.profile.bundles 中。插件以 link: 软链形式存在时,改本目录源码后重启 DSH 即生效。

其它 profile(例如 web)需在该 profile 目录手动 pnpm add link:<本机 dsh-cookie-guard 路径>,并把 "dsh-cookie-guard" 写入该 profile package.json 的 dsh.profile.bundles 数组,再重启 DSH。README 中的示例路径为作者本机路径,你应换成自己机器上的实际路径。

配置

可在 bundle 的 cordis.patch.yml(或 profile patch)里覆盖:

- insert:
    - id: cookie-guard
      name: dsh-cookie-guard
      config:
        maxHeaderBytes: 262144
        keepOtherCookies: 3
        cookieBudgetBytes: 4096
键 默认 说明
maxHeaderBytes 262144 监听器 maxHeaderSize;须大于 16 KiB,范围 16 KiB–1 MiB。
keepOtherCookies 3 除当前实例外,最多保留几条其它实例有效 Cookie,范围 0–8。
cookieBudgetBytes 4096 认证 Cookie 总预算,超出从最旧删,范围 256 B–16 KiB。

典型用法与验证

单元测试(README 称 22 项,含大请求头、多 Cookie、401 等):

cd adapted/dsh-cookie-guard
node --test

路径以你本机克隆或 link 的插件目录为准。

对运行中的 GUI 做无副作用探测(把端口换成当前 DSH Web 端口):

python3 - <<'PY'
import urllib.request, urllib.error
req = urllib.request.Request(
    "http://127.0.0.1:51302/",
    headers={"Cookie": "big=" + "a" * 20000},
)
try:
    print(urllib.request.urlopen(req, timeout=5).status)
except urllib.error.HTTPError as e:
    print(e.code, e.reason)
PY

安装插件后应看到 401 Unauthorized;未修复时为 431 Request Header Fields Too Large。

适用场景与注意

适合长期用 DSH 桌面端、频繁重启或多开端口,且曾在浏览器里堆出大量 127.0.0.1 登录 Cookie 的开发者。插件与 DSH 同进程权限运行,安装前建议阅读 仓库源码 与 MIT 许可证,确认符合你的安全策略。

边界事项(来自 README):

  • 删除依赖浏览器执行标准 Set-Cookie 过期语义;若个别环境不执行删除,提高后的头部上限仍可能让 UI 可用,旧 Cookie 最迟约 30 天过期。
  • 若历史 Cookie 已超过默认 256 KiB 上限,可把 maxHeaderBytes 调到 1048576 后重启;仍不行时需在浏览器 DevTools 中清一次 127.0.0.1 站点 Cookie,之后由插件接管。
  • 上游若改为稳定 Cookie 名,插件仍会保留「提高头部上限 + 清理旧 Cookie」的保险作用,README 认为不会与上游冲突。

回滚:用插件管理器移除 bundle,或在 profile 目录从 package.json 去掉依赖与 bundles 条目后 pnpm install 并重启 DSH。移除后 16 KiB 默认上限恢复,累积问题会再次出现。

DSH 生态强调「一切皆插件」;SkillHub 等社区目录独立于 DeepSeek / 幻方,并非官方应用商店。

结尾

dsh-cookie-guard 用监听器前置处理把「431 永久打不开」变成可进入认证或自动下发删除指令的正常路径,并对多开场景保留有限条其它实例 Cookie。需要安装命令与配置细节时,以 GitHub README 为准;浏览插件元信息可打开 SkillHub 目录页。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜