Skip to content

add(websub): ship a self-hub for this site's two feeds - #141

Merged
jdevalk merged 7 commits into
mainfrom
add/websub-hub-2026-07-30
Jul 31, 2026
Merged

add(websub): ship a self-hub for this site's two feeds#141
jdevalk merged 7 commits into
mainfrom
add/websub-hub-2026-07-30

Conversation

@jdevalk

@jdevalk jdevalk commented Jul 31, 2026

Copy link
Copy Markdown
Owner

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 targeting main with main merged 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.challenge handshake, no retry queue.

Ships inert — safe to merge now

Verified rather than assumed:

  • The [[d1_databases]] block in wrangler.toml is commented out, so there is no binding.
  • functions/websub.ts returns 503 on every path when the binding is absent.
  • The rel="hub" blocks in public/_headers are commented out, so nothing is advertised. grep confirms no live rel="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.json conflicted: this branch adds test:websub immediately after the sign:ard / check:ard lines that #139 deleted earlier today. Resolved by keeping test:websub and dropping both ard scripts. Confirmed afterwards that neither sign:ard nor check:ard survives, astro is still ^7.1.1, and the jwks.json block 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

npx wrangler d1 create specification-website-websub

Paste the id into wrangler.toml, uncomment the block, then:

npx wrangler d1 migrations apply specification-website-websub --remote

WEBSUB_PUBLISH_SECRET is needed as both a Pages secret and a GitHub Actions repo secret.

Changelog

None here. #136's added entry 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

jdevalk and others added 7 commits July 30, 2026 08:13
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>
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying specification-website with  Cloudflare Pages  Cloudflare Pages

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

View logs

@jdevalk
jdevalk merged commit 395337c into main Jul 31, 2026
7 of 8 checks passed
@jdevalk
jdevalk deleted the add/websub-hub-2026-07-30 branch July 31, 2026 10:03
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant