The site is a portfolio piece. So is this page.
This site is static HTML with one stylesheet. No framework, no build pipeline, no trackers. That's not a budget constraint — it's the same judgment I apply to products: complexity is a tax you should only pay for a return. A portfolio's job is to deliver information fast; nothing delivers information faster than text on a page.
The assistant on the homepage answers questions from a single career document. The fashionable architecture would be RAG — embeddings, a vector database, chunking, retrieval. I didn't build that, on purpose: RAG exists for corpora too large to fit in a model's context window, and my career document is about 25,000 tokens. It fits whole. So the entire document ships with every request as a cached system prompt.
What that buys: no retrieval misses (the model always sees everything), no extra infrastructure to break, and deterministic behavior — the same question always has the same grounding. Prompt caching makes it cheap: after the first request, the document costs about a tenth of the normal input rate. Choosing the boring architecture over the impressive-sounding one is the actual demonstration of judgment here.
The assistant runs on Claude Haiku. Per message: roughly 15K cached input tokens plus a 400-token capped reply comes to about a third of a cent. At realistic job-search traffic that's a couple of dollars a month. But "realistic" isn't a cost control, so the actual controls are layered:
Questions are capped at 400 characters and replies at 400 tokens. Each visitor gets 10 questions per session and 25 per day (tracked per IP). A global kill switch stops the assistant after 500 messages in a day — worst possible day, about $2 — and falls back to pointing at the resume. Above all of that, a hard monthly spend limit at the API account level means the absolute worst case is a number I chose, not a surprise.
Someone will try to jailbreak this bot. Probably today. The defense isn't a clever prompt — prompts are not security boundaries. The defense is that there's nothing behind the boundary: the bot's document is the public, recruiter-safe version of my career file, containing nothing I wouldn't say in an interview. A fully successful jailbreak yields an off-topic answer at my expense of a third of a cent. The system prompt still instructs the model to stay on topic, refuse persona changes, and decline salary questions — but the real control is that the worst case was designed to be boring.
This is the same principle I applied scoping the MCP server at Propela: don't try to make the agent unbreakable; decide what it's allowed to touch so that breaking it doesn't matter.
Cloudflare Pages (static hosting, free) · one Pages Function for the chat endpoint · Cloudflare KV for rate-limit counters · Cloudflare Turnstile for bot filtering · the Anthropic API (Claude Haiku) with prompt caching. Total infrastructure cost: the domain, plus single-digit dollars a month for inference — capped.