被 webhook 唤醒的 Agent
kind: webhook 的钩子是一次带签名的 POST,发到你指定的地址;另一头有程序运行 Agent。把它写进 Beacon 的配置:
agents:
- name: claude-oncall
kind: webhook
url: https://agents.example.com/hooks/beacon
secret_file: /etc/beacon/secrets/agent-hook-secret # 至少 24 个随机字符钩子什么时候被调用、各字段是什么含义、怎样重试、每次调用的结果记在哪里,见让 Agent 值班。另一头的 beacon-oncall 接收 webhook、校验签名、启动 Agent(无头 Agent 与 CI)。
调用
Beacon POST 一段 JSON,带这些请求头:
| 请求头 | 值 |
|---|---|
X-Beacon-Signature | sha256= 加上用密钥对请求体算出的 HMAC-SHA256(十六进制) |
X-Beacon-Event | firing 或 resolved |
X-Beacon-Delivery | 这次调用的 id,每次重试都一样 |
{
"delivery": "9f3c2a1b7e4d5c60",
"event": "firing",
"at": "2026-11-02T02:00:31Z",
"hook": "claude-oncall",
"alert": {
"id": "0cdfbada62dd", "status": "firing", "severity": "critical",
"title": "git.example.com is down", "description": "git.example.com has failed for 3 minutes from at least half of the probe locations …",
"service": "git.example.com",
"labels": {"alertname": "ServiceDown", "service": "git.example.com", "severity": "critical", "failure_domain": "origin-1"},
"alerts": 1, "starts_at": "2026-11-02T01:58:00Z"
},
"links": {
"alert": "https://status.example.com/api/v1/alerts/0cdfbada62dd",
"evidence": "https://status.example.com/api/v1/alerts/0cdfbada62dd/evidence",
"timeline": "https://status.example.com/api/v1/alerts/0cdfbada62dd/timeline",
"mcp": "https://status.example.com/mcp",
"runbook": "https://github.com/example/ops/blob/main/runbooks/ServiceDown.md"
},
"hint": "Beacon woke you for this alert group. Get its evidence in one call: …",
"untrusted": {"fields": ["alert.title", "alert.description", "alert.service", "alert.services", "alert.labels"], "note": "…"}
}如果告警是关于一个心跳的,而目录里这个心跳写明了它所在的机器(见心跳),firing 调用的 hint 会说出是哪台机器,并提示用 list_machines 查看怎么连上去。
resolved 的载荷字段相同,其中 at 是告警组恢复的时间(不是 resolved_after 之后调用发出的时间),另外多一个字段 firing_call:这一轮里叫醒 Agent 的那次调用,用来认出那次调用启动的运行,并说明那次运行是否可能还没结束:
"firing_call": {"delivery": "9f3c2a1b7e4d5c60", "at": "2026-11-02T02:00:31Z", "status": "ok", "recent": true}触发调用发生在告警组恢复前不到十分钟时,recent 为 true。这时 hint 会提醒:那次调用启动的运行可能还在写备注,先读 get_timeline(包括其中的备注),再写自己的收尾备注。
校验签名
自己写接收程序的话,先用原始请求体校验签名,再解析内容;at 超过十分钟的、delivery 已经见过的,一律拒绝:
import hashlib, hmac
def genuine(secret: bytes, body: bytes, signature: str) -> bool:
return hmac.compare_digest("sha256=" + hmac.new(secret, body, hashlib.sha256).hexdigest(), signature)载荷里没有任何凭据:Agent 用自己的令牌查询证据。这个令牌属于一个预设为值班的 Agent,能读证据、认领告警、写备注(无头 Agent 与 CI)。
信任边界
接收程序只校验签名、启动程序,它决定的就这么多。Agent 之后能做什么,取决于它的预设、它所有者的角色,以及它能连哪些机器。每次调用、每条命令、每次会话都经过 Beacon,Beacon 把它们记下来,在卡片和告警群里给人看;暂停开关能让它停下。载荷里没有凭据;接收程序和包装脚本只从文件读取密钥,既不记录密钥,也不记录载荷。提示词要求 Agent 把来自 Beacon 之外的内容当作数据,Beacon 也把这些字段标为 untrusted。