Approval Gateway · 腕上确认网关 腕上确认网关 · wrist-approval-gateway

Agent 要动手之前,
先在你手腕上问一句

无人值守地跑 Agent,最难的不是让它会做,而是让它在关键时刻停下来等你点头。 这套系统把「要不要做」推到你 iPhone / Apple Watch 上,你在表上点一下,结论回传给 Agent。 推送通道是自建的 Home Assistant —— 不用第三方推送服务,不用自写 iOS App。

446
项自检全部通过
9
套测试,含 80 项分级回归
0
个第三方推送服务
L0–L3
四档风险分级
01 / 问题

「危险操作要不要做」只有三种答案

前两种都是偷懒的正确,第三种才是能长期跑下去的。

全部放行

Agent 想干什么就干什么。它会替你 rm -rfgit push --forcenpm publish
方便,直到某一天不方便。

一律拦死

只读之外全部拒绝。安全,但 Agent 会卡在第一步,你得到的是一个不会干活的 Agent。

分级 + 腕上确认

无害操作静默放行,高危操作推到你手腕上等你点一下。
既不放任,也不卡死。

02 / 原理

一次确认,就是一次请求与一次结算

Agent 宿主的 PreToolUse hook 在命令真正执行之前拦下来,向本地网关发一个请求; 网关把卡片送出去、等你选择,再把结论回填给 hook。链路两端都是本地 HTTP / WebSocket, 推送这一跳才交给 Home Assistant。

# Agent 宿主 → 网关(本地回环 127.0.0.1)
POST /v1/approvals
{
  "tier":       "L3",
  "title":      "高危操作确认",
  "body":       "git push --force origin main",
  "ttl":        120,
  "wait":       90000,
  "session_id": "…"
}
# 网关 → Agent 宿主(回填结论)
200 OK
{
  "verdict":   "allow",
  "decidedBy": "ha",
  "latencyMs": 8420
}

# 其余时刻(超时 / 推送失败)
"verdict": "deny"  ← fail-closed,绝不放行

关键分工:Home Assistant 只负责送通知、收事件,不做任何判断。 令牌校验、重放拦截、超时兜底、审计留痕全部在网关里。

03 / 交互演示

走一遍完整链路

点下面的按钮,看一条 L3 命令怎么从 Agent 走到你的手腕、再走回来。 中途把「手机离线」打开,可以看它在推送失败时的兜底行为 —— 那是这套系统最见功力的地方。

Agent 宿主
待命
确认网关
待命
Home Assistant
待命
iPhone / Watch
待命
00.00等待开始 —— 点上面的按钮

04 / 分级

四档,决定打扰你的频率

分级只看命令文本的正则。设计目标不是「把危险的都抓出来」——而是 不把无害的判高:打扰预算一旦被噪音消耗完,人就会开始无脑点「允许」,整套方案等于白接。

L0
ls git status curl -I cd bash -n
不推送,也不介入宿主原本的权限流程
L1
Edit git add/commit node script.mjs rm -rf node_modules
不推送,只记审计
L2
git push gh pr create curl -X POST wrangler deploy
推到手表;超时或推送失败按默认拒绝
L3
git push --force git reset --hard sudo npm publish rm -rf ~/…
推到手表 + 要求设备已解锁;120 秒不响应即拒绝

复合命令取最危险的那一段,不让无害段稀释它: for f in a; do sudo true; done 仍是 L3。反过来,rm 会看目标路径是否落在 node_modules / dist / /tmp 这类常规清理目录,以及是否递归 —— 目录内清理判 L1,非递归删单文件判 L2,递归删外部路径判 L3。

05 / 保障

为什么它敢拦住而不是放行

推送失败时最糟的处理,是「静默放行」。这里的每一层都朝着 fail-closed 长。

signed

令牌 + HMAC 绑定

每次确认签一张绑定到「这一次动作」的令牌。手改过期时间会直接判定签名无效。

one-shot

单次消费

一次授权只放行一次。同一条命令再跑,还得重新问你。重放会被拦截。

ttl

时限兜底

L2 / L3 默认 120 秒无响应即拒绝。挂在你腕上的卡片过期后自己作废。

fail-closed

推不出去就拦住

推送失败不降级成放行,而是拦住命令,再让你在桌面上一次性放行这一条。

audit

全程留痕

每一次创建、结算、超时、撤销都写进 audit 日志,标明是谁决定的。

loopback

只走本机回环

网关 ↔ Home Assistant 走 127.0.0.1,不受局域网 IP 漂移影响,也不出网。