Stack
- Astro 6.3
- Static framework + one SSR endpoint
- Preact 10
- Two interactive islands (chat, contact form)
- Cloudflare Workers edge runtime
- Host - prerendered HTML plus /api/chat
- LangChain 1.x (core + openai)
- Chat message plumbing
- OpenAI gpt-5-mini
- Chat model
- Latin Modern Roman Computer Modern revival
- Typeface - the LaTeX-preprint reference
- Bun 1.3
- Package manager and script runner
Decisions
-
Astro + Cloudflare Workers, not Next.js + Vercel
Prerender everything except the chat endpoint. One SSR route. Static assets and the Worker share a single deploy. The site costs nothing at idle and serves HTML from the edge cache.
-
Preact, not React
Two interactive components on this site. Preact ships ~3 kB of runtime; React would ship ~45 kB. The DX is identical - JSX, hooks, the rest. The choice is invisible to a user; it shows up as a faster first paint.
-
Two-layer prompt-injection defense
The system prompt explicitly tells the model to treat <user-input> contents as data, not instructions. The wrapper escapes any literal closing tag a visitor might type, so they cannot forge a fresh role boundary. Neither layer alone is enough; both together hold.
-
Server-stateless chat
The conversation transcript lives in the browser; the server holds no session state and caps history to the last five exchanges. Each request is logged to D1 (question, reply, salted IP hash) for rate limiting and post-hoc improvement, and purged after a year - the privacy page spells it out.
-
Per-page prerender by default
`output: 'server'` with `export const prerender = true` on every page except /api/chat. Static where it can be, dynamic where it must be - the configuration mirrors the architecture, not the other way around.
-
Readable by machines on purpose
AI crawlers are welcome here: robots.txt allows them by name, and /llms.txt plus /llms-full.txt serve the whole site as plain text in the llmstxt.org format, generated from the same data files as the HTML. If an AI answers a question about Ehsan, it should have the facts.