add(websub): ship a self-hub for this site's two feeds - #141
Merged
Conversation
WebSub was republished as a W3C Recommendation on 2 June 2026 (new XSS mitigations in Security Considerations), and the repo had no page for it despite covering feed discovery and feed hygiene. rel="hub" and rel="self" are both IANA-registered link relations and auditable from outside the site, so this is a website property rather than a build technique. Status: optional — it only applies if you publish a feed and have a hub to point at; the platform does not break without it. - new page src/content/spec/foundations/websub.md (order 130, after feed-discovery/110 and feed-hygiene/120) - relatedSlugs wired on feed-discovery, feed-hygiene and link-headers - changelog entry 2026-07-30-websub.md - OG images regenerated; SKILL.md page count 164 -> 165 + digest resigned Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Makes the site a worked example of /spec/foundations/websub/ rather than a page describing something we do not run. A *self-hub*: hub.topic is allowlisted to /rss.xml and /changelog/rss.xml, so the endpoint cannot fetch arbitrary URLs or relay anyone else's content. That constraint is what makes a hub reasonable to run here — an open hub is a public service with a very different risk profile. - functions/websub.ts — subscribe/unsubscribe (WebSub 5.1), verification of intent (5.3), content distribution with Link rel=hub/self and an X-Hub-Signature HMAC-SHA256 (5.4), plus a bearer-gated hub.mode=publish (the PubSubHubbub 0.4 convention; WebSub 4 leaves this out of scope). - migrations/0001 — D1 subscription store. Leases expire lazily at distribution time, so the hub needs no cron trigger. - .github/workflows/websub-ping.yml — pings the hub after a content push, mirroring deploy-mcp.yml's trigger. Waits for the Pages deploy first; distributing a stale feed is worse than distributing late. - scripts/test-websub.mjs (npm run test:websub) — 35 assertions over the callback guard, lease clamping, constant-time token compare, and the HMAC against the RFC 4231 known vector. - privacy.astro — subscribing is the first thing on this site that stores data a visitor gives us. Says what is kept, for how long, and who sees it. The D1 binding is committed COMMENTED OUT and the hub 503s without it, so this is inert and safe to merge before the database exists. The rel="hub" advertisement is held back to the same commit that provisions D1 — shipping a hub link with nothing behind it is the exact failure the spec page warns about. wrangler.toml carries the go-live checklist. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts: # public/.well-known/agent-skills/index.json
The register recorded WebSub as turned down (too-narrow). This PR adds the page, so per the workflow #121 defines the entry is deleted in the same PR that supersedes it. The `too-narrow` group now has no entries and drops out of /considered/ on its own — considered.astro filters empty groups. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts: # package.json
Deploying specification-website with
|
| Latest commit: |
dd29c6c
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://8880cace.specification-website.pages.dev |
| Branch Preview URL: | https://add-websub-hub-2026-07-30.specification-website.pages.dev |
jdevalk
added a commit
that referenced
this pull request
Jul 31, 2026
…#143) The changelog records when *this spec* changed, not when the world did, so `date` is the day the entry lands on main. Three entries kept the date their PR was authored (28th, 28th, 30th) though all three merged on the 31st. - Re-date tdmrep, mcp-handshake and websub to 2026-07-31, renaming each file so the stem still matches. All three went live under an hour ago, so the GUID churn this causes in the changelog feed is as small as it will ever be. - Add a `changed` entry for ARIA in HTML reaching Recommendation and "Using ARIA" being discontinued (#138). - Add a `changed` entry for the expired aipref attachment draft (#131). That one was merged unlogged; it rewrote what the page says about the topic's standing, which is not a citation repair. - Record the dating rule in CLAUDE.md, including why renaming a live entry is not free: the filename stem is `entry.id`, which is both the `<li>` anchor and the feed's `#`-link. Deliberately not logged: #122 (worked-example callout gained a sentence; the recommendation did not move) and #141 (plumbing, and the hub is not live — fold it into the WebSub entry when D1 is provisioned). Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reopens #137, which GitHub auto-closed when its stacked base branch (
add/websub-2026-07-30) was deleted on merging #136. Same branch, same commits, now targetingmainwithmainmerged in.The full design rationale is in #137 — self-hub allowlisted to our own two feeds, SSRF-hardened callback guard, no delivery without the
hub.challengehandshake, no retry queue.Ships inert — safe to merge now
Verified rather than assumed:
[[d1_databases]]block inwrangler.tomlis commented out, so there is no binding.functions/websub.tsreturns 503 on every path when the binding is absent.rel="hub"blocks inpublic/_headersare commented out, so nothing is advertised.grepconfirms no liverel="hub"reaches the built output.Nothing changes for visitors on merge. The hub goes live in a separate commit that provisions D1 and uncomments the advertisement together — the seven-step checklist is in
wrangler.toml.Conflict resolved on the way in
package.jsonconflicted: this branch addstest:websubimmediately after thesign:ard/check:ardlines that #139 deleted earlier today. Resolved by keepingtest:websuband dropping bothardscripts. Confirmed afterwards that neithersign:ardnorcheck:ardsurvives,astrois still^7.1.1, and thejwks.jsonblock did not come back into_headers.Checks
npm run test:websub✓ (all assertions, including the HMAC against the RFC 4231 test-case-2 known vector) ·npm run build✓ ·npm run lint✓ ·npm run format:check✓ ·npm run check:skill✓ (166 pages, 10 categories, MCP 2026-07-28)Still not exercised end to end. Subscribe → verify → distribute needs a live D1 binding, which I cannot provision. #137's body said the same and it is still true — worth repeating rather than letting the green ticks imply otherwise.
Two things only you can do
Paste the id into
wrangler.toml, uncomment the block, then:WEBSUB_PUBLISH_SECRETis needed as both a Pages secret and a GitHub Actions repo secret.Changelog
None here. #136's
addedentry covers the page; #137 suggested extending it once the hub is live rather than logging plumbing separately, which still seems right — but it is your call.🤖 Generated with Claude Code