Skip to content

Arweave Fallback#2024

Closed
prxt6529 wants to merge 5 commits intomainfrom
arweave-fallback
Closed

Arweave Fallback#2024
prxt6529 wants to merge 5 commits intomainfrom
arweave-fallback

Conversation

@prxt6529
Copy link
Copy Markdown
Collaborator

@prxt6529 prxt6529 commented Mar 2, 2026

Summary by CodeRabbit

  • New Features

    • Client-side registration for a new service worker that retries Arweave asset requests via a fallback origin when direct fetches fail.
    • Runtime helpers to detect Arweave URLs and rewrite them to the fallback host to improve asset availability.
  • Chores

    • Image loading configuration updated to allow the fallback origin.
    • Security headers updated to permit connections to the fallback origin.

Signed-off-by: prxt6529 <prxt@6529.io>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 2, 2026

📝 Walkthrough

Walkthrough

Adds an Arweave fallback: service worker that proxies/falls back to ar-io.net, client-side registration component, URL-rewrite helpers, and config/CSP updates to allow ar-io.net.

Changes

Cohort / File(s) Summary
Service Worker
public/arweave-fallback-sw.js
New SW intercepts non-navigation GET fetches to arweave.net, attempts original fetch and, on failure or non-OK/opaque responses, fetches the equivalent resource from ar-io.net and returns that result (no client redirect).
Client registration
components/providers/ArweaveFallbackSwRegistration.tsx, components/providers/Providers.tsx
Adds a client-only React component that registers /arweave-fallback-sw.js (guards for missing navigator.serviceWorker, logs errors only outside production) and mounts it in the Providers tree.
Fallback utilities
lib/arweave-fallback.ts
New constants ARWEAVE_HOST, FALLBACK_HOST plus isArweaveUrl(url) and getArweaveFallbackUrl(url) to detect and rewrite Arweave URLs with safe try/catch fallbacks.
Config / Security
config/nextConfig.ts, config/securityHeaders.ts
Adds ar-io.net to Next.js images.remotePatterns and to CSP connect-src directives.

Sequence Diagram(s)

sequenceDiagram
    participant Browser as Browser
    participant SW as Service Worker
    participant Arweave as arweave.net
    participant Fallback as ar-io.net

    Browser->>SW: fetch(request to arweave.net)
    activate SW
    SW->>Arweave: fetch(original request)
    alt original returns OK/opaque
        Arweave-->>Browser: response
    else original fails or non-OK
        SW->>Fallback: fetch(rewritten request to ar-io.net)
        alt fallback returns response
            Fallback-->>Browser: response
        else fallback fails
            SW-->>Browser: return error response
        end
    end
    deactivate SW
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • simo6529

Poem

🐰 I hop through bytes and tangled nets,
When arweave stumbles, I place my bets.
A tiny SW hums a midnight tune,
ar-io springs in like a helpful boon—
Soft paws patching data under the moon.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Arweave Fallback' is vague and does not clearly convey the specific implementation details or purpose of the changes. Consider a more descriptive title that explains what the fallback does, such as 'Add Arweave service worker fallback to ar-io.net' or 'Implement Arweave fallback routing to ar-io.net'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch arweave-fallback

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (6)
public/arweave-fallback-sw.js (3)

4-11: Hostname comparison lacks case normalization.

Unlike the TypeScript helper in lib/arweave-fallback.ts (line 7), this function doesn't normalize the hostname to lowercase before comparison. While hostnames are typically lowercase, inconsistent handling could cause edge-case mismatches.

♻️ Suggested fix
 function isArweaveRequest(url) {
   try {
     const u = new URL(url);
-    return u.hostname === ARWEAVE_HOST || u.hostname === "www." + ARWEAVE_HOST;
+    const h = u.hostname.toLowerCase();
+    return h === ARWEAVE_HOST || h === "www." + ARWEAVE_HOST;
   } catch {
     return false;
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@public/arweave-fallback-sw.js` around lines 4 - 11, The hostname comparison
in isArweaveRequest should normalize casing to avoid mismatches; update
isArweaveRequest to call toLowerCase() on the parsed URL hostname and compare
against ARWEAVE_HOST.toLowerCase() and ("www."+ARWEAVE_HOST).toLowerCase() (or
precompute a lowercase www variant) so both sides are compared in lowercase
while preserving the existing try/catch behavior.

13-22: Redundant host assignment.

Setting u.hostname already updates the hostname portion of u.host. The subsequent assignment to u.host immediately overwrites it. While functionally correct, the u.hostname assignment on line 16 is unnecessary.

♻️ Simplified version
 function toFallbackUrl(url) {
   try {
     const u = new URL(url);
-    u.hostname = FALLBACK_HOST;
     u.host = FALLBACK_HOST + (u.port ? ":" + u.port : "");
     return u.toString();
   } catch {
     return null;
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@public/arweave-fallback-sw.js` around lines 13 - 22, In toFallbackUrl, the
assignment to u.host is redundant because setting u.hostname already updates the
host; remove the explicit u.host = ... line and just set u.hostname =
FALLBACK_HOST (keep the try/catch and return logic intact) so the hostname is
replaced without overwriting the host property redundantly.

35-45: Fallback triggers on any non-ok response, including 404s.

The current logic falls back to ar-io.net when the primary request returns a non-ok status (e.g., 404 Not Found). If the resource genuinely doesn't exist on Arweave, this causes an unnecessary retry to the fallback host, which will also likely 404.

Consider preserving and returning non-ok responses directly instead of triggering fallback:

♻️ Suggested fix to only fallback on network errors
       try {
         const res = await fetch(event.request, opts);
-        if (res.ok) return res;
-      } catch {}
+        return res; // Return any response (including 4xx/5xx)
+      } catch {
+        // Network error - try fallback
+      }
       const fallback = toFallbackUrl(event.request.url);
       if (!fallback) return fetch(event.request);
       try {
         return await fetch(fallback, opts);
       } catch {
         return fetch(event.request);
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@public/arweave-fallback-sw.js` around lines 35 - 45, The current handler
falls back to the ar-io URL whenever the primary fetch returns a non-ok response
(e.g., 404); change the logic to only attempt the fallback when the initial
fetch throws (network error) and to return non-ok responses directly.
Specifically, in the block using fetch(event.request, opts) and checking res.ok,
return res immediately when res is present (even if !res.ok) instead of
proceeding to compute toFallbackUrl; only call toFallbackUrl and attempt
fetch(fallback, opts) from the catch path that handles thrown errors (keep the
existing catch that falls back to fetch(event.request) as a last resort). Ensure
references: the initial fetch/res check and the toFallbackUrl fallback flow are
updated accordingly.
components/providers/ArweaveFallbackSwRegistration.tsx (1)

12-14: Silent error handling may hinder debugging.

Registration failures are silently swallowed. While this is acceptable for production resilience, consider logging errors in development to aid debugging if the service worker fails to register.

♻️ Optional: Log errors in development
     navigator.serviceWorker
       .register(SW_PATH, { scope: "/" })
-      .catch(() => {});
+      .catch((err) => {
+        if (process.env.NODE_ENV === "development") {
+          console.warn("Arweave fallback SW registration failed:", err);
+        }
+      });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/providers/ArweaveFallbackSwRegistration.tsx` around lines 12 - 14,
The service worker registration call using
navigator.serviceWorker.register(SW_PATH, { scope: "/" }) currently swallows
errors in .catch(() => {}); modify the catch to capture the error (e) and log it
in non-production environments so failures are visible during development—e.g.,
change the empty catch to a handler that checks NODE_ENV or a debug flag and
calls console.error or your app logger with a message referencing SW_PATH and
the caught error; keep silent behavior for production.
lib/arweave-fallback.ts (2)

14-24: Same redundant host assignment pattern as service worker.

Setting u.hostname on line 18 is immediately overwritten by u.host on line 19.

♻️ Simplified version
 export function getArweaveFallbackUrl(url: string): string | null {
   if (!isArweaveUrl(url)) return null;
   try {
     const u = new URL(url);
-    u.hostname = FALLBACK_HOST;
     u.host = FALLBACK_HOST + (u.port ? ":" + u.port : "");
     return u.toString();
   } catch {
     return null;
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/arweave-fallback.ts` around lines 14 - 24, The function
getArweaveFallbackUrl currently sets u.hostname and then immediately overwrites
it by setting u.host; remove the redundant assignment by keeping only the proper
host construction—replace the two assignments with a single u.host =
FALLBACK_HOST + (u.port ? ":" + u.port : ""); (or alternatively only set
u.hostname and append the port separately), ensuring you reference
getArweaveFallbackUrl and the FALLBACK_HOST constant when making the change.

1-2: Consider exporting constants for reuse and testing.

The ARWEAVE_HOST and FALLBACK_HOST constants could be useful for consumers (e.g., tests, other modules that need to reference these hosts).

♻️ Suggested change
-const ARWEAVE_HOST = "arweave.net";
-const FALLBACK_HOST = "ar-io.net";
+export const ARWEAVE_HOST = "arweave.net";
+export const FALLBACK_HOST = "ar-io.net";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/arweave-fallback.ts` around lines 1 - 2, Export the two host constants so
other modules and tests can reuse them: make ARWEAVE_HOST and FALLBACK_HOST
exported (e.g., add export before the const declarations or add a named export
for those symbols) and ensure any internal usage within the same module still
references the exported names (ARWEAVE_HOST, FALLBACK_HOST) so tests and
consumers can import them.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@components/providers/ArweaveFallbackSwRegistration.tsx`:
- Around line 12-14: The service worker registration call using
navigator.serviceWorker.register(SW_PATH, { scope: "/" }) currently swallows
errors in .catch(() => {}); modify the catch to capture the error (e) and log it
in non-production environments so failures are visible during development—e.g.,
change the empty catch to a handler that checks NODE_ENV or a debug flag and
calls console.error or your app logger with a message referencing SW_PATH and
the caught error; keep silent behavior for production.

In `@lib/arweave-fallback.ts`:
- Around line 14-24: The function getArweaveFallbackUrl currently sets
u.hostname and then immediately overwrites it by setting u.host; remove the
redundant assignment by keeping only the proper host construction—replace the
two assignments with a single u.host = FALLBACK_HOST + (u.port ? ":" + u.port :
""); (or alternatively only set u.hostname and append the port separately),
ensuring you reference getArweaveFallbackUrl and the FALLBACK_HOST constant when
making the change.
- Around line 1-2: Export the two host constants so other modules and tests can
reuse them: make ARWEAVE_HOST and FALLBACK_HOST exported (e.g., add export
before the const declarations or add a named export for those symbols) and
ensure any internal usage within the same module still references the exported
names (ARWEAVE_HOST, FALLBACK_HOST) so tests and consumers can import them.

In `@public/arweave-fallback-sw.js`:
- Around line 4-11: The hostname comparison in isArweaveRequest should normalize
casing to avoid mismatches; update isArweaveRequest to call toLowerCase() on the
parsed URL hostname and compare against ARWEAVE_HOST.toLowerCase() and
("www."+ARWEAVE_HOST).toLowerCase() (or precompute a lowercase www variant) so
both sides are compared in lowercase while preserving the existing try/catch
behavior.
- Around line 13-22: In toFallbackUrl, the assignment to u.host is redundant
because setting u.hostname already updates the host; remove the explicit u.host
= ... line and just set u.hostname = FALLBACK_HOST (keep the try/catch and
return logic intact) so the hostname is replaced without overwriting the host
property redundantly.
- Around line 35-45: The current handler falls back to the ar-io URL whenever
the primary fetch returns a non-ok response (e.g., 404); change the logic to
only attempt the fallback when the initial fetch throws (network error) and to
return non-ok responses directly. Specifically, in the block using
fetch(event.request, opts) and checking res.ok, return res immediately when res
is present (even if !res.ok) instead of proceeding to compute toFallbackUrl;
only call toFallbackUrl and attempt fetch(fallback, opts) from the catch path
that handles thrown errors (keep the existing catch that falls back to
fetch(event.request) as a last resort). Ensure references: the initial fetch/res
check and the toFallbackUrl fallback flow are updated accordingly.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 46e2cc8 and ef42b68.

📒 Files selected for processing (6)
  • components/providers/ArweaveFallbackSwRegistration.tsx
  • components/providers/Providers.tsx
  • config/nextConfig.ts
  • config/securityHeaders.ts
  • lib/arweave-fallback.ts
  • public/arweave-fallback-sw.js

Signed-off-by: prxt6529 <prxt@6529.io>
Comment thread public/arweave-fallback-sw.js Fixed
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@public/arweave-fallback-sw.js`:
- Line 5: The current check uses url.includes("arweave.net") which can match
arbitrary parts of the URL; update the conditional that uses the variable url
(in public/arweave-fallback-sw.js) to parse the request URL with new
URL(url).hostname and perform a strict match (e.g., hostname === "arweave.net"
or hostname.endsWith(".arweave.net") as appropriate) instead of includes,
keeping the existing event.request.mode === "navigate" early return behavior so
only true arweave hostnames are rewritten to the ar-io.net fallback.
- Around line 9-13: Restrict the fallback retry to safe HTTP methods by checking
event.request.method before attempting the fallback: allow only "GET" and
"HEAD". In the arweave-fallback-sw logic around opts, res, fallback and
FALLBACK_HOST, perform the initial fetch as-is but only compute and fetch the
fallback when event.request.method is "GET" or "HEAD" (otherwise return the
original response or null without retrying). This change prevents non-idempotent
methods from being retried against the fallback host.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ef42b68 and 63a93bd.

📒 Files selected for processing (1)
  • public/arweave-fallback-sw.js

Comment thread public/arweave-fallback-sw.js Outdated
Comment thread public/arweave-fallback-sw.js Outdated
prxt6529 added 2 commits March 2, 2026 14:43
Signed-off-by: prxt6529 <prxt@6529.io>
Signed-off-by: prxt6529 <prxt@6529.io>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
public/arweave-fallback-sw.js (1)

26-27: Regex replacement discards port, inconsistent with lib/arweave-fallback.ts.

The regex ^(https?:\/\/)([^/]+) replaces the entire host (including port) with just FALLBACK_HOST. For URLs like https://arweave.net:8080/path, the fallback becomes https://ar-io.net/path, losing the port. Meanwhile, getArweaveFallbackUrl in lib/arweave-fallback.ts preserves the port.

If port preservation isn't needed for this use case (standard HTTPS on port 443), this is fine. Otherwise, consider using URL parsing:

♻️ Suggested fix to preserve port
-      const fallback = event.request.url.replace(/^(https?:\/\/)([^/]+)/, `$1${FALLBACK_HOST}`);
+      const u = new URL(event.request.url);
+      u.hostname = FALLBACK_HOST;
+      const fallback = u.toString();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@public/arweave-fallback-sw.js` around lines 26 - 27, The fallback URL
construction uses a regex replacement in the event handler (the const fallback =
event.request.url.replace(...) that creates fallback and is returned via
Response.redirect) which drops any port from the original URL; update this to
parse the original URL (e.g., new URL(event.request.url)) and reconstruct the
fallback host preserving the original port when present (or otherwise mirror the
behavior of getArweaveFallbackUrl in lib/arweave-fallback.ts) so the resulting
fallback keeps the port if it existed; ensure you update the code that sets
fallback and leave the Response.redirect(fallback, 302) call unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@public/arweave-fallback-sw.js`:
- Around line 26-27: The fallback URL construction uses a regex replacement in
the event handler (the const fallback = event.request.url.replace(...) that
creates fallback and is returned via Response.redirect) which drops any port
from the original URL; update this to parse the original URL (e.g., new
URL(event.request.url)) and reconstruct the fallback host preserving the
original port when present (or otherwise mirror the behavior of
getArweaveFallbackUrl in lib/arweave-fallback.ts) so the resulting fallback
keeps the port if it existed; ensure you update the code that sets fallback and
leave the Response.redirect(fallback, 302) call unchanged.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63a93bd and 9da6634.

📒 Files selected for processing (3)
  • components/providers/ArweaveFallbackSwRegistration.tsx
  • lib/arweave-fallback.ts
  • public/arweave-fallback-sw.js

Signed-off-by: prxt6529 <prxt@6529.io>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Mar 2, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@public/arweave-fallback-sw.js`:
- Around line 28-37: The fallback currently calls fetch(fallbackUrl) which loses
the original Request's mode/credentials/headers; change fetchFallback to create
a new Request for the fallback URL that preserves the original request semantics
(e.g. use new Request(fallbackUrl, request) or explicitly copy request.method,
request.headers, request.mode, request.credentials, cache, redirect,
referrerPolicy, integrity, keepalive and body/clone when needed) and pass that
Request to fetch so mode/no-cors and credentials are preserved; update the
function fetchFallback to build and fetch this new Request.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9da6634 and 3d6ea92.

📒 Files selected for processing (1)
  • public/arweave-fallback-sw.js

Comment on lines +28 to +37
function fetchFallback(request) {
const originalUrl = new URL(request.url);
const fallbackUrl =
"https://ar-io.net" +
originalUrl.pathname +
originalUrl.search +
originalUrl.hash;

return fetch(fallbackUrl);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Fetch API default request properties mode credentials when calling fetch(url) vs original request semantics

💡 Result:

When you call fetch(url) with a string URL, the Fetch spec creates a new Request using default “request semantics” for any RequestInit fields you didn’t provide:

  • method: GET (and no request body)
  • mode: "cors" (so cross-origin requests use CORS; same-origin also works) [1][2]
  • credentials: "same-origin" (send cookies/auth headers only for same-origin requests; not for cross-origin) [1][3]
  • (Common other defaults: redirect: "follow", cache: "default".) [1]

In contrast, when you call fetch(request) with an existing Request object, it uses that object’s already-set properties (including mode, credentials, etc.), unless you explicitly override them in the second argument (which “clones” with overrides per the Fetch algorithms). The key difference is: string URL ⇒ defaults apply; Request object ⇒ its stored semantics apply. [1][3]

Sources:
[1] WHATWG Fetch Standard (request creation defaults for mode/credentials) (fetch.spec.whatwg.org)
[2] MDN: Using the Fetch API (default mode is cors) (developer.mozilla.org)
[3] MDN: Request.credentials (default is same-origin) (developer.mozilla.org)

Citations:


🏁 Script executed:

cd public && cat -n arweave-fallback-sw.js | head -50

Repository: 6529-Collections/6529seize-frontend

Length of output: 1232


Preserve request mode/semantics when calling the fallback host.

On Line 36, fetch(fallbackUrl) creates a new request with default fetch options (specifically mode: "cors" and credentials: "same-origin"). This can break cross-origin asset requests that were originally made with no-cors mode, since the fallback request will now attempt standard CORS negotiation instead of requesting an opaque response. If ar-io.net lacks the appropriate CORS headers, the fallback will fail.

🔧 Proposed fix
 function fetchFallback(request) {
   const originalUrl = new URL(request.url);
   const fallbackUrl =
     "https://ar-io.net" +
     originalUrl.pathname +
     originalUrl.search +
     originalUrl.hash;

-  return fetch(fallbackUrl);
+  return fetch(fallbackUrl, {
+    method: request.method,
+    mode: request.mode,
+    cache: request.cache,
+    redirect: request.redirect,
+    referrer: request.referrer,
+    referrerPolicy: request.referrerPolicy,
+    integrity: request.integrity,
+    credentials: "omit",
+  });
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@public/arweave-fallback-sw.js` around lines 28 - 37, The fallback currently
calls fetch(fallbackUrl) which loses the original Request's
mode/credentials/headers; change fetchFallback to create a new Request for the
fallback URL that preserves the original request semantics (e.g. use new
Request(fallbackUrl, request) or explicitly copy request.method,
request.headers, request.mode, request.credentials, cache, redirect,
referrerPolicy, integrity, keepalive and body/clone when needed) and pass that
Request to fetch so mode/no-cors and credentials are preserved; update the
function fetchFallback to build and fetch this new Request.

@prxt6529 prxt6529 closed this Mar 2, 2026
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.

2 participants