@qilin-agent/client-connection 0.0.0-stage → 3.1.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kkutysllb
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,37 @@
1
+ # Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
2
+ # section, a hash of its English and Chinese blocks outside code blocks and generated regions.
3
+ # After editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/client/connection/README.md
5
+ /:
6
+ en: 13b131457d03a224
7
+ zh: 541bd85b21259ff3
8
+ /qilin-client-connection:
9
+ en: bfbb88dd96c1e4cb
10
+ zh: 37988d4aa00fa2a5
11
+ /qilin-client-connection/summary:
12
+ en: a83b5e9fd1968062
13
+ zh: ac68628e185a4bff
14
+ /qilin-client-connection/table-of-contents:
15
+ en: 18792bf2fb4f8030
16
+ zh: 84c63acc7e0099ae
17
+ /qilin-client-connection/use-this-package:
18
+ en: ef04350968741ba3
19
+ zh: abcded86153ddd9b
20
+ /qilin-client-connection/browser-authentication-and-request-trust:
21
+ en: 73a9f2fd2c2d559e
22
+ zh: 4b629185fecee159
23
+ /qilin-client-connection/connection-generation:
24
+ en: 9ab2b92f7be612e3
25
+ zh: e2f6994b29f4a9cc
26
+ /qilin-client-connection/model-experience:
27
+ en: 88bbc33fe9ae9df8
28
+ zh: bfbab6d51b4abd40
29
+ /qilin-client-connection/model-experience/kv-cache-effect:
30
+ en: ca75c51c89c2c9b0
31
+ zh: de54f3467ef3b9be
32
+ /qilin-client-connection/known-limitations-and-deferred-work:
33
+ en: 21d90520c2e5d4b9
34
+ zh: c9ae88395b272977
35
+ /qilin-client-connection/known-limitations-and-deferred-work/dev-note:
36
+ en: e67f633dc6fe7773
37
+ zh: e88aeb617ffb74ea
package/README.md CHANGED
@@ -1,3 +1,87 @@
1
- # Temporary Holding Version
1
+ ---
2
+ description: "Browser-host wire layer for the web GUI: Remote RPC, event-stream delivery with reconnect, exact Fetch routes, the /api HTTP bridge, and the browser-trust fence."
3
+ kind: "package-reference"
4
+ ---
2
5
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
6
+ # @qilin-agent/client-connection
7
+
8
+ English | [中文](README.zh.md)
9
+
10
+ ## Summary
11
+
12
+ The package carries browser-to-Host Remote calls, exact Fetch responses, and connection generations. The Client plugin mounts `ctx.connection` with current-page loopback state, generic RPC, the active generation and its Host facts, observable recovery state, an immediate reconnect command, and the registration point for one generation source. A generation becomes visible when its source reports ready; source completion, failure, withdrawal, or an explicit stop clears it before `ConnectionController` applies its retry policy.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Browser authentication and request trust](#browser-authentication-and-request-trust)
18
+ - [Connection generation](#connection-generation)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ A static desktop page can provide `__QILIN_TRANSPORT__.streamBaseUrl` for the HTTP origin of its owned Host. The Gateway uses that origin for its WebSocket while HTTP transport remains independently selected. The desktop carrier owns authentication; setting the origin alone grants no access.
29
+
30
+ The browser uses HTTP POST for Remote unary calls. API Gateway owns the `/api/remote.mux` WebSocket and its logical streams; shell-owned compositions provide equivalent Remote streams, including a stream's uplink, through `connection.rpc.open` without opening a WebSocket. The browser plugin reads the page transport, recovery settings, and location, then delegates to `installConnection(ctx, options)`. A composition that owns its carrier may call the same installer directly; the whole-client test tier does so. Each invocation creates one Context-owned service, so several Client trees can use different carriers in one realm. The Host half always provides the carrier-neutral RPC and exact `GET`/`HEAD`/`POST` route registries. When a Web carrier is present it also owns the sole `/api` route, Fetch bridge, browser authentication, and Host/Origin checks; a shell-owned carrier dispatches the shared Fetch handler directly. 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 [`qilin-client-file-upload`](../file-upload/README.md).
31
+
32
+ -----
33
+
34
+ <a id="browser-authentication-and-request-trust"></a>
35
+ ## Browser authentication and request trust
36
+
37
+ Every Host RPC method and WebSocket stream requires one browser session; there is no method-specific loopback tier. Whichever authentication owns the deployment supplies that session. Each process mints a random launch token, and `qilin-web-app` prints and opens the application entry URL carrying it — `GET /workspace?token=...`. `frontend-static` delegates every configured index request to `ctx.connection.authorizeIndex`, which accepts that token only on `GET /workspace`, writes an authority-bound signed cookie, and redirects to the clean entry path; a missing, expired, malformed, or wrong-authority cookie then returns 401 before RPC dispatch. Static assets and configured public documents remain public. The HTTP carrier accepts no query token outside the entry exchange and no Authorization-header token. `src/web-entry.ts` authors the entry path as `WEB_ENTRY_PATH` and `ctx.connection.entryPath` publishes it, so an index server mounts the same value the handoff targets.
38
+
39
+ `ctx.connection.session` is the seat where an authentication capability installs an account-session authority. `install(authority)` seats one `ConnectionSessionAuthority` and returns the disposer releasing it; a second installation fails the installing plugin's load, because two session authorities cannot compose. The authority owns three verdicts: `authorizeIndex` owns the browser response for a gated index request, `isPublicApiRequest` marks the requests that belong to the authentication surface itself and need no session, and `verify` checks the request's session. While an authority claims the seat, `requestRejection` requires `verify` for every `/api` request it does not declare public, and `authorizeIndex` runs the launch-token exchange first — cleaning the printed URL into the entry redirect — then serves a gated index only for a verified session. Without an authority the device cookie stays the only browser credential, which is what a deployment with the gate disabled keeps.
40
+
41
+ The cookie signing secret is the owner-scoped `client-connection/browser-session` grant record in `ctx.credentials`. The local provider persists it in `$QILIN_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.
42
+
43
+ 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. `qilin 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).
44
+
45
+ Every admitted request speaks for one Peer, the operator. `ctx.connection.operator` is that `PeerScope`: its `ctx` is a Kylin scope that owns connection-lifetime registrations and is disposed with the Connection. `ctx.connection.admit(request)` runs the trust and authentication checks and answers with the rejection status or the operator; the `/api` route and the Gateway's WebSocket upgrade admit through it, and every RPC handler receives the Peer of its call. `OperatorPeer` is exported so a composition without Connection, such as the Gateway's in-process carrier, owns an operator scope with the same contract.
46
+
47
+ Authenticated shared HTTP requests pass through the `connection/request` waterfall before body transfer. A listener may refuse new requests or await `next()` through response completion; removing its owning fiber removes admission behavior. Desktop uses this hook to lock new API work during an approved installation without canceling already-admitted work. WebSocket stream ownership remains with API Gateway.
48
+
49
+ <a id="connection-generation"></a>
50
+ ## Connection generation
51
+
52
+ API Gateway Client registers the internal `$events` logical stream as the sole generation source, independently of whether any `$on` listener exists. The Host attaches all incremental listeners in the API Remotes source factory, then sends one `{ type: 'ready', clientId, host: { home } }` item before events. `ConnectionController` publishes that generation and calls `onConnected` only after the ready item arrives, so baseline acquisition cannot race ahead of incremental observation.
53
+
54
+ An ended `$events` stream, a Remote stream error, a non-ready opening item, or a malformed event item invalidates the current generation. A pending handshake logs a slow-Host warning after 3 seconds and logs the readiness timeout and aborts after 15 seconds by default, including time spent waiting for the physical socket. The source must stop delivery, release resources, and settle after cancellation before a replacement starts; late readiness from a cancelled source cannot publish a generation. While the browser reports network availability, the controller publishes `connecting` and retries with 50%–100% jitter under caps of 500ms, 1s, 2s, 4s, 8s, and 10s, continuing at the final cap until recovery. Every retry asks Gateway to replace the physical WebSocket once and reopens `$events`. The [continuous recovery decision](../../../.agents/notes/implemented/bug-fix/2026-09-05-continuous-client-recovery.md) owns the deadlines and retry policy.
55
+
56
+ `ctx.connection.reconnect()` interrupts active work, resets the sequence, and starts retry 1 immediately. Browser `offline` aborts active work, publishes `disconnected`, and suspends automatic attempts; the next `online` transition resets the sequence and starts at the 500ms tier. Only a ready item publishes `connected`. Gateway mux owns no independent retry schedule.
57
+
58
+ Set the Host Connection row's `config.recovery` to override retry caps, the growth factor, or handshake warning and cancellation times; the [configuration catalog](../../../docs/config-catalog.md#qilin-agentclient-connection) lists accepted fields. The Host validates these values and injects them into each served page. The Client validates the bootstrap data before providing Connection and uses those defaults when Gateway starts its loop; explicit `start()` timing overrides take precedence. The growth factor must be finite and at least one. Readiness, failure, cancellation, or a hard deadline that occurs before the warning cancels that warning. Reload the page after changing Host recovery configuration.
59
+
60
+
61
+ <a id="model-experience"></a>
62
+ ## Model Experience
63
+
64
+ None, as the wire consumer layer moves already-composed messages between browser and host; nothing here reaches a model request.
65
+
66
+ #### KV Cache effect
67
+
68
+ None; this package neither assembles nor sends a provider request.
69
+
70
+ ## Known Limitations and Deferred Work
71
+
72
+ <a id="known-limitations-and-deferred-work"></a>
73
+
74
+ - **Buffered `/api` routes retain each request body in memory** — `maxRequestBodyBytes` (default 300 MiB, sized for the default 200 MiB aggregate image limit after base64 expansion plus envelope headroom) bounds ordinary image and RPC envelopes. Opt-in streaming routes receive backpressured chunks and bypass the aggregate cap; route implementations own persistence, cancellation, and any storage quota.
75
+ - **The browser cookie is not marked `Secure`** — loopback HTTP is the shipped transport, so exposing the same authority over plaintext networking can expose the bearer cookie in transit.
76
+ - **The device cookie has no logout operation** — clearing the browser cookie ends one browser session; deleting the owner credential record and restarting `qilin` revokes every session.
77
+
78
+
79
+ <a id="dev-note"></a>
80
+ ### Dev Note
81
+
82
+ <details>
83
+ <summary>Working context for maintainers — click to expand</summary>
84
+
85
+ None.
86
+
87
+ </details>
package/README.zh.md ADDED
@@ -0,0 +1,87 @@
1
+ ---
2
+ description: "Web GUI 的浏览器-Host 线层:Remote RPC、带重连的事件流投递、精确 Fetch 路由、/api HTTP 桥与浏览器信任栅栏。"
3
+ kind: "package-reference"
4
+ ---
5
+
6
+ # @qilin-agent/client-connection
7
+
8
+ [English](README.md) | 中文
9
+
10
+ ## 概述
11
+
12
+ 本包承载浏览器到 Host 的 Remote 调用、精确 Fetch 响应与 connection generation。Client 插件挂载 `ctx.connection`,其中包含当前页面的 loopback 状态、通用 RPC、当前 generation 及其 Host 信息、可观察的恢复状态、立即重连命令,以及单一 generation source 的注册点。source 报告 ready 后 generation 才可见;source 结束、失败、被撤回或显式 stop 都会清空它,再由 `ConnectionController` 执行重试策略。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [浏览器认证与请求信任](#browser-authentication-and-request-trust)
18
+ - [Connection generation](#connection-generation)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与暂缓事项](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ 静态桌面页面可以通过 `__QILIN_TRANSPORT__.streamBaseUrl` 提供其所拥有 Host 的 HTTP origin。Gateway 将该 origin 用于 WebSocket,HTTP 传输仍独立选择。桌面载体负责认证;仅设置 origin 不会授予访问权限。
29
+
30
+ 浏览器通过 HTTP POST 执行 Remote 一元调用;API Gateway 自己拥有 `/api/remote.mux` WebSocket 及其逻辑流。由 shell 持有的组合通过 `connection.rpc.open` 提供等价的 Remote 流(包括流的上行),不打开 WebSocket。浏览器插件读取页面 transport、恢复设置与 location,再委托 `installConnection(ctx, options)`。持有自身载体的组合可以直接调用同一个安装函数;整机客户端测试档就是这一消费者。每次调用都会创建一个归所属 Context 的服务,因此同一 realm 中的多棵 Client 树可以使用不同载体。Host half 始终提供与载体无关的 RPC 注册表和精确 `GET`/`HEAD`/`POST` 路由注册表。存在 Web 载体时,它还持有唯一 `/api` route、Fetch bridge、浏览器认证与 Host/Origin 校验;由 shell 持有的载体则直接分派共享 Fetch handler。每条精确路由会在 bridge 读取任何字节前声明缓冲或流式请求体处理方式。Typert Gateway 认领生成的 Remote endpoint,功能包注册 Session 日志下载、原始文件上传等非 JSON 响应,未认领的请求返回 404。Loopback hostname 判定只供浏览器侧当前页面状态使用,留在包内。浏览器原始请求体传输由 [`qilin-client-file-upload`](../file-upload/README.zh.md) 提供。
31
+
32
+ -----
33
+
34
+ <a id="browser-authentication-and-request-trust"></a>
35
+ ## 浏览器认证与请求信任
36
+
37
+ 每个 Host RPC 方法和 WebSocket stream 都要求同一个浏览器会话,不存在按方法区分的 loopback 层;该会话由拥有本次部署的认证方式提供。每个进程生成一个随机启动令牌,`qilin-web-app` 打印并打开携带它的应用入口 URL——`GET /workspace?token=...`。`frontend-static` 把每个配置的 index 请求交给 `ctx.connection.authorizeIndex`,后者只在 `GET /workspace` 接受该令牌,写入绑定 authority 的签名 cookie,再重定向到干净的入口路径;缺失、过期、畸形或 authority 不匹配的 cookie 随后会在 RPC 分发前得到 401。静态资源与配置的公开文档保持公开。HTTP 载体不在入口交换之外接受 query token,也不接受 Authorization header token。入口路径由 `src/web-entry.ts` 以 `WEB_ENTRY_PATH` 定义,并由 `ctx.connection.entryPath` 发布,因此 index 服务器挂载的值与交接目标始终是同一个。
38
+
39
+ `ctx.connection.session` 是认证能力安置账户会话 authority 的席位。`install(authority)` 安置唯一的 `ConnectionSessionAuthority` 并返回释放该席位的 disposer;第二次安置会让安装方插件加载失败,因为两个会话 authority 无法组合。authority 拥有三项判定:`authorizeIndex` 拥有受门禁 index 请求的浏览器响应,`isPublicApiRequest` 标记属于认证表层本身、无需会话的请求,`verify` 校验请求的会话。authority 占据席位期间,`requestRejection` 对该 authority 未声明为公开的每个 `/api` 请求都要求 `verify` 通过;`authorizeIndex` 先执行启动令牌交换——把打印出的 URL 清理为入口重定向——再只为通过校验的会话提供受门禁 index。没有 authority 时,设备 cookie 仍是唯一的浏览器凭据,这正是禁用门禁的部署所保留的行为。
40
+
41
+ cookie 签名密钥是 `ctx.credentials` 中由 `client-connection/browser-session` 拥有的 grant 记录。本地提供方把它持久化到 `$QILIN_HOME/.credentials.yaml`;`BrowserAuth` 在 Connection 激活期间加载或创建该记录,并把密钥留在内存中,因此请求认证同步执行。删除或替换该记录会在下一次 Connection 激活时生效。cookie 携带绝对签发与过期区间,`cookieMaxAgeDays` 默认设为 30 天,并在确定性名称与签名 payload 中同时绑定规范化 hostname 和 port。它是 host-only、`Path=/`、`HttpOnly`、`SameSite=Strict`;随附服务器使用 loopback HTTP,因此刻意不设置 `Secure`。
42
+
43
+ 认证之前,每个请求仍经过 `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。`qilin 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)。
44
+
45
+ 每个被接纳的请求都代表同一个 Peer——操作者。`ctx.connection.operator` 就是这个 `PeerScope`:其 `ctx` 是拥有连接期注册的 Kylin scope,随 Connection 一起释放。`ctx.connection.admit(request)` 执行信任与认证检查,以拒绝状态或操作者作答;`/api` 路由与 Gateway 的 WebSocket 升级都经它接纳,每个 RPC 处理器都收到本次调用的 Peer。`OperatorPeer` 对外导出,供没有 Connection 的组合(例如 Gateway 的进程内载体)以同一约定拥有一个操作者 scope。
46
+
47
+ 通过认证的共享 HTTP 请求在传输请求体之前经过 `connection/request` waterfall。监听器可以拒绝新请求,或等待 `next()` 直到响应完成;释放所属 fiber 会移除准入行为。Desktop 使用此扩展点,在已批准的安装期间锁住新的 API 工作,而不取消已接纳的工作。WebSocket 流仍由 API Gateway 负责。
48
+
49
+ <a id="connection-generation"></a>
50
+ ## Connection generation
51
+
52
+ API Gateway Client 把内部 `$events` logical stream 注册为唯一 generation source,与有无 `$on` 订阅无关。Host 在 API Remotes source factory 同步挂好所有增量 listener 后,先发送唯一 `{ type: 'ready', clientId, host: { home } }` 项,再发送事件。`ConnectionController` 仅在收到该 ready 项后发布 generation 并调用 `onConnected`,因此 baseline 不会跑在增量 listener 前面。
53
+
54
+ `$events` 结束、返回 Remote stream error、收到非 ready 首项或畸形事件项,都会使当前 generation 失效。默认情况下,挂起的握手在 3 秒后记录 Host 响应缓慢告警,在 15 秒后记录就绪超时并中止,包含等待物理 socket 的时间。取消后,source 必须停止投递、释放资源并结束,替换 source 才能启动;已取消 source 迟到的 ready 不能发布 generation。浏览器报告网络可用时,Controller 发布 `connecting`,并在 500ms、1s、2s、4s、8s 与 10s 上限内采用 50%–100% 抖动重试,达到终档后继续尝试直到恢复。每次重试都要求 Gateway 替换一次物理 WebSocket,再重开 `$events`。[持续恢复决策](../../../.agents/notes/implemented/bug-fix/2026-09-05-continuous-client-recovery.zh.md)规定握手期限与重试策略。
55
+
56
+ `ctx.connection.reconnect()` 会中断活动工作、重置序列,并立即开始 retry 1。浏览器 `offline` 会中断活动工作、发布 `disconnected` 并暂停自动尝试;下一次 `online` 转换会重置序列并从 500ms 档开始。只有 ready 项会发布 `connected`。Gateway mux 不拥有独立重试调度。
57
+
58
+ 可通过 Host Connection 行的 `config.recovery` 覆盖重试上限、增长因子或握手告警与取消时间;[配置目录](../../../docs/config-catalog.zh.md#qilin-agentclient-connection)列出接受的字段。Host 校验这些值并注入每个已服务页面。Client 在提供 Connection 前校验启动数据,并在 Gateway 启动循环时采用这些默认值;显式传给 `start()` 的时序覆盖优先。增长因子必须是至少为一的有限数。若就绪、失败、取消或硬期限先于告警发生,该告警会被取消。修改 Host 恢复配置后需重新加载页面。
59
+
60
+
61
+ <a id="model-experience"></a>
62
+ ## 模型体验
63
+
64
+ 无。协议消费层只在浏览器与主机之间搬运已经组合好的消息;这里没有任何内容进入模型请求。
65
+
66
+ #### KV Cache 影响
67
+
68
+ 无;该包既不组装也不发送提供方请求。
69
+
70
+ ## 已知限制与暂缓事项
71
+
72
+ <a id="known-limitations-and-deferred-work"></a>
73
+
74
+ - **缓冲型 `/api` 路由会把每个请求体保留在内存里**:`maxRequestBodyBytes`(默认 300 MiB,按默认 200 MiB 图片总量上限经 base64 膨胀加信封余量得出)限制普通图片与 RPC 信封。显式启用的流式路由接收带背压的分块并绕过总量上限;路由实现负责持久化、取消与存储配额。
75
+ - **浏览器 cookie 不带 `Secure`**:随附载体是 loopback HTTP;若部署经明文网络暴露同一 authority,bearer cookie 可能在传输中泄露。
76
+ - **设备 cookie 没有 logout 操作**:清除浏览器 cookie 会结束单个浏览器会话;删除 owner 凭据记录并重启 `qilin` 会撤销全部会话。
77
+
78
+
79
+ <a id="dev-note"></a>
80
+ ### 开发备注
81
+
82
+ <details>
83
+ <summary>维护者工作上下文——点击展开</summary>
84
+
85
+ 无。
86
+
87
+ </details>