Summary

  • draft-saha-aadp-bound-permit-00 提议用短时签名许可,把一次依赖发送方域内状态的 AADP 决定带过信任边界,并同时绑定一个接收方、一把呈现者密钥、一笔 HTTP 请求和一个具体动作实例。
  • 许可验证通过仍不等于可以执行。被引用的 mandate 必须独立允许同一交易,接收方本地政策仍可否决,决定的 currentness 也必须成立;三层中的任何一层都不能替另一层扩权。
  • 接收方在其余检查全部通过后才原子消费 (iss, jti)。同体重试可以返回已存结果,但这不保证外部效果 exactly once;结果未知时必须对账,不能换一个新许可盲目重发。

对方缺少的不是公钥,而是状态

代理申请支付时,发送方的策略决定点可能查看了累计预算、实时预约、尚未过期的人工批准、先前执行次数和紧急停止开关。这些事实共同得出 permit。远端支付服务并不拥有这套状态,也不能在请求到达时重演原决定。

工作负载身份可以回答谁在调用,mandate 可以说明代理被委托做什么。它们都不能回答发送方的 40 欧元额度是否在那一刻已经被另一笔请求占用。远端需要的不是另一套万能授权,而是可验证的窄事实:某个被限定范围的签发方确实对这一个动作作过决定,而且到达的就是它所决定的请求。

Shamik Saha 的 action-bound permit 修订版 00 处理的正是这个缺口。它是活跃的个人 Internet-Draft,文首写明拟走 Standards Track;它不是 RFC、IETF 共识、工作组成果或生产部署证明。

草案自己的表述很克制:这是一个决定的信封,不是跨组织授权系统。

五道绑定把许可做窄

许可采用 JWS compact JWT,typ 必须是 aadp-permit+jwt。aud 只能有一个接收方;多个 audience 直接拒绝。cnf.jkt 指定呈现者的持钥证明密钥,HTTP 请求必须用这把密钥签名。跨信任边界若不验证请求签名,许可就只是 bearer token,草案禁止这种用法。

HTTP Message Signature 至少覆盖方法、authority、路径、query、Content-Digest、Idempotency-Key 和携带许可的字段。接收方在解析、重序列化或执行未协商解压前,以收到的原始字节重算 Content-Digest。两个语义相同但空格或编码不同的 JSON 也可能在这一层失败。

语义绑定另算一遍。每个注册动作类型规定如何从路径、query 与正文推导动作对象 A。接收方按 RFC 8785 规范化 JSON,加上域分隔符,再计算 action_digest。正文原字节一致,不代表金额、收款方或序号的动作解释一致;语义一样,也不能替被改写的传输字节开脱。

时间构成第五道边界。exp 不得晚于 AADP 的 execute_within。没有该义务时,exp - iat 最长 120 秒,动作类型只能缩短不能延长。接收方可以记录最多 60 秒的时钟偏差,但不能借偏差把底层截止时间向后推。

一个受众、一把钥匙、一笔请求、一个动作、一个短窗口。它难以复用,正是因为它不该被复用。

签发方的决定不是接收方的命令

执行条件是 AND。第一,签名决定与请求绑定全部验证。第二,许可引用 mandate 时,接收方要针对同一交易独立求值。第三,接收方自己的政策也必须允许。

以拟议的 Agent Authorization Envelope 为例,它描述代理被授予什么委托;bound permit 描述有状态 PDP 此刻作了什么决定。接收方获取 AAE 后,要比对 digest 并独立处理主体、有效期、单次使用、撤销与委托链。DENY、PENDING、digest 不一致或必需 mandate 无法取得都导致拒绝。bound permit 永远不能把 PENDING 变成 PERMIT。

随后,本地政策仍拥有最后否决权。支付服务即使认可签发方、确认金额和所有签名,也可以因账户、欺诈、制裁或服务限制拒绝。密码学能证明别的域曾作决定,不能把后果承担者变成被动执行器。

因此,可信签发方不是平面名单,而是 scope 表:哪些密钥、哪些动作类型、每个字段的上限、何时失效、最低 currentness 模式。某个 PDP 被信任决定小额转账,并不自动取得关闭账户或无限金额的权力。

短时签名也可能已经过时

签名之后、请求到达之前,政策版本可能被替换,mandate 可能被撤销。短寿命只能缩小这扇窗,不能看见窗内发生的变化。

草案要求许可明确声明两种模式之一。time-bounded 只看 exp,并承认期间的政策或 mandate 变化不会被发现;接收方只能对配置允许的签发方/动作组合接受它。status-checked 则要在验证当下,通过状态列表或签发方新近签名的 freshness statement,确认许可、政策版本和 mandate 仍然 current。

状态端点不可达、响应无法解析或声明过期,正常结果是 status-unavailable。只有本地明确配置、经过审计且风险分类允许时,才可有限 fail-open,并必须留下记录。它不能被埋进“token valid”这类总括日志。

Lu Heng 的现实分层在此很实用:许可记录过去某一刻的决定,currentness 记录后来还能确认什么,真正的外部效果又在下一层。需要把记录连接起来,却不能把前一层的符号当成后一层的事实。

最后一步才消费

草案把接收方验证排成十三步。结构、签发方与签名、受众、时间、动作类型、签发方 scope、正文摘要、呈现者签名、语义动作摘要、currentness、mandate、本地政策都先完成;第十三步才原子消费 (iss, jti),之后才尝试产生效果。

顺序本身是安全控制。错误受众、损坏正文或缺失签名不能烧掉合法的一次使用。本地政策拒绝也不应消费。若消费存储不可用,记录应是 could-not-check,而不是政策拒绝,更不是继续执行的理由。

jti 必须与 AADP 内部 permit_id 独立生成且不可相互推导。签发方保留映射;只有 jti 跨域,并同时成为 Idempotency-Key。内部决定/报告生命周期的标识符,不应因工程省事而获得外部凭证含义。

第一次呈现时,所有服务节点共享的耐久存储原子消费键、执行并保存结果。之后同一 (iss, jti) 加相同 Content-Digest 只返回存储结果,记录为重复,不产生第二次效果。相同键配不同正文则是 idempotency-conflict。第一次仍在运行时,后来的请求获得可重试响应。

但协议不能保证外部世界 exactly once。若发送方丢失响应,不知道付款是否发生,就不能再申请新许可重发。它必须报告 timeout,用 jti 或接收方动作号对账,无法确认时升级处理。幂等性约束重试,不会消灭未知状态。

让执行方签回它做了什么

跨域争议若只有两边各写各的日志,很难闭合。草案要求接收方在消费并尝试效果后返回签名 confirmation,引用许可、收到的请求、签名基摘要、动作摘要、适用的 mandate verdict、outcome 和本地动作号。呈现者再把 confirmation digest 写进 AADP report,作为 present_bound 的 discharge evidence。

三把签名各说一件事:PDP 签“我决定过”,呈现者签“这是我发送的请求”,接收方签“这是我对它做的处理”。签名拒绝可支持 failure 与 no_effect: true;普通错误响应不能证明没有效果。没有 confirmation 或 outcome 为 effect-unknown,都只能记 timeout。

设计只支持单跳。带 parent 的许可必须以 chained-permit-unsupported 拒绝。B 若继续调用 C,B 的域要按自己的状态重新决定、另发许可。上一跳可以作为 provenance 引用,却只能是证据,不能成为 C 的授权来源。

这正是 Minimum Initial Specification 的技术版本:只统一双方复核同一次尝试所需的最小绑定与拒绝语义,把信任选择、风险分类与本地后果留给承担者。共同记录用于暴露分歧,而不是建立跨域主权。

运行代码仍有两个公开缺口

草案引用 onedoor 仓库的固定提交。向量清单有 24 项,追踪文件声明 22 项已实现;V17 的“检测已替换政策”和 V22 的“确认回执引用了另一请求摘要”仍是 gap。草案另称 permit 测试共 92 项通过。

这构成可复现但有限的证据。实现与文档作者有关,所指提交的信息本身是仓库其他测试文本维护,并非专门的 permit 发布;尚无独立实现或生产测量。Running-Code Primacy 要求独立重跑、尤其补测两个缺口并公开不一致,而不是把一个仓库快照当作标准投票。

来源与限制

这些来源证明存在一份活跃个人提案、相关协议依赖和作者关联的实现快照;它们不证明 IETF 共识、RFC、独立互操作、安全部署、诚实政策、诚实接收方或 exactly-once 效果。本文只拥有修订版 00 的这一论点:把一次有状态 AADP 决定作为绑定受众、呈现者、请求与动作的单次尝试传递,并由接收方签回答案。