Files
RAG/docs/adr/0007-split-local-and-model-workers.md
YoVinchen ecdb10c37a
Some checks failed
verify / verify (push) Has been cancelled
Make the governed RAG evidence path executable end to end
Separate local parsing from model indexing, bind review decisions to immutable manifests, persist vectors behind active profiles, and expose retrieval, chat, evaluation, and document workflows through the React workbench.

Constraint: Live Bailian authentication currently fails for all three configured capabilities

Rejected: Direct upload-to-embedding flow | bypasses local review and manifest binding

Confidence: high

Scope-risk: broad

Directive: Keep private-data deployment blocked until authentication, RBAC, and separate database roles land

Tested: make verify; fresh and replay Docker document smoke; worker recovery smoke; frozen synthetic evaluation; migration 0003-0004 roundtrip

Not-tested: Successful live Bailian calls, OCR, real multi-user authorization
2026-07-13 05:58:11 +08:00

4.7 KiB
Raw Blame History

ADR-0007拆分本地解析 Worker 与模型 Worker 的信任边界

  • 状态: accepted
  • 日期: 2026-07-13

背景

文档入库同时涉及两类高风险资源:原始上传文件,以及访问云模型的能力。若一个通用 Worker 同时挂载上传卷、内部模型 Token 和模型网络,那么解析器漏洞、恶意 DOCX/PDF 或任务 payload 缺陷可能把未经审批的原文直接发送到云端。仅靠业务代码中的状态判断,无法形成可独立验证的 最小权限边界。

本项目仍采用模块化单体和同一个后端镜像拆分的是运行身份、capability、网络、卷和 Secret 不是拆成两套业务服务或数据库。两个 Worker 当前仍共用同一个 PostgreSQL 应用角色,因此该拆分 只隔离上传卷、模型 Token 和网络能力,不构成数据库授权边界。

决策

Compose 运行两个互斥能力的长期 Worker

边界 worker-local worker-model
capability document_parse embedding
数据库网络
内部模型网络
公网 egress 无;只能访问 internal model-gateway
上传卷
数据库 app Secret
model-gateway Token Worker 身份 Token
百炼 API Key

model-gateway 是唯一持有百炼 API Key 和普通 egress 网络的进程;它不连接数据库、不挂载上传 卷,也不发布宿主机端口。worker-model 只能发送数据库中已通过 manifest 审批的 cloud_text 不能读取原始文件。worker-local 能读取隔离上传卷,但没有模型网络与 Token即使解析器被恶意 文件影响,也缺少直接调用模型的凭据和路由。

两个 Worker 均以非 root 后端用户运行,根文件系统只读,使用临时 /tmpno-new-privilegescap_drop: ALL,不暴露端口。它们共用 PostgreSQL 任务队列,但领取 SQL 只匹配各自 required_capability。心跳、失败回写、阶段提交和最终激活必须匹配 job_id + lease_owner + lease_token 且租约仍有效,避免旧进程覆盖已重领任务。

上传卷初始化由一次性 upload-init 完成。该容器临时以 root 运行,但 network_mode: none、根 文件系统只读,只保留初始化目录所需的最小文件能力,成功后正常 Exited (0);长期 API 和 worker-local 不获得这些额外 capability。

被否决方案

  1. 单一通用 Worker 同时挂卷和模型 Token 部署简单,但把未审核原文与云出口放在同一 攻击面,违背 manifest 审批门禁。
  2. 只靠 Python if review_state == CLOUD_APPROVED 状态判断仍然需要,但不能替代网络、 Secret 和文件系统的纵深隔离。
  3. 每种任务拆成独立代码仓库/数据库: 当前单机规模没有对应收益,会引入分布式事务、双写、 部署和备份复杂度。
  4. worker-model 直接访问百炼公网: 会把供应商协议、Key 和公网出口扩散到业务进程, 无法集中轮换、限流和脱敏错误。

影响

  • 优点:原始文件与云调用能力不能在单个长期 Worker 中汇合Compose 契约可直接验证卷、网络 和 Secret不同任务可独立扩缩容。
  • 代价:需要维护两个 Worker 服务和 capability 路由;跨阶段任务只能通过数据库中的已审批 工件交接,不能依赖本地临时文件。
  • 限制Docker internal network 是重要纵深防线但不是形式化沙箱API 与两个 Worker 当前共享 数据库应用角色,文档 Actor 也仍是服务器固定的 synthetic 身份。真实多用户/私有数据部署必须 先落地认证、对象级 RBAC、分离数据库角色或受控存储过程并结合主机防火墙、出口 allowlist、 集中 Secret Manager、容器运行时策略和审计。

后续约束

  • 新增 OCR Worker 时默认归入本地高风险解析边界,除非独立 ADR 证明它需要模型出口OCR 结果仍须重新生成并审批 outbound manifest。
  • 任何服务若要同时获得上传卷与模型网络/Token必须先做威胁建模、更新 ADR 并增加 Compose 契约测试,不能通过临时排障静默扩大权限。
  • 新任务类型必须声明唯一 required_capability,并验证错误能力的 Worker 无法领取。
  • DOCUMENT_NAMESPACE_MODE 只能由服务端部署配置选择 fakebailian synthetic 命名空间; 请求不得携带任意 scope 绕过授权。真实多租户上线前必须用认证/RBAC 替代固定 Actor。
  • 变更 Worker 网络、卷、Secret 或部署拓扑时,必须重跑 document pipeline Docker E2E、租约 fencing smoke、Secret 扫描和 Compose 安全契约。