embed 更新 hook:语料变了告诉它的 embed
状态: 已在 v0.1.76 发布(2026-09-27)—— 设计与落地记录见 StandMeet 仓库的 docs/design/event-bus-outbox-webhooks.md。standmeet.com 那一侧在它自己的仓库里已写好,尚未部署。
embed 的更新 hook 是挂在 embed 上的一个 webhook 端点,范围就是这个 embed 的访问码。范围内的笔记一变,展示它的站点就会知道,并且只重读变了的部分。
想法从哪来
standmeet.com 通过一个 embed 把 owner 的语料展示成博客。它原本无从得知语料变了:盘点里“embed 的消费方(standmeet.com)”一栏的机制是“无”。更新 hook 是事件总线计划的第三期,standmeet.com 是它的第一个消费方。
一个字段,一个挂上的端点
embeds.create 和 embeds.update 接受 update_hook_url;embed 表单里有「更新 hook 地址」字段。填了就创建或更新这个 embed 的端点(这个 upsert 有自己的 UT):
event_types = ["corpus.note.changed"]。- 设上
embed_id,范围取自 embed 的访问码:角色 glob 减去码的 deny。由 access 域回答(EmbedAdmits),用的就是所有读取都走的entity.AllowsCorpusEntry。码被吊销或过期、embed 被删,一律不放行。raw://永不出。 - 响应带
update_hook {endpoint_id, url};签名密钥只在端点刚创建时返回。 - payload 是 thin 的;接收方用 embed 本来就用的授权 API 读正文。embed 怎样在不携带访问码的前提下认证,见 embed-credential-never-carries-the-code。
/api/v1/corpus-cards带updated_at(精确到秒),接收方据此判断哪些卡片变了。
erDiagram access_codes ||--o| embeds : "exposed by" embeds |o--o| webhook_endpoints : "update hook"
standmeet.com 收到更新的全过程
sequenceDiagram
autonumber
actor Owner
participant SM as sijie.xyz (StandMeet)
participant W as standmeet.com Worker
participant KV as Workers KV
actor V as Visitor
Owner->>SM: edit a note in the standmeet subtree
SM->>SM: trigger → outbox → relay → webhook.fanout
SM->>SM: EmbedAdmits, inside LANDING-BLOG's code scope ✓
SM->>W: webhook.deliver POST /api/corpus-hook (signed)
W->>W: verify signature (±5 min), skip a webhook-id seen in the last 24 h
W->>SM: GET /api/v1/corpus-cards (with updated_at)
W->>KV: diff against the index, find changed / new / gone
loop each change × zh/en
W->>SM: GET /api/v1/wiki/{path}?lang=
W->>KV: store the note
end
W->>KV: delete gone notes, update the index, remember the webhook-id
W-->>SM: 2xx
V->>W: GET /zh/blog/…
W->>KV: read note + index
W-->>V: HTML rendered per request (links resolved against the current index)
- 博客页、RSS、博客 sitemap 和“最新笔记”都在请求时从 Workers KV 渲染(Astro 的 Cloudflare 适配器)。只有博客路由设
prerender = false;落地页上的“最新笔记”是一个 server island。 POST /api/corpus-hook按 Standard Webhooks 验签,时间戳窗口 ±5 分钟;按webhook-id去重(在 KV 里记 24 小时);重新列出卡片,只抓updated_at变了的条目,两种语言都抓。- 链接在请求时按当前索引解析,重命名或删除不会留下失效链接。
- KV 为空时,第一个请求从实例把它填满(自举)。没有定时任务。
- 部署需要 KV namespace id 和
CORPUS_HOOK_SECRET。 - standmeet.com 这一侧在另一个仓库(
atmaxmoj/standmeet-landing)。
不要对账任务
owner 决定不要任何对账循环。送达要么有保证,要么失败看得见:outbox、持久任务、面板告警已经兜住(message-loss-guarantees)。对账是在掩盖不可靠的投递;投递可靠了就不需要。
验收
- e2e
embed-update-hook(已通过):embed 表单填 hook → 改笔记 → sink 收到;cards 带updated_at;改范围外的笔记不会到达 hook(哨兵式)。 - 真环境(未完成):在 sijie.xyz 改笔记 → 60 秒内 standmeet.com 页面更新,不部署。部署 standmeet.com 之后验证,归档到
docs/real-env-verification/。见 events-roadmap · events-test-plan。