Everything that keeps the key on your server.
Perishable is a small, opinionated bundle of defaults. Nothing here is a dashboard or an add-on tier — it is on the moment the proxy starts.
The whole point: your upstream secret never reaches the client.
Server-side key custody
The real OPENAI_API_KEY lives in server-side env on the proxy. The browser never sees it — it holds only a short-lived JWT. A scraped bundle yields nothing to bill against.
OpenAI-compatible surface
The proxy speaks the OpenAI request shape. Point your existing client code at the proxy URL and the calls look the same; only the credential handling changes.
Sessions are bound to a client and expire on their own, like a use-by date.
Client fingerprinting
A stable ID derived from browser characteristics. The session JWT is bound to it, so a token copied to a different browser is rejected at the proxy.
Entropy collection
The browser SDK watches for real input signals before it is allowed to mint a session. A headless scraper sitting at document.ready gets nothing to work with.
Short-lived JWT sessions
Tokens expire on a clock you control. The client SDK refreshes them before expiry via sessionOptions.expiryBuffer, so leaked tokens rot rather than lingering.
Per-fingerprint rate limiting
Configurable points-per-window with optional block duration, keyed to the fingerprint. Enough to stop a casual abuser from racking up a bill while you sleep.
Anything that speaks the OpenAI API shape works through the same proxy.
OpenAI
The default. Set OPENAI_API_KEY and start the proxy — no base URL needed.
Anthropic, OpenRouter, Ollama
Point OPENAI_BASE_URL at an OpenAI-compatible endpoint — Anthropic, OpenRouter, or a local Ollama — and the same fingerprint / JWT / rate-limit machinery applies.
A process you own, in whichever shape fits your stack. No hosted service.
CLI: npx perishable-proxy
The 90% case. One command starts the proxy next to your app, reading its config from the environment.
Embedded: PerishableServer
Instantiate new server.PerishableServer({…}) to embed the proxy inside an existing Node service when you need it in-process.
Browser SDK: PerishableOpenAI
client.PerishableOpenAI collects entropy, requests a session, and refreshes it before expiry — the client half of the handshake, done for you.
See it wired together
The quickstart runs the proxy and a client call in a couple of minutes. The architecture page traces the full request flow.