Methodology

AI chat: keys, what’s sent, and the limits

This is the trust contract behind AI chat. There is no AI built into this site — the chat page is a bring-your-own-key (BYOK) client: it calls the AI provider you choose with your key, directly from your browser. There is no server in the middle; there isn’t one to put there.

Your key: where it lives

  • In memory, by default. Paste a key and it lives only in the page’s memory. Close the tab and it’s gone — it never touches disk.
  • Optional encrypted storage. If you choose to save a key, it is encrypted with a key derived from your passphrase — PBKDF2 with 210,000 iterations and SHA-256, a random 16-byte salt, AES-GCM 256-bit with a random 12-byte IV — and only the encrypted blob is stored. The passphrase is never stored; a wrong passphrase fails closed with an error, and plaintext keys are never written to disk, logs, or URLs.
  • Deletion purges both. Deleting a key removes it from memory and deletes any encrypted copy, with a confirmation step.
  • Keys travel only in request headers (Authorization: Bearer, x-api-key, or x-goog-api-key depending on the provider) — never in URLs, never in logs.

What is sent when you press Send — and what is never sent

  • On Send, the provider receives: your messages (the conversation you see on screen), the model id, and the generation parameters (maximum output tokens, streaming on/off). That’s the entire payload.
  • Never sent: nothing leaves your browser until you press Send — typing, drafting, and switching models transmit nothing. Your API key is sent as a header to the provider only; it is not embedded in any URL. Nothing is sent to because there is nowhere to send it to.
  • Model listing: the “refresh models” button makes one GET to the provider’s model-list endpoint (with your key as a header) so you can pick from real model ids.
  • Responses stream in as server-sent events, chunk by chunk, so you see the answer as it’s generated. Cancel stops the stream; a hung request can’t wedge the page because cancellation doesn’t rely on the network layer honoring the abort.

Provider access notes — honest, not promotional

Your browser can only call providers that allow it (CORS). The page states each provider’s status plainly:

ProviderDirect browser access
OpenRouterSupported. Explicitly allows direct browser requests.
Anthropic ClaudeSupported via header. The app sends Anthropic’s required browser-access header automatically.
DeepSeekUnverified. Press Test connection first — if the browser blocks it, use a relay.
Google GeminiUnverified. Key-header requests are reported to work; verify with Test connection. The key goes in a header, never the URL.
OpenAIBlocked. OpenAI’s API sends no CORS headers, so browsers refuse the call. Use a custom endpoint pointed at your own local relay, or another provider.
Custom endpointUnverified. Your own OpenAI-compatible relay. KNOWN LIMITATION The page’s Content Security Policy currently allowlists only the built-in provider domains, so a custom domain is blocked by the browser until a future update.

The CSP itself is a deliberate allowlist: connect-src permits 'self' plus the five built-in provider origins, and nothing else. No wildcards.

Cost estimates

  • Before sending, the page shows an estimate of the request’s cost: prompt tokens ≈ 4 characters per token, multiplied by the provider’s published per-token price, plus the full output-token budget at the output price. It assumes you use the whole output budget — the worst case, not a promise.
  • Prices are approximate public list prices (USD per million tokens) captured 2026-11-04; providers change them. Every figure is labeled an estimate and the page links the provider’s pricing page.
  • Unknown models return “unknown” instead of an invented number — the page never guesses a price.

Timeouts, retries, errors

  • Timeout: 60 seconds. A slower provider aborts the request with a plain-language message instead of hanging.
  • Retries: up to 2 automatic retries, only on rate-limit (HTTP 429), honoring the provider’s Retry-After header (capped at 120 s; otherwise an increasing backoff). Other errors are shown immediately, never retried.
  • Error codes: invalid key (401), rate limited (429), model not found (404), rejected request (400), CORS/network blocked (plain guidance, key not exposed), timeout, and cancelled — each with a plain-language explanation and a next step.

Where conversations are kept

Chat history is saved in your browser’s IndexedDB (conversations store), inside Study Space storage — local to your device, included in backups, and never sent anywhere except to the provider when you press Send. Deleting a conversation removes it from your device.

← Back to methodology overview