状态 Webhook¶
status.openbot.run 上的状态页面可以将其事件和服务变更发送给 OpenBot 团队的
工具,例如 Slack 频道或值班服务。这些 webhook 仅供团队内部使用。
公开关注该页面的方式是它的订阅源:https://status.openbot.run/feed.xml。
status-page 工作流中的 notify 任务负责发送这些 webhook。它在每次发布
页面之后运行,因此新事件大约在发生后 5 分钟发出,或有人在 status issue
中编辑后不久发出。GitHub 可能会延迟启动计划运行。
添加订阅¶
订阅位于仓库密钥 STATUS_WEBHOOKS 中,格式为一个 JSON 列表:
[
{ "id": "oncall", "url": "https://example.com/hooks/status", "secret": "<at least 32 characters>" },
{ "id": "team-slack", "url": "https://hooks.slack.com/services/…", "format": "slack" },
{ "id": "ops-discord", "url": "https://discord.com/api/webhooks/…", "format": "discord", "events": ["incident.created", "service.changed"] }
]
| 字段 | 是否必需 | 含义 |
|---|---|---|
id |
是 | 由 1 到 40 个小写字母、数字和 - 组成的名称。日志和任务摘要中用它代替 URL 显示。不要更改它:待发送的投递和投递 ID 都会用到它。 |
url |
是 | 地址。必须使用 https。 |
secret |
仅 json 格式 |
签名密钥,至少 32 个字符。用 openssl rand -hex 32 生成。 |
format |
否 | json(默认)、slack(传入 webhook)或 discord(频道 webhook)。 |
events |
否 | 要发送的事件类型。不设置则发送全部事件和维护事件。 |
paused |
否 | true 会停止投递。待发送的投递会被移除。 |
要更改列表,请整体替换该密钥:
gh secret set STATUS_WEBHOOKS -R nightly-labs/openbot < webhooks.json。然后删除 webhooks.json。
该密钥不会出现在日志中:任务会在做任何其他事情之前屏蔽每个 URL 和密钥。列表中的
错误只会指出条目编号、字段或 id。无效的列表会停止任务,且任务不会发送任何内容。
要检查订阅,请从 Actions 选项卡运行 status-page 工作流,并勾选输入项 Also send a test
event to each active status webhook。每个未暂停的订阅都会收到一个 test 事件。
事件¶
| 类型 | 触发时机 |
|---|---|
incident.created |
新建事件 issue。 |
incident.updated |
事件下新增评论。带有 Resolved 阶段的评论不会单独发送:incident.resolved 会携带它。 |
incident.resolved |
事件 issue 关闭。它会携带最终更新。 |
incident.removed |
未解决的事件离开页面,例如误报以“不计划修复”关闭,或 issue 没有 status 标签。 |
maintenance.scheduled |
新建维护 issue。 |
maintenance.started |
维护窗口开始。 |
maintenance.updated |
维护 issue 下新增评论。 |
maintenance.completed |
维护窗口结束,或 issue 在结束前关闭。 |
maintenance.cancelled |
尚未结束的维护离开页面,例如在开始前关闭的 issue。 |
service.changed |
服务检查结果发生变化,例如从 ok 变为 down。仅在请求时发送。 |
test |
来自工作流输入项的测试事件。始终发送。 |
service.changed 不在默认事件中。一次检查可能因网络错误而失败一次,
而连续 2 次运行失败会打开自动事件,从而发送 incident.created。
incident.removed 和 maintenance.cancelled 会比实际变更晚一个运行周期发出,因为
计划运行和 issue 运行可能在较新的页面之后发布较旧的页面。重新打开的事件 issue
不会发送事件。它的下一条评论会发送 incident.updated,下一次关闭会再次发送
incident.resolved。
在没有 status-notify 分支的仓库上,第一次运行不会发送任何内容。它会将页面上
已有的事件标记为已发送。
载荷¶
json 订阅会收到以下版本 1 的请求体。后续的版本 1 请求体可能会增加新字段。
字段的含义不会改变。
{
"version": 1,
"id": "incident.updated:201:2026-10-16T11:00:00Z:identified",
"type": "incident.updated",
"createdAt": "2026-10-16T11:05:12.000Z",
"page": "https://status.openbot.run",
"incident": {
"id": 201,
"url": "https://github.com/nightly-labs/openbot/issues/201",
"title": "Invitation links fail",
"kind": "incident",
"impact": "partial",
"services": [{ "id": "accounts", "name": "Accounts and sign-in" }],
"startedAt": "2026-10-16T10:30:00Z",
"resolvedAt": null,
"update": { "at": "2026-10-16T11:00:00Z", "stage": "identified", "body": "A bad cache rule." }
},
"service": null
}
id用于标识事件。每个订阅收到的事件 ID 相同。createdAt是发现该事件的那次运行的时间。- 对于
service.changed和test,incident为null。incident.update是 该事件所发布的最新更新,或最新的更新。 - 仅
service.changed会设置service:{ "id", "name", "previous", "current", "checkedAt" }。状态结果为ok、degraded、down或unknown。 impact为degraded、partial、major或maintenance。stage为investigating、identified、monitoring、update、resolved、scheduled或completed。
slack 和 discord 订阅会收到一条带链接的简短消息。Discord 消息不会提及任何人,
即使事件文本中包含 @everyone。
请求头和签名¶
| 请求头 | 值 |
|---|---|
x-openbot-event |
事件类型。 |
x-openbot-delivery-id |
32 个十六进制字符。同一事件对同一订阅的每次重试都相同。 |
x-openbot-timestamp |
发送时间,Unix 秒。 |
x-openbot-signature |
当订阅有密钥时,为 sha256=<hex>。 |
签名是以密钥为 key、对 <timestamp>.<delivery>.<body> 计算的 HMAC-SHA256。
这与 OpenBot webhook 例程的格式相同。接收方必须:
- 根据原始请求体计算签名,并以恒定时间进行比较。
- 拒绝与自身时钟相差超过 5 分钟的时间戳。
- 保留最近 2 天的 delivery ID;对重复投递直接返回
200,而不再次执行处理。
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(headers: Headers, body: string, secret: string) {
const timestamp = headers.get("x-openbot-timestamp") ?? "";
const delivery = headers.get("x-openbot-delivery-id") ?? "";
const expected = `sha256=${createHmac("sha256", secret).update(`${timestamp}.${delivery}.${body}`).digest("hex")}`;
const actual = headers.get("x-openbot-signature") ?? "";
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
return fresh && actual.length === expected.length && timingSafeEqual(Buffer.from(actual), Buffer.from(expected));
}
重试¶
只有 10 秒内返回 2xx 响应才算成功。重定向视为失败。失败后,后续重试会在后续运行中进行,分别间隔 5、10、20、40、80、160 和 320 分钟:总共 8 次重试,约 11 小时。之后该 delivery 会被移除,任务摘要会以警告形式显示它。一次失败的重试会暂停该订阅的其他 delivery,直到下一次运行。每个订阅在一次运行中最多重试 25 次。
事件可能乱序到达,接收方也可能再次收到同一事件。请使用 delivery ID 和 incident.update.at 时间,而不是按到达顺序判断。
同一时间只运行一个 webhook 任务,GitHub 只保留最新的等待中的任务。最新任务无法再次看到的服务变更,以及测试事件,可能会丢失。如果测试事件没有到达,请重新运行工作流。
状态¶
任务将其状态保存在 status-notify 分支上的 notify.json 中,作为每次运行都会替换的一个提交。它保存订阅 ID、发现的事件 ID,以及带请求体的等待中的 deliveries。它绝不保存 URL 或密钥。该仓库是公开的,因此任何人都能读取订阅 ID、每个等待中的 delivery 的重试时间和最后错误(如 HTTP 500)。请使用不暴露任何私有信息的 ID。如果删除该分支,下一次运行将重新开始:它会把所有事件标记为已发送、不发送任何内容,并丢失等待中的 deliveries。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 openbo/openbot