# 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 后端用户运行,根文件系统只读,使用临时 `/tmp`、`no-new-privileges` 和 `cap_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` 只能由服务端部署配置选择 `fake` 或 `bailian` synthetic 命名空间; 请求不得携带任意 scope 绕过授权。真实多租户上线前必须用认证/RBAC 替代固定 Actor。 - 变更 Worker 网络、卷、Secret 或部署拓扑时,必须重跑 document pipeline Docker E2E、租约 fencing smoke、Secret 扫描和 Compose 安全契约。