Perishable / skelfresearch
Features

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.

Key protection

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.

Session trust

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.

Providers

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.

How you run it

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.