跳转至

状态 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 例程的格式相同。接收方必须:

  1. 根据原始请求体计算签名,并以恒定时间进行比较。
  2. 拒绝与自身时钟相差超过 5 分钟的时间戳。
  3. 保留最近 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