@lvxianchao/dsh-web-guard 2.1.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.1.1 — 2026-11-05
4
+
5
+ ### Docs
6
+
7
+ - README and the admin page now describe the persisted session cache and the dead-session redirect; both still claimed sessions lived in memory and that a restart signed everyone out.
8
+ - Screenshots re-shot with the corrected wording.
9
+
10
+
3
11
  ## 2.1.0 — 2026-11-05
4
12
 
5
13
  ### Added
package/README.md CHANGED
@@ -173,10 +173,21 @@ node -e "import('./lib/auth.js').then(m=>console.log(m.hashPassword(process.argv
173
173
  - Session tokens are stored server-side as SHA-256 digests; cookies are `HttpOnly`, `SameSite=Lax`, and `Secure` when TLS is on.
174
174
  - Login POSTs are checked for a same-hostname `Origin`. The session cookie's `SameSite` attribute is the primary CSRF defence.
175
175
  - Repeated failures from one IP trigger a temporary lock (in-memory, cleared on restart).
176
- - `/logout` ends the current browser session. To revoke everything, change the password and restart, or delete `$DSH_HOME/dsh-web-guard.json`.
176
+ - **The session table is cached on disk** (`<profile>/data/dsh-web-guard-sessions-<port>.json`, mode `0600`, atomic tmp+rename), so **restarting DSH no longer signs signed-in browsers out**. The file holds SHA-256 digests, never tokens — neither the cache nor a heap dump can mint a working cookie. The filename carries the listener port, so a shadow instance cannot overwrite the live one.
177
+ - `/logout` ends the current browser session. To revoke everything (a suspected leak, say), use the admin page's **sign out all devices** — **a restart is not a revocation any more**.
177
178
 
178
179
  **This is not multi-user.** One password, one privilege level. The harness agent can execute code, so anyone who gets in has code execution on that host — which is the product's design, not a bug in the gate.
179
180
 
181
+ ## When the session is gone
182
+
183
+ If the session disappears (password changed, sign-out-all, or no activity within `sessionDays`), an open tab does not sit on "reconnecting" forever:
184
+
185
+ - it probes `/__guard/session` when the tab becomes visible, when the network comes back, after a WebSocket close, and every 30 seconds
186
+ - only an explicit **401** triggers a redirect — a failed fetch (offline, server restarting) never does
187
+ - the redirect carries `?next=`, so you land back where you were after signing in
188
+
189
+ Restarts (upgrades, config changes) are invisible: the session table is restored from disk and the page reconnects on its own.
190
+
180
191
  ## Admin page
181
192
 
182
193
  A shield button appears at the bottom-right of the harness UI once you are signed in. It opens `/__guard/admin` (bookmark the URL if you prefer):
package/README.zh.md CHANGED
@@ -167,10 +167,21 @@ node -e "import('./lib/auth.js').then(m=>console.log(m.hashPassword(process.argv
167
167
  - 会话 token 服务端只存 SHA-256 摘要;cookie 是 `HttpOnly` + `SameSite=Lax`,启用 TLS 时加 `Secure`。
168
168
  - 登录 POST 校验 `Origin` 的 hostname 与请求 `Host` 一致(只比 hostname,因为反代常剥端口)。CSRF 的第一道防线仍是 `SameSite`。
169
169
  - 同一 IP 连续失败会临时锁定(内存计数,重启清零)。
170
- - `/logout` 只退当前浏览器;要全部吊销就改密码并重启,或删掉 `$DSH_HOME/dsh-web-guard.json`。
170
+ - **会话表现在会落盘**(`<profile>/data/dsh-web-guard-sessions-<port>.json`,`0600`,临时文件 + rename 原子写入),所以**重启 DSH 不会把已登录的浏览器踢下线**。文件里存的同样是 SHA-256 摘要,不是 token——拿到这个文件或内存 dump 都无法伪造 cookie。文件名带监听端口,影子实例不会覆盖正式实例。
171
+ - `/logout` 只退当前浏览器。要全部吊销(比如怀疑泄露),用管理页的**退出所有设备**:**重启不再等于吊销**。
171
172
 
172
173
  **这不是多用户系统**:一个密码,一个权限级别。DSH 的 agent 本身能执行代码,任何登录者都拥有该机器的执行权限——这是产品设计,不是闸门的缺陷。
173
174
 
175
+ ## 掉线之后
176
+
177
+ 会话没了(改过密码、点了退出所有设备、或超过 `sessionDays` 没活动)时,已经开着的标签页不会卡在"重新连接中":
178
+
179
+ - 切回标签页、网络恢复、WebSocket 断开、以及每 30 秒,页面都会向 `/__guard/session` 探一次
180
+ - 只有拿到明确的 **401** 才跳转(断网导致的请求失败不会误判)
181
+ - 跳转目标带 `?next=`,登录后回到原来那个地址
182
+
183
+ 需要重启(升级、改配置)则完全无感:会话表会从磁盘恢复,页面自己重连回来。
184
+
174
185
  ## 管理页
175
186
 
176
187
  登录后 DSH 界面右下角会出现一个盾牌按钮,点开就是 `/__guard/admin`(也可以直接把地址收藏起来):
package/lib/i18n.js CHANGED
@@ -114,7 +114,7 @@ const ZH = {
114
114
  signOutAll: '退出所有设备',
115
115
  confirmSignOutAll: '将退出所有设备上的登录,包括当前这个。继续?',
116
116
  secSessions: '活跃会话',
117
- hintSessions: '每个浏览器一条记录。会话存在内存里,重启 DSH 会全部失效。',
117
+ hintSessions: '每个浏览器一条记录。会话落盘保存,重启 DSH 不会把人踢下线;要全员下线请点「退出所有设备」。',
118
118
  colSource: '来源',
119
119
  colBrowser: '浏览器',
120
120
  colLastSeen: '最后活动',
@@ -169,7 +169,7 @@ const EN = {
169
169
  signOutAll: 'Sign out everywhere',
170
170
  confirmSignOutAll: 'This signs out every device, including this one. Continue?',
171
171
  secSessions: 'Active sessions',
172
- hintSessions: 'One row per browser. Sessions live in memory and are cleared when DSH restarts.',
172
+ hintSessions: 'One row per browser. Sessions are persisted, so restarting DSH keeps everyone signed in — use "Sign out all devices" to revoke them.',
173
173
  colSource: 'Source',
174
174
  colBrowser: 'Browser',
175
175
  colLastSeen: 'Last seen',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvxianchao/dsh-web-guard",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "Password login for the DeepSeek Harness Web UI, so it can be exposed on the internet: sign in from anywhere instead of chasing one-off tokens, with reverse-proxy quirks handled.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",