From 40ba5d6d8ba5b60bac5f1a1534ba290e21b305a4 Mon Sep 17 00:00:00 2001 From: Joost de Valk Date: Fri, 31 Jul 2026 15:35:56 +0200 Subject: [PATCH] feat(websub): advertise the hub on both feeds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step 5 and 6 of the go-live checklist in wrangler.toml. The D1 binding landed in c588a61, so the hub can now serve subscriptions and it is safe to advertise — advertising first is the failure the spec page warns about. - public/_headers: rel="hub" + rel="self" on /rss.xml and /changelog/rss.xml. - Both RSS endpoints: the atom:link rel="hub" fallback copy. - api-catalog: a `hub` linkset entry. - websub.md: the worked-example callout, and why this is a self-hub rather than an open one. `updated` bumped. ALLOWED_TOPICS in functions/websub.ts lists exactly the two rel="self" URLs now advertised, so all three copies of the topic list agree. Co-Authored-By: Claude Opus 5 (1M context) --- public/.well-known/api-catalog | 6 ++++++ public/_headers | 18 +++++++----------- src/content/spec/foundations/websub.md | 4 +++- src/pages/changelog/rss.xml.ts | 6 +++--- src/pages/rss.xml.ts | 6 +++--- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/public/.well-known/api-catalog b/public/.well-known/api-catalog index 8d37a291..60ca6d16 100644 --- a/public/.well-known/api-catalog +++ b/public/.well-known/api-catalog @@ -26,6 +26,12 @@ "title": "Changelog feed" } ], + "hub": [ + { + "href": "https://specification.website/websub", + "title": "WebSub hub for this site's feeds" + } + ], "sitemap": [ { "href": "https://specification.website/sitemap-index.xml", diff --git a/public/_headers b/public/_headers index 8642a62a..8cad0829 100644 --- a/public/_headers +++ b/public/_headers @@ -64,23 +64,19 @@ Content-Type: text/javascript; charset=utf-8 Cache-Control: public, max-age=600, must-revalidate -# Feeds — WebSub discovery. HELD BACK UNTIL THE HUB IS LIVE: advertising -# rel="hub" while /websub cannot accept subscriptions is the exact failure -# /spec/foundations/websub/ warns about (an advertisement is not a delivery -# mechanism), so these blocks are uncommented in the same commit that -# provisions the D1 database — see the checklist in wrangler.toml. +# Feeds — WebSub discovery. # # rel="hub" names the hub that pushes updates for this topic; rel="self" is the # topic's canonical URL, which is the identity subscribers key on. Subscribers # must check Link headers before embedded elements (WebSub §4), so this is the # authoritative copy and the atom:link elements inside each feed are the # fallback. Keep all three in sync with ALLOWED_TOPICS in functions/websub.ts. -# -# /rss.xml -# Link: ; rel="hub", ; rel="self" -# -# /changelog/rss.xml -# Link: ; rel="hub", ; rel="self" + +/rss.xml + Link: ; rel="hub", ; rel="self" + +/changelog/rss.xml + Link: ; rel="hub", ; rel="self" # Long cache for fingerprinted assets /_astro/* diff --git a/src/content/spec/foundations/websub.md b/src/content/spec/foundations/websub.md index cbde9fab..1f0bb190 100644 --- a/src/content/spec/foundations/websub.md +++ b/src/content/spec/foundations/websub.md @@ -7,7 +7,7 @@ status: optional order: 130 appliesTo: [all] relatedSlugs: [feed-discovery, feed-hygiene, canonical-url, link-headers] -updated: "2026-07-30T00:00:00.000Z" +updated: "2026-07-31T00:00:00.000Z" sources: - title: "WebSub — W3C Recommendation, 2 June 2026" url: "https://www.w3.org/TR/websub/" @@ -62,6 +62,8 @@ Or, inside an Atom feed: **4. Keep the feed correct anyway.** WebSub is additive. Subscribers that do not implement it keep polling, so [feed hygiene](/spec/foundations/feed-hygiene/) — stable GUIDs, valid markup, correct `Last-Modified` and `ETag` — still governs how well those clients behave. The push path carries the same feed body, so a malformed feed is malformed for everyone. +**This site ships it.** Both [`/rss.xml`](/rss.xml) and [`/changelog/rss.xml`](/changelog/rss.xml) advertise `rel="hub"` and `rel="self"` on the response and inside the feed, pointing at a hub we run ourselves at `/websub`. It is deliberately a *self-hub*: `hub.topic` is allowlisted to those two feeds, so the endpoint cannot be pointed at arbitrary URLs or made to relay anyone else's content. An open hub is a public service with a different risk profile, and running one is not a prerequisite for the advice above — pointing at a hosted hub is the ordinary choice. + ## Common mistakes - Advertising `rel="hub"` without pinging the hub on publish. Nothing is ever distributed, and nothing errors. diff --git a/src/pages/changelog/rss.xml.ts b/src/pages/changelog/rss.xml.ts index f813f328..2aa59def 100644 --- a/src/pages/changelog/rss.xml.ts +++ b/src/pages/changelog/rss.xml.ts @@ -56,9 +56,9 @@ export async function GET(context: APIContext) { customData: [ "en-GB", ``, - // WebSub hub for this topic — held back until the hub is live, along with - // the Link header in public/_headers. See the checklist in wrangler.toml. - // ``, + // WebSub hub for this topic. Fallback copy of the Link header in + // public/_headers — subscribers check headers first (WebSub §4). + ``, `${lastBuild.toUTCString()}`, "weekly", "1", diff --git a/src/pages/rss.xml.ts b/src/pages/rss.xml.ts index 057e2c3b..a263d7b0 100644 --- a/src/pages/rss.xml.ts +++ b/src/pages/rss.xml.ts @@ -34,9 +34,9 @@ export async function GET(context: APIContext) { customData: [ "en-GB", ``, - // WebSub hub for this topic — held back until the hub is live, along with - // the Link header in public/_headers. See the checklist in wrangler.toml. - // ``, + // WebSub hub for this topic. Fallback copy of the Link header in + // public/_headers — subscribers check headers first (WebSub §4). + ``, `${lastBuild.toUTCString()}`, "weekly", "1",