Files
qinglong/docs/adr/ADR-0367-single-process-authenticated-local-run-http-api.md
T

49 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-0367:单进程、受认证的 Local Run HTTP API
- 状态:Proposed(实现完成,固定物理设备证据待补)
- 日期:2026-08-11
- 关联:QL-RFC-0001 D-45、D-46、D-49、D-52、D-175、D-257、D-269、D-278、D-279ADR-0347、ADR-0366
## 背景
Local 3.0 已有完整 Run、API Credential、Project Policy、安全审计、Owner Pepper 和 MCP 只读纵向链路,但部署用户仍没有稳定的 `/api/v3` HTTP 产品入口。直接复用 Legacy Express 会延续 Controller/Model/service-locator 架构;把 HTTP 放进 MCP package 会把 MCP SDK/Zod 带入普通 API 制品;另起 sidecar 又会让低内存路由器承担第二个 Node 进程和第二个 SQLite authority。
该能力满足 D-278 对新 package 的严格例外:它是默认关闭、独立交付的网络 listener 与安全入口,不是为了整理目录而拆出的 projection/codec 微包。它必须形成完整单进程 Profile,而不是只有一条 route 的壳。
## 决策
1. 新增 `@qinglong/local-api` 组合根,发布 `ql3-local-api`。它在同一 Node 进程中启动现有 Local Application 和 HTTP listener,复用 Application 已打开的唯一 SQLite operation authority;不得启动 sidecar、第二个数据库连接、scheduler 或 watcher。
2. `@qinglong/local-application` 增加可选、依赖反转的 product-surface port。默认未注入时构建、导入、启动、资源和制品闭包不变;注入时只能取得 `runs/apiCredentials/ownerPepper/projectPolicy/securityAudit` 五个窄 authority。Application 恢复完成并启动 scheduler/execution lifecycle 后才开放 surface;停机先撤 listener admission 并 drain,再停 scheduler/execution,最后关闭 SQLite。
3. 首个且唯一 route 为 `GET /api/v3/projects/{projectId}/runs/{runId}`operation/permission 固定为 `run.get`/`run.read`,与 Cluster API 同构。原始 path 必须是无 query、无 `%` 编码、无尾斜杠的 canonical ASCIIProject/Run ID 各最多 128 字符。任何 body、Transfer-Encoding 或非零 Content-Length 都在读取 body 前拒绝。
4. v1 listener 只允许 `127.0.0.1``::1`。远端访问必须由同机受管 TLS reverse proxy 终止;进程不信任 Forwarded/X-Forwarded-*,也不允许配置非 loopback 地址。LAN TLS、Unix socket 与代理身份绑定需要后续独立 ADR。
5. 每个请求必须执行 route resolution → exact Bearer credential → Project Policy → durable security audit → credential/pepper fence confirm → bounded Run read。认证不得缓存;撤销、版本、subject、有效期、secret digest、pepper key state/material digest 任一变化都在读取前失败关闭。
6. HTTP Run 响应复用 Runtime Core 的 `qinglong.run.get` 低敏投影并映射为 Cluster 同构 `{run}`;不存在和跨 Project 均为 404。request/ref/trigger/attempt、executor handle、错误摘要、Artifact/日志位置、Secret 和数据库 row 不出网。
7. transport 固定 header、URL、并发、请求时限、keep-alive、响应和 drain 上限。Edge 默认并发 4Standalone 默认 32;超载在认证前拒绝,避免攻击者制造无界 credential/Policy/SQLite 队列。
8. workspace hard cap 由 17 提升到 18,仅授权这个完整 deployable/authority package。新增 route、projection、认证 helper 或 client 必须进入该 package 的领域目录,不能继续增加 importer。
## 不采用方案
- **Legacy `back` Express route**:继续绑定 2.x Sequelize/Controller 与共享服务定位器。
- **MCP package 内增加 HTTP**:普通 API 制品会安装无关 MCP SDK,协议和攻击面混合。
- **独立 Local API sidecar**:低配设备承担第二个 Node RSS 和 SQLite connection;与单进程目标冲突。
- **把 listener 放进 Owner Console/CLI**:把短生命周期高权限 ceremony 变成长驻网络 authority。
- **默认把 HTTP 编进 headless Application artifact**:所有路由器即使不用 API 也支付 flash/审计面成本。
## 完成门
- [x] canonical route、认证拒绝/不可用、Policy deny/approval/allow、audit failure、credential fence、cross-Project 404、损坏 Run、过载、body-before-auth 和 graceful drain 均有定向测试;
- [x] 真实 loopback HTTP + SQLite + API Credential + Pepper + Policy + durable Audit E2E
- [x] headless Edge/Standalone 十二档现有 artifact 数字不回归;新增 API Profile 有独立 bytes/files/RSS 预算;
- [x] 18-package build/test、dependency/source boundary、Local image与完整 backend 通过;
- [x] GitNexus compare/detect-changes 通过(已跟踪 diff 为 risk `low`、affected processes `0`;本分支尚未纳入 index 的新增文件另由重建后的代码图与 package/source/import gates 覆盖);
- [ ] 固定物理路由设备 RSS/flash/并发报告完成前保持 Proposed,不宣称默认开放远程 API。
## 实现与验证证据(2026-08-11
- workspace 为 18 package、1,015 个 TypeScript source997 个位于领域子目录,18 个是受审 public/binary root entry。`local-api` 自身为 8 个 source,其中 7 个分布在六个内部领域目录,根只保留 `cli.ts`;当前没有 single-source 或 12 source 未登记薄包。
- `local-api` 定向门 15/15,包括真实 SQLite authority、API Credential、Owner Pepper、Project Policy、durable audit 与 loopback HTTP E2E,以及 Edge 并发 4 的过载拒绝和 accepted-work drain。
- Edge API artifact 为 5,073,844 bytes / 515 files / 54 loaded modules,启动 RSS 增量 12,517,376 bytesStandalone API artifact 为 5,073,988 bytes / 515 files / 54 loaded modulesRSS 增量 12,582,912 bytes,均低于 6 MiB / 640 files / 24 MiB 门。
- Runtime Core 的 bounded Run read projection 已成为纯叶子 subpathAPI 导入闭包不再加载 Tool Registry/SemVer,投影模块门为不超过 4 个 loaded modules。
- 十二档既有 Local artifact 全部 compatible;默认 Local Application image 静态闭包不含 `@qinglong/local-api`。package boundary、source/dependency firewall 均无 finding;完整 backend 为 1,156 pass / 2 条条件 skip / 0 fail。
- 尚欠的是固定物理路由器的 flash、冷启动/稳态 RSS 与并发 1/4 报告。该缺口只阻止 ADR 转为 Accepted,不回退已经验证的 loopback-only、默认关闭实现。