RPC proxy
The browser needs a Solana RPC endpoint and a websocket for account subscriptions. Pointing it straight at the upstream provider would ship the provider's API key to every visitor. So the key stays on the API, and a proxy forwards requests on the browser's behalf.
Both halves, HTTP and websocket, live on the same path, which is why the web app can hand the Solana client one URL and derive the websocket form from it.
The HTTP half
The HTTP route forwards the JSON-RPC body verbatim. Every method the upstream supports works, and upstream errors pass through unchanged. The browser never learns the upstream URL or key.
The websocket half
The websocket half attaches to the HTTP server's upgrade event, because Express does not handle websockets itself. Each browser socket gets exactly one upstream socket, with frames buffered until the upstream connection finishes opening.
Websockets are exempt from CORS, so the upgrade handler checks the request's Origin against the same allowlist the HTTP side gets for free. Requests with no origin (server-side clients) are allowed through and still hit the per-IP caps.
Protecting the credit budget
Three limits keep the proxy from burning the upstream credit budget:
- A per-IP request rate limit.
- A cap on websocket connections per IP.
- A cap on total websocket connections.
Optional by design
The upstream API key is optional. Without it the API still boots, and the RPC route answers with a 503 rather than failing to start. That keeps every other part of the API usable in an environment that has no RPC provider configured.
One capability the wallet cannot guarantee
Both the web and claim apps use the same Solana wallet adapter, pointed at this proxy. One method the adapter does not guarantee is message signing: a Ledger over some wallets has no such method. Anything that proves ownership by signature has to branch on the method being present rather than assume it. See Airdrop and claim for where that matters.