Privacy

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.

System map: the Mitoshi app, its Zcash wallet and per-position keys in the browser send every user call over Oblivious HTTP to a Cloudflare relay that sees the IP, then to a Vercel gateway in Dublin that sees the request, then to allowlisted upstreams: the Zcash light server, NEAR Intents 1Click, and Polymarket, Hyperliquid and Jupiter. Public prices come through the relay.
The same map the README shows. Every user call takes the sealed path.

The client

StandardRFC 9458 Oblivious HTTP
SuiteDHKEM(X25519, HKDF-SHA256), HKDF-SHA256, AES-128-GCM; a fresh HPKE context per request
KeyThe gateway's key configuration is pinned in the app (packages/net/src/constants.ts), so the relay can't swap in its own
Inner requestcredentials: omit, no-referrer; up to 8 MiB
EndpointPOST /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/img and 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 tickers channel is skipped (about 700 updates a second for all markets); statistics gives the price, sent at most once a second. If a feed is unavailable, the relay answers 503 with Retry-After: 60 and 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):

UpstreamWhat for
zcash-mainnet.chainsafe.dev, zcash-testnet.chainsafe.devSix lightwalletd methods
1click.chaindefuser.comNEAR Intents quotes, deposits, status (the partner key is added here; the browser never sees it)
api.hyperliquid.xyzMarkets, accounts, signed orders
gamma-api, clob, relayer-v2, data-api, bridge.polymarket.comEvents, books, orders, gasless wallets, positions, the bridge
api.perpetuals.polymarket.comPolymarket Perps
polygon.drpc.orgRead-only Polygon calls, no sends
lite-api.jup.ag, api.geckoterminal.com, api.mainnet-beta.solana.comSolana 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

PicturePathWho sees what
Public ones (market and token lists)GET /relay/img?u=…, cached a dayThe relay sees which public picture, like any public price
Pictures of what you hold (positions, orders)inside an OHTTP message to img.mitoshi.internalThe 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.

On this page