第四步:Webhook 与生产化约束¶
前几章解决的是功能建模。这一章把入口暴露到 HTTP 网络上,因此要同时处理鉴权、验签、 请求体校验、出站回调限制和观测能力。
更接近生产的配置¶
[runtime]
adapters = ["webhook"]
plugins = ["src/echo_runtime/plugins/echo.py:EchoPlugin"]
superusers = ["webhook-admin"]
[adapter.webhook]
host = "0.0.0.0"
port = 8090
path = "/events"
access_token = "change-me"
signature_provider = "github"
signature_secret = "change-me-too"
reply_url_allowlist = ["hooks.example.com"]
[plugin.management]
allow_reload = true
allow_introspection = true
reload_requires_superuser = true
introspection_requires_superuser = true
这个配置刻意显式写出安全边界:入口需要 token 和签名,管理能力需要 superuser, 出站回调只允许明确域名。
请求进入时发生什么¶
Webhook 请求进入后会经过以下步骤:
HTTP request
-> access token check
-> signature verification
-> JSON/content-type validation
-> payload normalization
-> Event dispatch
-> optional reply_url delivery
任何一步失败,适配器都会记录指标和审计事件,便于排查是鉴权失败、签名失败还是 payload 不符合预期。
验签 provider¶
WebhookAdapter 支持三类签名策略:
generic自定义 HMAC-SHA256 请求头,适合内部系统或没有标准 provider 的平台。
github兼容 GitHub 的
X-Hub-Signature-256。stripe兼容 Stripe 的
Stripe-Signature,包含时间戳窗口和防重放检查。
如果平台本身已有标准签名格式,优先使用 provider;如果没有,再使用 generic。
出站回复¶
Webhook 事件可以携带 reply_url,但它本质上是外部输入,不能默认信任。iamai 的默认策略
会拒绝 private/loopback 地址、非 HTTPS 地址和重定向。生产环境应使用最小范围的
reply_url_allowlist。
上线前检查¶
运行:
uv run python -m iamai --config config.toml config-check
确认以下项都成立:
非 loopback 监听配置了
access_token;对公网 webhook 配置了
signature_secret;reply_url_allowlist只包含可信域名;管理插件命令只允许 superuser 执行;
/metrics能看到请求、拒绝、handler 执行和错误指标;日志里能看到结构化 audit 事件。
Checkpoint¶
到这里你已经把一个本地 Runtime 推进到可暴露网络的形态。后续新增平台时,优先把协议差异放进 Adapter,把业务逻辑留在 Plugin。