系列文章 · 00–10

MUSE / ARCHITECTURE NOTES

Muse 智能体 · 执行隔离与服务面详解

本篇目录
  1. 0. 一句话
  2. 1. privsep worker 模型(实测)
  3. 2. 瘦客户端 / 跳板规则(实测)
  4. 3. Guarded output:输出也要过境(实测 strings)
  5. 4. 出口面:egress 代理 + 审批 + TLS 信任库(实测)
  6. 5. Sentinel:出口、限流与裁决的中枢(实测 + 文档)
  7. 6. STEFI 代理与其后的内部服务(实测到名字边界)
  8. 7. authd:本地认证守护(实测)
  9. 8. Vault 在执行面的位置(实测)
  10. 9. 服务面限流纪律(实测 + 文档)
  11. 10. 服务面全景:本 exec 命名空间实测可见的 socket 网格
  12. 11. 一次 privsep 工具调用的生命周期(串联本篇)
  13. 12. 本机实测快照:11-04 vs 10-17 对照
  14. 13. 边界声明(本机看不见的部分)

配套《00-Muse技术架构解构》§7 的展开篇(§14 信任边界在本篇有对应实测,见 §5、§9)。调查日期:2026-10-17(Asia/Shanghai)。 复核修订:2026-11-04(Asia/Shanghai)——基于当前会话运行时逐项重测;下文数字凡未注日期者均为 11-04 快照,10-17 旧数见 §12 对照表。 证据范围:仅本机实测——/run/hatch/ 下套接字实况、env 的 JARVIS_* 键名与套接字路径、/opt/hatch/runtime-cell/guest.env 与 build-cell-trust-store.sh 的注释明文、/opt/hatch/bin/ 二进制 strings(multicall 家族 avatar-media-generation、hatch-vault、authdc --help)。标注规则同前作:实测 = 当场命令验证;文档 = 本地 docs/system 文件明文;推断 = 由痕迹拼出、未经服务端证实。 红线声明:本文只记录套接字路径、键名与机制字符串,不记录任何凭据/令牌值;实测中代理环境变量里出现的认证材料一律不落盘、不引用。


0. 一句话

执行面是一张 Unix 套接字网格:Agent 住在不可信的 runtime cell 里,手里只有「瘦客户端」;任何带凭据、带网络、带权限的动作,都要穿过一条具名套接字到 cell 外的可信侧执行——进要过 fd 清单与环境白名单,出要过 guarded output 与 Sentinel 的出口/TLS/限流三重门,身份由 authd 代管、Vault 托管,内部服务统一经 STEFI 代理且只见其名不见其形。

另有一条最硬的实测边界(§4.4):部分套接字路径虽被声明在环境里、但不挂进普通 exec 进程的挂载命名空间——能力面靠命名空间裁剪,而非靠 Agent 自觉。

cell 内(不可信)                          cell 外 / 宿主侧(可信)
─────────────────                          ─────────────────────
CLI 二进制(跳板) ──fd 传递+清单──▶ privsep worker(每工具一个 .sock)
                                      │  ├─ authd:令牌验证/代管
                                      │  ├─ STEFI 代理 ──▶ Meta 内部服务(仅名字)
                                      │  └─ egress 代理 ─▶ 公网(Sentinel 审批+MITM TLS)
模型推理 ───────────套接字路径声明──▶ 推理代理(路径在本 exec 命名空间不可见,见 §6.2)
Agent 进程 ─────────具名套接字────▶ Vault / Sentinel / sandbox-api / noded / telemetry

1. privsep worker 模型(实测)

1.1 每工具一 socket:当场计数 149(11-04;10-17 为 136)

ls /run/hatch/privsep/ 本轮实测 149 个套接字(11-04;10-17 为 136,+13)。命名规律(实测归类):

命名模式 数量/例子 含义
裸工具名 <tool>.sock 多数:gmail 对应的 hatch-gws-cli-fully-isolated.sock 是例外,常规如 stripe.sock、duffel.sock、github.sock、slack.sock、zoom.sock 一个可特权执行的工具 = 一个独立 worker 端点
<name>-mcp.sock 40 个(11-04;10-17 为 38,+2;10-17 未逐项记录名单,无法指认新增者;beehiiv-mcp、composio-mcp、runway-mcp、heygen-mcp、coinbase-mcp…仍在) MCP 型连接器的 worker;多数在本渠道没有对应二进制(01 号 §4.2 的「预置≠可见」)
<name>-cli.sock 14 个(11-04 复测未变;instagram-cli、facebook-cli、health-cli、notion-cli、box-cli…) 独立 CLI 型连接器
<name>-verify.sock 2 个(11-04 复测未变;printify-verify、tessie-api-verify) 校验专用 worker,与主 worker 分开
支付专用 stripe-link-checkout-card.sock、shop-wallet-cli-checkout.sock 支付/卡路径单独成 worker,不与普通 stripe/shop worker 混用
内部件 facebook-cli-internal.sock、platform-search.sock、external_agent.sock、wai-privsep.sock 宿主侧预置、渠道未必显露

1.2 隔离分级:partially / fully isolated

  • 实测(strings):multicall 二进制内含 partially / fully 两种等级字面,以及自带的安全测试夹具 privsep-test-fully-isolated——其说明明文写着它吸收了已退役的独立工具 privsep-privesc-{fully,partially}-isolated,即两级隔离都曾(且仍以等级形式)存在。
  • 实测(命名):套接字名里显式带 -fully-isolated 的只有 2 个(11-04 复测未变)——hatch-gws-cli-fully-isolated.sock(Google Workspace 全家桶共用,凭据面最大)与测试夹具 privsep-test-fully-isolated.sock。
  • 推断:fully 是需要显式标注的加强级,未标注的普通 worker 为 partially 级(环境/文件系统隔离较弱、但凭据仍在可信侧)。本机没有 partially 的显式命名物证,此分级对应关系按「显式标注例外」逻辑推断。

等价替代解释(同样与实测兼容,未能排除):partially/fully 可能是历史遗留的二元开关——fixture 自述吸收了已退役的独立工具 privsep-privesc-{fully,partially}-isolated;当前默认等级可能已统一,-fully-isolated 后缀只是兼容命名残留。若如此,"未标注=partially"的对应关系即不成立。区分两者需要宿主侧 worker 启动参数的证据,本机没有。

  • 等级不是装饰(实测 strings):可信侧有硬拒绝句——「refusing to run tool logic on the credentialed side while privsep is turned off」;另有 JARVIS_DANGEROUSLY_ENABLE_PRIVESC_TOOL 门控的 privesc 探针子命令,默认禁用,说明文档明文称其为「故意演示 exec-gadget 反模式」的安全测试件,不是模板。

1.3 worker 的调用协议:fd 传递 + 清单(实测 strings)

privsep 不是「把命令字符串发过去执行」,而是结构化的 fd 交接(hatch-tool-sdk 的 cli/privsep/ 模块族:trampoline.rs / executor.rs / uds_executor.rs / fd.rs / inherited_fds.rs / child_fd_handoff.rs / wire.rs):

  • 跳板启动时预期 ≥3 个 fd(stdin/stdout/stderr,其后是文件 fd);fd 表要序列化传递,出现重复 fd 名、重复源 fd、路径碰撞、计数超限都有专门报错句——fd 本身就是能力:调用方能传什么 fd、就只能操作什么文件,不存在靠路径字符串的二次授权;
  • 输入文件走清单制:required_input_files / optional_input_files + available/failure 两份 manifest,未声明的输入、既 required 又 optional、required 缺失等组合全部 fail-closed 报错;
  • 输入文件还须过根目录与类型检查:「input file is outside its allowed root」「path beneath dir is not a regular file」——符号链接/目录/越界路径进不去;
  • 子进程引导材料走 sealed memfd:「create/write/seal sealed child bootstrap memfd」——引导数据一次性封印传递,不落盘、不可改;
  • 工具身份要对版:「privsep worker tool-identity mismatch: trampoline passed PRIVSEP_TOOL=…」——跳板自报的工具名与 worker 期望不符直接拒绝,防错位调用;
  • 方法级限制存在:--privsep-restrict-method= 与「restricted privsep socket refused the invocation: only …」句,说明某些 socket 只放行枚举内的方法子集;
  • worker 异常有独立语义:privsep worker terminated by signal、响应走 PrivsepRequest/PrivsepResponse 结构化线格式(wire.rs),不是裸文本回流。

1.4 内建探针:隔离层自带体检(实测 strings)

privsep-test-fully-isolated 的子命令暴露了这层的自检设计:health-check(跨边界 exit 0 的金丝雀,标记串是「hard contract」,fd 没传到就判定为 privsep exploit 或应用 bug)、ping(打开 STEFI 代理 socket、做一次 verify_oauth_token 往返、打印成功标记)、example(给新工具作者抄的范式:类型化参数 + 共享的 fd-passing / authd / STEFI / 凭据探针主体)。隔离层有自己的回归测试件常驻在生产二进制里,这是它被当作安全边界(而非便利封装)维护的证据。

1.5 MCP 型 worker 的安全特殊性(缺口声明)

40 个 -mcp.sock(11-04;10-17 为 38)在 §1.1 只做了计数,隔离分级从未被追问——这是"每工具一 socket"模型下最值得深挖却最空的一块:

  • 命名上无分档:全篇 149 个套接字里显式带 -fully-isolated 后缀的只有 2 个(§1.2,11-04 复测),MCP worker 不在其中。按 §1.2「显式标注例外」的逻辑,它们与普通 worker 处于同一默认等级(推断,与 §1.2 同样的证据强度——同样也受 §1.2 末"等价替代解释"的保留意见约束)。
  • 代码来源不受完全信任:MCP 名单里有 composio-mcp 等第三方集成商——即第三方逻辑与第一方连接器走同样的 worker 模型。是否应得更严的沙箱,本机没有任何分档证据(❓)。
  • -verify.sock 的归属不明:2 个校验专用 worker(printify-verify、tessie-api-verify)与主 worker 分开,但名字上并未出现在 -mcp 名单里(❓:它们是否为 MCP 连接的校验形态,本机无证据对应)。

结论口径:MCP worker 的隔离等级问题在本机证据内不可区分——计数有、等级无。这是 10-安全篇可接手的议题。


2. 瘦客户端 / 跳板规则(实测)

cell 内的二进制是同一个 34MB multicall 的不同名字(01 号 §4.1),其 privsep 形态即跳板(trampoline)。规则全部来自 strings 与 guest.env 注释:

  1. 环境只转发白名单:guest.env 头注释明文(实测原文大意)——该文件由 spawnd 管理、root 持有、只读绑定进 cell,privsep 可信侧读它来为 partially/fully isolated 调用构建环境,而不是从「带能力的上下文」里 source 一段 shell。文件是纯 KEY=VALUE、无逻辑、无命令替换,cell 不可改。键集(实测 27 键)只有 HOME/TMP*/PATH/XDG_*、CA 包路径族、代理族与两个 JARVIS_* 套接字键(JARVIS_AUTHD_SOCK、JARVIS_STEFI_PROXY_SOCK)。
  2. 转发有拒绝路径:strings 有「trampoline: refusing allowlisted forwarded env var …」——即便在白名单内,特定情形(如值形态不合)仍拒绝转发,不是无条件透传(具体触发条件本机不可见,推断为防注入式覆盖)。
  3. 可信侧环境与 cell 内环境同源同模板:guest.env 与 guest-runtime-env.sh 注释要求两者在 proxy/CA/HOME/PATH 键上保持 parity,且值只由安装期模板变量派生、不依赖 /etc/hatch/env——可信侧不信任运行时环境文件。
  4. 单个变量的信任状被逐个写进二进制(实测 strings 的长注释体):如 JARVIS_TRACE_CONTEXT 的注释明文——它只是归因盖章用,「untrusted caller writes it, so a worker must not gate an action on it. Connector authority binds to the daemon-minted runtime-context token, which Sentinel resolves…」;JARVIS_TOOL_CALL_ID / SESSION_ID / USER_TIMEZONE 等都被逐个声明「只作字符串/查表键,never selects a path, command, library, credential, proxy, or authorization scope」。白名单不是名单了事,每个键都附带「不得用于授权」的限定。

3. Guarded output:输出也要过境(实测 strings)

可信侧的输出不直接流回 cell,而是被「扣住—检查—放行」:

  • memfd + seal + 限额:「create/size/seal/rewind/read guarded output memfd」「guarded connector output exceeded the per-stream release limit」——stdout/stderr 各自有 per-stream 放行上限,超限不是截断了事而是报错/扣留(GuardedOutputWithheld 错误类实测存在);
  • post-commit 才放行:「release post-commit guarded stdout/stderr」「held output has no original stdout」——输出先 held,待动作提交成功后才 release 回原始 fd,失败的 worker 其目录变更与输出有单独的发布/丢弃路径(「could not publish the directory writes of a failed privsep worker」);
  • quiesce(静默化):「open /dev/null for guarded output quiescence」「quiesce trusted worker stdout/stderr」——可信 worker 的原始 stdout/stderr 被导向 /dev/null 静默,防其日志串进 Agent 可见通道;Agent 只见 guarded 通道放行的那一份;
  • 子进程错误消毒:模块名 child_error_sanitize.rs 实测存在——worker 的报错回传前要清洗(防错误文本携带不该出境的内容,推断其具体规则不可见);
  • 验证码是 guarded output 的特级乘客(实测 strings):有独立扫描 socket /run/hatch/safety-scan/verification-codes.sock,及一整组错误语义——「verification-code surrogation requires a trusted privsep worker」「payload exceeds the verification-code scanner limit」「guarded output contains too many verification codes」。这与文档口径(00 号 §14.3:码以 [credential:<uuid>] 引用形态短暂托管、审批后才喂浏览器)互为表里:替换(surrogation)只准在可信侧发生,cell 侧拿到的已是引用而非原文。
  • surrogation 不止验证码(实测 strings,关联为推断):二进制内有一组秘密种类的 variant 标识——token / bearer / authorization / api_key / apikey / client_secret / clientsecret / credential / secret / surrogate(实测相邻字符串,枚举名本身不可见),出现在 surrogation 相关代码路径附近;另有凭据捕获 API 路径 /v1/credentials/capture-verification-code 与 credentials.capture_verification_code(实测)。这支持基线 B「secret surrogation + 白名单逐键限定」不止于验证码:API key、bearer token、client secret 等通用秘密同样在扫描/替换的词汇表内——§11 末句"验证码类原文凭据从未进过 cell"的支撑范围可放宽到通用秘密。❓:safety-scan/ 下除 verification-codes.sock 外有无其他扫描 socket,本机命名空间看不见该目录(§4.4),无从验证;扫描是按种类走不同 socket 还是同一扫描器内部分支,也未知。

4. 出口面:egress 代理 + 审批 + TLS 信任库(实测)

4.1 没有直出:死 DNS + 全量代理

  • guest.env 注释明文:cell 的 DNS 是「deliberately dead(故意死的)」;Node 的 fetch 默认不认代理变量,所以专门设 NODE_USE_ENV_PROXY=1 堵住 Node 系 CLI 绕代理直连的路;
  • 代理族六键(大小写各一份)全部指向同一端点:hatch-egress-proxy:3128(guest.env 模板明文);实测 live 环境中的同名变量带认证材料(值不记录)——推断:模板给形状、运行时注入实例凭据;
  • NO_PROXY 只含 loopback 与 VM 内部地址段——即「本机/内部套接字直连,其余一切走代理」。

4.2 TLS:Sentinel 是唯一的中间人,且这是写死的 invariants

  • cell 侧 CA 材料实测:/run/hatch/egress-tls/ 下 ca-bundle.pem(182,885 B)+ hatch-egress-ca.pem(745 B);后者证书实测 subject = issuer = CN=Hatch Sandbox Egress CA, O=Hatch(自签),有效期是合成的 1975–4096 年——一张不靠真实时间、只靠预置分发的锚证书;
  • build-cell-trust-store.sh 头注释(实测)把规则说死:宿主侧只向 cell 发布 恰好一个 信任锚——Sentinel 的 egress MITM CA,「EXACTLY ONE anchor … a security invariant, not an inventory」;Sentinel 启动时在内存中生成该 CA、只发布证书(私钥不出 Sentinel),经 /run/hatch/cell-anchors 只读绑定进 cell 并喂给 NSS 数据库;注释还留了事故教训:VM 入口链证书曾被放进系统信任库、后被移除,因为「系统级 CA 会为每个主机名背书,包括 Sentinel 不终止的流量」——需要额外信任的客户端只能自己 pin;
  • 推论(推断,两层拆开):阻止出口绕过的主结构是强制代理路由(§4.1:死 DNS + 代理变量 + NO_PROXY 仅 loopback)——流量在 TCP 层就被钉死在 egress 代理上,TLS 信任库改不改都不改变这一路由事实。单锚信任库是纵深防御的一环:它解决的是脚本注释里那次事故教训——防止系统级 CA 为"Sentinel 不终止的流量"背书。至于 HTTP CONNECT 隧道内再套一层 TLS(TLS-in-TLS)能否绕过审查,取决于 Sentinel 对 CONNECT 目标的 allowlist 策略,该策略在宿主侧,本机无证据,不作结论。因此严谨的表述是:绕过审查需要同时突破代理路由与 CONNECT 策略两层,本机只能确证第一层存在。

4.3 审批三套接字(实测键名)

出口审批不在 cell 里判,三个 JARVIS_* 键(实测键名与路径,值仅为路径;11-04 env 复核:键名与路径均未变):

键 路径 角色(按命名,推断分工)
JARVIS_DAEMON_EGRESS_APPROVAL_SOCK /run/hatch/sentinel/daemon-egress-approvals.sock daemon 侧出口审批的主通道(挂在 sentinel 目录下)
JARVIS_EGRESS_APPROVAL_ADMIN_SOCK /run/hatch/sentinel/egress-approvals-admin.sock 审批的管理面(登记/裁决)
JARVIS_EGRESS_APPROVAL_EVENTS_SOCK /run/hatch/daemon/egress-approvals-events.sock 审批事件流(挂在 daemon 目录下,供订阅状态变化)

另有工具 SDK 内的 cli/privsep/sentinel_egress.rs 模块与 HATCH_PRIVSEP_SENTINEL_EGRESS 标记(实测 strings)——privsep 调用自身的出网也走 Sentinel egress 裁决,不是 worker 就能直连。特性开关名里有 abra_hatch_network_egress_screen_enforcement、abra_hatch_sentinel_exfil_hardening(实测 strings,仅名字)——出口筛查与防外泄硬化是可远程开关的策略项。

4.4 一个边界实测:路径声明 ≠ 本命名空间可见

本轮在 exec 环境里 ls /run/hatch/proxy/、/run/hatch/sentinel/、/run/hatch/daemon/、/run/hatch/memory/、/run/hatch/safety/ 全部不存在(11-04 复测:proxy/、sentinel/、daemon/、memory/、safety/ 仍全部不存在;rescue/ 同样不可见),而 /run/hatch/ 顶层实测可见的只有:auth/ browser-broker-liveness/ cell-anchors/ egress-tls/ noded/ privsep/ resume/ runtime-cell/ sandbox/ sandbox-api/ telemetry/(11 常驻)及本轮出现的 vault/(见 §8)、新增的 upload-store/(store.sock + publisher-root 在岗)、以及 10-17 时不可见的 vault-encrypt/、vault-hmac/(各含其 socket,见 §8.1 更新)。即:推理/STEFI/Sentinel/安全服务的套接字路径被声明在环境里、但不挂进普通 exec 进程的挂载命名空间——Agent 的 shell 连「看一眼这些 socket 文件」都做不到,能碰到它们的只有被专门接线的进程(daemon、privsep 跳板)。这是本篇最硬的一条隔离实测:能力面靠命名空间裁剪,而非靠 Agent 自觉(11-04 复测:裁剪口径仍在,但 vault-encrypt/、vault-hmac/、新增 upload-store/ 可见——裁剪边界随部署调整,非一成不变)。

4.5 紧急停止与审计:本篇未覆盖的穿透问题

基线(A):控制平面负责 stop/pause/audit。本篇的隔离描述只覆盖了正常调用路径,以下三问本机证据不足,列为 ❓:

  1. **停止穿透哪一层?**实测 privsep worker terminated by signal 证明 worker 可被信号终止,但用户侧"停止"按钮杀的是 cell 内跳板、可信侧 worker,还是两者?Guarded output 处于 held 态的输出在取消时丢弃还是已部分 release?strings 中未见 cancel 语义的对应错误类。
  2. **审计日志落在哪侧?**privsep 调用的审计记录是由 worker 写 telemetry(/run/hatch/telemetry/ 可见),还是由 Sentinel/daemon 侧统一写?本机未追踪 telemetry 的错误文案。
  3. 审批卡片的人类侧回流:§4.3 的三条审批 socket 止于命名;卡片如何渲染、用户点击如何回流为 Sentinel 的裁决,全程在宿主侧/客户端,不在本机证据内。

建议后续用 strings grep cancel/audit/approval_scope 补一轮,或在 10-安全篇中统一收口。

4.6 降级路径:单点故障时的行为(实测为主,缺口明示)

Sentinel 与 egress 代理是执行面的单点。本机能确证的降级语义有三条(全部实测 strings),没有"服务挂了就全停"的统一结论:

  1. surrogation 失败是 fail-open:「verification-code surrogation failed; releasing guarded output unsurrogated」——扫描/替换失败时,guarded 输出不带替换地直接放行,而不是扣留或报错。这与 §3「替换只准在可信侧发生」形成一组张力:正常路径可信侧替换,异常路径放行未替换原文。❓:放行是直接给 cell 还是附带告警给 Agent,本机无证据。
  2. 引用兑换依赖 egress 代理就绪:「…so a surrogate credential cannot be redeemed; the egress proxy is still starting up」——代理启动中时 [credential:uuid] 引用无法兑换(浏览器侧拿码的链路被卡住),属于启动期降级:引用还在,但兑换端没起来。
  3. privsep 瞬断有有限重试:「privsep worker transient disconnect; retrying round-trip」+ TRANSIENT_RETRY_MAX_ATTEMPTS 上限——worker 侧瞬时断连走重试而不是立即失败;另有 socket 激活服务的等待语义「gave up waiting for a socket-activated service to answer」,说明服务起不来时调用方会等、也会放弃(放弃后的形态本机无下文,❓)。

本机无证据的两问(❓,与缺口声明一致):egress 代理 3128 彻底不可用时,工具调用是 fail-closed 报错还是排队重试——strings 未见 egress_unavailable 类错误语义;abra_hatch_network_egress_screen_enforcement 这类特性开关被远程关闭后,出口退化成什么形态——开关名实测存在(§4.3),"关掉后发生什么"无追问证据。


5. Sentinel:出口、限流与裁决的中枢(实测 + 文档)

Sentinel 在本机的可见面是「一个套接字键 + 一批语义字符串 + 一个 CA」,但把它们拼起来,它的职责至少四项:

  1. 出口政策权威:§4.2 的 trust-store 脚本明文称 Sentinel「terminates the cell's egress TLS by design and is the policy authority」——终止 TLS、做出口裁决,是 egress 审批三套接字的宿主;
  2. HTTP API 面:JARVIS_SENTINEL_HTTP_API_SOCKET=/run/hatch/sentinel/http-api.sock(实测键名/路径;11-04 env 复核未变;目录在本 exec 命名空间不可见,同 §4.4);另有安全服务键 JARVIS_SECURITY_SOCK=/run/hatch/safety/security.sock 与之并列;
  3. 连接器限流的执行者:strings 实测错误文案「Connector request rate limited by Sentinel」,配额失败结构 ConnectorQuotaFailure 带 retry_after_seconds / limit / requested_units / used_units / remaining_units 字段——限流不是 provider 说了算,Sentinel 在本地先判(详见 §9);
  4. 运行时上下文令牌的解析者:§2 第 4 条的 strings 明文——连接器权威绑在 daemon 现签的 runtime-context token 上,「which Sentinel resolves against its daemon-registered registry」;归因用的 trace blob 谁都能写、不作授权输入,授权只认 Sentinel 能在其登记表里查到的令牌。(此处仅指连接器/出口授权面;面向用户的敏感操作审批(approval cards)属控制平面,本篇未覆盖——见新增 §4.5。)

文档侧的旁证(文档):WhatsApp 通道文档明文「Decisions belong to Sentinel; never infer one from free…(裁决归 Sentinel,不要从自由文本推断)」——Sentinel 也是外部消息通道结构化控制的裁决者,不止管网络出口。本机能确证的就到这一层:Sentinel 的判定逻辑、登记表内容、策略规则库都在宿主侧,不在 cell 文件内(见 §11)。


6. STEFI 代理与其后的内部服务(实测到名字边界)

6.1 代理本体

  • 入口:JARVIS_STEFI_PROXY_SOCK=/run/hatch/proxy/stefi.sock——同时出现在 guest.env 模板与 live 环境(实测键名/路径;11-04 env 复核未变);
  • 客户端形态:工具 SDK 内「build stefi proxy client」(strings),工具访问 Meta 内部服务不直连、统一经此代理;
  • caller 校验(实测 strings):错误文案 invalid stefi caller authorization token 独立存在——代理对调用方做令牌校验,不是「能连上 socket 就能用」;§1.4 的 ping 探针做的正是「打开 STEFI socket + 一次 verify_oauth_token 往返」,其端点形态为 http://localhost/hatch/verify_oauth_token(strings 中的 UDS-over-HTTP 风格路径),返回里有用户标识字段(仅知字段名,不记录值)。

6.2 边界声明:只到名字为止

STEFI 之后能从本机文件里读到的只有名字(00 号 §7 已列):WWW / MSL / CAGI / Mango·Pariscope,以及 strings 里的 MslBrowserService through Stefi、require_cagi(CAGI 路由被指定时的 fail-closed 语义:「Fail closed unless the measured-CVM CAGI route is selected」)。这些系统的实现、拓扑、数据都不在本机——本文对 STEFI 之后的全部陈述止于「存在此名、经此代理、带 caller 校验」,与 00 号 §17 的边界声明一致。另外注意 §4.4 的实测:/run/hatch/proxy/ 在本 exec 命名空间根本不存在,STEFI socket 对普通 shell 进程同样是「声明了路径、摸不到实体」。


7. authd:本地认证守护(实测)

  • 本体端点:/run/hatch/auth/authd.sock,实测为 srw-rw-rw- nobody:nogroup,是本轮在 /run/hatch/auth/ 下唯一可见的 socket;
  • 调试客户端 authdc(实测 --help):明文自述「Talks to its Unix socket ($JARVIS_AUTHD_SOCK or /run/hatch/auth/authd.sock)…authd gates most endpoints by peer credentials」——按对端凭据(SO_PEERCRED 类机制,推断具体实现)门控,特权命令要在宿主侧 root 下跑,cell 内普通调用只能碰被放行的子集;
  • 命令面(实测):file(auth-files 操作)、cred(dynamic credentials 动态凭据)、record(按 UUID/外部键查目录记录,只输出消毒后的 catalog 投影)——即 authd 管的不只是 OAuth 令牌,还有「认证文件」与「动态凭据」两类对象,目录对外只给 sanitized projection;
  • 入口侧门控:env 有 JARVIS_AUTHD_INGRESS_ALLOWED_USERS 键(只记键名)——authd 的 ingress 有允许用户清单;
  • 与 STEFI 的分工(实测 strings 互证):工具侧报错「missing hatch user token: ensure shared credentials are available through authd」——用户令牌的可用性由 authd 保证;STEFI 侧用 verify_oauth_token 验令牌有效性。一个管「令牌在哪、给不给」,一个管「令牌真不真」,在 ping 探针里被串成一次往返体检。
  • Google 系另有 hatch_gws_auth(12MB 独立二进制,实测)与 hatch-gws-auth.sock——GWS 的授权面自成一路,但最终仍落在 hatch-gws-cli-fully-isolated worker 后面(§1.2)。

8. Vault 在执行面的位置(实测)

8.1 形态:常驻守护进程,不是 CLI

  • 二进制 hatch-vault 实测 10MB、stripped ELF;它没有给 Agent 用的命令行面——本轮误以 --help 调用时,它直接按守护进程启动并 fatal 退出,报错大意为「vault-health unix socket bind failed at /run/hatch/vault/health.sock: active listener is present; refusing to replace it」。这条报错本身就是实测证据:Vault 是单例常驻服务,且拒绝二义启动;
  • socket 家族实测(ls /run/hatch/vault/):auth.sock、cvm.sock、health.sock 三个(11-04 复测未变),均为 root:root srw-rw----——比 authd 的 srw-rw-rw- 更严,普通用户进程连连接权限都没有;strings 还显示两个旁路服务面:/run/hatch/vault-encrypt/encrypt.sock 与 /run/hatch/vault-hmac/hmac.sock(加密与 HMAC 被拆成独立 socket 服务;10-17 时本 exec 命名空间见不到其目录,11-04 复测已可见,各含其 socket——命名空间裁剪的口径随部署有调整,见 §4.4 更新);
  • 调用方校验:strings 有端点路径 credentials/v1/auth-file/get 与请求头名 X-Hatch-Verified-Vault-Caller、「bearer/notary auth disabled on the auth socket」——auth socket 走本地可信通道免令牌,而另一类入口要 notary(公证)令牌校验,且公钥缺失时该认证方式直接禁用(fail-closed)。

8.2 它在执行面管什么

按 strings 与文档拼图(实测 + 文档):

  1. 凭据文件的受控读取:credentials/v1/auth-file/get + AuthFileGetResponse——连接器/遥测等可信组件经 Vault 拿认证文件,Agent 本人不经手(遥测代理的访问令牌刷新也要「read … through authd」并经 Vault 侧校验,strings 互见 authd 与 vault 两路);
  2. CVM 密钥托管(本机为 standard VM,只见其形):cvm.sock 服务 cvm_enroll / cvm_open、RV(reliable volume)密钥的 LUKS key-slot 管理、芯片 allowlist 与 attestation bundle 校验、迁移密钥(migration key)的签发/持有状态机——这一整套是 confidential VM 的磁盘密钥与跨机迁移面(文档 data-handling.md:CVM 的持久存储由客户自管密钥加密)。standard VM 上它处于未 enroll 侧(状态字面 cvm_unenrolled / cvm_locked 等实测存在),但守护进程照常在岗;
  3. Secure Vault 的服务端(文档,00 号 §14.3):用户经安全录入卡存入的网站密码/API 凭据由 Vault 托管,浏览器只在结账/登录步经它取用;一次性验证码不入 Vault、走 §3 的 guarded-output 托管引用。Agent 在执行面与 Vault 的关系是「无关系」:没有任何给 Agent 的 vault 工具/命令,hatch-vault 二进制在 /opt/hatch/bin/ 里对 Agent 可见但不可用(跑起来是个守护进程)——可见 ≠ 可用,在凭据面被贯彻到极致。

9. 服务面限流纪律(实测 + 文档)

服务面的「纪律」有三层,越往下越硬:

层 机制 证据
行为层(Agent 纪律) 429 / connector_rate_limited / 限流型 403 = 该 provider 域硬停;terminal_for_attempt=true 时本轮终局;禁止换端点、降速率、派子 Agent 或排队续跑绕过 文档(system prompt「Service limits」节,00 号 §14 已引)
裁决层(Sentinel) 连接器调用先过 Sentinel 的三维配额:shared_pool_request_rate(共享池请求率)、connector_request_rate(单连接器请求率)、connector_weighted_units(加权单位)——任一触发即 ConnectorQuotaFailure,带 retry_after_seconds 回执,Agent 只能按回执等/停,不能猜 实测 strings(错误类与字段名)
清单层(manifest) 每个连接器 manifest 自带 units/分钟成本表,在 worker 侧 enforce(01 号 §4.3)——防单个工具打爆 provider 配额 文档 + 01 号实测

另有一条 pacing 痕迹(实测 strings):CLI 侧有「write/read CLI pacing request/response」字面——推断工具调用在发出前还有一轮向可信侧申请节奏许可的往返,与 Sentinel 配额是同一纪律的两种实现面,本机无法确证其分工。除连接器外,推理面同样有纪律痕迹:字符串里 ModelProxyUnavailable / ModelProxyBusy / ModelProxyTimeout 与 CAGI 429 终局语义(01 号/浏览器工具说明)表明模型代理的限流回执也被结构化成错误类,而不是裸 HTTP 状态码让 Agent 自由解读。


10. 服务面全景:本 exec 命名空间实测可见的 socket 网格

除 privsep 与上述「声明不可见」的服务外,本轮在 /run/hatch/ 实测可见的服务面(全部为 Unix socket,属主多为 nobody:nogroup 或 root):

目录 内容(实测) 角色
auth/ authd.sock §7
vault/ auth.sock / cvm.sock / health.sock(root, 0600 类权限;11-04 复测未变) §8
vault-encrypt/ vault-hmac/ encrypt.sock / hmac.sock(11-04 新可见;10-17 时不可见) §8.1 旁路服务面
upload-store/ store.sock + publisher-root(11-04 新见) 上传存储(推断,按命名)
sandbox-api/ api.sock(root) 沙箱/Spaces 执行 API
sandbox/ space-inference.sock / space-media.sock / space-privileged.sock / space-web-search.sock(root) Spaces 的四路专用服务:推理、媒体、特权、搜索各行其道
noded/ control.sock / ephemeral.sock / http-api.sock 节点守护(浏览器节点等)的控制面
telemetry/ telemetry.sock / bugreport.sock + scuba/ 子目录 遥测上报(另有 JARVIS_TELEMETRY_PROXY_SOCK 键指向于此)
egress-tls/ cell-anchors/ CA 材料(§4.2) 信任锚分发
browser-broker-liveness/ 两个 process-lock 文件 浏览器 broker 的存活锁
resume/ root 持有的恢复目录 运行时恢复/接管(00 号 §8 的 restart checkpoints 在文件面的落点之一,推断)

与之对照,只在 env 里声明、本命名空间不可见的服务键(实测键名全表;11-04 env 复核:键名与路径均未变,proxy/ sentinel/ daemon/ memory/ safety/ rescue/ 目录仍不可见):JARVIS_INFERENCE_PROXY_SOCK(推理)、JARVIS_STEFI_PROXY_SOCK(§6)、JARVIS_SENTINEL_HTTP_API_SOCKET 与三条 egress 审批键(§4.3/§5)、JARVIS_SECURITY_SOCK、JARVIS_MEMORY_SOCK、JARVIS_SANDBOX_API_SOCK(此条反而可见)、JARVIS_RESCUE_SIGNAL_SOCK、JARVIS_AUTHD_SOCK(可见)、JARVIS_TELEMETRY_PROXY_SOCK(可见)。可见性的分野大致是:Agent 日常工具链要用的(privsep/auth/沙箱/遥测)挂进来,裁决与推理面(proxy/sentinel/safety/memory)只给路径不给实体(推断,按实测分布归纳)。


11. 一次 privsep 工具调用的生命周期(串联本篇)

Agent 调 CLI(cell 内,multicall 跳板)
 → 跳板组装 PrivsepRequest:类型化参数 + 输入文件清单 + fd 表(sealed memfd 引导)
 → 连 /run/hatch/privsep/<tool>.sock,worker 验工具身份(PRIVSEP_TOOL 对版)
 → 可信侧执行:guest.env 白名单环境构建(宿主侧读模板,不 source cell 文件)
   ├─ 需要用户令牌 → authd(peer 凭据门控);内部服务 → STEFI(caller 令牌校验)
   ├─ 需要出网 → sentinel_egress 裁决 → egress 代理 → Sentinel MITM TLS(单锚信任库)
   └─ 配额 → Sentinel 三维限流 + manifest units 双判,超限回 ConnectorQuotaFailure
 → 输出回程:worker stdout/stderr 先 quiesce 到 /dev/null
   → guarded output(memfd+seal+per-stream 上限)扣留
   → 含验证码则过 safety-scan 扫描、替换为 [credential:uuid] 引用
   → 动作 post-commit 后才 release 回跳板 fd
 → Agent 拿到结构化结果(JSON 信封),验证码类原文凭据从未进过 cell

12. 本机实测快照:11-04 vs 10-17 对照

项 11-04 快照 10-17 旧数
privsep 套接字 149(当场 ls 计数;-mcp.sock 40、-cli.sock 14、-verify.sock 2、显式 -fully-isolated 2) 136(-mcp.sock 38、-cli.sock 14、-verify.sock 2、-fully-isolated 2)
authd 未重测 /run/hatch/auth/authd.sock 在岗(srw-rw-rw-);authdc 子命令 file/cred/record
Vault auth.sock / cvm.sock / health.sock 复测未变;vault-encrypt/、vault-hmac/ 目录新可见(见 §8.1) hatch-vault 10MB stripped ELF,单例守护;三 socket(root, srw-rw----)
egress 未重测 代理 hatch-egress-proxy:3128;CA 包 182,885 B + egress CA 745 B(自签,CN=Hatch Sandbox Egress CA,1975–4096);信任锚恰好 1 个(脚本明文 invariant)
审批键 env 复核:三个键名与路径均未变 egress 审批 3 键(daemon/admin/events),路径分属 sentinel/ 与 daemon/ 目录
命名空间可见性 常驻可见 11 个目录(§4.4 清单)+ vault/ + 新见 upload-store/、vault-encrypt/、vault-hmac/;proxy/ sentinel/ daemon/ memory/ safety/ rescue/ 仍仅 env 声明、实体不可见 常驻可见 11 个目录(§4.4 清单);本轮另见非常驻 vault/(见 §8)。proxy/ sentinel/ daemon/ memory/ safety/ 仅 env 声明、实体不可见
guest.env 未重测 45 行、27 键,spawnd 管理、root 持有、只读入 cell

13. 边界声明(本机看不见的部分)

privsep worker 的宿主侧实现、Sentinel 的策略与登记表、STEFI 之后的内部服务、egress 审批的人机判定逻辑、Vault 的密钥材料与 notary 公钥来源、authd 的凭据存储后端——均不在 cell 文件内,本文只记录其接口痕迹(socket 名、键名、错误文案、脚本注释),凡超出痕迹的分工判断已标 推断。本文未记录任何凭据/令牌/验证码的值,代理认证材料与用户标识字段一律只记「存在此字段」。