docs(sandbox): 补齐「lark-cli 按 bot 拆配置」这条沙盒前置条件 - #701
Conversation
沙盒/读隔离的白名单本来就是按「每个 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
left a comment
There was a problem hiding this comment.
复审结论:需要修改。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,不是只影响旧版本。
在合并这份“安全前置条件”文档前,至少需要二选一:
- 修正 Linux policy:deny
~/.local/share/lark-cli,仅 carve-out 本 bot 所需的 master key +appsecret_<self>.enc(并补策略/编译器测试);或 - 明确把该隔离保证限定为 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 也通过。按群内要求,未执行合并。
|
我让agent直接改下 |
感觉linux下基线需要增强下 |
沙盒白名单设计上按「每个 bot 只放行自己那份 lark-cli 密钥」,macOS 早已在 keystore 上闭合, Linux 漏了这半边:lark-cli 在 Linux 把密钥落在共享 store(`appsecret_<appId>.enc` 每 app 一份 + 共享 `master.key`),而 Linux fs-policy 只有一条 baseline `ro(~/.local/share)`、没有 更深的 deny——任何开 sandbox/readIsolation 的 bot 在沙盒内都能只读到所有兄弟 bot 的 appsecret 密文 + master key,可改写自身 config 指向兄弟 appId 从而冒用其身份。daemon 跑在 Linux,任何 Linux bot 开沙盒即触发。 keystore 落点由 `LARKSUITE_CLI_DATA_DIR` 决定(`<clean(value)>/lark-cli`,仅当合法绝对路径; 否则回退 `$HOME/.local/share/lark-cli`),不读 `XDG_DATA_HOME`(strace 实测 v1.0.76 + 官方 keychain_other.go::StorageDir / SafeEnvDirPath 交叉核对)。 改动: - fs-policy.ts:Linux transport 会话镜像 macOS carve-out——deny(store 根) + 深一层 ro(master.key)(Linux 文件名,非 macOS 的 master.key.file)+ ro(appsecret_<self>.enc); 兄弟密文 / 用户 token / 未来文件全 deny-by-default。 - 双候选 store 全锁:lark-cli 对一个 env 值只可能开 `<resolved>/lark-cli` 或默认 `$HOME/.local/share/lark-cli`,其字符校验规则难以逐字符复刻,故两个 store 都锁(transport 都 deny+都 carve 自身;no-transport 都进 authority roots 零 carve-out),无论它选哪个兄弟+ master 都 deny、自身可读。常态去重成一个。 - BOT_HOME 嵌套 authority:insideAuthority 的 BOT_HOME 例外改为按 root 作用,只豁免作为 BOT_HOME 祖先的 root;嵌套在 BOT_HOME 内更具体的 authority root(如 LARKSUITE_CLI_DATA_DIR =<BOT_HOME> 时的 keystore)仍限制,堵深层 hostile user RW 逃过 dropAuthority 把 master.key 解析成 readWrite 的逃逸。 - no-transport(apiOnly/HTTP 虚拟):keystore 纳入 computeNoTransportAuthorityRoots 冻结、 零 carve-out(连自身 key 也不给)。该 store 加入仅在 platform==='linux'——macOS 用 Library/… store,在 darwin 冻结 Linux 路径会改变 darwin 行为(no-transport workingDir 落 ~/.local/share/lark-cli 会新失败),故严格按 platform 门控,darwin 与旧行为逐字节一致。 - 路径规范化:新增纯函数 cleanPosixAbsPath(Go filepath.Clean 语义 + 控制字符拒绝,对齐 SafeEnvDirPath)+ resolveLarkCliLinuxStoreDir。worker 从冻结宿主环境取 process.env.LARKSUITE_CLI_DATA_DIR,Clean 后经 canonicalNearestAncestor(realpath 最深存在 祖先 + 回接不存在尾段,解 symlink 前缀而不在缺失叶子上抛错)规范化,作为显式 FsPolicyContext.larkCliLinuxStore 传入;policy 保持纯函数不读 env。larkCliLinuxStorePath 对 不可规范化 override 返回 null(不发 carve-out、store 保持 deny-by-default),绝不静默回退默认。 - 沙盒 child-pin:prepareDirectSandbox 新增 larkCliDataDir,worker 传 larkCliChildDataRoot (canonStore)——仅当 canonical store basename 仍是 lark-cli 才 --setenv dirname(store) 作 child 的 LARKSUITE_CLI_DATA_DIR,使沙盒内 lark-cli 与 policy 落同一 canonical 命名空间;若 lark-cli leaf 是 symlink 指向别名目录则不钉(child 回退默认 store,默认也已锁)→ symlink 数据 根不再命名空间分叉。 - per-bot env:LARKSUITE_CLI_DATA_DIR 纳入 per-bot-env.ts 保留键——用户在 bots.json.env 配它 会被拒 + 可诊断,不再被沙盒 host --setenv/--unsetenv 静默覆盖。 影响面:仅 Linux 且开 sandbox/readIsolation 的会话行为变化(收紧)。darwin 路径完全不变(新逻辑 均在 platform==='linux' 分支,含 no-transport authority roots 的 platform gate)。transport 拿 自身 key carve-out、no-transport 整个 keystore 冻结。PtyBackend/TmuxBackend 共用同一编译产物无差异; 所有 CLI 开沙盒都受同一收紧规则保护。prepareDirectSandbox 新增可选入参,旧调用兼容。 验证: - pnpm build 绿。 - fs-policy 单测:自身 RO / 兄弟+token+未来文件 deny / 无关工具链不误伤;自定义 store rehome; 双候选 store 全锁(transport+no-transport)+ default-only 去重;BOT_HOME 嵌套 authority; cleanPosixAbsPath 与 resolveLarkCliLinuxStoreDir 的「绝对 Clean/相对忽略/控制字符拒绝/`..` 归一」 真值表;larkCliChildDataRoot 同名/别名 symlink/root/不可用输入;no-transport authority roots 按 platform 门控 + darwin 与旧行为一致回归。per-bot-env 单测覆盖新保留键。 - bwrap 内核级 e2e:真 bubblewrap 下兄弟 appsecret + master.key 读不到、ls 不列兄弟、自身可读、 store 不可写;LARKSUITE_CLI_DATA_DIR=<BOT_HOME> + no-transport + 敌意 user RW 直指 master.key 仍全拒。 - 变异测试:去 store deny / 破 no-transport 门 / 碰错 master.key 文件名 / 去默认 store 锁 / 去 child-pin basename 判定 / 去 platform 门控,各让对应用例(含内核级)翻红,确认测试有牙。 - live(真 sandbox + 真 lark-cli):默认 store auth 正常 + 兄弟内核级拦;自定义/含 `..`/symlink 数据根 store 同样 auth 正常 + 兄弟拦;no-transport 下自身 key 亦被冻结。 Refs #701(该文档 PR 保持 docs,待本代码修复合入后 rebase 修正 Linux 密钥落点表述 + 首次 init 只能在宿主沙盒外终端执行)。
沙盒白名单设计上按「每个 bot 只放行自己那份 lark-cli 密钥」,macOS 早已在 keystore 上闭合, Linux 漏了这半边:lark-cli 在 Linux 把密钥落在共享 store(`appsecret_<appId>.enc` 每 app 一份 + 共享 `master.key`),而 Linux fs-policy 只有一条 baseline `ro(~/.local/share)`、没有 更深的 deny——任何开 sandbox/readIsolation 的 bot 在沙盒内都能只读到所有兄弟 bot 的 appsecret 密文 + master key,可改写自身 config 指向兄弟 appId 从而冒用其身份。daemon 跑在 Linux,任何 Linux bot 开沙盒即触发。 keystore 落点由 `LARKSUITE_CLI_DATA_DIR` 决定(`<clean(value)>/lark-cli`,仅当合法绝对路径; 否则回退 `$HOME/.local/share/lark-cli`),不读 `XDG_DATA_HOME`(strace 实测 v1.0.76 + 官方 keychain_other.go::StorageDir / SafeEnvDirPath 交叉核对)。 改动: - fs-policy.ts:Linux transport 会话镜像 macOS carve-out——deny(store 根) + 深一层 ro(master.key)(Linux 文件名,非 macOS 的 master.key.file)+ ro(appsecret_<self>.enc); 兄弟密文 / 用户 token / 未来文件全 deny-by-default。 - 双候选 store 全锁:lark-cli 对一个 env 值只可能开 `<resolved>/lark-cli` 或默认 `$HOME/.local/share/lark-cli`,其字符校验规则难以逐字符复刻,故两个 store 都锁(transport 都 deny+都 carve 自身;no-transport 都进 authority roots 零 carve-out),无论它选哪个兄弟+ master 都 deny、自身可读。常态去重成一个。 - BOT_HOME 嵌套 authority:insideAuthority 的 BOT_HOME 例外改为按 root 作用,只豁免作为 BOT_HOME 祖先的 root;嵌套在 BOT_HOME 内更具体的 authority root(如 LARKSUITE_CLI_DATA_DIR =<BOT_HOME> 时的 keystore)仍限制,堵深层 hostile user RW 逃过 dropAuthority 把 master.key 解析成 readWrite 的逃逸。 - no-transport(apiOnly/HTTP 虚拟):keystore 纳入 computeNoTransportAuthorityRoots 冻结、 零 carve-out(连自身 key 也不给)。该 store 加入仅在 platform==='linux'——macOS 用 Library/… store,在 darwin 冻结 Linux 路径会改变 darwin 行为(no-transport workingDir 落 ~/.local/share/lark-cli 会新失败),故严格按 platform 门控,darwin 与旧行为逐字节一致。 - 路径规范化:新增纯函数 cleanPosixAbsPath(Go filepath.Clean 语义 + 控制字符拒绝,对齐 SafeEnvDirPath)+ resolveLarkCliLinuxStoreDir。worker 从冻结宿主环境取 process.env.LARKSUITE_CLI_DATA_DIR,Clean 后经 canonicalNearestAncestor(realpath 最深存在 祖先 + 回接不存在尾段,解 symlink 前缀而不在缺失叶子上抛错)规范化,作为显式 FsPolicyContext.larkCliLinuxStore 传入;policy 保持纯函数不读 env。larkCliLinuxStorePath 对 不可规范化 override 返回 null(不发 carve-out、store 保持 deny-by-default),绝不静默回退默认。 - 沙盒 child-pin:prepareDirectSandbox 新增 larkCliDataDir,worker 传 larkCliChildDataRoot (canonStore)——仅当 canonical store basename 仍是 lark-cli 才 --setenv dirname(store) 作 child 的 LARKSUITE_CLI_DATA_DIR,使沙盒内 lark-cli 与 policy 落同一 canonical 命名空间;若 lark-cli leaf 是 symlink 指向别名目录则不钉(child 回退默认 store,默认也已锁)→ symlink 数据 根不再命名空间分叉。 - per-bot env:LARKSUITE_CLI_DATA_DIR 纳入 per-bot-env.ts 保留键——用户在 bots.json.env 配它 会被拒 + 可诊断,不再被沙盒 host --setenv/--unsetenv 静默覆盖。 影响面:仅 Linux 且开 sandbox/readIsolation 的会话行为变化(收紧)。darwin 路径完全不变(新逻辑 均在 platform==='linux' 分支,含 no-transport authority roots 的 platform gate)。transport 拿 自身 key carve-out、no-transport 整个 keystore 冻结。PtyBackend/TmuxBackend 共用同一编译产物无差异; 所有 CLI 开沙盒都受同一收紧规则保护。prepareDirectSandbox 新增可选入参,旧调用兼容。 验证: - pnpm build 绿。 - fs-policy 单测:自身 RO / 兄弟+token+未来文件 deny / 无关工具链不误伤;自定义 store rehome; 双候选 store 全锁(transport+no-transport)+ default-only 去重;BOT_HOME 嵌套 authority; cleanPosixAbsPath 与 resolveLarkCliLinuxStoreDir 的「绝对 Clean/相对忽略/控制字符拒绝/`..` 归一」 真值表;larkCliChildDataRoot 同名/别名 symlink/root/不可用输入;no-transport authority roots 按 platform 门控 + darwin 与旧行为一致回归。per-bot-env 单测覆盖新保留键。 - bwrap 内核级 e2e:真 bubblewrap 下兄弟 appsecret + master.key 读不到、ls 不列兄弟、自身可读、 store 不可写;LARKSUITE_CLI_DATA_DIR=<BOT_HOME> + no-transport + 敌意 user RW 直指 master.key 仍全拒。 - 变异测试:去 store deny / 破 no-transport 门 / 碰错 master.key 文件名 / 去默认 store 锁 / 去 child-pin basename 判定 / 去 platform 门控,各让对应用例(含内核级)翻红,确认测试有牙。 - live(真 sandbox + 真 lark-cli):默认 store auth 正常 + 兄弟内核级拦;自定义/含 `..`/symlink 数据根 store 同样 auth 正常 + 兄弟拦;no-transport 下自身 key 亦被冻结。 Refs #701(该文档 PR 保持 docs,待本代码修复合入后 rebase 修正 Linux 密钥落点表述 + 首次 init 只能在宿主沙盒外终端执行)。
沙盒白名单设计上按「每个 bot 只放行自己那份 lark-cli 密钥」,macOS 早已在 keystore 上闭合, Linux 漏了这半边:lark-cli 在 Linux 把密钥落在共享 store(`appsecret_<appId>.enc` 每 app 一份 + 共享 `master.key`),而 Linux fs-policy 只有一条 baseline `ro(~/.local/share)`、没有 更深的 deny——任何开 sandbox/readIsolation 的 bot 在沙盒内都能只读到所有兄弟 bot 的 appsecret 密文 + master key,可改写自身 config 指向兄弟 appId 从而冒用其身份。daemon 跑在 Linux,任何 Linux bot 开沙盒即触发。 keystore 落点由 `LARKSUITE_CLI_DATA_DIR` 决定(`<clean(value)>/lark-cli`,仅当合法绝对路径; 否则回退 `$HOME/.local/share/lark-cli`),不读 `XDG_DATA_HOME`(strace 实测 v1.0.76 + 官方 keychain_other.go::StorageDir / SafeEnvDirPath 交叉核对)。 改动: - fs-policy.ts:Linux transport 会话镜像 macOS carve-out——deny(store 根) + 深一层 ro(master.key)(Linux 文件名,非 macOS 的 master.key.file)+ ro(appsecret_<self>.enc); 兄弟密文 / 用户 token / 未来文件全 deny-by-default。 - 双候选 store 全锁:lark-cli 对一个 env 值只可能开 `<resolved>/lark-cli` 或默认 `$HOME/.local/share/lark-cli`,其字符校验规则难以逐字符复刻,故两个 store 都锁(transport 都 deny+都 carve 自身;no-transport 都进 authority roots 零 carve-out),无论它选哪个兄弟+ master 都 deny、自身可读。常态去重成一个。 - BOT_HOME 嵌套 authority:insideAuthority 的 BOT_HOME 例外改为按 root 作用,只豁免作为 BOT_HOME 祖先的 root;嵌套在 BOT_HOME 内更具体的 authority root(如 LARKSUITE_CLI_DATA_DIR =<BOT_HOME> 时的 keystore)仍限制,堵深层 hostile user RW 逃过 dropAuthority 把 master.key 解析成 readWrite 的逃逸。 - no-transport(apiOnly/HTTP 虚拟):keystore 纳入 computeNoTransportAuthorityRoots 冻结、 零 carve-out(连自身 key 也不给)。该 store 加入仅在 platform==='linux'——macOS 用 Library/… store,在 darwin 冻结 Linux 路径会改变 darwin 行为(no-transport workingDir 落 ~/.local/share/lark-cli 会新失败),故严格按 platform 门控,darwin 与旧行为逐字节一致。 - 路径规范化:新增纯函数 cleanPosixAbsPath(Go filepath.Clean 语义 + 控制字符拒绝,对齐 SafeEnvDirPath)+ resolveLarkCliLinuxStoreDir。worker 从冻结宿主环境取 process.env.LARKSUITE_CLI_DATA_DIR,Clean 后经 canonicalNearestAncestor(realpath 最深存在 祖先 + 回接不存在尾段,解 symlink 前缀而不在缺失叶子上抛错)规范化,作为显式 FsPolicyContext.larkCliLinuxStore 传入;policy 保持纯函数不读 env。larkCliLinuxStorePath 对 不可规范化 override 返回 null(不发 carve-out、store 保持 deny-by-default),绝不静默回退默认。 - 沙盒 child-pin:prepareDirectSandbox 新增 larkCliDataDir,worker 传 larkCliChildDataRoot (canonStore)——仅当 canonical store basename 仍是 lark-cli 才 --setenv dirname(store) 作 child 的 LARKSUITE_CLI_DATA_DIR,使沙盒内 lark-cli 与 policy 落同一 canonical 命名空间;若 lark-cli leaf 是 symlink 指向别名目录则不钉(child 回退默认 store,默认也已锁)→ symlink 数据 根不再命名空间分叉。 - per-bot env:LARKSUITE_CLI_DATA_DIR 纳入 per-bot-env.ts 保留键——用户在 bots.json.env 配它 会被拒 + 可诊断,不再被沙盒 host --setenv/--unsetenv 静默覆盖。 影响面:仅 Linux 且开 sandbox/readIsolation 的会话行为变化(收紧)。darwin 路径完全不变(新逻辑 均在 platform==='linux' 分支,含 no-transport authority roots 的 platform gate)。transport 拿 自身 key carve-out、no-transport 整个 keystore 冻结。PtyBackend/TmuxBackend 共用同一编译产物无差异; 所有 CLI 开沙盒都受同一收紧规则保护。prepareDirectSandbox 新增可选入参,旧调用兼容。 验证: - pnpm build 绿。 - fs-policy 单测:自身 RO / 兄弟+token+未来文件 deny / 无关工具链不误伤;自定义 store rehome; 双候选 store 全锁(transport+no-transport)+ default-only 去重;BOT_HOME 嵌套 authority; cleanPosixAbsPath 与 resolveLarkCliLinuxStoreDir 的「绝对 Clean/相对忽略/控制字符拒绝/`..` 归一」 真值表;larkCliChildDataRoot 同名/别名 symlink/root/不可用输入;no-transport authority roots 按 platform 门控 + darwin 与旧行为一致回归。per-bot-env 单测覆盖新保留键。 - bwrap 内核级 e2e:真 bubblewrap 下兄弟 appsecret + master.key 读不到、ls 不列兄弟、自身可读、 store 不可写;LARKSUITE_CLI_DATA_DIR=<BOT_HOME> + no-transport + 敌意 user RW 直指 master.key 仍全拒。 - 变异测试:去 store deny / 破 no-transport 门 / 碰错 master.key 文件名 / 去默认 store 锁 / 去 child-pin basename 判定 / 去 platform 门控,各让对应用例(含内核级)翻红,确认测试有牙。 - live(真 sandbox + 真 lark-cli):默认 store auth 正常 + 兄弟内核级拦;自定义/含 `..`/symlink 数据根 store 同样 auth 正常 + 兄弟拦;no-transport 下自身 key 亦被冻结。 Refs #701(该文档 PR 保持 docs,待本代码修复合入后 rebase 修正 Linux 密钥落点表述 + 首次 init 只能在宿主沙盒外终端执行)。
Refs #683。纯文档,零代码改动。
问题
沙盒 / 读隔离的白名单本来就是按「每个 bot 用自己的 lark-cli 配置目录」设计的:
~/.lark-cli~/Library/Application Support/lark-cli(整目录)…/master.key.file、…/appsecret_<自己appId>.enc~/.lark-cli-bots/<自己appId>但这个前提从没写进任何文档。机器没做过「按 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:BOTMUX_LARK_APP_ID→LARKSUITE_CLI_CONFIG_DIR→~/.lark-cli-bots/<appId>~/.zshenv映射(为什么必须是.zshenv不是.zshrc)、per-botconfig init+default-as bot、开放平台开 scopeconfig show/auth status,以及用会话线上的 Seatbelt profile 做内核级读探测交叉链接:
docs/file-sandbox.md「启用」段、docs/isolated-bot-deploy.md部署步骤 + 排错表。边界部分记下的几条实测 / 读码结论
<appId>_<openId>.enc不在 carve-out 内,--as user及一切依赖用户 token 的命令读不到。sandboxPaths.readOnly整目录开洞:user 源 rank=3 > baseline rank=0 确实能覆盖 deny 让命令跑起来,但等于把所有 bot 的 appsecret 密文 + master key 交给当前 bot 的 agent,是安全回退。apiOnlybot / HTTP 虚拟会话)下 lark-cli 一律不可用:整个飞书授权面被冻结,per-bot 目录与 keystore carve-out 都不发放。这是有意的。whoami子命令(fix(sandbox): macOS 沙盒 deny lark-cli 认证/配置路径,agent 在沙盒内无法读 auth/whoami/doctor(Full Disk Access 无法覆盖 Seatbelt) #683 里提到的那个)。自查身份用auth status/config show。没做的(可后续单独提)
这份 PR 只补文档。真正把隐式契约变成显式行为的两件事没做:
LARKSUITE_CLI_CONFIG_DIR(该目录存在时),让它不再依赖用户手改 shell rc——现在的.zshenv方案在不经过登录 shell 的 spawn 路径上、以及非 zsh 机器上会静默失效;~/.lark-cli-bots/<appId>/config.json缺失时 spawn 前 warn 一条,把静默失败变成指向文档的提示。验证
文档里的命令输出、路径、行为都在 macOS + lark-cli 1.0.56 上对着当前 master 的
fs-policy.ts逐条核过。🤖 Generated with Claude Code