perishable vs Cloudflare vs Vault: LLM Keys Out of the Browser

perishable is a self-hosted proxy and browser SDK that keeps LLM API keys off the client. How it compares with Cloudflare's gateways and HashiCorp Vault.

The problem

You want a feature on a static site, a JAMstack app or a public demo to call an LLM. The call has to carry an API key. If the key is in client-side JavaScript or a mobile bundle, anyone who opens developer tools or unpacks the app can copy it and spend your budget. If it is not, you need a backend to hold it — which, for a demo or a documentation playground, is a disproportionate amount of infrastructure.

perishable is Skelf’s narrow answer to that one problem. This post compares it with the tools people usually reach for first — Cloudflare’s gateway products and HashiCorp Vault — and is clear about where each one is the better choice.

What perishable is

perishable is a self-hosted Node proxy plus a TypeScript browser SDK, published on npm as perishable. The flow:

  1. The proxy runs on a machine you control, with the upstream API key in its server-side environment. The browser never sees it.
  2. The browser SDK collects entropy — real mouse and keyboard input — and derives a browser fingerprint.
  3. The SDK asks the proxy for a session (POST /session). After checking the fingerprint and entropy, the proxy mints a short-lived JWT bound to that fingerprint.
  4. AI requests from the browser carry the JWT. The proxy verifies it, applies a per-fingerprint rate limit, attaches the upstream key and forwards the request.
  5. The SDK refreshes the session before it expires. A token copied into a different browser is rejected, and a leaked token stops working when it expires.

The upstream can be any OpenAI-compatible API: OpenAI by default, Anthropic via OPENAI_BASE_URL, OpenRouter, or a local Ollama instance. CORS origin allow-listing is built in.

The scope statement is deliberately blunt. perishable is not a hosted service — there is no perishable cloud. It is not an observability or analytics layer. It is not a per-user authentication system. And it is not bot-proof: it raises the cost of casual abuse, not the cost of a determined attacker. What it prevents is the realistic failure — a key scraped from a public bundle and used at scale within hours.

Licence: pending. The source is public, but the repository has no licence file yet, so we do not describe perishable as open source until one is added.

What the alternatives are

Cloudflare API Gateway is Cloudflare’s general API management product: schema validation, rate limiting, JWT validation and traffic analytics for APIs fronted by Cloudflare. Cloudflare AI Gateway is the AI-specific product: a hosted edge layer for analytics, logging, caching, rate limiting, retries and model fallback across many providers, wired in by changing a base URL. Both are mature and managed. Neither ships a browser SDK for fingerprint-bound client sessions; bot management is a separate Cloudflare product.

HashiCorp Vault is the de-facto secret manager. It stores secrets, issues dynamic short-lived credentials for many backends, and keeps detailed audit logs. It assumes the code requesting a secret runs somewhere you control and can authenticate to Vault — a server, a pod, a CI job. It is not designed to hand anything to an anonymous browser. Since 2023 it has been released under the Business Source License.

The dimensions

DimensionperishableCloudflare (API / AI Gateway)HashiCorp Vault
Primary jobKeep the LLM key out of the browserAPI management; AI traffic observability and controlSecret storage and dynamic credentials
DeploymentSelf-hosted Node processHosted on Cloudflare’s edgeSelf-hosted cluster or managed service
Caller it is built forAnonymous browserYour clients and serversAuthenticated workloads you control
Client-side SDKYes: entropy, fingerprint, session refreshNoNo
Short-lived client tokensFingerprint-bound JWT sessionsJWT validation (you issue the tokens)Dynamic secrets and leases for workloads
Rate limitingPer fingerprintYesNot its job
Analytics, logging, cachingOut of scopeYes (AI Gateway)Audit logs of secret access
Provider supportOpenAI-compatible: OpenAI, Anthropic, OpenRouter, OllamaMany providers (AI Gateway)n/a
Bot resistanceRaises cost of casual abuse; not bot-proofSeparate bot-management productn/a
LicenceLicence pending (no licence file in the repository yet)Proprietary serviceBusiness Source License

When to use which

Use perishable when:

  • A browser or mobile client must call an OpenAI-compatible API directly, and the operator’s key must not ship in the bundle.
  • You cannot identify the visitor — a public demo, a docs playground, a free-to-try feature — so there is no user login to issue credentials against.
  • You want to own the path end to end and run one small process.

Use Cloudflare when:

  • You are already on Cloudflare and want edge analytics, caching, retries and fallback for AI traffic — mostly server-side traffic.
  • You need general API management across many APIs, of which LLM calls are one.

Use Vault when:

  • The code calling the LLM runs on infrastructure you control and you want one system for all your secrets, with rotation, leases and audit.
  • You have the operational capacity to run it, or use the managed offering.

Use proper user authentication when you know who your users are. Issue credentials against identity; fingerprinting is what you reach for when you cannot.

They stack

These are not mutually exclusive. perishable’s OPENAI_BASE_URL can point at a Cloudflare AI Gateway endpoint, giving you browser → perishable → Cloudflare → provider: perishable keeps the key out of the client, Cloudflare does the analytics and caching. The proxy’s own upstream key can be stored in Vault and injected into its environment at deploy time. Each tool does its own job.

Where perishable loses

  • Maturity. Cloudflare and Vault are mature, well-supported products. perishable is a small project with a narrow scope.
  • Observability. It keeps local process logs and nothing more. If you need dashboards, cost breakdowns or caching, put a gateway behind it.
  • Determined attackers. A patient attacker with a real or headless browser can obtain sessions. Rate limits cap the damage per fingerprint; they do not make abuse impossible.
  • Licensing. Until a licence file is in the repository, legal teams evaluating it for production use will reasonably say no.
  • The best fix is not needing it. If your product can let users bring their own key, the threat disappears rather than being made more expensive.

Trying it

From the perishable quickstart:

npm install perishable

# start the proxy (OpenAI by default); the key stays server-side
OPENAI_API_KEY=sk-... 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!' }]
});

Then tune the per-fingerprint rate limit (points per window and an optional block duration) and run the proxy behind your normal reverse proxy and TLS.