ADR-0038:独立评审补隔离卫生与 OS 强制边界(2026-10-11)

状态:用户确认;已实现并随 0.6.0 发布(2026-10-23;CHANGELOG 0.6.0 定版节未列条目——docs/history.md P4 条注记,2026-10-24 修正)。决策 #52。仅适用于 lzy 发起的独立评审及复核,不改变 drive/queue 默认执行方式。2026-10-11 增修订节(分级保证,用户拍板)。

背景与取舍

ADR-0031 的候选快照、独立 HOME、provider 允许表与转录检查能够减少污染,但父环境继承、绝对路径访问和任意网络连接仍可跨出隔离面。用户确认先补齐隔离卫生(A),再为独立评审建立 OS 强制边界(B);暂不投入运行不可信代码的容器/VM(C)。评审读写需求较窄,先在该入口承担平台兼容与网络准入成本,不能把成功启动或事后转录检查当作 OS 隔离证明。

契约

边界与验收

本决策不建立容器/VM,不保证预算硬帽、评审正确性或其他同权限宿主进程无法篡改账本。既有候选身份、转录、用量与事后复核继续保留。验收须分别记录真实进程下的允许读取/写入/API 请求、拒绝越界文件与网络访问、子进程继承、环境污染排除、正常/中断清理及 SIGKILL 后恢复;未运行或未支持的平台不能计为通过。发布状态与实测结果另行记录,不由本 ADR 宣称。

修订节:分级保证(2026-10-11,用户拍板)

触发:原契约「OAuth-only 认证仍不可用」与「后端缺席拒启动」叠加后,lzy review run 仅在「macOS ∧ Seatbelt 探针通过 ∧ 个人 key 腿在场」单格形态可用。实际分发中仅 start-plan(OAuth 账号认证)是默认形态——统一门义务评审对多数用户物理不可达,goal loop 的 finish 面死锁;这是对 0.5.0 已可用行为(M3 三评审实证)的回归,且 doctor 对该形态无预诊断。

拍板:B 后端=增强,不是门槛。 评审运行分两级,条件由机器钉死、无用户手选降级面:

配置错误不降级:provider env 在场但不可读/过滤后零有效腿仍 fail-closed 拒(provider-config-unreadable/no-sandbox-provider 原判据不变)。「有 key 腿但无 OS 后端」(Linux/Windows)同样落 app-layer tier——后端缺席是平台事实而非用户选择,不再构成拒启动。app-layer tier 不冒充 OS 隔离:评审判决消费面(统一门回执、报告)读到 isolation.level=app-layer 时按 0.5.0 保证面口径引用。

二改:OAuth-only 完成面(2026-10-11 深夜,用户追问「OAuth-only 改动呢?」暴露的缺口)

一改的救济没有覆盖它自己的触发主体:app-layer tier 定义为「隔离 HOME+envAuth」,而评审 preflight 的认证门(M2 N4#8)只认 provider env——纯 OAuth(仅 start-plan、无可用 key 腿)机器在两个 tier 下都被 no-auth 拒;且「配置在场但零有效腿」被一改钉成 fail-closed 拒,恰好命中默认形态(标准 provider 配置天然含禁用腿)。本机实弹(p0-ledger-fix 收口)撞线证实。

拍板三项:

  1. 空交集≠配置损坏:provider 配置可解析但零 enabled+key∩允许表 腿=「无沙盒可用腿」的自然态,落 app-layer tier(不再 no-sandbox-provider 硬拒);仅不可读/结构不可识别维持硬拒(resolveSandboxProviderConfig 返 null 语义)。
  2. app-layer tier 的 OAuth 凭据透传:tier=app-layer ∧ 宿主 OAuth 凭据(~/.zcode/v2/credentials.json)在场 ∧ 无可用 provider 腿 → 复制凭据进隔离 HOME(0600,随 runDir 清理登记焚毁,doctor/residues 与 provider 副本同族点名)。暴露中性论证:app-layer 无 OS 边界,评审会话与宿主同 uid,本可直读宿主同权限文件——拒绝透传不减少任何暴露,只把整个默认用户群挡在门外;沙盒 tier 维持 provider-env-only(Seatbelt 内凭据复制=真暴露,oauthFallback 只认 app-layer)。运行档 isolation.auth 如实记 oauth-copy|provider-env。
  3. 认证门语义:envAuth 首判保持(既有夹具面不破);oauthFallback 为增路非替代。auth 判定注入面修正:deps.sandboxCapability 为函数时须调用(一改实现漏调用致注入面全部意外走 app-layer——同批契约测试断言同侧未 exposing)。

诚实记录:一改提交信息「仅 start-plan OAuth 评审面恢复可达」系过度声明——一改只恢复了「配置在场但不可沙盒化」机器;本二改才完成触发主体(纯 OAuth)的救济。

三改:转录读限标定(2026-10-15,沙盒首射活体证伪)

16 MiB 读取上限系设计期估值、从未经真会话检验(0.5.0 期评审全在无上限的 app-layer)。沙盒首射数据:duty 会话主转录 9.6 MiB 在限内通过;recheck 深验证会话(90+ 轮模型调用,转录尺寸 ∝ 轮数×上下文载荷)24–25.5 MiB 被上限按合同拒——fail-closed 落 invalid(isolation-breach),单轮烧 14–16 分且修复语义无从判读。标定:上限 16→64 MiB(4× 观测峰值余量);读取守卫族(regular 文件/拒链接/读取期变化检测/路径边界/maxBuffer 2×LIMIT 同步)全部不变。