Skip to content

fix(gateway): retire dweb.link before it is switched off on 2026-09-21 - #124

Merged
ehsan6sha merged 1 commit into
mainfrom
fix/retire-dweb-gateway
Sep 12, 2026
Merged

fix(gateway): retire dweb.link before it is switched off on 2026-09-21#124
ehsan6sha merged 1 commit into
mainfrom
fix/retire-dweb-gateway

Conversation

@ehsan6sha

Copy link
Copy Markdown
Member

Why now

The IPFS Foundation is shutting dweb.link down for good on 2026-09-21 (gatewaychanges.ipfs.io). It is the app's default gateway. The HTTP 429s already appearing on live sites — with Retry-After around half an hour — are its announced escalating pauses, not load.

Left alone, on the 21st: every bare fxfiles.top link stops resolving, and every new generation bakes dead asset URLs into immutable HTML.

Why changing the constant is not enough

IpfsGatewayHelper.init() writes the default into secure storage on first run. So everyone who has ever launched the app has the dweb template persisted, and a new defaultTemplate would reach new installs only — every existing user would sail into the shutdown with the code looking correct.

retiredTemplates is the part that actually moves them: a stored value matching a retired template is replaced at startup. That deliberately overrides what looks like a choice — for almost everyone it was just the old default, and honouring it would hand them a broken site.

The decision is split into a pure resolveStoredTemplate so it can be tested without a storage backend. It is the single thing standing between an existing user and a dead gateway, so it is covered directly rather than incidentally.

Two traps found in the same area:

  • Reassigning defaultTemplate = filebaseTemplate makes two keys of the const _frontDoorKeys map identical — a compile error — and makes presetLabelFor return the wrong label. Hence a distinct dwebTemplate constant.
  • dweb is dropped from presets: offering a gateway that init() migrates away from on the next launch is a trap.

The same problem on the shared link

Links copied since the gateway picker shipped carry an explicit ?gw=dweb — and an explicit key beats the default by design, which here would mean beating it into a dead host.

So dweb is removed from the Worker's allowlist entirely, which makes those links fall back rather than fail. The "opinion" that explicit key encoded was manufactured by the default, not expressed by anyone.

Also

  • fx (ipfs.cloud.fx.land) joins Filebase as a preset and an allowlisted key. Verified 2026-09-12 to serve these CIDs with correct content types — and unlike any third party it is ours, so there is a live fallback if Filebase ever moves.
  • The IPNS fallback becomes a plain 502. It pointed at {name}.ipns.dweb.link, which never resolved anyway (w3name does not publish to the DHT — measured 2026-05-30) and now points at a dead host. Redirecting there turned our error into a more confusing one.
  • The subdomain-safety guard goes with dweb. Both remaining gateways are path-style, where a case-sensitive CIDv0 or an over-63-char CID is harmless. Noted in source and README in case a subdomain gateway ever returns.

Testing

  • flutter test1078 passed, 25 skipped
  • node --test cloudflare/33 passed, including one asserting that no redirect target on any code path is a dweb host
  • Gateway helper — 27 passed, covering the migration, idempotency, and that no retired template is offered as a preset
  • flutter build web --release -t lib/main_web.dart — ✅ (the exact command deploy-pages.yml runs)
  • flutter analyze — no errors

Scope — what this does NOT do

This stops the bleeding. It does not make a gateway change reach sites that are already generated: their asset URLs are absolute and live inside immutable HTML, so no setting can rewrite them.

That needs an fxfiles.top/a/ indirection so the gateway becomes a runtime decision. Its core mechanic is already validated — a local wrangler dev prototype driving real Chrome showed images loading through a cross-origin 302 at dimensions identical to direct controls (3/3), and pointing the redirect at a dead host correctly broke exactly those images (0/3) while controls stayed green. That work is planned separately and deliberately not bundled here.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AwMWmCivEpYTmmzzmjSTAf

The IPFS Foundation is shutting dweb.link down for good on 2026-09-21
(gatewaychanges.ipfs.io). It is the app's default gateway. The HTTP 429s already
showing up on live sites -- with Retry-After around half an hour -- are its
announced escalating pauses, not load.

Left alone, on the 21st: every bare fxfiles.top link stops resolving, and every
new generation bakes dead asset URLs into immutable HTML.

WHY CHANGING THE CONSTANT IS NOT ENOUGH

`IpfsGatewayHelper.init()` WRITES the default into secure storage on first run,
so everyone who has ever launched the app has the dweb template persisted. A new
`defaultTemplate` would therefore reach new installs only, and every existing
user would sail into the shutdown. `retiredTemplates` is the part that actually
moves them: a stored value matching a retired template is replaced at startup.
That deliberately overrides what looks like a choice -- for almost everyone it
was just the old default, and honouring it would hand them a broken site.

The migration decision is split into a pure `resolveStoredTemplate` so it can be
tested without a storage backend. It is the single thing standing between an
existing user and a dead gateway, so it is covered directly.

THE SAME PROBLEM ON THE SHARED LINK

Links copied since the gateway picker shipped carry an EXPLICIT `?gw=dweb` --
and an explicit key beats the default by design, which here would mean beating
it into a dead host. So `dweb` is removed from the Worker's allowlist entirely,
which makes those links fall back rather than fail. The "opinion" the explicit
key encoded was manufactured by the default, not expressed by anyone.

ALSO

- `fx` (ipfs.cloud.fx.land) joins Filebase as a preset and an allowlisted key.
  Verified 2026-09-12 to serve these CIDs with correct content types, and unlike
  any third party it is ours -- so there is a live fallback if Filebase moves.
- dweb is dropped from `presets`: offering a gateway that `init()` migrates away
  from on the next launch is a trap.
- The IPNS fallback to `{name}.ipns.dweb.link` becomes a plain 502. It never
  resolved anyway (w3name does not publish to the DHT, measured 2026-05-30) and
  now points at a dead host; redirecting there turned our error into a more
  confusing one.
- The subdomain-safety guard goes with it. Both remaining gateways are
  path-style, where a case-sensitive CIDv0 or an over-63-char CID is harmless.
  Noted in the source and README in case a subdomain gateway ever returns.

Tests: 27 Dart (migration, presets, resolver keys, link decoration), 33 Node for
the Worker, including one that asserts no redirect target on any code path is a
dweb host.

This is the first of three steps. It stops the bleeding but does NOT yet make a
gateway change reach sites that are already generated -- their asset URLs are
absolute and inside immutable HTML. That needs the fxfiles.top/a/ indirection,
which is planned separately and whose core mechanic is already validated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwMWmCivEpYTmmzzmjSTAf
@ehsan6sha
ehsan6sha merged commit 01ad5ad into main Sep 12, 2026
2 of 3 checks passed
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