前言¶
DSH 的認證依賴瀏覽器 cookie。每次啓動時 DSH 會按監聽地址籤一條新 cookie,名字裏含端口;端口是隨機的,cookie 歸屬範圍卻只到主機、不含端口。於是瀏覽器每次請求都會把歷史上所有 dsh-auth-* 一起帶上,攢到約 70 條後 Cookie 頭接近 Node.js 默認的 16 KiB上限,服務端在應用代碼執行前就返回 431 Request Header Fields Too Large,表現是白屏、加載卡住或認證失敗。
下面介紹 yan3536 維護的 dsh-cookie-janitor 插件(分類 admin-security,GitHub 1 stars,MIT)。它掛在 Host 的 HTTP 服務上,在請求進入應用之前把屬於已消失端口的舊 cookie 裁掉,並對已經溢出的請求做一次 302 兜底。README 裏給出的離線自測覆蓋 24 個用例。
它解決什麼問題¶
DSH 的瀏覽器認證 cookie 名由地址推導:
cookie 名 = "dsh-auth-" + base64url(sha256("host:port"))
名字裏帶了端口,而端口並不固定:
- 桌面版啓動時寫死
--port 0,讓操作系統挑空閒端口; - 命令行
dsh --profile web --port 0同理。
於是 30 天內啓動 N 次就堆 N 條無用 cookie,且有效期不因使用而續期。單條約 221 字節,README 實測 73 條時 Cookie 頭 16277 B 仍能通過,74 條時 16500 B 拿到 431;因爲 Host、User-Agent 等頭同樣佔用額度,實際觸頂會略早,按 70 條左右估算。
已有做法是手動在瀏覽器裏清掉 127.0.0.1 的站點數據,能解一次急,但下次啓動仍然開始重新堆。這個插件把清理放進請求鏈路,不再需要人工介入。
三層行爲¶
插件按請求發生順序做三件事:
- 請求入口:從
Cookie頭裏摘掉屬於其他端口的dsh-auth-*,應用看不到這些 cookie; - 響應出口:對被摘掉的那些下發
Max-Age=0,讓瀏覽器從 cookie 庫裏刪除; - 溢出兜底:如果請求頭已經大到 Node 拒絕解析,就把 431 換成一次 302 回到原地址並附清除指令,用戶看到的是刷新一次,而不是崩潰。
當前正在使用的那條 cookie 永遠不動,刪掉它會把用戶從正在使用的實例裏登出。清理完成後第二次請求的 Cookie 頭就只剩一條,之後不再增長。
安裝¶
從 Git 倉庫裝:
dsh plugin --profile web add github:yan3536/dsh-cookie-janitor
離線調試源碼時,先克隆再用絕對路徑裝:
git clone https://github.com/yan3536/dsh-cookie-janitor
dsh plugin --profile web add /絕對路徑/dsh-cookie-janitor
裝完必須完全退出 DSH 再啓動。插件掛在 Host 的 HTTP 服務上,只有在啓動時才能裝上,刷新頁面不生效。
桌面版用戶把 --profile web 換成 --profile desktop。桌面版 profile 被 Electron 獨佔,必須先完全退出 DSH 再執行命令,否則會報 Open DeepSeek Harness Desktop once to initialize its profile, then fully quit it before running dsh plugin --profile desktop.
卸載:
dsh plugin --profile web remove dsh-cookie-janitor
配置¶
全部可選,默認值即推薦值。需要改時寫進 profile 的 cordis.patch.yml:
- id: cookie-janitor
name: dsh-cookie-janitor
config:
keepForeign: 0 # 允許保留幾條非當前端口的 cookie;0 = 全清
pruneRequests: true # 關掉就只剩溢出兜底
rescueOverflow: true # 關掉則 431 原樣返回
maxHeaderBytes: 0 # >0 時把 Node 的頭上限抬到這個字節數
attachTimeoutMs: 15000
maxHeaderBytes 只是把症狀往後推,真正止血的是 keepForeign: 0。同一瀏覽器需要同時連多個 DSH 實例時,把 keepForeign 設成實際並用的實例數,插件按瀏覽器發送順序保留最新的那幾條。
會不會誤刪¶
不會。判定完全按 DSH 自己的算法來:
- 只碰
dsh-auth-開頭的 cookie,主題、語言、第三方 cookie 一律不動; - 當前
Host對應的那條永遠保留; - 被刪的那些對應的監聽端口在當前進程裏不存在,留着沒有用處。
如果哪天 DSH 換了 cookie 命名規則,插件識別不到任何 dsh-auth-*,於是什麼都不做,最壞情況是退回原狀。
離線驗證¶
node test/smoke.mjs
24 個用例,不依賴 DSH、不聯網:16 個純函數(cookie 名推導、authority 歸一化、Cookie 頭解析、清理策略的六種情形、過期指令拼裝、原始報文與 target 抓取);6 個端到端(真實 node:http 服務器加裸 socket,覆蓋正常透傳、乾淨請求逐字節不變、20 KiB 頭被救成 302、關掉兜底後恢復原生 431、放寬上限生效、關掉清理後原樣透傳);2 個健壯性(沒有 web 服務時靜默、畸形配置不拋)。
要求與邊界¶
插件版本 0.1.0,在 DSH 0.2.0-rc.2 上實測通過,要求 Node ≥ 20(用到 Buffer.toString('base64url') 與 replaceAll)。插件沒有聲明 engines.dsh:DSH 的兼容檢查一旦看到這個鍵就按獨佔判定,而預發佈版低於 0.2.0,寫上反而把當前能用的版本擋在外面。
插件只用 webServer 服務的兩個公開成員:server 與 protocol;不復用未導出的內部符號,cookie 名是自己按 sha256(authority) + base64url 復算的,因爲 BrowserAuth、COOKIE_PREFIX、cookieName 沒有從 @deepseek-ai/dsh-client-connection 導出。
需要注意的邊界:
- 只覆蓋 Host 自己的 HTTP 監聽。桌面版主窗口走
dsh-app://自定義協議,那條路徑的認證 cookie 由主進程內存變量持有,不寫瀏覽器 cookie 庫,本來也不會堆積。 - 清理發生在下次請求時,不是安裝的那一瞬間。裝完第一次打開頁面,插件纔看到並清掉舊 cookie;如果此刻請求頭已超限,走溢出兜底。
- 不改 DSH 的端口策略。
--port 0是給遠程訪問留的正規能力,插件只在 cookie 側做減法。
適用場景與注意¶
適合用 DSH web profile 且頻繁重啓、出現過白屏或 431 的本地開發者;也適合在本地排查請求頭問題的維護者。插件以當前 dsh 進程的權限運行,裝的是倉庫裏的 cordis.patch.yml;安裝前應自己讀一遍源碼,確認許可證是 MIT 再啓用。
手動清理可以解一次急:打開 http://127.0.0.1:<端口>,從地址欄左側的站點信息裏刪除 Cookie 和網站數據,或在開發者工具 Application → Cookies 裏逐個刪掉 dsh-auth-*。代價是下次啓動重新開始堆。
小結¶
dsh-cookie-janitor 的位置很窄:解決的是「端口變化導致歷史認證 cookie 無限累積、最終撞上請求頭上限」這一個具體問題,做法是在請求進入應用前裁掉舊 cookie 並讓瀏覽器刪除它們,同時對已溢出的請求做 302 兜底。它不改變 DSH 的端口策略,也不碰與 dsh-auth- 無關的 cookie。
目錄頁:https://www.skillhub.cn/plugins/yan3536/dsh-cookie-janitor
源碼:https://github.com/yan3536/dsh-cookie-janitor