@sparkelf/dsh-patch-browser-auth-mode 0.1.0-rc.22 → 0.1.0-rc.27

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/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write patches/npm/browser-auth-mode/README.md
5
- README.md: 4f4c0f8d9a14eb9318fc4eedeab899b2e37ede54
6
- README.zh.md: cb9910445171567bcc0b9d17f8aee7d6d0e40753
5
+ README.md: 9f44f0bdd416d7348f673938d56c9b722817bc94
6
+ README.zh.md: 6115cb86c7cd45fbf2a839b6f73ae18aa7f551fa
package/README.md CHANGED
@@ -6,4 +6,4 @@ This data-only package adds one explicit `browserAuthentication: required | disa
6
6
 
7
7
  Plus selects `disabled` because its accepted local Web workflow prioritizes direct access over browser identity. This exposes the complete Host API, including Shell, files, and Sessions, to every process that can reach an accepted authority; it does not only affect DataOps. The shipped CLI still binds loopback and rejects `--host 0.0.0.0`, while custom trusted authorities remain a deployment responsibility.
8
8
 
9
- The target is exact official source base `d347e703908d0406b7a7ef80e3a0e594d86b2215`. The package has no JavaScript entry, lifecycle script, credential material, or fallback variant. Retire it when official DSH exposes an equivalent profile-selected browser-authentication policy.
9
+ The target is exact official source base `fb2c4b9e698e30edb738bca4cf0618587db7d203`. The package has no JavaScript entry, lifecycle script, credential material, or fallback variant. Retire it when official DSH exposes an equivalent profile-selected browser-authentication policy.
package/README.zh.md CHANGED
@@ -6,4 +6,4 @@
6
6
 
7
7
  Plus选择`disabled`,因为其accepted local Web workflow优先保证直接访问,不保留browser identity。该选择会把包含Shell、files与Sessions在内的完整Host API开放给所有能访问accepted authority的进程,并非只影响DataOps。shipped CLI仍绑定loopback并拒绝`--host 0.0.0.0`,custom trusted authorities仍由deployment负责。
8
8
 
9
- target是exact official source base `d347e703908d0406b7a7ef80e3a0e594d86b2215`。本package没有JavaScript entry、lifecycle script、credential material或fallback variant。official DSH暴露等价profile-selected browser-authentication policy后retire本package。
9
+ target是exact official source base `fb2c4b9e698e30edb738bca4cf0618587db7d203`。本package没有JavaScript entry、lifecycle script、credential material或fallback variant。official DSH暴露等价profile-selected browser-authentication policy后retire本package。
package/package.json CHANGED
@@ -4,11 +4,11 @@
4
4
  "formatVersion": 1,
5
5
  "variants": [
6
6
  {
7
- "dsh": ">=0.1.3-alpha.1",
7
+ "dsh": ">=0.1.5-rc.2",
8
8
  "file": "./patches/browser-auth-mode.patch",
9
9
  "id": "browser-authentication-policy-mode",
10
10
  "target": {
11
- "baseRevision": "d347e703908d0406b7a7ef80e3a0e594d86b2215",
11
+ "baseRevision": "fb2c4b9e698e30edb738bca4cf0618587db7d203",
12
12
  "kind": "dsh-source",
13
13
  "paths": [
14
14
  "packages/client/connection/"
@@ -31,5 +31,5 @@
31
31
  "url": "git+https://github.com/SparkElf/deepseek-harness-plus.git"
32
32
  },
33
33
  "type": "module",
34
- "version": "0.1.0-rc.22"
34
+ "version": "0.1.0-rc.27"
35
35
  }
@@ -1,69 +1,3 @@
1
- diff --git a/packages/client/connection/README.i18n.yaml b/packages/client/connection/README.i18n.yaml
2
- index e1e4ff6e1c..36cdef1d32 100644
3
- --- a/packages/client/connection/README.i18n.yaml
4
- +++ b/packages/client/connection/README.i18n.yaml
5
- @@ -2,5 +2,5 @@
6
- # side as of the last confirmed-consistent state. Both languages carry equal authority;
7
- # after editing either side, bring the other along and re-record with:
8
- # pnpm run verify-translation-pairing --write packages/client/connection/README.md
9
- -README.md: 91e96a34d15463fd4246bf8fd8aee6112aaa54c4
10
- -README.zh.md: fc6686e688170427528cb51db51e166f3c447f26
11
- +README.md: 4abcf01ffc9a4bed9e6b8499e7fe6ea7f8efd2f7
12
- +README.zh.md: 8584c9063d1fd941a13ae9bd14c5ff58067e0778
13
- diff --git a/packages/client/connection/README.md b/packages/client/connection/README.md
14
- index 91e96a34d1..4abcf01ffc 100644
15
- --- a/packages/client/connection/README.md
16
- +++ b/packages/client/connection/README.md
17
- @@ -25,18 +25,18 @@ The package carries browser-to-Host Remote calls, exact Fetch responses, and con
18
- <a id="use-this-package"></a>
19
- ## Use this package
20
-
21
- -The browser uses HTTP POST for Remote unary calls. API Gateway owns the `/api/remote.mux` WebSocket and its logical streams; in-process compositions provide equivalent Remote streams through `connection.rpc.open` without opening a WebSocket. The Host half owns the sole `/api` route, Fetch bridge, browser authentication, Host/Origin checks, and exact `GET`/`HEAD`/`POST` route registry. Each exact route declares buffered or streaming request-body handling before the bridge reads any bytes. Typert Gateway claims generated Remote endpoints, feature packages register non-JSON responses such as Session-log downloads and raw file uploads, and unclaimed requests return 404. Loopback hostname classification remains package-internal to the browser-facing Client state. Browser raw-body transfer is provided by [`dsh-client-file-upload`](../file-upload/README.md).
22
- +The browser uses HTTP POST for Remote unary calls. API Gateway owns the `/api/remote.mux` WebSocket and its logical streams; in-process compositions provide equivalent Remote streams through `connection.rpc.open` without opening a WebSocket. The Host half owns the sole `/api` route, Fetch bridge, browser-authentication policy, Host/Origin checks, and exact `GET`/`HEAD`/`POST` route registry. Each exact route declares buffered or streaming request-body handling before the bridge reads any bytes. Typert Gateway claims generated Remote endpoints, feature packages register non-JSON responses such as Session-log downloads and raw file uploads, and unclaimed requests return 404. Loopback hostname classification remains package-internal to the browser-facing Client state. Browser raw-body transfer is provided by [`dsh-client-file-upload`](../file-upload/README.md).
23
-
24
- -----
25
-
26
- <a id="browser-authentication-and-request-trust"></a>
27
- ## Browser authentication and request trust
28
-
29
- -Every Host RPC method and WebSocket stream requires one browser session; there is no method-specific loopback tier. Each process mints a random launch token. `dsh-web-app` prints and opens the ordinary root URL with `?token=...`; `frontend-static` delegates root and index requests to `ctx.connection.authorizeIndex`, which accepts that token only on `GET /`, writes an authority-bound signed cookie, and redirects to clean `/`. A missing, expired, malformed, or wrong-authority cookie returns 401 before RPC dispatch. Static assets remain public. The HTTP carrier accepts no query token outside the root exchange and no Authorization-header token.
30
- +`browserAuthentication` defaults to `required`. In that mode every Host RPC method and WebSocket stream requires one browser session; there is no method-specific loopback tier. Each process mints a random launch token. `dsh-web-app` prints and opens the ordinary root URL with `?token=...`; `frontend-static` delegates root and index requests to `ctx.connection.authorizeIndex`, which accepts that token only on `GET /`, writes an authority-bound signed cookie, and redirects to clean `/`. A missing, expired, malformed, or wrong-authority cookie returns 401 before RPC dispatch. Static assets remain public. The HTTP carrier accepts no query token outside the root exchange and no Authorization-header token.
31
-
32
- -The cookie signing secret is the owner-scoped `client-connection/browser-session` grant record in `ctx.credentials`. The local provider persists it in `$DSH_HOME/.credentials.yaml`; `BrowserAuth` loads or creates the record during Connection activation and retains the secret in memory, so request authentication is synchronous. Deleting or replacing the record takes effect on the next Connection activation. Cookies carry an absolute issue/expiry interval, defaulting to 30 days through `cookieMaxAgeDays`, and bind the normalized hostname plus port in both their deterministic name and signed payload. They are host-only, `Path=/`, `HttpOnly`, and `SameSite=Strict`; they deliberately omit `Secure` because the shipped server uses loopback HTTP.
33
- +The cookie signing secret is the owner-scoped `client-connection/browser-session` grant record in `ctx.credentials`. The local provider persists it in `$DSH_HOME/.credentials.yaml`; `BrowserAuth` loads or creates the record during Connection activation and retains the secret in memory, so request authentication is synchronous. Deleting or replacing the record takes effect on the next Connection activation. Cookies carry an absolute issue/expiry interval, defaulting to 30 days through `cookieMaxAgeDays`, and bind the normalized hostname plus port in both their deterministic name and signed payload. They are host-only, `Path=/`, `HttpOnly`, and `SameSite=Strict`; they deliberately omit `Secure` because the shipped server uses loopback HTTP. A profile that explicitly selects `browserAuthentication: disabled` creates no launch token or signing record, prints the clean application URL, admits index requests, and treats Host API requests as authenticated after the independent Host/Origin fence succeeds.
34
-
35
- -Before authentication, every request still passes `src/api-request-trust.ts`. Its `Host` must be loopback or match a `trustedHosts` entry: exact on `host:port`, any port on port-less entries, both sides WHATWG-normalized. An attached `Origin` must equal that Host and `sec-fetch-site: cross-site` is refused. Malformed configured authorities fail plugin load. These checks defend DNS rebinding and cross-site browser requests; they never establish identity. A failed Host/Origin check returns 403, while a trusted but unauthenticated request returns 401. `dsh web --host 0.0.0.0` remains unsupported. Decision records: [browser request trust](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.md) and [browser token authentication](../../../.agents/notes/implemented/architecture/2026-08-24-browser-token-authentication.md).
36
- +Every request first passes `src/api-request-trust.ts`. Its `Host` must be loopback or match a `trustedHosts` entry: exact on `host:port`, any port on port-less entries, both sides WHATWG-normalized. An attached `Origin` must equal that Host and `sec-fetch-site: cross-site` is refused. Malformed configured authorities fail plugin load. These checks defend DNS rebinding and cross-site browser requests; they never establish identity. A failed Host/Origin check returns 403. Under the default required policy, a trusted but unauthenticated request returns 401; the disabled policy proceeds after trust succeeds. `dsh web --host 0.0.0.0` remains unsupported. Decision records: [browser request trust](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.md) and [browser token authentication](../../../.agents/notes/implemented/architecture/2026-08-24-browser-token-authentication.md).
37
-
38
- <a id="connection-generation"></a>
39
- ## Connection generation
40
- diff --git a/packages/client/connection/README.zh.md b/packages/client/connection/README.zh.md
41
- index fc6686e688..8584c9063d 100644
42
- --- a/packages/client/connection/README.zh.md
43
- +++ b/packages/client/connection/README.zh.md
44
- @@ -25,18 +25,18 @@ kind: "package-reference"
45
- <a id="use-this-package"></a>
46
- ## 使用本包
47
-
48
- -浏览器通过 HTTP POST 执行 Remote 一元调用。API Gateway 自己拥有 `/api/remote.mux` WebSocket 及其逻辑流。进程内组合通过 `connection.rpc.open` 提供等价的 Remote 流,不打开 WebSocket。Host half 拥有唯一 `/api` route、Fetch bridge、浏览器认证、Host/Origin 校验与精确 `GET`/`HEAD`/`POST` 路由注册表。每条精确路由会在 bridge 读取任何字节前声明缓冲或流式请求体处理方式。Typert Gateway 认领生成的 Remote endpoint,功能包注册 Session 日志下载、原始文件上传等非 JSON 响应,未认领的请求返回 404。Loopback hostname 判定只供浏览器侧当前页面状态使用,留在包内。浏览器原始请求体传输由 [`dsh-client-file-upload`](../file-upload/README.zh.md) 提供。
49
- +浏览器通过 HTTP POST 执行 Remote 一元调用。API Gateway 自己拥有 `/api/remote.mux` WebSocket 及其逻辑流。进程内组合通过 `connection.rpc.open` 提供等价的 Remote 流,不打开 WebSocket。Host half 拥有唯一 `/api` route、Fetch bridge、浏览器认证策略、Host/Origin 校验与精确 `GET`/`HEAD`/`POST` 路由注册表。每条精确路由会在 bridge 读取任何字节前声明缓冲或流式请求体处理方式。Typert Gateway 认领生成的 Remote endpoint,功能包注册 Session 日志下载、原始文件上传等非 JSON 响应,未认领的请求返回 404。Loopback hostname 判定只供浏览器侧当前页面状态使用,留在包内。浏览器原始请求体传输由 [`dsh-client-file-upload`](../file-upload/README.zh.md) 提供。
50
-
51
- -----
52
-
53
- <a id="browser-authentication-and-request-trust"></a>
54
- ## 浏览器认证与请求信任
55
-
56
- -每个 Host RPC 方法和 WebSocket stream 都要求同一个浏览器会话,不存在按方法区分的 loopback 层。每个进程生成一个随机启动令牌。`dsh-web-app` 打印并打开带 `?token=...` 的普通根 URL;`frontend-static` 把根路径和 index 请求交给 `ctx.connection.authorizeIndex`,后者只在 `GET /` 接受该令牌,写入绑定 authority 的签名 cookie,再重定向到干净的 `/`。缺失、过期、畸形或 authority 不匹配的 cookie 会在 RPC 分发前得到 401。静态资源保持公开。HTTP 载体不在根路径交换之外接受 query token,也不接受 Authorization header token。
57
- +`browserAuthentication`默认为`required`。在该模式下,每个 Host RPC 方法和 WebSocket stream 都要求同一个浏览器会话,不存在按方法区分的 loopback 层。每个进程生成一个随机启动令牌。`dsh-web-app` 打印并打开带 `?token=...` 的普通根 URL;`frontend-static` 把根路径和 index 请求交给 `ctx.connection.authorizeIndex`,后者只在 `GET /` 接受该令牌,写入绑定 authority 的签名 cookie,再重定向到干净的 `/`。缺失、过期、畸形或 authority 不匹配的 cookie 会在 RPC 分发前得到 401。静态资源保持公开。HTTP 载体不在根路径交换之外接受 query token,也不接受 Authorization header token。
58
-
59
- -cookie 签名密钥是 `ctx.credentials` 中由 `client-connection/browser-session` 拥有的 grant 记录。本地提供方把它持久化到 `$DSH_HOME/.credentials.yaml`;`BrowserAuth` 在 Connection 激活期间加载或创建该记录,并把密钥留在内存中,因此请求认证同步执行。删除或替换该记录会在下一次 Connection 激活时生效。cookie 携带绝对签发与过期区间,`cookieMaxAgeDays` 默认设为 30 天,并在确定性名称与签名 payload 中同时绑定规范化 hostname 和 port。它是 host-only、`Path=/`、`HttpOnly`、`SameSite=Strict`;随附服务器使用 loopback HTTP,因此刻意不设置 `Secure`。
60
- +cookie 签名密钥是 `ctx.credentials` 中由 `client-connection/browser-session` 拥有的 grant 记录。本地提供方把它持久化到 `$DSH_HOME/.credentials.yaml`;`BrowserAuth` 在 Connection 激活期间加载或创建该记录,并把密钥留在内存中,因此请求认证同步执行。删除或替换该记录会在下一次 Connection 激活时生效。cookie 携带绝对签发与过期区间,`cookieMaxAgeDays` 默认设为 30 天,并在确定性名称与签名 payload 中同时绑定规范化 hostname 和 port。它是 host-only、`Path=/`、`HttpOnly`、`SameSite=Strict`;随附服务器使用 loopback HTTP,因此刻意不设置 `Secure`。显式选择`browserAuthentication: disabled`的profile不创建启动令牌或签名记录,打印clean application URL,接受index requests,并在独立Host/Origin fence通过后把Host API requests视为authenticated。
61
-
62
- -认证之前,每个请求仍经过 `src/api-request-trust.ts`。其 `Host` 必须是 loopback,或与 `trustedHosts` 条目匹配:带端口的 `host:port` 精确匹配,不带端口的条目匹配任意端口,两侧均经 WHATWG 归一化。若附带 `Origin`,它必须等于该 Host;`sec-fetch-site: cross-site` 一律拒绝。畸形配置 authority 会让插件加载失败。这些检查防御 DNS rebinding 与跨站浏览器请求,绝不建立身份。Host/Origin 校验失败返回 403;Host 可信但未认证的请求返回 401。`dsh web --host 0.0.0.0` 仍不受支持。决策记录:[浏览器请求信任](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.zh.md)与[浏览器令牌认证](../../../.agents/notes/implemented/architecture/2026-08-24-browser-token-authentication.zh.md)。
63
- +每个请求首先经过 `src/api-request-trust.ts`。其 `Host` 必须是 loopback,或与 `trustedHosts` 条目匹配:带端口的 `host:port` 精确匹配,不带端口的条目匹配任意端口,两侧均经 WHATWG 归一化。若附带 `Origin`,它必须等于该 Host;`sec-fetch-site: cross-site` 一律拒绝。畸形配置 authority 会让插件加载失败。这些检查防御 DNS rebinding 与跨站浏览器请求,绝不建立身份。Host/Origin 校验失败返回403;默认required策略下,Host可信但未认证的请求返回401,disabled策略则在trust通过后继续。`dsh web --host 0.0.0.0` 仍不受支持。决策记录:[浏览器请求信任](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.zh.md)与[浏览器令牌认证](../../../.agents/notes/implemented/architecture/2026-08-24-browser-token-authentication.zh.md)。
64
-
65
- <a id="connection-generation"></a>
66
- ## Connection generation
67
1
  diff --git a/packages/client/connection/src/browser-auth.ts b/packages/client/connection/src/browser-auth.ts
68
2
  index 10353d9a06..dbeb256022 100644
69
3
  --- a/packages/client/connection/src/browser-auth.ts
@@ -100,7 +34,7 @@ index 10353d9a06..dbeb256022 100644
100
34
  private readonly maxAgeMilliseconds: number
101
35
 
102
36
  diff --git a/packages/client/connection/src/index.ts b/packages/client/connection/src/index.ts
103
- index a274cef190..b4ff8cfda6 100644
37
+ index 98a4354760..b895a649c2 100644
104
38
  --- a/packages/client/connection/src/index.ts
105
39
  +++ b/packages/client/connection/src/index.ts
106
40
  @@ -8,7 +8,7 @@ import type { WebRoute } from '@deepseek-ai/dsh-host-webserver'
@@ -110,9 +44,9 @@ index a274cef190..b4ff8cfda6 100644
110
44
  -import { BrowserAuth } from './browser-auth.ts'
111
45
  +import { BrowserAuth, DISABLED_BROWSER_AUTHENTICATION } from './browser-auth.ts'
112
46
  import { HostConnectionService } from './rpc-host.ts'
47
+ import { ConnectionRecoveryConfigSchema, resolveConnectionConfig, type ConnectionRecoveryConfig } from './recovery-config.ts'
113
48
 
114
- export type {
115
- @@ -78,6 +78,8 @@ export interface ConnectionConfig {
49
+ @@ -81,6 +81,8 @@ export interface ConnectionConfig {
116
50
  * bind. An entry that is not a bare, canonical authority fails plugin load.
117
51
  */
118
52
  trustedHosts?: string[]
@@ -121,16 +55,16 @@ index a274cef190..b4ff8cfda6 100644
121
55
  /** Absolute browser-session lifetime in days. Default: 30. */
122
56
  cookieMaxAgeDays?: number
123
57
  /** Maximum buffered JSON body for every `/api` request. Default: 300 MiB. */
124
- @@ -86,6 +88,7 @@ export interface ConnectionConfig {
125
-
58
+ @@ -90,6 +92,7 @@ export interface ConnectionConfig {
126
59
  export const Config: z<ConnectionConfig> = z.object({
60
+ recovery: ConnectionRecoveryConfigSchema.default({}),
127
61
  trustedHosts: z.array(String).default([]),
128
62
  + browserAuthentication: z.union(['required', 'disabled']).default('required'),
129
63
  cookieMaxAgeDays: z.natural().min(1).default(30),
130
64
  maxRequestBodyBytes: z.natural().min(1).default(DEFAULT_MAX_REQUEST_BODY_BYTES),
131
65
  })
132
- @@ -100,16 +103,21 @@ export const Config: z<ConnectionConfig> = z.object({
133
- export async function apply(ctx: Context, config?: ConnectionConfig): Promise<void> {
66
+ @@ -105,16 +108,21 @@ export async function apply(ctx: Context, config?: ConnectionConfig): Promise<vo
67
+ const recovery = resolveConnectionConfig(config?.recovery)
134
68
  // The Loader resolves schema defaults; hand-built test contexts may pass none.
135
69
  const trustedHosts = config?.trustedHosts ?? []
136
70
  + const browserAuthentication = config?.browserAuthentication ?? 'required'
@@ -150,8 +84,8 @@ index a274cef190..b4ff8cfda6 100644
150
84
  - await BrowserAuth.create(ctx.root, ctx.credentials, cookieMaxAgeDays),
151
85
  + browserAuth,
152
86
  )
153
- const fetchHandler = connection.createSharedFetchHandler(API_PATH)
154
- const route: WebRoute = {
87
+ ctx.inject(['webServer'], (webCtx) => {
88
+ assertImageBodyCapacity(webCtx, maxRequestBodyBytes)
155
89
  diff --git a/packages/client/connection/src/rpc-host.ts b/packages/client/connection/src/rpc-host.ts
156
90
  index d74734e132..df5dc854fb 100644
157
91
  --- a/packages/client/connection/src/rpc-host.ts