A paywall the browser enforces and the server ignores
A quota counter in localStorage in front of a paid model route is not authorization. Anyone can clear storage, edit the counter in devtools, or POST to the route directly and spend your API bill.
What can go wrong
AI apps often gate paid generation with a client-side counter: dailyUsage or generationQuota read from localStorage immediately before fetch("/api/generate"). If the matching server route checks nothing but process.env.OPENAI_API_KEY before calling the model, the counter is theater. The route is a public endpoint that burns your money.
It happened for real
Enrichlead (Mar 2026) shipped subscription enforcement only in the browser; users cleared devtools and maxed API keys (Autonoma failure taxonomy, Mar 2026).
How to check yours
Seatbelt flags this automatically (partial). Repo read puts client-side quota shapes on the agent checklist with the paths to open; URL read soft-flags storage-gated calls to paid routes in served code. A checklist line means open the route yourself.
Honest holes: a server-side check that lives behind an abstraction the scan cannot follow, or metering done by a third-party gateway, may look missing when it is not.
Ask your agent: "Open every route that calls a paid model API. What runs before the SDK call: a rate limiter, a usage row, a Stripe meter, or session auth? If nothing does, add one."
Manual check: call the generate endpoint with curl while signed out. If it returns model output with no auth header, the client counter was theater.
Fix direction
Enforce quota, rate limits, or session auth in the server handler, before the model call. The browser counter can stay for UX; it just cannot be the enforcement.
Paste into your agent: "In every API route that calls a paid model, enforce a server-side check before the SDK call: session auth plus a usage row or rate limiter. Reject unauthenticated calls."