A2A webhook 派发与签名。
A2A 协议 webhook 长任务交互机制 — 给 >5 分钟长任务用 (SSE 在家庭路由器 / NAT 下会被超时杀掉,webhook 是唯一可靠路径)。
设计
- 异步派发:
tasks/sendWithWebhook立即返回taskId + accepted; Agent 任务在后台 Task 内执行;状态转变时 POST 给callbackUrl - 4 类回调(payload
event字段):task.accepted— Task 接受,session 已创建task.statusUpdate— Agent 运行中状态变化(working / tool_calling)task.artifactUpdate— 流式 message_delta(可选)task.completed— Agent 正常结束 + 完整回复task.failed— Agent abort / timeout / error
- HMAC-SHA256 签名:用户传
webhookSecret,CMDC 在X-CMDC-Signatureheader 带sha256=<hex>,回调方可验签防伪造 - 重试:3 次指数退避(1s / 2s / 4s),失败写入 dead letter log
v0.4.0 实现说明
- HTTP 客户端用 Erlang 内置
:httpc(零额外依赖) - WebhookDispatcher 用
Task.start_link异步运行 + EventBus subscribe - 不持久化(内存);进程崩溃后失败的 callback 不补发(留 v0.4.1)
使用
# 1. 客户端发起
POST /v1/a2a/tasks/sendWithWebhook
{
"id": "task-abc",
"callbackUrl": "https://my-app.com/webhook",
"webhookSecret": "my-shared-secret",
"message": {"role": "user", "parts": [{"type": "text", "text": "..."}]}
}
# 2. CMDC 立即返回
→ 202 {"jsonrpc": "2.0", "id": ..., "result": {"taskId": "task-abc", "status": "accepted"}}
# 3. 后台异步派发回调
POST https://my-app.com/webhook
X-CMDC-Signature: sha256=<hex>
Body: {"event": "task.completed", "taskId": "...", "result": {...}}
# 4. 客户端验签
assert verify_signature(body, header, "my-shared-secret")
Summary
Functions
构造 Webhook 标准 payload。
同步 POST 一个 webhook payload,失败按指数退避重试 3 次。
对 JSON-encoded body 用 secret 签名,返回 sha256=<hex>。
校验签名。expected_header 是收到的 X-CMDC-Signature 值。
Types
Functions
构造 Webhook 标准 payload。
@spec dispatch(callback_url(), payload(), keyword()) :: :ok | {:error, term(), pos_integer()}
同步 POST 一个 webhook payload,失败按指数退避重试 3 次。
选项
:secret— 启用 HMAC 签名(推荐):timeout_ms— 每次请求超时(默认 5_000):dispatch_fn— 注入替代 HTTP 函数(用于测试)
返回 :ok 或 {:error, reason, attempts}。
对 JSON-encoded body 用 secret 签名,返回 sha256=<hex>。
适合放进 X-CMDC-Signature header。
校验签名。expected_header 是收到的 X-CMDC-Signature 值。
使用 :crypto.hash_equals/2(OTP 25+)做常数时间比较防 timing attack;
老 OTP 自动降级到 byte_size + 逐字符比较。