Oblivious HTTP
Every call about you is sealed in your browser, carried by a relay that can't read it and opened by a gateway that can't see who sent it.
The client
| Standard | RFC 9458 Oblivious HTTP |
| Suite | DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, AES-128-GCM; a fresh HPKE context per request |
| Key | The gateway's key configuration is pinned in the app (packages/net/src/constants.ts), so the relay can't swap in its own |
| Inner request | credentials: omit, no-referrer; up to 8 MiB |
| Endpoint | POST /relay/ohttp on the same origin, content type message/ohttp-req |
The relay: knows who, not what
A Cloudflare Worker (mitoshi-web) with Cloudflare's targeted placement near AWS eu-west-1, and its logs off.
- Forwards only the sealed body to the gateway, with a fresh header set: no IP, cookies, user agent, referrer or language.
- Answers only
/relay/ohttp,/relay/imgand the three live feeds; anything else is a 404. - Holds one live WebSocket per venue (Hyperliquid, Polymarket, Polymarket Perps) in a Durable Object and shares it
with everyone watching. Only public market channels are allowed: a subscription that names a user is refused. The
Polymarket Perps
tickerschannel is skipped (about 700 updates a second for all markets);statisticsgives the price, sent at most once a second. If a feed is unavailable, the relay answers 503 withRetry-After: 60and the app backs off for a minute: live ticks pause, while prices and history still load over Oblivious HTTP.
The gateway: knows what, not who
A Vercel Function in Dublin (dub1, AWS eu-west-1). It accepts only messages that carry the relay's token, opens
each one, and calls only an allowlist of 14 API hosts with exact methods and path prefixes (pictures go through a
separate, checked image fetch, below):
| Upstream | What for |
|---|---|
zcash-mainnet.chainsafe.dev, zcash-testnet.chainsafe.dev | Six lightwalletd methods |
1click.chaindefuser.com | NEAR Intents quotes, deposits, status (the partner key is added here; the browser never sees it) |
api.hyperliquid.xyz | Markets, accounts, signed orders |
gamma-api, clob, relayer-v2, data-api, bridge.polymarket.com | Events, books, orders, gasless wallets, positions, the bridge |
api.perpetuals.polymarket.com | Polymarket Perps |
polygon.drpc.org | Read-only Polygon calls, no sends |
lite-api.jup.ag, api.geckoterminal.com, api.mainnet-beta.solana.com | Solana swaps, charts, balances and the trip home |
Every upstream request leaves with the same browser-like user agent for everyone and drops cookie, origin, referrer,
user-agent and forwarding headers. Redirects aren't followed. A public answer may be cached and shared between users
for seconds up to a few minutes; anything carrying a credential never is. Polymarket's event listings are trimmed to the
fields the app reads before they are sealed back (one evening of sports markets can otherwise pass 10 MB); a single
event is never trimmed (services/gateway/src/shape.ts).
Why the gateway isn't on Cloudflare
Tests on 2026-10-22 showed that a Cloudflare Worker passes the visitor's own country on to every Cloudflare-hosted site it calls, and Polymarket, 1Click and Jupiter sit behind Cloudflare. From Vercel's Dublin region, every request leaves from Ireland.
Pictures
| Picture | Path | Who sees what |
|---|---|---|
| Public ones (market and token lists) | GET /relay/img?u=…, cached a day | The relay sees which public picture, like any public price |
| Pictures of what you hold (positions, orders) | inside an OHTTP message to img.mitoshi.internal | The relay can't see which; the gateway fetches it without knowing who asked, and the browser gets a blob |
The image fetch accepts only HTTPS, refuses local and internal hosts and literal IP addresses, re-checks each of up to three redirects, and takes PNG, JPEG, WebP, GIF, AVIF or SVG up to 2 MiB.
The builder signer
Polymarket's gasless Deposit Wallets need builder headers. The gateway holds that secret and signs only four relayer
paths; on /submit it signs only WALLET-CREATE and WALLET requests to the Deposit Wallet factory. Anything else gets
a 403. Through OHTTP it sees which wallet asks, never who.