Share

外观
风格

从 15 次输密码到 1 次:envchain 多 Scope 机制与扩容指南

2026年2月14日 · 专栏

envchain 多 scope 机制图

这篇是一次真实踩坑后的收敛方案。

我们原来的痛点很直白:同一条 envchain mom <cmd>,可能连续弹十几次密码框,体验非常差,自动化也跑不稳。

这次改造后的目标只有一个:

  • 同一 scope 在同一个解锁窗口内,最多输入一次密码。

先说结论:新机制是什么

我们把 secrets 注入机制从“单 keychain + 混合授权”,改成了“多 scope keychain + 显式授权边界”。

核心变化有 5 个:

  1. scope 级存储隔离
  • 每个 namespace 对应一个独立 keychain 文件,比如:mom.keychain-dbwork.keychain-db
  • 物理隔离后,测试环境和生产环境不会互相污染。
  1. 命令保持不变,路径自动映射
  • 仍然用 envchain <scope> <cmd>,不改调用习惯。
  • 通过 ENVCHAIN_KEYCHAIN_DIR<scope> 自动映射到 DIR/<scope>.keychain-db
  1. 授权策略从“模糊信任”改为“可验证信任”
  • ACL 里同时使用 Trusted Applicationpartition_id
  • partition_id 里必须包含当前 envchain 二进制的 cdhash,否则会出现“每个 key 都重复授权”。
  1. 锁策略显式化
  • 对每个 scope keychain 统一设置 timeout=1200(20 分钟)。
  • 超时后需要重新解锁一次;窗口内不重复输入。
  1. 后台任务默认 fail-fast
  • 非交互任务建议启用 ENVCHAIN_NO_PROMPT=1
  • 拿不到授权就直接失败,不要挂住等待用户输入。

原理图(和执行链路)

你可以把它理解成四步流水线:

  1. 命令入口:envchain mom <cmd>
  2. 命名映射:找到 ~/Library/Keychains/envchain-scopes/mom.keychain-db
  3. 授权判断:校验 ACL / partition_id / cdhash
  4. 注入执行:把 scope 下的 key 注入子进程

如果第 3 步授权配置不完整,就会退化成“每个 key 单独确认”,也就是我们最痛的 15 次输入。

为什么以前会出现 15 次输入

根因通常是这两个之一:

  1. binary hash 变了,但 ACL 还是旧 hash
  • 比如你升级或重编译过 envchaincdhash 会变化。
  • keychain 里还信任旧 cdhash,每个条目都要重新问一次。
  1. 命令走错 keychain
  • ENVCHAIN_KEYCHAIN_DIR 没生效时,可能回退到 login.keychain
  • 如果 login.keychain 里也有同名 envchain-<scope> 条目,会触发混乱授权行为。

如果你要新增一个 scope,该怎么做

下面是可复制的最短流程。示例 scope 用 work

1) 创建 scope keychain 并设定超时

SCOPE=work
KC_DIR="$HOME/Library/Keychains/envchain-scopes"
KC_PATH="$KC_DIR/${SCOPE}.keychain-db"

mkdir -p "$KC_DIR"
security create-keychain "$KC_PATH"      # 首次创建会提示你设置该 keychain 密码
security set-keychain-settings -lut 1200 "$KC_PATH"

2) 把 secrets 写入这个 scope

envchain --keychain "$KC_PATH" --set "$SCOPE" AI_API_KEY OPENAI_API_KEY DATABASE_URL

3) 一次性修正 partition list(关键)

CDHASH=$(codesign -dv --verbose=4 /opt/homebrew/bin/envchain 2>&1 | awk -F= '/^CDHash=/{print $2}')
security set-generic-password-partition-list \
  -s "envchain-${SCOPE}" \
  -S "apple-tool:,apple:,cdhash:${CDHASH}" \
  "$KC_PATH"

这一步做完,才真正具备“一次输入,窗口内复用”的基础。

4) 开启自动映射(命令习惯不变)

export ENVCHAIN_KEYCHAIN_DIR="$HOME/Library/Keychains/envchain-scopes"

建议把这一行写进 ~/.zshrc~/.zprofile

5) 验证行为

envchain work /bin/echo first
envchain work /bin/echo second

预期:

  • 第一条:最多 1 次输入
  • 第二条:0 次输入(在 20 分钟窗口内)

给自动化任务的推荐配置

交互命令和后台命令建议分开策略:

  • 日常终端:正常 envchain <scope> <cmd>
  • 定时/CI/守护进程:
ENVCHAIN_NO_PROMPT=1 envchain <scope> <cmd>

这样做的好处是:拿不到授权就立刻失败,你可以在日志里快速定位问题,而不是挂死等待密码。

运维排查清单(实用)

检查当前 envchain 的 cdhash

codesign -dv --verbose=4 /opt/homebrew/bin/envchain 2>&1 | rg '^CDHash='

检查 scope keychain 的 partition_id

security dump-keychain -a "$HOME/Library/Keychains/envchain-scopes/mom.keychain-db" \
| awk '/"acct"<blob>="AI_API_KEY"/{f=1} f&&/authorizations \(1\): partition_id/{p=1;next} p&&/description:/{print;exit}'

检查有没有误走 login.keychain

security dump-keychain "$HOME/Library/Keychains/login.keychain-db" \
| rg '"svce"<blob>="envchain-mom"'

如果你在 login.keychain 和 scope keychain 里都看到同名 service,就要先收敛到单一来源,避免行为飘忽。

安全边界说明

这套方案是“本机工程化安全”,不是“绝对零风险”。

它解决的是:

  • 明文 .env 到处散落
  • 多环境串用
  • 交互注入体验差

它不直接解决的是:

  • 机器被完全攻破后的根权限风险
  • 团队级审批/审计/最小权限治理

如果你已经进入多人协作、审计合规场景,应该再叠加 Vault/云 KMS 这类服务。

最后给你一个最小模板

以后你每新增一个 scope,就按这个顺序:

  1. create-keychain
  2. set-keychain-settings -lut 1200
  3. envchain --keychain ... --set <scope> ...
  4. set-generic-password-partition-list(带当前 cdhash)
  5. envchain <scope> /bin/echo first && second

这样你就能把“重复输密码”这个历史问题,压到可控范围内。

如果你愿意,我下一篇可以写成「一键脚本化版本」:输入 scope 名称和 key 列表,自动完成创建、授权、验收和回滚脚本。