Fix WebSocket URL scheme and drop stale dev-only CSP override

BACKEND_WS_URL was set to https://, but new WebSocket() requires a
ws://wss:// scheme and throws a SyntaxError otherwise. Also removes
the Firefox-only CSP override that worked around Firefox upgrading a
plaintext ws:// dev connection to wss:// — now that the backend is
real wss:// behind TLS, there's nothing left to upgrade.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Tn7uDYjTuZEbLPwpEiSCUw
This commit is contained in:
2026-09-08 12:02:44 +02:00
co-authored by claude
parent 3cc150508d
commit bee164ebf9
3 changed files with 8 additions and 25 deletions
+2 -2
View File
@@ -87,8 +87,8 @@ Note the naming mismatch with the wire protocol below: the frontend's internal `
## Backend connection (implemented) ## Backend connection (implemented)
- `src/shared/config.ts` — `BACKEND_HTTP_URL`/`BACKEND_WS_URL`, currently hardcoded to `localhost:8080` (dev only; `host_permissions` in `wxt.config.ts` must stay in sync with whatever host is configured here). Deliberately not `:3000` — that's this extension's own Vite dev server port (`npm run dev`), and running the backend on the same port breaks the dev popup silently: its script tags point at Vite, but the backend answers instead, so nothing ever renders. If you see a blank popup with `http://localhost:3000/...` script tags in "View Page Source" that 404 or return something unexpected, this port collision is the first thing to check. - `src/shared/config.ts` — `BACKEND_HTTP_URL`/`BACKEND_WS_URL`, pointed at the real deployed backend (`api.osias.trade`, TLS). `host_permissions` in `wxt.config.ts` must stay in sync with whatever host is configured here.
- **Firefox-only CSP override** in `wxt.config.ts`: Firefox's *implicit* default extension-pages CSP includes `upgrade-insecure-requests`, which silently rewrites the WS client's `ws://localhost:8080/ws` connection to `wss://` and breaks it against the plaintext local dev backend (no TLS in dev, deliberately — see backend/CLAUDE.md). Symptom: `Content-Security-Policy: Upgrading insecure request 'ws://...' to use 'wss'` in the console, followed by a failed connection, no other error. Fixed by declaring an explicit `content_security_policy.extension_pages` (otherwise identical to Firefox's own default) for the Firefox build only — an explicit CSP replaces the implicit one entirely, dropping the upgrade directive. Chrome doesn't have this behavior, so the override is gated on `browser === 'firefox'` in the manifest function. If the real deployed backend ever moves to plain `ws://` too (vs. `wss://` behind a real domain), this override needs to travel with it; if the backend gets TLS, this whole override becomes unnecessary and should be removed rather than left as dead configuration. - The Firefox-only CSP override that used to live in `wxt.config.ts` (working around Firefox upgrading a plaintext `ws://` dev connection to `wss://`) has been removed now that the backend is real `wss://` behind TLS — `upgrade-insecure-requests` has nothing to upgrade. Re-add it, scoped to `browser === 'firefox'`, only if a plaintext dev backend comes back into the loop.
- `src/background/backend-client.ts` — session storage only (`getSessionToken()`/`getStoredSession()`/`storeSession(token, walletAddress)`/`clearSessionToken()`). Stores the wallet address alongside the token, not just the token — see "Session storage tracks which wallet it belongs to" below for why. Getting a token in the first place is `wallet-auth.ts`'s job. - `src/background/backend-client.ts` — session storage only (`getSessionToken()`/`getStoredSession()`/`storeSession(token, walletAddress)`/`clearSessionToken()`). Stores the wallet address alongside the token, not just the token — see "Session storage tracks which wallet it belongs to" below for why. Getting a token in the first place is `wallet-auth.ts`'s job.
- `src/background/wallet-auth.ts` — `handleWalletConnected()` runs the REST auth flow (`POST /auth/nonce` → Phantom signature → `POST /auth/verify` → session token) once a content script reports a connected wallet. Also `requestWalletReconnect()` (silent reconnect after the backend invalidates a session), `requestWalletDisconnect()` (sign-out), `requestAccountSwitch()` (explicit account switch) — all three just message whichever tabs are on a supported site; `wallet-connect.ts` in the content script does the actual work. - `src/background/wallet-auth.ts` — `handleWalletConnected()` runs the REST auth flow (`POST /auth/nonce` → Phantom signature → `POST /auth/verify` → session token) once a content script reports a connected wallet. Also `requestWalletReconnect()` (silent reconnect after the backend invalidates a session), `requestWalletDisconnect()` (sign-out), `requestAccountSwitch()` (explicit account switch) — all three just message whichever tabs are on a supported site; `wallet-connect.ts` in the content script does the actual work.
- `src/background/ws-client.ts` — the WS client described above: connects to `/ws?token=...`. `connectWsClient()` returns a controller with `reconnectNow()` so the background script can short-circuit the backoff wait right after a fresh token arrives. Auth failures (`4001` close, `auth_expired`/`session_revoked` errors) do **not** auto-retry with backoff — they call `onAuthExpired()` instead, since retrying with a known-bad token can't succeed; only real disconnects (network drop, backgrounded browser) use the protocol's suggested backoff schedule. - `src/background/ws-client.ts` — the WS client described above: connects to `/ws?token=...`. `connectWsClient()` returns a controller with `reconnectNow()` so the background script can short-circuit the backoff wait right after a fresh token arrives. Auth failures (`4001` close, `auth_expired`/`session_revoked` errors) do **not** auto-retry with backoff — they call `onAuthExpired()` instead, since retrying with a known-bad token can't succeed; only real disconnects (network drop, backgrounded browser) use the protocol's suggested backoff schedule.
+3 -8
View File
@@ -1,11 +1,6 @@
/** /**
* Nexa backend location. Dev-only default — swap for the real deployed * Nexa backend location. `host_permissions` in wxt.config.ts must be kept in
* backend host before shipping (see backend/CLAUDE.md, "Relationship to the * sync with this host.
* frontend"). `host_permissions` in wxt.config.ts must be kept in sync with
* this host.
*/ */
// Port 8080, deliberately not 3000 — that's WXT/Vite's dev server port for
// this extension, and colliding with it breaks the dev popup silently (its
// script tags point at Vite, but the backend answers instead).
export const BACKEND_HTTP_URL = 'https://api.osias.trade'; export const BACKEND_HTTP_URL = 'https://api.osias.trade';
export const BACKEND_WS_URL = 'https://api.osias.trade/ws'; export const BACKEND_WS_URL = 'wss://api.osias.trade/ws';
+3 -15
View File
@@ -7,13 +7,11 @@ export default defineConfig({
modules: ['@wxt-dev/module-react'], modules: ['@wxt-dev/module-react'],
// Target Manifest V3 on both Chromium and Firefox (modern Firefox / Zen support it). // Target Manifest V3 on both Chromium and Firefox (modern Firefox / Zen support it).
manifestVersion: 3, manifestVersion: 3,
manifest: ({ browser }) => ({ manifest: {
name: 'Nexa', name: 'Nexa',
description: 'Your blockchain powered agent to help with your trading emotions.', description: 'Your blockchain powered agent to help with your trading emotions.',
permissions: ['storage'], permissions: ['storage'],
// axiom.trade: the site adapter target. localhost:8080: the Nexa backend // axiom.trade: the site adapter target. The other entry is the Nexa backend.
// (dev only — swap/extend for the real backend host before shipping).
// Not 3000 — that's this extension's own Vite dev server port.
host_permissions: ['https://axiom.trade/*', BACKEND_HTTP_URL + "/*"], host_permissions: ['https://axiom.trade/*', BACKEND_HTTP_URL + "/*"],
browser_specific_settings: { browser_specific_settings: {
gecko: { gecko: {
@@ -21,15 +19,5 @@ export default defineConfig({
id: '[email protected]', id: '[email protected]',
}, },
}, },
// Firefox's implicit default extension-pages CSP includes },
// upgrade-insecure-requests, which silently rewrites our ws:// WS client
// connections to wss:// and breaks them against the plaintext local dev
// backend (no TLS in dev — see backend/CLAUDE.md). Declaring our own CSP
// (identical to the standard default otherwise) replaces Firefox's
// implicit one and drops that directive. Chrome doesn't have this
// behavior, so this is Firefox-only.
...(browser === 'firefox'
? { content_security_policy: { extension_pages: "script-src 'self'; object-src 'self'" } }
: {}),
}),
}); });