Skip to content

release: Wayfinder v2.0.0 — Solana migration + AR.IO rebrand#252

Merged
vilenarios merged 46 commits into
mainfrom
alpha
Jun 19, 2026
Merged

release: Wayfinder v2.0.0 — Solana migration + AR.IO rebrand#252
vilenarios merged 46 commits into
mainfrom
alpha

Conversation

@vilenarios

Copy link
Copy Markdown
Contributor

Release: Wayfinder v2.0.0

Production release merging the complete Solana migration and AR.IO rebrand from alpha.

Breaking Changes

  • @ar.io/sdk peer dependency bumped from >=3.12.0 to >=4.0.0
  • All Ao-prefixed types renamed (AoARIOReadARIORead, AoGatewayGateway, etc.)
  • AO backend no longer supported — Solana is the only backend

Highlights

SDK & Network

  • @ar.io/sdk v4.0.2 stable (Solana-only)
  • @solana/kit v6.8.0
  • Mainnet default with bundled QuickNode RPC
  • Retry with exponential backoff for RPC rate limits
  • Periodic gateway sync (24h via chrome.alarms)
  • Fetch timeouts on all outbound calls

New Feature: Top Staked Routing

  • Round-robin across top 20 highest-staked gateways with ping health checks
  • Set as the default routing strategy

Resiliency

  • Ping result caching (120s TTL)
  • Storage migrations for AO-era and stale devnet program IDs
  • Graceful fallback on RPC failures

AR.IO 2026 Rebrand

  • Purple primary (#5427C8), Besley headings, Plus Jakarta Sans body
  • White primary text, lavender secondary
  • Dark/light theme with flash-free transitions
  • Consistent container hierarchy (L0→L1→L2→L3)

Versions

Package Version
@ar.io/wayfinder-core 2.0.0
@ar.io/wayfinder-extension 2.0.0
@ar.io/wayfinder-react 2.0.0
Monorepo 1.0.0

CI Status

All workflows passing: Core Library, Chrome Extension, React Components, Diff Check, CodeRabbit.

Test Results

  • 130/130 unit tests pass
  • 6 live integration tests are flaky (dependent on gateway network state — pre-existing, not related to this release)

Test plan

  • yarn build — all 5 packages clean
  • CI green on all workflows
  • Extension tested in Chrome (dark + light mode, all 4 pages)
  • Gateway sync works on mainnet via QuickNode RPC
  • All routing strategies functional (Top Staked, Fastest Ping, Balanced, Static)
  • No remaining AO references in source code
  • All docs and READMEs updated

🤖 Generated with Claude Code

dtfiedler and others added 30 commits February 10, 2026 21:55
Replace all hardcoded arweave.net and permagate.io defaults with
turbo-gateway.com, which is a verified AR.IO gateway supporting all
AR.IO-specific APIs.

Changes:
- Update DEFAULT_TRUSTED_GATEWAY in wayfinder-core
- Replace preferred routing default
- Update round-robin default gateways
- Update extension fallback gateways
- Update CLI default gateway
- Update all README examples
- Update test script defaults

Rationale:
- arweave.net is no longer an AR.IO gateway
- Does not support /ar-io/peers, /ar-io/info endpoints
- turbo-gateway.com confirmed to support all AR.IO APIs

No breaking changes - all user configurations still work.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Update test gateway from permagate.io to turbo-gateway.com to match the
gateway defaults changed in commit 954e60e. The tests were failing in CI
with 502 Bad Gateway errors because permagate.io was experiencing issues.

Fixes 4 failing integration tests:
- should emit default events on the wayfinder event emitter when request is made
- should emit events and trigger request callbacks when request is made with custom events
- should execute callbacks provided to the wayfinder constructor
- should call verification for ArNS names with resolved transaction ID

All tests now pass (124/124).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The x-arns-resolved-at header contains a timestamp that varies between
parallel requests, causing the test to be flaky. Even when using
Promise.all, the requests don't start at exactly the same millisecond,
so the gateway generates different timestamps.

Fix by:
- Filtering out x-arns-resolved-at from the deep equality check
- Adding explicit assertions that the timestamp header exists on both responses

This ensures we still verify ArNS functionality while avoiding flaky
timestamp comparisons.

Fixes the intermittent failure:
  ✖ should fetch the data using the selected gateway

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add test fix details to the gateway replacement changeset:
- Updated integration tests to use turbo-gateway.com
- Fixed flaky timestamp comparison in ArNS header test

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Releases:
  @ar.io/wayfinder-core@1.9.2
  @ar.io/wayfinder-extension@1.0.23
  @ar.io/wayfinder-cli@0.0.5
  @ar.io/wayfinder-react@1.0.29

[skip ci]
npm publish was failing with a warning that the bin path was invalid.
The published v0.0.4 uses 'dist/index.js' without the './' prefix.
This matches the format that npm expects for bin scripts.

Fixes the publish error:
  npm warn publish "bin[wayfinder]" script name dist/index.js was invalid and removed

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
First step of the Solana migration. wayfinder-core's only @ar.io/sdk
touchpoint is a type-only import of `AoARIORead` in network.ts; that
type has the same shape on the 4.x-solana.X line as on 3.x, so no code
changes are required. Bumping the devDependency exercises the
Solana-capable SDK during local + CI test runs, giving us confidence
the package stays compatible as we migrate the rest of the monorepo.

- packages/wayfinder-core/package.json: devDep `@ar.io/sdk`
  `^3.13.0` -> `^4.0.0-solana.8`. peerDependency `>=3.12.0` left
  unchanged (the constraint already permits 4.x; loosening lockstep
  with AO-era consumers is intentional).
- packages/wayfinder-core/src/version.ts: synced by the build's
  update-version script. No semantic change.
- package-lock.json: regenerated by npm install.
- .changeset/wayfinder-core-solana-sdk.md: patch-bump changeset
  documenting the SDK-compat verification.

Verified:
- npm run lint:check (biome) passes
- npm run build (tsc) passes — confirms the SDK boundary still
  type-checks against 4.x-solana.8
- npm test passes against the bumped SDK

Out of scope for this commit (deferred to follow-up): the
wayfinder-extension AR.IO SDK migration (Solana-only refactor of
storage schema, settings UI, and ARIO.init call sites).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Solana migration uses `solana` as a long-lived integration
branch (mirroring the same pattern used in ar-io-node, ar-io-sdk,
ar-io-observer). Without `solana` in the CI workflow triggers, none
of the feature PRs we land into the branch get lint/test/build signal
before merge — only the eventual `solana -> alpha` rollup would.

Add `solana` to the `branches:` list in:
- pr-check.yml (Diff Check — paths-filter classification)
- core.yml (Core Library — lint/test/build)
- extension.yml (Chrome Extension — lint/test/build)
- react.yml (React Components & Hooks — lint/test/build)

Deliberately NOT touched:
- chrome.yml is workflow_dispatch only (manual publish to the Chrome
  store); branch triggers don't apply.
- release.yaml drives changesets-based publishing — we don't want it
  accidentally publishing from the `solana` integration branch. It
  stays scoped to main/alpha.

No changeset required: workflow-only change, no consumer impact.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ding (#246)

Foundation work for the AO -> Solana migration (PR A of two). Purely
additive — no existing code consumes the new exports yet, so this commit
introduces zero runtime behavior change.

- src/types.ts:
  * NetworkPreset enum ('mainnet' | 'devnet' | 'custom')
  * SolanaNetworkConfig shape (rpcUrl + 4 program IDs)

- src/constants.ts:
  * AR_IO_SOLANA_DEVNET preset carrying the public devnet RPC and the
    four program addresses from ar-io-solana-contracts/program-ids/
    devnet.json. Includes a docstring noting the public RPC's rate
    limits.
  * AR_IO_SOLANA_MAINNET = null placeholder (AR.IO Solana mainnet is
    not yet deployed; will be populated in a future commit).
  * AR_IO_SOLANA_PRESETS keyed lookup for use by the settings UI.

- src/config/defaults.ts:
  * SOLANA_NETWORK_DEFAULTS = { network: 'devnet', ...devnet preset }
    for use by the upcoming migration commit when it populates
    EXTENSION_DEFAULTS / chrome.storage on first install.

The follow-up PR ('refactor(extension): drop AO entirely; route through
Solana') will: rewrite the four ARIO.init call sites in background.ts
and settings.ts; replace the AO Process ID / CU URL form fields in
settings.html with a network preset selector + Solana RPC + program ID
inputs; add storage migration logic for existing AO-era users
(silent reset to devnet defaults + force GAR resync); drop
@permaweb/aoconnect; bump @ar.io/sdk to ^4.0.0-solana.8.

Verified:
- npm run build -w @ar.io/wayfinder-extension passes (tsc + bundle)
- biome lint clean

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(extension)!: drop AO entirely; route through Solana

BREAKING change: AO support is removed from wayfinder-extension. The
extension now talks to AR.IO exclusively through the Solana-backed
@ar.io/sdk@4.x. Default network on fresh install is AR.IO Solana
devnet; mainnet is reserved for when the AR.IO Solana mainnet
deployment ships.

Builds on the additive foundation landed in the previous commit
(refactor(extension): add Solana network type/constant/default
scaffolding).

Code:
- src/background.ts: drop AOProcess + @permaweb/aoconnect + AO process
  constant imports; add arioFromStorage() helper that constructs a
  read-only Solana ARIO from chrome.storage.local with EXTENSION_
  DEFAULTS fallback; rewrite the four ARIO.init call sites to use it;
  add migrateStorageFromAOEra() that detects pre-existing AO-era
  storage keys, removes them, invalidates the cached gateway registry,
  and lets the next sync repopulate from Solana.
- src/settings.ts: drop ARIO.mainnet() fallback in favor of a
  Solana-backed ARIO.init({backend: 'solana', ...}) built from current
  storage; replace handleProcessIdChange + handleAoCuUrlChange with a
  consolidated handleNetworkConfigChange covering all six new fields
  (network, rpcUrl, four program IDs); validate inputs via URL() for
  the RPC and @solana/kit `address()` for program IDs; preset
  selection auto-fills + disables the per-field inputs.
- src/constants.ts: remove ARIO_MAINNET_PROCESS_ID and
  DEFAULT_AO_CU_URL.
- src/config/defaults.ts: drop the AO `processId`/`aoCuUrl` entries
  from EXTENSION_DEFAULTS; replace with the Solana network defaults
  (`network`, `rpcUrl`, four program IDs).
- src/settings.html: replace "AR.IO Process ID" + "AO Compute Unit
  URL" sections with a Network preset selector, a Solana RPC URL
  input, and a collapsible "Advanced: AR.IO Program IDs" panel
  exposing the per-program addresses. Mainnet preset is `disabled`
  until AR.IO Solana mainnet ships.

Dependencies:
- @ar.io/sdk: ^3.21.0 -> ^4.0.0-solana.8
- @solana/kit: added at ^2.1.0 (used by SDK's Solana backend and by
  the extension's address() validation in settings + the settings-
  side fallback ARIO instance)
- @permaweb/aoconnect: removed

Storage migration:
- AO-era users had `processId` + `aoCuUrl` in chrome.storage.local.
  On first run after upgrade migrateStorageFromAOEra() removes them
  along with the cached `localGatewayAddressRegistry`, then the
  startup defaults block writes the new Solana keys. Background
  re-init then connects to AR.IO Solana devnet and the next sync
  repopulates the registry from a Solana gateway source. Users see
  the extension keep working with no settings prompt.

Verified locally:
- `npm run build -w @ar.io/wayfinder-extension` passes (tsc + vite)
- `biome check` passes
- Bundle size shrinks (vendor chunk: 5.3 MB -> 3.8 MB) thanks to
  dropping the AO compute stack and the aoconnect dependency.

Out of scope (deferred): no automated tests exist in the extension
package; validation is purely manual via a Chrome dev profile. The
PR description includes a step-by-step smoke-test checklist.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(extension): clean up AO relics + wire reinitializeArIO

Follow-up on the AO -> Solana migration: a self-review surfaced six
remaining AO relics + one real bug. Bundled here as a single
clean-up commit; the original 02a7660 stays as the readable
migration commit.

Real bugs:

  - gateways.ts: the gateway-ping health check probed `info.processId`
    on /ar-io/info, which AR.IO Solana gateways no longer emit (replaced
    by `programIds`). Without this fix, every modern AR.IO gateway
    would be marked Unhealthy by the extension's ping tool. Switched
    the check to `info.programIds` (typeof object).

  - background.ts (updateAdvancedSettings handler): handler was
    calling `chrome.storage.local.set(request.settings)`, but the
    settings page already writes storage directly and now sends the
    message with no `settings` field. The handler was effectively a
    no-op for storage and never rebuilt the ARIO instance — so a user
    changing the Solana RPC URL or program IDs wouldn't see the
    underlying SDK pick up the change until the next service-worker
    restart. Replaced the (dead) storage write with a call to
    `reinitializeArIO()` so the ARIO read instance rebuilds from the
    new storage on every settings change.

Dead code removed:

  - background.ts: `setAoCuUrl` message handler (only reachable from
    the now-deleted AO CU URL form field). Plus its allowlist entry.
    Plus the orphan `setArIOProcessId` allowlist entry that never had
    a handler.

  - settings.ts: the "Reset all settings" button still removed the
    legacy `processId` / `aoCuUrl` storage keys instead of the new
    Solana keys, so pressing it left the user's storage untouched.
    Updated to remove the six new network keys; the startup defaults
    block then repopulates them with the devnet preset on next load.

Build hygiene:

  - vite.config.js: the manualChunks `webIndex` chunk still listed
    `@permaweb/aoconnect` (now removed from dependencies). Replaced
    with `@solana/kit` to keep the chunk grouping useful.

Docs:

  - README.md: "Network Configuration" section described the old
    "Process ID" + "AO CU URL" fields. Replaced with the new Network
    preset / Solana RPC URL / Advanced program IDs description.

Note: `reinitializeArIO` (now wired into `updateAdvancedSettings`)
was orphaned by the `setAoCuUrl` removal; lint would have flagged
it. Pre-emptively re-wired before lint caught it.

Verified locally:
- `npm run build -w @ar.io/wayfinder-extension` passes (tsc + vite)
- `biome check` passes
- Final grep: no remaining unintentional AO references in the
  extension src/, README, vite config, or manifest. The remaining
  AO references in background.ts are all inside
  `migrateStorageFromAOEra()` (intentional — that function detects
  and removes AO-era storage keys).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* ci: re-trigger workflows after base retarget

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…robustness + dead code) (#248)

Surfaced by a post-merge end-to-end scan of the extension's Solana
migration. Three categories of cleanup:

1. Cross-network contamination via the mainnet fallback gateway

   FALLBACK_GATEWAY in constants.ts is hardcoded to turbo-gateway.com
   (mainnet). routing.ts uses it as a last-resort fallback when
   normal routing fails. After the migration the extension defaults
   to AR.IO Solana devnet, where the gateway registry is sparsely
   populated — so the fallback fires often and silently serves
   mainnet content under the user's devnet network. Now the fallback
   is gated on `network === 'mainnet'` in storage; on devnet or a
   custom network the original routing error is propagated instead,
   surfacing the empty-registry condition to the user rather than
   masking it with mainnet data.

2. Startup robustness against bad stored network config

   The boot IIFE in background.ts called
   `arIO = await arioFromStorage()` without a try/catch.
   arioFromStorage() invokes @solana/kit's address() on each stored
   program-ID, which throws on non-base58 input. If a user
   previously persisted a bad value via the custom-network preset,
   the IIFE would crash before reaching debouncedInitializeWayfinder(),
   leaving routing inoperable until manual reset. Now wrapped with a
   fallback to bundled EXTENSION_DEFAULTS that mirrors the existing
   reinitializeArIO() catch path.

3. Dead-code removal

   - `resetAdvancedSettings` message handler: had logic but no UI
     button calling it after the migration. The startup defaults
     block plus the existing `updateAdvancedSettings` flow cover the
     same surface.
   - `updateVerificationMode` allowlist entry: pure dead string, no
     handler ever existed in the post-migration tree.

   Both removed from the validMessages allowlist; the resetAdvanced
   Settings handler body removed too.

Verified locally:
- npm run build -w @ar.io/wayfinder-extension passes
- npm run lint:check (biome + eslint, 121 files) passes
- No new tests added (extension package has none; smoke test
  remains a manual Chrome dev-profile exercise).

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…249)

wayfinder-core itself is chain-agnostic — it consumes only the
`AoARIORead` type and delegates instance construction to the caller.
But the published README only showed `ARIO.mainnet()` (the AO-era
AR.IO mainnet) examples, which is misleading in the post-Solana-
migration ecosystem.

Rewrite the four example blocks to use the SDK 4.x Solana backend:

  const ario = ARIO.init({
    backend: 'solana',
    rpc: createSolanaRpc('https://api.devnet.solana.com'),
    coreProgramId: address('...'),
    garProgramId: address('...'),
    arnsProgramId: address('...'),
    antProgramId: address('...'),
  });

The first example carries a comment explaining how to switch to
mainnet (drop the program-ID overrides; the SDK ships mainnet
defaults) once AR.IO Solana mainnet deploys, and reaffirms that
wayfinder-core is chain-agnostic — same APIs work for any backend
the SDK supports.

Sections updated:
- Getting Started: Using with AR.IO Network
- NetworkGatewaysProvider reference
- CompositeGatewaysProvider example
- CompositeRoutingStrategy examples 1 + 2

Docs-only. No code changes. wayfinder-core's `network.ts` already
type-checked against SDK 4.x in the earlier compat-verification
commit (991e450).

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
BREAKING CHANGE: Drop AO backend support entirely; Solana is now the
only backend for the AR.IO on-chain registry.

SDK migration:
- Rename Ao-prefixed types: AoARIORead→ARIORead, AoGateway→Gateway,
  AoGatewayWithAddress→GatewayWithAddress across all packages
- Remove `backend: 'solana'` from all ARIO.init() calls (Solana is
  the only backend, parameter no longer exists)
- Bump @ar.io/sdk from ^4.0.0-solana.8 to ^4.0.2
- Bump @solana/kit from ^2.1.0 to ^6.8.0
- Bump wayfinder-core peer dep from @ar.io/sdk>=3.12.0 to >=4.0.0

Program IDs & mainnet:
- Update devnet program IDs to match SDK v4.0.2 deployment
- Populate mainnet program IDs (AR.IO Solana mainnet is now live)
- Enable mainnet preset in extension settings UI
- Add storage migration for users with stale devnet program IDs

Build:
- Fix Vite bundling for @solana/kit v6 (resolve.alias to deduplicate
  conflicting v5/v6 @solana/* sub-packages from x402-fetch)

Version bumps:
- wayfinder-core: 1.9.2 → 2.0.0
- wayfinder-extension: 1.0.23 → 2.0.0
- wayfinder-react: 1.0.29 → 2.0.0
- monorepo: 0.0.14 → 1.0.0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous hardcoded paths to @solana/kit/node_modules/ only worked
with yarn's nesting layout. CI uses npm, which hoists differently and
the nested paths don't exist.

Switch to createRequire anchored to @solana/kit's main entry, so
each @solana/* sub-package resolves to the version kit was built
against regardless of package manager hoisting strategy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Fetch timeouts — prevent indefinite hangs on slow/dead endpoints:
   - TrustedPeersGatewaysProvider: 10s timeout (configurable)
   - ContiguousDataRetrievalStrategy: 30s timeout (configurable)
   - ChunkDataRetrievalStrategy: 10s metadata, 30s per chunk (configurable)
   - Extension DNS lookup: 5s timeout

2. Ping result caching — avoid re-pinging all gateways on every request:
   - Wrap FastestPingRoutingStrategy with SimpleCacheRoutingStrategy
     (120s TTL) in the extension's routing setup
   - Stale cache is returned on ping failure (built into SimpleCacheRoutingStrategy)

3. Periodic gateway registry sync — recover from failed startup syncs:
   - chrome.alarms fires every 60 minutes
   - Skips sync if last sync was recent (staleness check via lastSyncTime)
   - Added "alarms" permission to manifest.json

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Switch default network preset from devnet to mainnet
- Restyle the Advanced: AR.IO Program IDs section:
  - Proper collapsible disclosure triangle
  - Stacked label+input layout with spacing
  - Monospace font for pubkey inputs
  - Description text with muted styling

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The public Solana RPC endpoints (mainnet-beta, devnet) aggressively
rate-limit, returning HTTP 403 / SolanaError #8100002. Each
getGateways() page fetch is now wrapped in retryWithBackoff() which:

- Retries up to 5 times on 403, 429, 5xx, and network errors
- Uses exponential backoff with jitter (2s base, 15s cap)
- Lets application errors (bad address, deserialization) bubble immediately

This makes the gateway sync work reliably with the free public RPC
endpoints without requiring a premium QuickNode/Helius endpoint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The public api.mainnet-beta.solana.com actively rejects requests from
Chrome extension service workers (HTTP 403). Switch the mainnet
default to a QuickNode endpoint that works from browser contexts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update Chrome Web Store section (no longer "coming soon")
- Add yarn install + core build step to dev instructions
- Update project structure with actual .ts filenames and new files
- Add @ar.io/sdk v4.x and @solana/kit to key technologies
- Clarify network preset defaults and RPC details

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update the entire extension UI to match the AR.IO brand kit (v2026-03-30):

Colors:
- Primary accent: teal rgb(45, 211, 190) → purple #5427C8
- Secondary: dark teal → lavender #DFD6F7
- Dark backgrounds: neutral grays (#050505 scale) → purple-tinted darks (#0e0a1c scale)
- Text on dark: gray #cacad6 → lavender #D4C6FF
- Gradients: peach-to-purple (#f7c3a1→#df9be8) → purple spectrum (#5427C8→#DFD6F7)
- Light theme: slate grays → Lavender Wash #f1ecff / Warm Neutral #F6F4EF

Typography:
- Headings: Rubik → Besley (weight 800, tight line-height 1.08)
- Body: Rubik/Inter → Plus Jakarta Sans
- Monospace: JetBrains Mono (kept)

Layout:
- Border radii increased: 4/8/12/16px → 8/12/20/24px
- Strokes: neutral gray-blue rgba → purple-tinted rgba

Files updated:
- variables.css: Complete design token rewrite
- popup.css, settings.css, gateways.css, gateways-enhanced.css, performance.css:
  All hardcoded colors replaced with new brand values
- All 4 HTML files: Google Fonts updated to Besley + Plus Jakarta Sans

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- All Besley headings: font-weight 800, letter-spacing -0.025em, line-height 1.08
- Popup logo h1: switched from body font to heading font (Besley)
- Nav card h3s: switched to Besley heading font
- Toggle unchecked state: #0e0a1c → #26203a for visible contrast
- Focus rings: added box-shadow glow on all text/url inputs
- Filter buttons: border-radius 6px → var(--radius-sm, 8px)
- Back buttons: border-radius 10px → var(--radius-md, 12px)
- Fixed 4 remaining font-weight: 600 headings in settings.css

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tooltips, buttons, selects, close buttons, and list items had 4-6px
radii. Brand spec minimum is 8px (--radius-sm). Updated 13 elements
across popup.css, settings.css, gateways.css, and performance.css.

Intentionally kept small radii on: scrollbar thumbs (3px), range
slider tracks (3px), decorative accent bars (2px), tiny badges (3px),
checkboxes (3px), and progress bars (3px) — these are sub-element
details where 8px would look wrong.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…bg consistency

Theme support:
- gateways.ts and performance.ts had NO theme application — light mode
  was completely broken on these pages. Added applyTheme() + system
  preference listener to both, matching popup.ts/settings.ts pattern.

Card background consistency:
- Gateways container used L1 (#1a1527) instead of L0 (#0e0a1c) as
  page background — one shade off from popup/settings/performance.
- Gateways back-button used L1 instead of L0 (inconsistent with
  settings/performance back buttons).
- 8 hardcoded #26203a hover states replaced with
  var(--colors-container-containerL2) so light theme hovers work.
- Toggle switch border/bg hardcoded values → CSS variables.

All 4 pages now have identical theme detection, identical background
hierarchy (L0 page → L1 cards → L2 hover → L3 inputs), and all
container colors use CSS variables for correct light/dark theming.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extension icon:
- Replace old icon with latest AR.IO brand icon from brand kit
  (ario-white-on-black-round) at 128/48/16px sizes

Theme flicker fix:
- Add inline <script> in <head> of all 4 HTML pages that sets
  data-theme synchronously before CSS paints — eliminates the flash
  of dark theme when navigating pages in light mode

Popup layout tightening:
- Reduce section gap from 18px to 12px
- Reduce stat card height from 89.7px to 72px
- Reduce nav card gap from 16px to 8px between cards
- Reduce card padding from 16px to 12px vertically
- Shrink strategy/feature icons from 32px to 28px
- Remove transform/box-shadow from nav-card hover (was inconsistent
  with strategy-card hover behavior — now all cards hover identically)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…pages

- Popup: replaced var(--bg-secondary) alias with explicit
  var(--colors-container-containerL1) on stat-card, strategy-card,
  nav-card — matches settings/gateways/performance convention
- Fixed duplicate .strategy-card block (second override) that still
  used --bg-secondary with larger padding
- Settings: network-option background was containerL0 (same as page
  bg, invisible) — changed to containerL1 to match other cards

All cards across all 4 pages now use identical token:
  background: var(--colors-container-containerL1, #1a1527)
  hover: var(--colors-container-containerL2, #26203a)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Apply consistent design token usage across all CSS:
- Card/section elements use containerL1 (raised from page background)
- Hover states use containerL2
- Input fields use containerL3
- Page background, headers, footers remain containerL0

Fixes:
- popup.css: nav-cards gap 8px→12px to match parent section gap
- settings.css: .theme-option, .advanced-option, .mode-option,
  .option-card, .provider-option, .verification-strategy-selector
  .strategy-option, .trusted-gateways-config .mode-option all
  changed from L0 to L1; hover states from L0 to L2
- performance.css: .filter-section, .usage-section, .action-card
  changed from L0 to L1
- gateways.css: .search-box input changed from L0 to L3

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vilenarios and others added 14 commits June 16, 2026 17:15
…s light mode

- Fix advanced settings section hover: .advanced-options → .advanced-config
  in the :not() exclusion list so hovering sub-items doesn't grey out the
  entire card
- Improve toggle switch unchecked state: use containerL3 instead of L2
  with visible border for better contrast against card backgrounds
- Add light mode overrides for gateways page: header border, filter
  section, filter buttons, search input all now use proper light theme
  tokens instead of dark-mode rgba values

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Full pixel-level audit of popup, settings, gateways, and performance
pages. Every element checked against the L0/L1/L2/L3 container
hierarchy, font families, and text color tokens.

Popup:
- .stat-label: textHigh → textMid (secondary text should be muted)
- .strategy-description: textHigh → textMid
- .nav-content p: textHigh → textMid

Settings:
- .back-button:hover: L0 → L2 (was invisible hover state)
- .input-with-button input: L0 → L3 (input fields must be L3)

Performance:
- .back-button:hover: L0 → L2 (no visual hover feedback)
- .stat-card: --bg-secondary → containerL1 (undefined legacy var)
- .stat-value: --text-high → neutrals-100 (undefined legacy var)
- .stat-label: --text-high → textMid (secondary text muted)
- Light mode: add missing overrides for filter-section, filter-btn,
  sort-select, stat-card, stat-value, stat-label, usage-section,
  section-header

Gateways:
- .stat-card: --bg-secondary → containerL1 (undefined legacy var)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rings

Structural consistency:
- Gateways page header/footer now fixed-position (was relative),
  matching popup/settings/performance — fixes inconsistent scroll
  behavior. Content padding updated to account for fixed elements.
- Settings back-button radius 10px → var(--radius-md, 12px) to match
  gateways/performance pages.

Border consistency:
- Popup stat-cards now have 1px solid var(--border-subtle) border
  (was missing — strategy/nav cards had it, stat-cards didn't).
- Normalized ALL border opacities from 0.08 to 0.10 across settings.css
  (14 occurrences — strategy options, theme options, advanced options,
  buttons, mode options, etc.).

Focus accessibility:
- Added purple focus ring (box-shadow glow) to network select dropdown
  in settings — was the only input without one.

Light theme:
- Added [data-theme="light"] block to gateways-enhanced.css for modal
  stat cards — was completely missing, causing dark styles in light mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e pages

- Add explicit light mode background override for .gateways-container
  and .performance-container (CSS variable fallback was showing dark bg)
- Add light mode overrides for gateways header, footer, stat cards
- Add light mode overrides for performance container

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove overflow:hidden from .gateways-container — it was clipping the
scrollable .gateways-content child, preventing scrolling to the top
of the gateway list behind the fixed header.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dark is the default theme, so it should appear first in the theme
selector. Having Light first was confusing since it visually stood
out with its white preview styling, making users think it was selected.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Page was painting with dark defaults before the async
chrome.storage.local.get() resolved with the user's theme preference.

Fix: hide the page via `html:not([data-theme-ready]) { opacity: 0 }`
inline style, then set `data-theme-ready` in the same callback that
applies the theme. The browser only paints once the theme is set,
eliminating the flash. Applied to all 4 HTML pages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ance

Chrome MV3 Content Security Policy blocks inline scripts. Move the
theme initialization logic from inline <script> tags to an external
theme-init.js file loaded via <script src="theme-init.js">.

The opacity:0 → data-theme-ready technique still prevents the
dark-to-light flash, now without violating CSP.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Gateways and performance pages had dark fallback (#0e0a1c) on the
html/body elements that was bleeding through as a grey hue at the
top of the page in light mode. Override html, body, and the main
container to #ffffff directly in the [data-theme="light"] block.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Text colors:
- textHigh: #d4c6ff (lavender) → #ffffff (white) for primary text
- textMid: #9b93b4 → #d4c6ff (lavender) for secondary/description text
- iconHigh: #d4c6ff → #ffffff for primary icons
- Muted text (#9b93b4) unchanged

Gateways scroll:
- Restore overflow:hidden on .gateways-container (needed for flex
  child scroll constraint)
- Change min-height:100vh → height:100vh so the container is fixed
  size and .gateways-content overflow-y:auto actually activates

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New routing strategy that round-robins across the top 20 gateways
by total delegated stake, with ping health checks on each selection.

- Add `limit` option to ChromeStorageGatewayProvider
- Add `topStaked` case in routing.ts using RoundRobinRoutingStrategy
  with a stake-sorted, limited gateway provider wrapped in
  PingRoutingStrategy for health verification
- Add strategy card in settings UI with star icon
- Add label mapping in popup.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CLAUDE.md: fix default network to mainnet (was still saying devnet)
- Extension CLAUDE.md: add topStaked strategy, update cache TTL
- Extension README: add Top Staked to features list and routing config

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(core): bump @ar.io/sdk devDependency to ^4.0.0-solana.8

First step of the Solana migration. wayfinder-core's only @ar.io/sdk
touchpoint is a type-only import of `AoARIORead` in network.ts; that
type has the same shape on the 4.x-solana.X line as on 3.x, so no code
changes are required. Bumping the devDependency exercises the
Solana-capable SDK during local + CI test runs, giving us confidence
the package stays compatible as we migrate the rest of the monorepo.

- packages/wayfinder-core/package.json: devDep `@ar.io/sdk`
  `^3.13.0` -> `^4.0.0-solana.8`. peerDependency `>=3.12.0` left
  unchanged (the constraint already permits 4.x; loosening lockstep
  with AO-era consumers is intentional).
- packages/wayfinder-core/src/version.ts: synced by the build's
  update-version script. No semantic change.
- package-lock.json: regenerated by npm install.
- .changeset/wayfinder-core-solana-sdk.md: patch-bump changeset
  documenting the SDK-compat verification.

Verified:
- npm run lint:check (biome) passes
- npm run build (tsc) passes — confirms the SDK boundary still
  type-checks against 4.x-solana.8
- npm test passes against the bumped SDK

Out of scope for this commit (deferred to follow-up): the
wayfinder-extension AR.IO SDK migration (Solana-only refactor of
storage schema, settings UI, and ARIO.init call sites).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore(ci): track `solana` branch alongside main/alpha

The Solana migration uses `solana` as a long-lived integration
branch (mirroring the same pattern used in ar-io-node, ar-io-sdk,
ar-io-observer). Without `solana` in the CI workflow triggers, none
of the feature PRs we land into the branch get lint/test/build signal
before merge — only the eventual `solana -> alpha` rollup would.

Add `solana` to the `branches:` list in:
- pr-check.yml (Diff Check — paths-filter classification)
- core.yml (Core Library — lint/test/build)
- extension.yml (Chrome Extension — lint/test/build)
- react.yml (React Components & Hooks — lint/test/build)

Deliberately NOT touched:
- chrome.yml is workflow_dispatch only (manual publish to the Chrome
  store); branch triggers don't apply.
- release.yaml drives changesets-based publishing — we don't want it
  accidentally publishing from the `solana` integration branch. It
  stays scoped to main/alpha.

No changeset required: workflow-only change, no consumer impact.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(extension): add Solana network type/constant/default scaffolding (#246)

Foundation work for the AO -> Solana migration (PR A of two). Purely
additive — no existing code consumes the new exports yet, so this commit
introduces zero runtime behavior change.

- src/types.ts:
  * NetworkPreset enum ('mainnet' | 'devnet' | 'custom')
  * SolanaNetworkConfig shape (rpcUrl + 4 program IDs)

- src/constants.ts:
  * AR_IO_SOLANA_DEVNET preset carrying the public devnet RPC and the
    four program addresses from ar-io-solana-contracts/program-ids/
    devnet.json. Includes a docstring noting the public RPC's rate
    limits.
  * AR_IO_SOLANA_MAINNET = null placeholder (AR.IO Solana mainnet is
    not yet deployed; will be populated in a future commit).
  * AR_IO_SOLANA_PRESETS keyed lookup for use by the settings UI.

- src/config/defaults.ts:
  * SOLANA_NETWORK_DEFAULTS = { network: 'devnet', ...devnet preset }
    for use by the upcoming migration commit when it populates
    EXTENSION_DEFAULTS / chrome.storage on first install.

The follow-up PR ('refactor(extension): drop AO entirely; route through
Solana') will: rewrite the four ARIO.init call sites in background.ts
and settings.ts; replace the AO Process ID / CU URL form fields in
settings.html with a network preset selector + Solana RPC + program ID
inputs; add storage migration logic for existing AO-era users
(silent reset to devnet defaults + force GAR resync); drop
@permaweb/aoconnect; bump @ar.io/sdk to ^4.0.0-solana.8.

Verified:
- npm run build -w @ar.io/wayfinder-extension passes (tsc + bundle)
- biome lint clean

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(extension)!: drop AO entirely; route through Solana (#247)

* refactor(extension)!: drop AO entirely; route through Solana

BREAKING change: AO support is removed from wayfinder-extension. The
extension now talks to AR.IO exclusively through the Solana-backed
@ar.io/sdk@4.x. Default network on fresh install is AR.IO Solana
devnet; mainnet is reserved for when the AR.IO Solana mainnet
deployment ships.

Builds on the additive foundation landed in the previous commit
(refactor(extension): add Solana network type/constant/default
scaffolding).

Code:
- src/background.ts: drop AOProcess + @permaweb/aoconnect + AO process
  constant imports; add arioFromStorage() helper that constructs a
  read-only Solana ARIO from chrome.storage.local with EXTENSION_
  DEFAULTS fallback; rewrite the four ARIO.init call sites to use it;
  add migrateStorageFromAOEra() that detects pre-existing AO-era
  storage keys, removes them, invalidates the cached gateway registry,
  and lets the next sync repopulate from Solana.
- src/settings.ts: drop ARIO.mainnet() fallback in favor of a
  Solana-backed ARIO.init({backend: 'solana', ...}) built from current
  storage; replace handleProcessIdChange + handleAoCuUrlChange with a
  consolidated handleNetworkConfigChange covering all six new fields
  (network, rpcUrl, four program IDs); validate inputs via URL() for
  the RPC and @solana/kit `address()` for program IDs; preset
  selection auto-fills + disables the per-field inputs.
- src/constants.ts: remove ARIO_MAINNET_PROCESS_ID and
  DEFAULT_AO_CU_URL.
- src/config/defaults.ts: drop the AO `processId`/`aoCuUrl` entries
  from EXTENSION_DEFAULTS; replace with the Solana network defaults
  (`network`, `rpcUrl`, four program IDs).
- src/settings.html: replace "AR.IO Process ID" + "AO Compute Unit
  URL" sections with a Network preset selector, a Solana RPC URL
  input, and a collapsible "Advanced: AR.IO Program IDs" panel
  exposing the per-program addresses. Mainnet preset is `disabled`
  until AR.IO Solana mainnet ships.

Dependencies:
- @ar.io/sdk: ^3.21.0 -> ^4.0.0-solana.8
- @solana/kit: added at ^2.1.0 (used by SDK's Solana backend and by
  the extension's address() validation in settings + the settings-
  side fallback ARIO instance)
- @permaweb/aoconnect: removed

Storage migration:
- AO-era users had `processId` + `aoCuUrl` in chrome.storage.local.
  On first run after upgrade migrateStorageFromAOEra() removes them
  along with the cached `localGatewayAddressRegistry`, then the
  startup defaults block writes the new Solana keys. Background
  re-init then connects to AR.IO Solana devnet and the next sync
  repopulates the registry from a Solana gateway source. Users see
  the extension keep working with no settings prompt.

Verified locally:
- `npm run build -w @ar.io/wayfinder-extension` passes (tsc + vite)
- `biome check` passes
- Bundle size shrinks (vendor chunk: 5.3 MB -> 3.8 MB) thanks to
  dropping the AO compute stack and the aoconnect dependency.

Out of scope (deferred): no automated tests exist in the extension
package; validation is purely manual via a Chrome dev profile. The
PR description includes a step-by-step smoke-test checklist.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(extension): clean up AO relics + wire reinitializeArIO

Follow-up on the AO -> Solana migration: a self-review surfaced six
remaining AO relics + one real bug. Bundled here as a single
clean-up commit; the original 02a7660 stays as the readable
migration commit.

Real bugs:

  - gateways.ts: the gateway-ping health check probed `info.processId`
    on /ar-io/info, which AR.IO Solana gateways no longer emit (replaced
    by `programIds`). Without this fix, every modern AR.IO gateway
    would be marked Unhealthy by the extension's ping tool. Switched
    the check to `info.programIds` (typeof object).

  - background.ts (updateAdvancedSettings handler): handler was
    calling `chrome.storage.local.set(request.settings)`, but the
    settings page already writes storage directly and now sends the
    message with no `settings` field. The handler was effectively a
    no-op for storage and never rebuilt the ARIO instance — so a user
    changing the Solana RPC URL or program IDs wouldn't see the
    underlying SDK pick up the change until the next service-worker
    restart. Replaced the (dead) storage write with a call to
    `reinitializeArIO()` so the ARIO read instance rebuilds from the
    new storage on every settings change.

Dead code removed:

  - background.ts: `setAoCuUrl` message handler (only reachable from
    the now-deleted AO CU URL form field). Plus its allowlist entry.
    Plus the orphan `setArIOProcessId` allowlist entry that never had
    a handler.

  - settings.ts: the "Reset all settings" button still removed the
    legacy `processId` / `aoCuUrl` storage keys instead of the new
    Solana keys, so pressing it left the user's storage untouched.
    Updated to remove the six new network keys; the startup defaults
    block then repopulates them with the devnet preset on next load.

Build hygiene:

  - vite.config.js: the manualChunks `webIndex` chunk still listed
    `@permaweb/aoconnect` (now removed from dependencies). Replaced
    with `@solana/kit` to keep the chunk grouping useful.

Docs:

  - README.md: "Network Configuration" section described the old
    "Process ID" + "AO CU URL" fields. Replaced with the new Network
    preset / Solana RPC URL / Advanced program IDs description.

Note: `reinitializeArIO` (now wired into `updateAdvancedSettings`)
was orphaned by the `setAoCuUrl` removal; lint would have flagged
it. Pre-emptively re-wired before lint caught it.

Verified locally:
- `npm run build -w @ar.io/wayfinder-extension` passes (tsc + vite)
- `biome check` passes
- Final grep: no remaining unintentional AO references in the
  extension src/, README, vite config, or manifest. The remaining
  AO references in background.ts are all inside
  `migrateStorageFromAOEra()` (intentional — that function detects
  and removes AO-era storage keys).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* ci: re-trigger workflows after base retarget

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>

* fix(extension): Solana migration follow-ups (fallback gate + startup robustness + dead code) (#248)

Surfaced by a post-merge end-to-end scan of the extension's Solana
migration. Three categories of cleanup:

1. Cross-network contamination via the mainnet fallback gateway

   FALLBACK_GATEWAY in constants.ts is hardcoded to turbo-gateway.com
   (mainnet). routing.ts uses it as a last-resort fallback when
   normal routing fails. After the migration the extension defaults
   to AR.IO Solana devnet, where the gateway registry is sparsely
   populated — so the fallback fires often and silently serves
   mainnet content under the user's devnet network. Now the fallback
   is gated on `network === 'mainnet'` in storage; on devnet or a
   custom network the original routing error is propagated instead,
   surfacing the empty-registry condition to the user rather than
   masking it with mainnet data.

2. Startup robustness against bad stored network config

   The boot IIFE in background.ts called
   `arIO = await arioFromStorage()` without a try/catch.
   arioFromStorage() invokes @solana/kit's address() on each stored
   program-ID, which throws on non-base58 input. If a user
   previously persisted a bad value via the custom-network preset,
   the IIFE would crash before reaching debouncedInitializeWayfinder(),
   leaving routing inoperable until manual reset. Now wrapped with a
   fallback to bundled EXTENSION_DEFAULTS that mirrors the existing
   reinitializeArIO() catch path.

3. Dead-code removal

   - `resetAdvancedSettings` message handler: had logic but no UI
     button calling it after the migration. The startup defaults
     block plus the existing `updateAdvancedSettings` flow cover the
     same surface.
   - `updateVerificationMode` allowlist entry: pure dead string, no
     handler ever existed in the post-migration tree.

   Both removed from the validMessages allowlist; the resetAdvanced
   Settings handler body removed too.

Verified locally:
- npm run build -w @ar.io/wayfinder-extension passes
- npm run lint:check (biome + eslint, 121 files) passes
- No new tests added (extension package has none; smoke test
  remains a manual Chrome dev-profile exercise).

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>

* docs(core): rewrite README examples from AO mainnet to Solana syntax (#249)

wayfinder-core itself is chain-agnostic — it consumes only the
`AoARIORead` type and delegates instance construction to the caller.
But the published README only showed `ARIO.mainnet()` (the AO-era
AR.IO mainnet) examples, which is misleading in the post-Solana-
migration ecosystem.

Rewrite the four example blocks to use the SDK 4.x Solana backend:

  const ario = ARIO.init({
    backend: 'solana',
    rpc: createSolanaRpc('https://api.devnet.solana.com'),
    coreProgramId: address('...'),
    garProgramId: address('...'),
    arnsProgramId: address('...'),
    antProgramId: address('...'),
  });

The first example carries a comment explaining how to switch to
mainnet (drop the program-ID overrides; the SDK ships mainnet
defaults) once AR.IO Solana mainnet deploys, and reaffirms that
wayfinder-core is chain-agnostic — same APIs work for any backend
the SDK supports.

Sections updated:
- Getting Started: Using with AR.IO Network
- NetworkGatewaysProvider reference
- CompositeGatewaysProvider example
- CompositeRoutingStrategy examples 1 + 2

Docs-only. No code changes. wayfinder-core's `network.ts` already
type-checked against SDK 4.x in the earlier compat-verification
commit (991e450).

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>

* feat!: migrate to @ar.io/sdk v4.0.2 stable (Solana-only) — v2.0.0

BREAKING CHANGE: Drop AO backend support entirely; Solana is now the
only backend for the AR.IO on-chain registry.

SDK migration:
- Rename Ao-prefixed types: AoARIORead→ARIORead, AoGateway→Gateway,
  AoGatewayWithAddress→GatewayWithAddress across all packages
- Remove `backend: 'solana'` from all ARIO.init() calls (Solana is
  the only backend, parameter no longer exists)
- Bump @ar.io/sdk from ^4.0.0-solana.8 to ^4.0.2
- Bump @solana/kit from ^2.1.0 to ^6.8.0
- Bump wayfinder-core peer dep from @ar.io/sdk>=3.12.0 to >=4.0.0

Program IDs & mainnet:
- Update devnet program IDs to match SDK v4.0.2 deployment
- Populate mainnet program IDs (AR.IO Solana mainnet is now live)
- Enable mainnet preset in extension settings UI
- Add storage migration for users with stale devnet program IDs

Build:
- Fix Vite bundling for @solana/kit v6 (resolve.alias to deduplicate
  conflicting v5/v6 @solana/* sub-packages from x402-fetch)

Version bumps:
- wayfinder-core: 1.9.2 → 2.0.0
- wayfinder-extension: 1.0.23 → 2.0.0
- wayfinder-react: 1.0.29 → 2.0.0
- monorepo: 0.0.14 → 1.0.0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): use dynamic resolution for @solana/* Vite aliases

The previous hardcoded paths to @solana/kit/node_modules/ only worked
with yarn's nesting layout. CI uses npm, which hoists differently and
the nested paths don't exist.

Switch to createRequire anchored to @solana/kit's main entry, so
each @solana/* sub-package resolves to the version kit was built
against regardless of package manager hoisting strategy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* perf(core,extension): add fetch timeouts, ping caching, periodic sync

1. Fetch timeouts — prevent indefinite hangs on slow/dead endpoints:
   - TrustedPeersGatewaysProvider: 10s timeout (configurable)
   - ContiguousDataRetrievalStrategy: 30s timeout (configurable)
   - ChunkDataRetrievalStrategy: 10s metadata, 30s per chunk (configurable)
   - Extension DNS lookup: 5s timeout

2. Ping result caching — avoid re-pinging all gateways on every request:
   - Wrap FastestPingRoutingStrategy with SimpleCacheRoutingStrategy
     (120s TTL) in the extension's routing setup
   - Stale cache is returned on ping failure (built into SimpleCacheRoutingStrategy)

3. Periodic gateway registry sync — recover from failed startup syncs:
   - chrome.alarms fires every 60 minutes
   - Skips sync if last sync was recent (staleness check via lastSyncTime)
   - Added "alarms" permission to manifest.json

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(extension): change periodic gateway sync to every 24 hours

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(extension): default to mainnet, improve program IDs UI

- Switch default network preset from devnet to mainnet
- Restyle the Advanced: AR.IO Program IDs section:
  - Proper collapsible disclosure triangle
  - Stacked label+input layout with spacing
  - Monospace font for pubkey inputs
  - Description text with muted styling

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): add retry with backoff for Solana RPC rate limits

The public Solana RPC endpoints (mainnet-beta, devnet) aggressively
rate-limit, returning HTTP 403 / SolanaError #8100002. Each
getGateways() page fetch is now wrapped in retryWithBackoff() which:

- Retries up to 5 times on 403, 429, 5xx, and network errors
- Uses exponential backoff with jitter (2s base, 15s cap)
- Lets application errors (bad address, deserialization) bubble immediately

This makes the gateway sync work reliably with the free public RPC
endpoints without requiring a premium QuickNode/Helius endpoint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): use QuickNode RPC for Solana mainnet

The public api.mainnet-beta.solana.com actively rejects requests from
Chrome extension service workers (HTTP 403). Switch the mainnet
default to a QuickNode endpoint that works from browser contexts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs(extension): update README for v2.0.0

- Update Chrome Web Store section (no longer "coming soon")
- Add yarn install + core build step to dev instructions
- Update project structure with actual .ts filenames and new files
- Add @ar.io/sdk v4.x and @solana/kit to key technologies
- Clarify network preset defaults and RPC details

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(extension): rebrand UI to AR.IO 2026 design system

Update the entire extension UI to match the AR.IO brand kit (v2026-03-30):

Colors:
- Primary accent: teal rgb(45, 211, 190) → purple #5427C8
- Secondary: dark teal → lavender #DFD6F7
- Dark backgrounds: neutral grays (#050505 scale) → purple-tinted darks (#0e0a1c scale)
- Text on dark: gray #cacad6 → lavender #D4C6FF
- Gradients: peach-to-purple (#f7c3a1→#df9be8) → purple spectrum (#5427C8→#DFD6F7)
- Light theme: slate grays → Lavender Wash #f1ecff / Warm Neutral #F6F4EF

Typography:
- Headings: Rubik → Besley (weight 800, tight line-height 1.08)
- Body: Rubik/Inter → Plus Jakarta Sans
- Monospace: JetBrains Mono (kept)

Layout:
- Border radii increased: 4/8/12/16px → 8/12/20/24px
- Strokes: neutral gray-blue rgba → purple-tinted rgba

Files updated:
- variables.css: Complete design token rewrite
- popup.css, settings.css, gateways.css, gateways-enhanced.css, performance.css:
  All hardcoded colors replaced with new brand values
- All 4 HTML files: Google Fonts updated to Besley + Plus Jakarta Sans

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): pixel-perfect brand compliance pass

- All Besley headings: font-weight 800, letter-spacing -0.025em, line-height 1.08
- Popup logo h1: switched from body font to heading font (Besley)
- Nav card h3s: switched to Besley heading font
- Toggle unchecked state: #0e0a1c → #26203a for visible contrast
- Focus rings: added box-shadow glow on all text/url inputs
- Filter buttons: border-radius 6px → var(--radius-sm, 8px)
- Back buttons: border-radius 10px → var(--radius-md, 12px)
- Fixed 4 remaining font-weight: 600 headings in settings.css

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): enforce brand minimum 8px radius on interactive elements

Tooltips, buttons, selects, close buttons, and list items had 4-6px
radii. Brand spec minimum is 8px (--radius-sm). Updated 13 elements
across popup.css, settings.css, gateways.css, and performance.css.

Intentionally kept small radii on: scrollbar thumbs (3px), range
slider tracks (3px), decorative accent bars (2px), tiny badges (3px),
checkboxes (3px), and progress bars (3px) — these are sub-element
details where 8px would look wrong.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): add theme support to gateways/performance + fix card bg consistency

Theme support:
- gateways.ts and performance.ts had NO theme application — light mode
  was completely broken on these pages. Added applyTheme() + system
  preference listener to both, matching popup.ts/settings.ts pattern.

Card background consistency:
- Gateways container used L1 (#1a1527) instead of L0 (#0e0a1c) as
  page background — one shade off from popup/settings/performance.
- Gateways back-button used L1 instead of L0 (inconsistent with
  settings/performance back buttons).
- 8 hardcoded #26203a hover states replaced with
  var(--colors-container-containerL2) so light theme hovers work.
- Toggle switch border/bg hardcoded values → CSS variables.

All 4 pages now have identical theme detection, identical background
hierarchy (L0 page → L1 cards → L2 hover → L3 inputs), and all
container colors use CSS variables for correct light/dark theming.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): update icon, fix theme flicker, tighten popup layout

Extension icon:
- Replace old icon with latest AR.IO brand icon from brand kit
  (ario-white-on-black-round) at 128/48/16px sizes

Theme flicker fix:
- Add inline <script> in <head> of all 4 HTML pages that sets
  data-theme synchronously before CSS paints — eliminates the flash
  of dark theme when navigating pages in light mode

Popup layout tightening:
- Reduce section gap from 18px to 12px
- Reduce stat card height from 89.7px to 72px
- Reduce nav card gap from 16px to 8px between cards
- Reduce card padding from 16px to 12px vertically
- Shrink strategy/feature icons from 32px to 28px
- Remove transform/box-shadow from nav-card hover (was inconsistent
  with strategy-card hover behavior — now all cards hover identically)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): normalize card backgrounds to containerL1 across all pages

- Popup: replaced var(--bg-secondary) alias with explicit
  var(--colors-container-containerL1) on stat-card, strategy-card,
  nav-card — matches settings/gateways/performance convention
- Fixed duplicate .strategy-card block (second override) that still
  used --bg-secondary with larger padding
- Settings: network-option background was containerL0 (same as page
  bg, invisible) — changed to containerL1 to match other cards

All cards across all 4 pages now use identical token:
  background: var(--colors-container-containerL1, #1a1527)
  hover: var(--colors-container-containerL2, #26203a)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): fix container background hierarchy across all pages

Apply consistent design token usage across all CSS:
- Card/section elements use containerL1 (raised from page background)
- Hover states use containerL2
- Input fields use containerL3
- Page background, headers, footers remain containerL0

Fixes:
- popup.css: nav-cards gap 8px→12px to match parent section gap
- settings.css: .theme-option, .advanced-option, .mode-option,
  .option-card, .provider-option, .verification-strategy-selector
  .strategy-option, .trusted-gateways-config .mode-option all
  changed from L0 to L1; hover states from L0 to L2
- performance.css: .filter-section, .usage-section, .action-card
  changed from L0 to L1
- gateways.css: .search-box input changed from L0 to L3

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): fix advanced settings hover, toggle contrast, gateways light mode

- Fix advanced settings section hover: .advanced-options → .advanced-config
  in the :not() exclusion list so hovering sub-items doesn't grey out the
  entire card
- Improve toggle switch unchecked state: use containerL3 instead of L2
  with visible border for better contrast against card backgrounds
- Add light mode overrides for gateways page: header border, filter
  section, filter buttons, search input all now use proper light theme
  tokens instead of dark-mode rgba values

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): comprehensive design system audit fixes across all pages

Full pixel-level audit of popup, settings, gateways, and performance
pages. Every element checked against the L0/L1/L2/L3 container
hierarchy, font families, and text color tokens.

Popup:
- .stat-label: textHigh → textMid (secondary text should be muted)
- .strategy-description: textHigh → textMid
- .nav-content p: textHigh → textMid

Settings:
- .back-button:hover: L0 → L2 (was invisible hover state)
- .input-with-button input: L0 → L3 (input fields must be L3)

Performance:
- .back-button:hover: L0 → L2 (no visual hover feedback)
- .stat-card: --bg-secondary → containerL1 (undefined legacy var)
- .stat-value: --text-high → neutrals-100 (undefined legacy var)
- .stat-label: --text-high → textMid (secondary text muted)
- Light mode: add missing overrides for filter-section, filter-btn,
  sort-select, stat-card, stat-value, stat-label, usage-section,
  section-header

Gateways:
- .stat-card: --bg-secondary → containerL1 (undefined legacy var)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): cross-page consistency pass — borders, layout, focus rings

Structural consistency:
- Gateways page header/footer now fixed-position (was relative),
  matching popup/settings/performance — fixes inconsistent scroll
  behavior. Content padding updated to account for fixed elements.
- Settings back-button radius 10px → var(--radius-md, 12px) to match
  gateways/performance pages.

Border consistency:
- Popup stat-cards now have 1px solid var(--border-subtle) border
  (was missing — strategy/nav cards had it, stat-cards didn't).
- Normalized ALL border opacities from 0.08 to 0.10 across settings.css
  (14 occurrences — strategy options, theme options, advanced options,
  buttons, mode options, etc.).

Focus accessibility:
- Added purple focus ring (box-shadow glow) to network select dropdown
  in settings — was the only input without one.

Light theme:
- Added [data-theme="light"] block to gateways-enhanced.css for modal
  stat cards — was completely missing, causing dark styles in light mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): fix light mode backgrounds on gateways and performance pages

- Add explicit light mode background override for .gateways-container
  and .performance-container (CSS variable fallback was showing dark bg)
- Add light mode overrides for gateways header, footer, stat cards
- Add light mode overrides for performance container

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): fix gateways page scroll clipping

Remove overflow:hidden from .gateways-container — it was clipping the
scrollable .gateways-content child, preventing scrolling to the top
of the gateway list behind the fixed header.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): reorder theme options — Dark first (default)

Dark is the default theme, so it should appear first in the theme
selector. Having Light first was confusing since it visually stood
out with its white preview styling, making users think it was selected.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): eliminate dark-to-light theme flash on page navigation

Page was painting with dark defaults before the async
chrome.storage.local.get() resolved with the user's theme preference.

Fix: hide the page via `html:not([data-theme-ready]) { opacity: 0 }`
inline style, then set `data-theme-ready` in the same callback that
applies the theme. The browser only paints once the theme is set,
eliminating the flash. Applied to all 4 HTML pages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): move theme init to external script for MV3 CSP compliance

Chrome MV3 Content Security Policy blocks inline scripts. Move the
theme initialization logic from inline <script> tags to an external
theme-init.js file loaded via <script src="theme-init.js">.

The opacity:0 → data-theme-ready technique still prevents the
dark-to-light flash, now without violating CSP.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): force white background on html/body in light mode

Gateways and performance pages had dark fallback (#0e0a1c) on the
html/body elements that was bleeding through as a grey hue at the
top of the page in light mode. Override html, body, and the main
container to #ffffff directly in the [data-theme="light"] block.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): white primary text in dark mode, fix gateways scroll

Text colors:
- textHigh: #d4c6ff (lavender) → #ffffff (white) for primary text
- textMid: #9b93b4 → #d4c6ff (lavender) for secondary/description text
- iconHigh: #d4c6ff → #ffffff for primary icons
- Muted text (#9b93b4) unchanged

Gateways scroll:
- Restore overflow:hidden on .gateways-container (needed for flex
  child scroll constraint)
- Change min-height:100vh → height:100vh so the container is fixed
  size and .gateways-content overflow-y:auto actually activates

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(extension): add Top Staked routing strategy

New routing strategy that round-robins across the top 20 gateways
by total delegated stake, with ping health checks on each selection.

- Add `limit` option to ChromeStorageGatewayProvider
- Add `topStaked` case in routing.ts using RoundRobinRoutingStrategy
  with a stake-sorted, limited gateway provider wrapped in
  PingRoutingStrategy for health verification
- Add strategy card in settings UI with star icon
- Add label mapping in popup.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(extension): default routing strategy to topStaked

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: update docs for topStaked default and mainnet

- CLAUDE.md: fix default network to mainnet (was still saying devnet)
- Extension CLAUDE.md: add topStaked strategy, update cache TTL
- Extension README: add Top Staked to features list and routing config

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
@vilenarios vilenarios requested a review from a team as a code owner June 19, 2026 00:17
@changeset-bot

changeset-bot Bot commented Jun 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 054a5ae

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@ar.io/wayfinder-extension Major
@ar.io/wayfinder-core Major
@ar.io/wayfinder-react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@vilenarios, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 19 minutes and 5 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d14feb4d-d1be-465a-9bc7-300b8f4bae49

📥 Commits

Reviewing files that changed from the base of the PR and between c01f072 and 054a5ae.

⛔ Files ignored due to path filters (5)
  • package-lock.json is excluded by !**/package-lock.json
  • packages/wayfinder-extension/assets/icon128.png is excluded by !**/*.png
  • packages/wayfinder-extension/assets/icon16.png is excluded by !**/*.png
  • packages/wayfinder-extension/assets/icon48.png is excluded by !**/*.png
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (62)
  • .changeset/extension-solana-followups.md
  • .changeset/extension-solana-foundation.md
  • .changeset/extension-solana-only.md
  • .changeset/wayfinder-core-readme-solana.md
  • .changeset/wayfinder-core-solana-sdk.md
  • .github/workflows/core.yml
  • .github/workflows/extension.yml
  • .github/workflows/pr-check.yml
  • .github/workflows/react.yml
  • ARWEAVE_NET_ANALYSIS.md
  • CLAUDE.md
  • GATEWAY_AUDIT_FINAL.md
  • GATEWAY_MIGRATION_SUMMARY.md
  • experimental/wayfinder-cli/CHANGELOG.md
  • experimental/wayfinder-cli/package.json
  • experimental/wayfinder-cli/src/commands/info.ts
  • package.json
  • packages/wayfinder-core/CHANGELOG.md
  • packages/wayfinder-core/README.md
  • packages/wayfinder-core/package.json
  • packages/wayfinder-core/src/client.ts
  • packages/wayfinder-core/src/constants.ts
  • packages/wayfinder-core/src/gateways/network.ts
  • packages/wayfinder-core/src/gateways/trusted-peers.ts
  • packages/wayfinder-core/src/retrieval/chunk.ts
  • packages/wayfinder-core/src/retrieval/contiguous.ts
  • packages/wayfinder-core/src/routing/round-robin.ts
  • packages/wayfinder-core/src/version.ts
  • packages/wayfinder-core/src/wayfinder.test.ts
  • packages/wayfinder-core/src/wayfinder.ts
  • packages/wayfinder-extension/CHANGELOG.md
  • packages/wayfinder-extension/CLAUDE.md
  • packages/wayfinder-extension/README.md
  • packages/wayfinder-extension/assets/css/gateways-enhanced.css
  • packages/wayfinder-extension/assets/css/gateways.css
  • packages/wayfinder-extension/assets/css/performance.css
  • packages/wayfinder-extension/assets/css/popup.css
  • packages/wayfinder-extension/assets/css/settings.css
  • packages/wayfinder-extension/assets/css/variables.css
  • packages/wayfinder-extension/manifest.json
  • packages/wayfinder-extension/package.json
  • packages/wayfinder-extension/src/adapters/chrome-storage-gateway-provider.ts
  • packages/wayfinder-extension/src/background.ts
  • packages/wayfinder-extension/src/config/defaults.ts
  • packages/wayfinder-extension/src/constants.ts
  • packages/wayfinder-extension/src/gateways.html
  • packages/wayfinder-extension/src/gateways.ts
  • packages/wayfinder-extension/src/performance.html
  • packages/wayfinder-extension/src/performance.ts
  • packages/wayfinder-extension/src/popup.html
  • packages/wayfinder-extension/src/popup.ts
  • packages/wayfinder-extension/src/routing.ts
  • packages/wayfinder-extension/src/settings.html
  • packages/wayfinder-extension/src/settings.ts
  • packages/wayfinder-extension/src/theme-init.js
  • packages/wayfinder-extension/src/types.ts
  • packages/wayfinder-extension/vite.config.js
  • packages/wayfinder-react/CHANGELOG.md
  • packages/wayfinder-react/README.md
  • packages/wayfinder-react/package.json
  • packages/wayfinder-react/src/version.ts
  • scripts/x402-fetch.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch alpha

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.

vilenarios and others added 2 commits June 18, 2026 20:23
- Replace dead arnsProcessId (X-ArNS-Process-Id) with Solana-era
  arnsAntProgramId (X-ArNS-Ant-Program-Id) and arnsAntId
  (X-ArNS-Ant-Id)
- Add new root data headers: rootPath, rootItemOffset, rootItemSize
- Add HTTPSIG headers: Signature-Input, Signature (RFC 9421)
- Add Arweave tx metadata headers: owner, ownerAddress, signature,
  signatureType, tagCount

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vilenarios vilenarios merged commit b47e957 into main Jun 19, 2026
18 of 20 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.

2 participants