Skip to content

docs(sandbox): 补齐「lark-cli 按 bot 拆配置」这条沙盒前置条件 - #701

Open
xu4wang wants to merge 1 commit into
deepcoldy:masterfrom
xu4wang:docs/lark-cli-per-bot
Open

docs(sandbox): 补齐「lark-cli 按 bot 拆配置」这条沙盒前置条件#701
xu4wang wants to merge 1 commit into
deepcoldy:masterfrom
xu4wang:docs/lark-cli-per-bot

Conversation

@xu4wang

@xu4wang xu4wang commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Refs #683纯文档,零代码改动。

问题

沙盒 / 读隔离的白名单本来就是按「每个 bot 用自己的 lark-cli 配置目录」设计的:

路径 档位
~/.lark-cli deny
~/Library/Application Support/lark-cli(整目录) deny
…/master.key.file…/appsecret_<自己appId>.enc readOnly carve-out
~/.lark-cli-bots/<自己appId> readWrite

这个前提从没写进任何文档。机器没做过「按 bot 拆 lark-cli 配置」时,沙盒内所有 lark-cli 命令都以 not configured / config_file: operation not permitted 失败,而报错完全不指向根因——#683 里就一路被误导到「给 node / codex 开完全磁盘访问权限」上去(TCC 与 Seatbelt 是两套互相独立的强制访问控制,TCC 授权对 Seatbelt 的 deny 规则毫无作用)。

fs-policy.ts 里其实已经有一行注释提到「botmux's lark-cli identity mapping lives there(~/.zshenv)」——契约存在于代码注释里,用户侧无从得知。

改动

新增 docs/lark-cli-per-bot.md

  • 为什么要拆(白名单怎么切的、每条 deny/carve-out 的理由)
  • 原理链路:BOTMUX_LARK_APP_IDLARKSUITE_CLI_CONFIG_DIR~/.lark-cli-bots/<appId>
  • 配置步骤:~/.zshenv 映射(为什么必须是 .zshenv 不是 .zshrc)、per-bot config init + default-as bot、开放平台开 scope
  • 验证:config show / auth status,以及用会话线上的 Seatbelt profile 做内核级读探测
  • 边界与排错

交叉链接docs/file-sandbox.md「启用」段、docs/isolated-bot-deploy.md 部署步骤 + 排错表。

边界部分记下的几条实测 / 读码结论

  • 沙盒下只有 bot 身份可用:用户登录态 <appId>_<openId>.enc 不在 carve-out 内,--as user 及一切依赖用户 token 的命令读不到。
  • 不要用 sandboxPaths.readOnly 整目录开洞:user 源 rank=3 > baseline rank=0 确实能覆盖 deny 让命令跑起来,但等于把所有 bot 的 appsecret 密文 + master key 交给当前 bot 的 agent,是安全回退。
  • no-transport turn(apiOnly bot / HTTP 虚拟会话)下 lark-cli 一律不可用:整个飞书授权面被冻结,per-bot 目录与 keystore carve-out 都不发放。这是有意的。
  • lark-cli 1.0.56 没有 whoami 子命令fix(sandbox): macOS 沙盒 deny lark-cli 认证/配置路径,agent 在沙盒内无法读 auth/whoami/doctor(Full Disk Access 无法覆盖 Seatbelt) #683 里提到的那个)。自查身份用 auth status / config show

没做的(可后续单独提)

这份 PR 只补文档。真正把隐式契约变成显式行为的两件事没做:

  1. worker spawn 时直接注入 LARKSUITE_CLI_CONFIG_DIR(该目录存在时),让它不再依赖用户手改 shell rc——现在的 .zshenv 方案在不经过登录 shell 的 spawn 路径上、以及非 zsh 机器上会静默失效;
  2. 沙盒开启但 ~/.lark-cli-bots/<appId>/config.json 缺失时 spawn 前 warn 一条,把静默失败变成指向文档的提示。

验证

文档里的命令输出、路径、行为都在 macOS + lark-cli 1.0.56 上对着当前 master 的 fs-policy.ts 逐条核过。

🤖 Generated with Claude Code

沙盒/读隔离的白名单本来就是按「每个 bot 用自己的 lark-cli 配置目录」
设计的:deny 共享的 `~/.lark-cli` 与 macOS keystore 整目录,只放行
`~/.lark-cli-bots/<自己appId>`(rw)+ 本 bot 的 `master.key.file` /
`appsecret_<自己appId>.enc`(ro)。但这个前提从没写进文档,机器没配过时
沙盒内所有 lark-cli 命令会以 `not configured` /
`config_file: operation not permitted` 失败,而报错完全不指向根因——
还会把人误导到「给 node/codex 开完全磁盘访问权限」上去(TCC 与 Seatbelt
是两套独立的强制访问控制,对 seatbelt 的 deny 毫无作用)。

新增 docs/lark-cli-per-bot.md:白名单切法与理由、
BOTMUX_LARK_APP_ID → LARKSUITE_CLI_CONFIG_DIR 的映射链路、配置步骤、
验证(含用线上 profile 做内核级读探测)、边界与排错。

边界部分记下几条实测/读码结论:沙盒下只有 bot 身份可用(用户 token
`<appId>_<openId>.enc` 不在 carve-out 内);不要用 sandboxPaths.readOnly
整目录开洞(user rank 3 > baseline 0 能覆盖 deny,但会把所有 bot 的
appsecret 密文 + master key 暴露给当前 agent);no-transport turn 会冻结
整个飞书授权面、per-bot 目录与 keystore carve-out 都不发放;lark-cli
1.0.56 没有 whoami 子命令,自查用 auth status / config show。

同时在 docs/file-sandbox.md「启用」与 docs/isolated-bot-deploy.md
部署步骤 + 排错表交叉链接。纯文档,无代码改动。

Refs deepcoldy#683

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@deepcoldy deepcoldy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复审结论:需要修改。macOS 的 deny/carve-out 因果没有讲反,但 Linux 安全语义与 lark-cli 1.0.56 的实际落盘行为相反,当前文字会给出错误的跨 bot 隔离保证。

[P1] Linux 密钥并不在 per-bot 配置目录,当前策略会把所有 bot 的密文和 master key 暴露为 readOnly

docs/lark-cli-per-bot.md:22 写道「Linux(bwrap)把密钥直接放在 per-bot 目录里」,但我用 PR 明确声称实测的 lark-cli 1.0.56 在隔离 HOME 下复现:

.lark-cli-bots/cli_review_56/config.json
.local/share/lark-cli/appsecret_cli_review_56.enc
.local/share/lark-cli/master.key

LARKSUITE_CLI_CONFIG_DIR 只重定向 config.json,Linux fallback keystore 仍落在 ~/.local/share/lark-cli。当前 commonHomeBaseline() 又把整个 ~/.local/share 设为 readOnly,且 Linux 没有对 ~/.local/share/lark-cli 的 deeper deny/carve-out。直接调用策略真源得到:

~/.local/share/lark-cli/master.key                 readOnly
~/.local/share/lark-cli/appsecret_cli_other.enc    readOnly
~/.lark-cli-bots/cli_self/config.json              readWrite
~/.lark-cli-bots/cli_other/config.json             none

因此 Linux 沙盒内的 bot 能读到兄弟 bot 的 appsecret 密文和共享 master key;还可以改写自己可写的 config 指向兄弟 appId/key id,从而以兄弟 app 身份调用 lark-cli。这个问题也存在于当前安装的 1.0.76,不是只影响旧版本。

在合并这份“安全前置条件”文档前,至少需要二选一:

  1. 修正 Linux policy:deny ~/.local/share/lark-cli,仅 carve-out 本 bot 所需的 master key + appsecret_<self>.enc(并补策略/编译器测试);或
  2. 明确把该隔离保证限定为 macOS,并警告 Linux 文件沙盒下 lark-cli 的跨 bot secret 隔离尚未成立。

否则 file-sandbox.md 新增的跨平台前置条件会把一个实际未闭合的 secret 边界描述成已闭合。

[P2] 不要建议在隔离 bot 会话内首次 config init

docs/lark-cli-per-bot.md:72 暗示在 bot 会话内加 --force-init 即可。但首次配置时 per-bot 目录尚不存在,worker 的 existence filter 不会发放这条 allow;即便预建了目录,Linux 的 ~/.local/share/lark-cli、macOS 的 keystore carve-out 在沙盒内也只能读,config init 无法写入新 appsecret/master key。--force-init 只绕过 lark-cli 的 Agent-context 检查,不绕过 bwrap/Seatbelt。

建议把步骤 2 明确限定为沙盒外的宿主终端;若要保留 --force-init 说明,需限定为非隔离的 OPENCLAW/HERMES 类 Agent 会话。

其余我复核的 macOS 路径、source-rank/deepest-prefix、no-transport gate、用户 token 不 carve-out、Seatbelt profile 路径均与源码/测试一致。纯文档 diff 的 git diff --check 也通过。按群内要求,未执行合并。

@deepcoldy

Copy link
Copy Markdown
Owner

我让agent直接改下

@xu4wang

xu4wang commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

我让agent直接改下

感觉linux下基线需要增强下

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants