Skip to content
back to achievements

Case study · Proxy

Side projectProject period2026 – Present

Layered protection for a public AI endpoint

Combined Tor blocking, Cloudflare Turnstile, per-IP rate limits, a shared daily budget and an owner-only authenticator pass, so a request that should not reach a model never does.

01

Why

Proxy is public, has no login, and every answer spends model tokens. Left alone, one script could drain the budget or turn a portfolio assistant into a free general-purpose chatbot. The cheapest place to stop that is before any model is called.

02

Decision

I put the checks in a deliberate order, cheapest first: Tor blocking, a Cloudflare Turnstile bot check, a per-visitor limit, then one shared daily budget for the whole service. The order matters: a bot that fails Turnstile cannot use up a shared address's allowance. Failed requests are refunded, so a visitor's three answers are three real answers, not three attempts. The per-visitor counters live in a Durable Object so they are exact across locations and survive restarts. For testing I added an owner-only pass (a signed, one-hour code from an authenticator app) that skips the visitor limit, so the limit never gets in my way and no secret sits in the code.

How it fits

Hover or focus a box for the story behind it.

A visitor's question first meets the checks (Tor, bot check, limits, daily budget). Greetings, abuse, private-life questions and injection attempts get a fixed reply with no model. Everything else goes to the router, which decides what part of the notes the question needs, and only then does a model write the answer.A visitor types a question in the chat widget on the site, or calls the public API.Question From the chatTor traffic is blocked, a Turnstile bot check runs, each address gets a few questions, and a shared daily budget caps the cost for everyone.Checks At the doorA small decision model (Jev, through OpenRouter) says what the question is about. If it is unsure, slow or over its own limit, keyword rules decide instead.Router Jev + rulesOnly the part of the notes the topic needs is sent: about 1,000 tokens for a typical question instead of the whole knowledge base.Context Needed notesThe main model answers from that context only. Rules tell it to say so when the notes do not cover something instead of guessing.Model Writes replyThe reply streams back to the chat, with project, skill, timeline or contact cards when they help.Answer Text and cardsRefused with a clear error code. The widget shows a short explanation and, where it makes sense, how to reach Arul instead.Refused Blocked earlyGreetings, thanks, abuse, private-life questions, off-topic requests and injection attempts get a prepared reply, so they cost nothing and cannot talk a model into anything.Fixed reply No model callStopped before a model

The question passes the checks, the router picks the part of the notes it needs (a small model, with keyword rules as the fallback), and the main model writes a short answer from only that context. Project cards or a contact card are added when they help.

03

What I built

An endpoint that answers for me is public, so abuse has to be stopped before it costs anything. Combined Tor blocking, Cloudflare Turnstile verification, per-IP rate limits, a shared daily budget and an owner-only authenticator pass (signed, one-hour, TOTP-based). A request that should not reach a model never does.

04

Trade-offs

  • Three answers per visitor is strict: I treat it as a lifetime cap on purpose, because every answer spends model tokens. It is counted per network address, so people on one shared network share an allowance.
  • Tor users are blocked outright.
  • The shared daily budget means a very busy day ends with a polite "please get in touch" for later visitors instead of a bigger bill.

05

Outcome

A request that should not reach a model never does, and the worst-case spend is a known number (about $7 a month at the daily cap) instead of "whatever traffic brings".

Technologies