Perishable / skelfresearch
Dev infrastructure · API key proxy

Ship AI features without shipping your API keys.

Perishable is a small Node proxy that sits between your frontend and any OpenAI-compatible API. Your secret key stays on the server. Clients get short-lived, fingerprint-bound JWTs that expire on their own — like a tub of yoghurt.

Read the docs Source on GitHub USE BY · 15 MIN

The problem

You want to call OpenAI (or Anthropic, or OpenRouter, or a local Ollama) from a browser, an iOS binary, or an Electron app. The naive path puts your sk-… key into something a stranger can dump with strings.

A scraped key gets you a billing surprise and a key rotation sprint. Both are avoidable.

The shape

Run npx perishable-proxy next to your app. The proxy holds the real key. The client SDK collects browser entropy and asks for a session. The session is a JWT bound to a fingerprint, with a short TTL, behind a rate limiter.

It's a process, not a SaaS. Your traffic doesn't leave your infra.

Request flow

What actually happens

browser ──[ POST /session + fingerprint + entropy ]──> perishable │ ├── verify entropy looks human ├── issue short-lived JWT (bound to fp) ▼ browser ──[ POST /openai/v1/chat/completions + Bearer JWT ]──> perishable ──[ Authorization: Bearer sk-… ]──> OpenAI ▲ └── rate-limit per fp, refuse expired or stolen tokens

Drawn from the project README. The exact endpoints, header names and token TTLs are in the docs.

What it gives you

Five things, on by default

Client fingerprinting

A stable ID derived from browser characteristics. The session JWT is bound to it, so a copied token from a different browser is rejected.

Entropy collection

The client SDK watches for real input (mouse, keyboard) before it's allowed to mint a session. A headless scraper sitting at document.ready gets nothing.

Short-lived JWT sessions

Tokens expire on a clock you control. The client SDK refreshes them before expiry (sessionOptions.expiryBuffer). Leaked tokens rot.

Per-client rate limits

Configurable points-per-window with optional block duration. Enough to stop a casual abuser from racking up an OpenAI bill while you sleep.

OpenAI-compatible surface

If a provider speaks the OpenAI API shape — OpenAI itself, Anthropic via compatible base URL, OpenRouter, Ollama — point OPENAI_BASE_URL at it and go.

CLI and SDK

npx perishable-proxy for the 90% case; new server.PerishableServer({…}) when you need to embed it in an existing Node service.

Honest scope

What Perishable is not

  • It is not an analytics / observability layer. It proxies; it does not aggregate spend dashboards per tenant.
  • It is not a hosted gateway. You run the process. Traffic flows through your infra, not Skelf-Research's.
  • It does not eliminate the need for server-side auth on sensitive endpoints. A short-lived JWT raises the cost of abuse; it does not authorise a user to delete their account.
  • Fingerprint + entropy is bot-resistance, not bot-proof. A motivated attacker with a real browser can still get a session. They just can't get a million of them cheaply.
60-second start

Install, set a key, point a client at it

npm install perishable

# Start the proxy (OpenAI by default)
OPENAI_API_KEY=sk-... npx perishable-proxy

# Or proxy Anthropic / OpenRouter / Ollama instead
OPENAI_API_KEY=sk-ant-... \
  OPENAI_BASE_URL=https://api.anthropic.com/v1 \
  npx perishable-proxy
import { client } from 'perishable';

client.PerishableOpenAI.initEntropyCollection();

const ai = new client.PerishableOpenAI({
  proxyUrl: 'https://your-proxy.example.com'
});

const res = await ai.createChatCompletion({
  model: 'gpt-4',
  messages: [{ role: 'user', content: 'Hello!' }]
});

That's verbatim from the README. The rest — config file, programmatic server, refresh tuning — is in the docs.

Recent notes

From the workbench

Why a proxy beats a serverless function for AI key safety

2026-05-28

A serverless function can hide your API key. It will also cost you streaming, latency, observability, and your sanity. Here is the trade space, drawn honestly.

Short-lived tokens for AI: the OAuth-style answer

2026-05-20

OAuth solved this problem for the web a decade ago. Here is what the same idea looks like wrapped around an AI provider's API.

Your client-side AI key is in 4 mobile screenshots already

2026-05-12

A walkthrough of how API keys baked into mobile and browser bundles get extracted in minutes, and what to do other than pray.

All notes →