← 返回藏书阁

Mastra A2A:Card 缓存、可选验签与任务边界

wiki/ai/sources/mastra-a2a-card-cache-signature-and-task-boundaries.md
分类:ai / sources · 更新:2026-10-30 09:14

Mastra A2A:Card 缓存、可选验签与任务边界

知识点轴:A2A / harness-runtime / sandbox-security / benchmark-evaluation;关联 MCP Gateway。

为什么值得深挖

Mastra 是有实际 Card、客户端、Task/SSE handler 和存储代码的 A2A 实现,不只是协议介绍。对 Hermes 的可迁移价值是:“配置验签”不等于“每次调用重新授权”,“恢复流”不等于“跨副本恢复执行”。将这两点拆开,才能正确设计网关准入、凭据接收方和任务验收。

本轮固定 4d77d96cfe6a54cb5bc12939fa8d001308cdf088,阅读核心 a2a-agent.ts、JS client、signature helper、server handler/store/protocol/tasks 与部分核心测试。属于源码级实践证据;没有安装 SDK、运行上游测试、取 live Card 或部署互通。GitHub API 当次为 mastra-ai/mastra 29,198 stars,仅作发现信号,不构成安全质量证明。

一阶机制:发现、信任、凭据、状态是四条链

1. Bootstrap 缓存的是下一跳,不只是介绍文本

核心 #getBootstrap() 在缓存存在且未要求 forceRefresh 时直接返回;实际抓取后才调用可选 verifyAgentCard.verify(card, {cardUrl, fetchedAt}),再将 card.url 作为 execution URL,并缓存 streaming 能力。

因此,固定代码中没有在这个 cache-hit 分支重新验签/重取 Card。不能把 fetchedAt 参数当成实现了 TTL,也不能把 refresh 能力当成每次调用都刷新。上游测试可见 hook 成功/拒绝与缓存复用,这是读到的测试源码,不是本机测试通过记录。若信任撤销或 endpoint 轮换,应用需要定义刷新、失效、重新授权策略。

2. 两种 verifier API 的语义不同

路径固定代码行为不能据此推断
核心 A2AAgent提供可选自定义 verifyAgentCard.verify hook;抛错会阻止 bootstrap 完成hook 存在就强制签名、默认可信 key 来源或持续撤销检查
JS client verifyAgentCardSignatureIfPresent无 signatures 时直接返回;有签名时 canonicalize 去签名 payload,检查算法,通过调用者 keyProvider 验证;任一签名成功即返回“配置了此 helper”就拒绝 unsigned Card、多签门限或所有签名者都可信

后一个 helper 把 kid/jku 交给 keyProvider,而不是替调用者建立信任。可信域/密钥 allowlist、是否必须 signed、轮换/撤销、多个 signer 的接受策略,都要在应用层显式定义。和 a2a-signed-card-trust-bootstrap 的“可信 key 引导”互补,不能将两个客户端 API 拼成一个理想的强制验签流程。

3. Card 验签不等于 execution URL 有权接收同一凭据

核心 #sendMessage POST 到 bootstrap 的 execution URL;共享 #request 会合并实例 headers 与单次 headers。因而评审必须核对:发现源、被声明的执行地址、实际请求 origin,以及这个 origin 是否有权接收所配置的 Bearer。一个 Card 可以签名正确,却仍不满足调用者的目标 allowlist 或 credential audience。

这里是接收方绑定的审查点,不是已确认的凭据外泄漏洞。未审完整 redirect/网络部署/自定义 fetch,也没有向第三方发送凭据。与 MCP-Gateway-Runtime 的逐跳凭据接收方和 a2a-cli-client-timeouts-fallback-and-credential-scope 的发现/调用分验采用同一合同。

4. 版本化内存状态与 durable replay 不等价

固定 InMemoryTaskStore 用 agentId-taskId 字符串键和进程内 Map 保存 task/version/listeners,expectedVersion 可拒绝旧版本覆盖;get/list 的这层接口没有 caller/tenant 参数。route 声明 requiresAuth: true 只说明存在认证接点,不能替代 task owner 的资源授权审计,更不能仅凭底层无身份字段就断言生产跨用户越权。

handleTaskResubscribe 先发送当前 task 快照,再等待更高版本并生成 artifact/status 差分;终态或 interrupted 状态结束本次流。它证明了一种“快照 + 后续更新”机制,不证明断线前每个事件都可重放、跨进程共享监听、执行接管或业务 exactly-once。与 a2a-python-cluster-state-stream-execution-boundaries、rasa-a2a-state-affinity-and-push-delivery-boundaries 对读。

服务端固定代码还按 A2A-Version 区分缺省/0.3 与 1.0、拒绝不支持值,Card 响应含 Vary: A2A-Version;这是该版本实现的路由兼容证据,不是所有客户端和 binding 已完成互通认证。

对 Hermes / 网关的可执行启发

以下是隔离环境验收设计,不是本轮已经运行的结果:

  1. Card 信任矩阵:unsigned、可信签名、未知 key、篡改 payload、可信 signer 但不允许的 execution origin;分别记录 discovery 成功、hook 运行、最终 dispatch 与零额外外发。
  2. 撤销时点矩阵:同一对象 cache-hit、强制 refresh、替换 Card/密钥和 policy epoch 后分别调用;要求撤销 deadline 明确,不能仅记 hook 曾运行。
  3. owner 矩阵:用户 A 建任务,用户 B 与匿名分别 get/list/resubscribe/cancel/push-config;拒绝时检查 task 内容和真实 effect 均未泄漏/改变。agentId 不是用户身份。
  4. 恢复矩阵:断流、同进程重连、换 pod、重启、远端取消;分别记录 snapshot、后续事件、业务执行者与副作用计数,不用 SSE 200 代替完成。

这些合同可以进入 A2A-Agent2Agent-Protocol / Agent-Benchmarks 的题池;本轮不修改 Hermes 活跃插件、生产 gateway、凭据或权限。

失败边界与未解问题

  • Hook 可选、signature-if-present 接受 unsigned,都是 API 语义,不自动意味着缺陷;要看部署策略是否要求更强保证。
  • 缓存生命周期与请求级授权必须分别设计;合法旧签名不表示当前权限仍有效。
  • task store 的本地版本检查不等于持久 CAS、租户隔离、跨 pod 事件总线或恢复执行。
  • 尚未核完整 host middleware、实际 keyProvider、每种客户端/binding、push 安全及企业部署。不能将本页作为用户实例漏洞报告或生产控制已上线证明。

写入记录

  • 2026-10-30 09:11 CST:新增固定 Mastra A2A 实现深读,拆分 bootstrap cache、两种验签语义、凭据接收方及 task/stream/durability 边界;附隔离验收矩阵,未声称本机 SDK/E2E 通过。