当 Agent 跨公司、跨仓库协作:Ratify 如何把“谁授权了什么”变成可离线验证的证明
AI Agent 开始替人做事以后,身份认证已经不够了。一个 GitHub Token 或 OAuth 会话或许能说明“请求来自某个账号”,却很难让接收方判断:这个 Agent 是谁授权的、只能访问哪个资源、能做哪一种操作、权限何时到期,以及任务执行中途被撤销后是否还应继续执行。
这不是把 Token 再包一层的需求,而是“委托授权”本身需要可验证的证据。Ratify Protocol 是 Identities AI 维护的一个仍处于 alpha 阶段的协议与多语言 SDK:它为人→Agent 和 Agent→Agent 的委托生成签名证明,并让接收方在不回调中心授权服务的前提下验证这份证明。代码采用 Apache-2.0,协议规范采用 CC BY 4.0;仓库当前提供 Go、TypeScript、Python、Rust、C/C++ 五套 SDK。
本文不把 Ratify 说成 IAM、OAuth、MCP 或 A2A 的替代品。更准确的理解是:前者负责认证、传输或本地策略,Ratify 在一个动作即将被接受时补上“该动作是否有经过约束的委托授权”的密码学证明。
为什么普通 Bearer Token 很难表达 Agent 的授权边界
Bearer Token 的强项是让服务验证持有者能否访问接口;它通常不携带一条可由任意接收方独立核验的授权链。面对跨组织的 Agent 任务,常见风险至少有四类。
- 权限过宽:外部 Agent 拿到一个长期有效的仓库或 API 凭据,实际只需要修改某个目录的一次权限。
- 转授膨胀:协调 Agent 把自己拥有的广泛权限转给子 Agent,而接收端不知道子 Agent 是否越权。
- 执行中撤销无处落地:任务已经在队列或远端运行,管理员撤销原始授权后,接收端仍可能按旧凭据继续执行。
- 事后难以说明:日志能看到请求发生了,却缺少一份可以离线复验的“谁批准、批准范围、验证结果”的证据链。
因此,授权不能只是一段“允许访问”的字符串。对接收端而言,至少要能检查委托人、主体、资源范围、操作范围、有效期、是否撤销,以及这次呈现是否是新的而非重放的。
Ratify 的三段流程:委托、呈现、验证
Ratify 将交互拆为 DELEGATE、PRESENT、VERIFY 三步。委托方签发 DelegationCert,其中命名被授权主体、scope 和过期时间;Agent 在发起具体交互时携带该证书,并针对验证方给出的新 challenge 签名;验证方检查整条证明包。
协议的签名是混合式的:同一份规范化字节同时由 Ed25519 和 ML-DSA-65 签名,验证时两者都必须通过。ML-DSA-65 是 NIST FIPS 204 中的算法名称;这里的工程意义不是宣称系统自动“量子安全”,而是避免把未来迁移完全押在单一签名算法上。
Ratify 还规定 JSON 的规范化序列化:对象键按字典序、没有无意义空白、使用 UTF-8,并处理数值表示。这个看似底层的细节决定了 Go 服务签出的授权证书,能否被 Python 或 Rust 的验证器按字节验证;如果各语言各自序列化 JSON,逻辑相同也会因字节不同而验签失败。
一个概念化的授权链可以写成这样:
Alice ── delegate: repo:docs:write, expires=2026-09-01 ──> Agent-A Agent-A ── delegate: repo:docs:write ────────────────────> Agent-B Verifier checks: effective scope = intersection of every certificate scope certificate expiry + revocation + requested operation fresh challenge signature from the presenting agent
重点是交集,而不是并集。若上游只允许 repo:docs:write,下游不能借“转授”得到 repo:*;接收方应以整条链能够授予的最小共同范围判定动作,而不是只相信最后一跳的自述。
先运行跨语言验证,而不是直接接进生产权限路径
仓库 README 当前列出 v1.0.0-alpha.17 的安装方式。对于 Python 项目,可以先安装固定预发布版本;对于 Go 项目,则直接固定模块版本。版本固定的目的不是追求形式感,而是确保演示、测试向量和运行时使用同一套协议语义。
pip install ratify-protocol==1.0.0a17 go get github.com/identities-ai/ratify-protocol@v1.0.0-alpha.17 git clone https://github.com/identities-ai/ratify-protocol cd ratify-protocol go test ./...
在评估阶段,更值得运行的是仓库提供的 demo 和测试,而不是立刻让它放行生产写操作。README 给出的 Go demo 路径如下:
go run ./demos/go
它覆盖正向委托与多种拒绝情形,例如篡改 scope 后验签失败、用 meeting:attend 的授权尝试 meeting:record 被 scope 拒绝,以及证书被撤销后的拒绝结果。把这些负向案例接入 CI,才能确认团队定义的“越权”确实没有被误当成正常请求。
挑战响应解决什么,又没有解决什么
证书本身可以在有效期内被复制;所以 Ratify 让验证方生成 challenge,呈现方对 challenge 与时间签名,验证方再检查其新鲜度。旧的证明包无法直接回答新的随机 challenge,从而降低简单录包重放的风险。
但“签名新鲜”不等于“业务动作绝不重复”。官方文档明确提醒:仅有时间窗口的无状态验证,仍可能在窗口内遇到重复呈现。一个真正发放 challenge 的验证方需要把 challenge 设计成一次性使用;对于会产生副作用的接口,还应实施请求幂等键、任务去重和审计记录。密码学验证与业务幂等不是二选一,而是两条都必须存在的防线。
同样,离线验证并不意味着可以忽略撤销分发。验证方必须获得可信的撤销信息,才能判断某份尚未过期的委托是否已失效。网络断开场景下,系统需要明确接受的是哪一种风险:使用缓存的撤销快照、缩短授权有效期,还是对高风险操作强制在线确认。不要把“验证时不依赖中心服务”误读为“撤销永远即时可见”。
如果团队已有 MCP 或 A2A 传输层,可以把 Ratify 放在接收适配器的授权判定点,而不是改写消息协议。发送方负责携带证明,接收方负责绑定本次请求的资源、动作和 challenge;中间的路由服务只转发,不应拥有替代接收方签署“允许执行”的权力。这样即使消息经过多个供应商,最终执行系统仍能用同一套规则拒绝不合规的委托。
还要区分“证明谁授权了动作”和“动作本身是否安全”。授权通过后,接收端仍应检查输入校验、沙箱边界、数据脱敏、人工审批和操作幂等性。一个拥有合法 repo:docs:write 证明的 Agent,依然可能提交恶意内容;密码学证明解决责任与边界问题,不能替代内容安全和业务风控。
一个适合落地的接入顺序
如果你的团队正在让外部 Agent 修改文档、发起工单、调用受限 API 或跨仓库交接任务,建议按以下顺序试点:
- 从低风险、可回滚动作开始:例如仅允许某个 Agent 向指定目录提交文档变更,不要先放开部署或资金类操作。
- 把 scope 绑定到资源和动作:用可审计的资源路径、操作名、有效期定义授权;避免
admin一类无法复核的笼统 scope。 - 在接收端做最终判定:Ratify 证明授权链,接收端仍要执行自身的 RBAC、路径限制、审批和速率限制。
- 把拒绝测试写成回归用例:过期、撤销、scope 不匹配、篡改、重复 challenge 都应有预期失败结果。
- 把 alpha 当 alpha:协议仓库明确标注 alpha,fixture bytes 可能随预发布版本变化。固定依赖版本,先在隔离环境完成跨语言互操作与升级演练,再决定是否用于关键路径。
Ratify 的价值不在于让 Agent 获得更多权限,而在于让权限交接变得更窄、更可验证、也更容易拒绝。随着 Agent 跨越组织和系统边界,能回答“谁在什么期限内允许它做这件事”的证据,往往比“它带着什么 Token”更重要。