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",