dsh-cookie-doctor:清理 DSH 桌面版堆積的 dsh-auth-cookie,阻止 Web authentication failed 復發

前言

DSH 的理念是「一切皆插件」,插件以當前 dsh 進程的權限運行,能改 host 裏的東西。對桌面版用戶來說,比較磨人的一類故障是:使用過程中重啓若干次後,DSH 必然起不來,彈窗報 desktop welcome: Web authentication failed,並建議重裝或禁用插件。手動刪掉 Cookie 庫能救回來,但重啓幾十次又會出現同樣的問題。soyorinhhh/dsh-cookie-doctor 就是針對這個故障的插件:清理堆積的 dsh-auth-* cookie,並從根上阻止它們再次堆積。它由 soyorinhhh 維護,分類是 admin-security,GitHub 上 1 個 star。

下面按「是什麼、爲什麼有用、怎麼裝、怎麼用、需要注意什麼」介紹。

這是什麼

一句話定位:DSH 桌面版「Web authentication failed」崩潰治理插件。它的目標不是幫你繞過問題,而是把根因找出來並止住。

項 內容
插件名稱 dsh-cookie-doctor
維護者 soyorinhhh
分類 admin-security
版本 0.1.0(見 package.json)
許可證 MIT
運行要求 Node >= 22.13.0
源碼 https://github.com/soyorinhhh/dsh-cookie-doctor
目錄頁 https://www.skillhub.cn/plugins/soyorinhhh/dsh-cookie-doctor

插件掛載在 host 進程裏,接管 dsh-host-webserver 創建的 http.Server。它不改 DSH 的鑑權邏輯,也不改端口、不碰 host 頭;cookie 名照舊按 host:port 哈希,插件只是把這個 session 用不到的歷史 cookie 一條條作廢。

問題根因

sessions cookie 的名字裏帶了端口,而 cookie 本身不按端口隔離。@deepseek-ai/dsh-client-connection 裏是這麼算 cookie 名的:

const COOKIE_PREFIX = "dsh-auth-";
/** Canonical request authority used as the cookie name and signed audience. */
function cookieName(authority) {
    return COOKIE_PREFIX + encodeBase64Url(createHash("sha256").update(authority).digest());
}

而 authority 來自請求頭裏的 host:

function requestAuthority(headers) {
    const host = header(headers, "host");          // "127.0.0.1:54321"
    ...
    return new URL(`http://${host}`).host;
}

桌面版每次啓動都隨機換端口,於是:

  1. 每次啓動都算出一個新的 cookie 名(dsh-auth- + host:端口 的哈希);
  2. Chromium 存 cookie 不看端口,這些 cookie 全都堆在 127.0.0.1 這同一個域下;
  3. shell 的 welcome backend 會把庫裏該域下的所有 cookie 拼成一個 Cookie 頭髮出去:
   async () => (await session.defaultSession.cookies.get({ url: ready.url }))
       .map((cookie) => `${cookie.name}=${cookie.value}`).join("; ")
  1. 每條這種 cookie 大約 220~260 字節,攢到 70 條出頭就頂到 Node 默認的 maxHeaderSize(16384 字節);
  2. Node 的 HTTP 解析器在進入業務代碼之前就直接返回 431,Electron 那邊看到的就是認證失敗。

README 給出的實測(test/simulate.mjs 場景 A)是:第 73 次啓動時請求頭 16423 字節,返回 431。README 還提到,截至 0.2.1-rc.3 官方仍未修復。

插件做了什麼

三個動作:

動作 作用
server.maxHeaderSize = 256KB 把天花板抬到約 1000 條 cookie,已經攢了 70+ 條的機器也能立刻啓動
每條請求都算一遍「本輪該有的 cookie 名」,把其它 dsh-auth-* 用 Set-Cookie: …=; Max-Age=0 過期掉 Chromium 自己清理,庫裏永遠只剩 1 條,不再堆積
統計寫進 ~/.dsh/cookie-doctor/report.json 能直接看到到底有沒有生效

過期指令走的是同一條 Chromium 網絡棧,所以和當初寫下它們的通道完全一致。README 裏的實測結果:

場景 結果
A. 不裝插件 第 73 次啓動 431 崩潰
B. 裝插件,從零開始 90 次啓動 Cookie 頭恆爲 223 字節 / 1 條
C. 裝插件,機器已攢 76 條(請求頭 17098 字節,已超默認上限) 不崩,第一次請求就自愈到 1 條

另外,README 記錄了這個插件在真實 DSH host 裏跑過:dsh --profile ctest --port 18099 起來後,帶 20000 字節 Cookie 頭髮請求,返回 401 而不是 431,響應裏帶上了過期指令。

插件對 apply() 做了整體 try/catch:即使初始化異常也只會打一行警告,不會讓 host 啓動失敗。

安裝

README 裏的安裝方式不是 dsh plugin add,而是倉庫自帶的安裝腳本:

node scripts/install.mjs            # 裝進 ~/.dsh/profiles/desktop
node scripts/install.mjs --dry-run  # 先看它要幹什麼

腳本做的事(不需要聯網、不需要 pnpm):

  1. ~/.dsh/plugins/dsh-cookie-doctor → 指向本倉庫(Windows 用 junction,不用管理員權限)
  2. <profile>/node_modules/dsh-cookie-doctor → 指向上面的目錄,讓 Node 能解析到
  3. 在 profile 的 package.json 里加上 dependencies 和 dsh.profile.bundles(改之前自動備份)

裝完重啓 DeepSeek Harness。控制台會打印一行:

[cookie-doctor] 已接管 host HTTP 通道:maxHeaderSize 16384 → 262144,過期策略 開啓

卸載:

node scripts/install.mjs --uninstall

按 README 的說法,插件的 bundle patch 路徑是 ./cordis.patch.yml,配置項寫在這個文件(或 profile 層覆寫)裏。

已經起不來了怎麼辦

插件只能在 host 起來之後才跑;當請求頭已經超過 16KB 時 host 根本收不到請求,所以已經病發的機器得先在外面清一次:

# 1. 完全退出 DeepSeek Harness(連托盤一起退)
node scripts/diagnose.mjs --dry-run   # 先體檢:會看到 dsh-auth-* 有幾十條
node scripts/repair.mjs --dry-run     # 看會刪多少
node scripts/repair.mjs               # 真刪,自動備份原庫
# 2. 重新啓動 DSH

repair.mjs 只刪 dsh-auth-* 那些行,別的 cookie 一條不動。刪之前會自動生成 Cookies.bak-<時間戳>。

如果連 DSH 都啓動不了、只想手動來,README 給出的等價做法是刪掉:

%APPDATA%\@deepseek-ai\dsh-desktop\Network\Cookies

以及同目錄的 Cookies-journal。效果等價,但會連別的 cookie 一起清掉。

體檢腳本

node scripts/diagnose.mjs          # 看當前堆了多少、會不會炸
node scripts/diagnose.mjs --json   # 機器可讀

README 給出的輸出示例:

cookie 庫:C:\Users\me\AppData\Roaming\@deepseek-ai\dsh-desktop\Network\Cookies
cookie 總數:80(其中 dsh-auth-* 80 條,其它 0 條)

  127.0.0.1  → 80 條,拼成請求頭約 20400 字節
     dsh-auth-Ab3x…
     …

🔴 已經炸了:請求頭超過 16KB,啓動必定報 Web authentication failed
   Node 默認請求頭上限 16384 字節,當前最壞 20400 字節

需要注意:DSH 運行中 cookie 庫被 Chromium 獨佔鎖定,體檢和修復都要先完全退出 DSH。只想看一眼的話,可以先複製一份庫文件,再用 --db <副本> 分析。

配置

改 cordis.patch.yml 裏的 config(或 profile 層覆寫):

鍵 默認 說明
maxHeaderBytes 262144 抬到多少。0 = 不動默認值(不推薦,默認值就是那個坑)
purgeStale true 是否過期掉歷史 cookie
maxPurgePerResponse 200 一條響應最多帶多少條過期指令
warnAuthCookies 8 超過這麼多條就警告一次
reportPath $DSH_HOME/cookie-doctor/report.json 統計落盤位置
quiet false 關掉日誌

測試

README 提供了兩個可以自己跑的測試:

node test/simulate.mjs      # 復現故障 + 驗證修復(含病發機器自愈)
node test/repair.test.mjs   # 體檢/修復腳本,用 Chromium 同結構的 cookie 庫跑

test/simulate.mjs 的實測輸出:

### A. 不裝插件(復現線上故障)
  首次:請求頭 223 字節,庫中 1 條
  末次:請求頭 16423 字節,庫中 73 條
  ❌ 第 73 次啓動失敗:auth=303 welcome=431

### B. 裝插件(從零開始)
  峯值請求頭 223 字節 / 庫中 1 條,90 次啓動全程正常

### C. 裝插件 + 已攢 76 條(病發機器)
  首次:請求頭 17098 字節 → 自愈到 1 條,全程正常

幾種取捨

  • 抬高 maxHeaderSize 有沒有副作用? README 的解釋是:host 只監聽迴環地址,抬高上限只是讓「頭部異常大」的請求不至於被解析器直接掐掉;真正的訪問控制仍然由 /api 的 loopback + Origin 圍欄負責。不想要可以設 maxHeaderBytes: 0。
  • 爲什麼不乾脆把 cookie 名改成不帶端口? 那樣 authority 就失去了端口綁定(isAuthenticated 會校驗 payload.authority === authority),等於放寬鑑權語義;而且改的是 DSH 自己的代碼,插件做不到、也不該越界。所以插件選擇「保留語義,只清理垃圾」。

適用場景與注意

適合:

  • 用 DSH 桌面版,遇到過重啓後報 desktop welcome: Web authentication failed,手動刪 Cookie 庫能救但會復發的人。
  • 想先看清楚自己機器上 dsh-auth-* 堆了多少條再決定要不要改配置的人。
  • 不想重裝 DSH、不想禁用插件的人。

需要注意:

  • 插件以當前 dsh 進程的權限運行,並在 host 進程裏接管 HTTP server。安裝前應檢查源碼與許可證(MIT)。
  • 插件是第三方個人維護的,README 裏明確寫了「100% vibe coding,來源自 DeepSeek V4.1 Flash Harness 端」。要在自己的環境裏驗證一下,別直接當作官方修復。
  • 病發的機器需要先在外面執行 repair.mjs,插件本身只能阻止後續堆積。
  • DSH 運行期間 cookie 庫被鎖定,體檢和修復前必須完全退出 DSH(包括托盤)。

小結

這個插件的價值在於把一個只能靠刪文件解決的偶發崩潰,變成兩步可復現的操作:先 diagnose.mjs 看清當前堆了多少,再裝插件(或先 repair.mjs 清一次)阻止復發。它不碰鑑權語義,所以風險面比較明確。

注:DSH 社區目錄是獨立站點,與 DeepSeek / 幻方無官方從屬關係。

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

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

小夜