状态 —— P2-A 已完整落地:回调
(1) 用 HMAC-SHA256 可签名(
X-BeeOS-Signature),
(2) 失败按文档化的指数退避重试(1m → 5m → 30m → 2h → 12h,
之后死信),
(3) 通过 GET .../deliveries 和
POST .../deliveries/{id}/redeliver 可审计 + 可重放。单次尝试 10s 超时仍按尝试生效,但失败尝试现在能熬过进程崩溃,
由后台重试 worker 接管。
1. 生命周期
2. 注册 webhook
请求
响应(201)
token 和 secret 在任何 list / get 响应里都永不返回 ——
注册后仅服务端使用。has_secret 布尔值让 UI / SDK 不暴露 secret
本身的情况下显示 “已启用签名”。轮换 secret 用一个新值 Set 即可
(传 "" 不影响现有 secret —— 要清空请 DELETE + 重新注册)。
列出 / 删除
3. 当前限制
约束投递契约的几点:4. Payload 格式
BeeOS 支持三种 renderer,由注册时的protocol_filter 列选择。
通过此 OpenAPI 端点注册的 webhook 始终用 openapi renderer。
openapi renderer —— TaskEvent 信封
匹配 GET /tasks/{id}/events 的 SSE 流 payload,可共享 decoder。
每次状态切换(中间态 + 终态)都发。
status 值:queued、running、input_required、
auth_required、completed、failed、canceled、timeout、
rejected(见
调用智能体 §状态参考)。
final: true 仅在终止态。
a2a 与 generic renderer
供 A2A JSON-RPC 客户端和无 protocol filter 注册的通用 / 旧版订阅者
使用。OpenAPI Gateway 不可选 —— 仅文档参考。详见
webhook_renderer.go。
5. 接收方 checklist
针对当前投递语义,生产级接收方需要:- 跑在 HTTPS 上。 无例外 ——
token和secret在传输中都敏感。 - 先验签(
X-BeeOS-Signature)—— 注册了secret时。用 §6 的 recipe。不匹配或时间戳过旧返回401。 - 再验可选的
Authorizationbearer token —— 注册了token时。 不匹配返回401。 - 1s 内 ACK(10s 上限)。payload 内部排队;不在 inline 跑业务逻辑。
- 保持幂等 —— retry 调度器可能引入重复(at-least-once 投递)。
按
(task_id, status, timestamp)作为 key,在接收方去重。 - 对必须不丢的终止态 webhook(如计费相关),周期性轮询
GET /tasks/{id}。Webhook 是”快速通知”;API 是”真相”。 - 丢弃未知
type值 —— renderer 的扩展按 ADR-0017 是加法(额外 字段 / 新类型);不要对新事件类型崩溃。
6. HMAC 签名(P2-A)
注册时设secret 开启 HMAC-SHA256 body 签名(见 §2)。投递器随后
在每次回调上发两个额外 header:
X-BeeOS-Event: task.stateX-BeeOS-Task-Id: ch-uuidX-BeeOS-Signature: t=<unix>,v1=<hex>,其中hex = hmac_sha256(secret, "<unix>." || body)
X-BeeOS-Webhook-Format、可选的
Authorization: Bearer <token>)不变。轮换期可同时叠加签名 +
bearer token。
验证 recipe —— Python
验证 recipe —— Node.js / Express
为什么有 ±5 分钟时差检查?
没有时间戳检查时,捕获一次有效回调的攻击者可以永久重放。拒绝过旧或 未来日期的时间戳缩小重放窗口。±5 分钟是 BeeOS 推荐值;封闭网络 接收方可以更严。7. 投递审计日志 + 手动重放(P2-A part 3)
每次回调尝试现在都被记录为一行持久行,可通过两个 REST 端点列出和 重放。这些行能熬过进程崩溃(lease 过期后 retry worker 接管), 所以临时部署 / 滚动重启不再丢失飞行中的回调。重试日程
失败一次后,按固定退避重排:
行在尝试之间保持
pending(schedule 触发时 worker 把它从 failed
翻回 pending)。
POST .../redeliver 手动重放无论之前自动重试
多少次,都会把行 clone 为一行新的 pending。
生命周期状态
GET .../deliveries
limit 钳制到 [1, 200],默认 50。
token、HMAC secret 永不返回 ——
只有诊断字段。要验证接收方应该看到什么,请查阅你自己的源任务
状态切换日志;renderer 字段告诉你应用了哪个 payload schema。
POST .../redeliver
failed 或 dead_letter clone 为一行新的 pending。clone
重发完全一致的 payload 字节(和 HMAC 签名)—— 接收方可以把
手动重放的回调和自动重试同等对待。
返回 202 + 新 pending 行:
每次 redeliver 调用都入队一行新的 pending —— 反复调用产生反复
投递。把此端点当作手动救场,不是调用方侧”网络出错就重试”的工具。
另请参阅
- 调用智能体 —— 提交触发 webhook 的任务
- 错误参考 —— webhook CRUD 端点返回的 code
- 认证与 API Key —— 注册端点所需凭证
backend/services/a2a/pkg/application/webhook_renderer.go—— payload renderer 源码backend/services/a2a/pkg/application/task_service.go的firePushWebhooks—— 投递循环backend/services/a2a/pkg/application/webhook_delivery_worker.go—— 重试队列 worker