← Misc
Tech

How This Site Works

A tour under the hood: how a git push becomes a live site, and how the two dynamic pieces, the sticker poll and the AI concierge, actually run. It all sits on Cloudflare's free tier (there is a separate page on why that platform is such a good deal).

git pushfrom my machineGitHubmain branchPages buildnext build, ~1 min to liveVisitoranywhereCloudflare edgeDNS · TLS · CDN · WAF · DDoSStatic assetspages + posts, from the nearest cachePages Functions/api/* · /mcpD1 (SQLite)sticker votesWorkers AIClaude + Llama + embedsVectorizepage-content RAG

From git push to live

The site is a Next.js app compiled to a fully static export. There is no deploy button and no CI to babysit: I push to the main branch on GitHub, Cloudflare Pages notices, runs the build, and ships the output to its edge network. About a minute from commit to live, with an isolated preview deployment for every branch so I can see a change before it merges.

Because the whole thing is static, the "infrastructure" is a directory in the same repo. Anything dynamic lives under functions/, and those deploy on the very same push. The code and its infrastructure are one commit, which is the property that makes the rest of this list cheap.

DNS and the edge

Cloudflare is the authoritative DNS for chrisjaimon.com, so a lookup resolves on the same network that serves the site. Every request then passes through the edge, which terminates TLS with an auto-renewed certificate, absorbs DDoS traffic, applies WAF rules, and serves each page from a cache in one of 300+ locations near the visitor.

Two small pieces of edge logic live in the repo. A _redirects file maps old URLs to new ones, and a tiny middleware Function permanently redirects the default *.pages.dev hostnames to the custom domain, so there is exactly one canonical site and all the SEO consolidates onto it.

Pages and Functions

Static assets come from Pages. The dynamic API is Pages Functions: file-routed Workers under functions/api/, running on V8 isolates with no cold start. Each file is an endpoint. They share the same domain as the site, so there is no CORS, no separate backend host, and no separate deploy.

The voting app: Functions plus D1

The laptop-sticker poll is the simplest dynamic feature and a good illustration. /api/sticker-vote is a Function backed by D1, a serverless SQLite database at the edge. The design decision worth noting: there is no counter to increment. Each voter gets an anonymous UUID in an HttpOnly cookie, that UUID is the primary key of the votes table, and a vote is a single INSERT ... ON CONFLICT DO NOTHING. Double clicks and racing tabs collapse into one row, and the tallies are a GROUP BY over the table itself, so the count can never drift from the votes.

The AI concierge: Claude, with a fallback

The chat box on the home page is a Function, /api/chat, talking to models through Cloudflare's Workers AI binding, which is keyless from inside a Worker. The primary model is Claude Haiku 4.5; when it is unavailable the same request falls back to Llama 3.3 70B on Workers AI, which is free. The routing is a small ladder in the Function: try Haiku streaming, then Haiku non-streaming, then Llama streaming, then Llama, and if everything fails it returns a graceful message. The reader never sees which rung answered.

The system prompt carries a curated brief about my work, and the model is told to ground every claim in it and never speculate. That is why the concierge politely declines questions that have nothing to do with me: it has no general knowledge to draw on, only the brief, so "tell me about someone else" has nothing to answer from.

Generative UI with OpenUI

The concierge does not answer in plain text. It composes a UI. Using OpenUI, an open standard for model-generated interfaces, I define a component library (the site's own sections, plus a growing set of primitives: comparison tables, timelines, metric tiles, bar charts, percentage gauges, flow and Mermaid diagrams, and photo galleries) and a build step turns that library into the exact vocabulary the model is allowed to emit. The model streams back a compact UI language; the browser renders it progressively as the tokens arrive.

This solved three things at once. Responses now stream, so the first content appears in about two seconds instead of after the whole reply lands. Composition is free: ask to compare two projects and you get an actual comparison table, ask for the platform's scale and you get a chart, ask how the AI gateway routes and you get a diagram, rather than one fixed layout every time. And the content stays grounded, because the components that carry real data pull it from the site's content files by slug. The model arranges the UI; it never authors the facts.

The same components, live

Not screenshots. Each block below is real openui-lang, the exact language the model streams, rendered right here through the concierge's own components. The Mermaid diagram parses and draws in your browser; every number is grounded in real work.

RAG over the site, with Vectorize

The curated brief holds summaries. For genuine depth, the concierge can read the pages themselves. A build step splits the full text of every project, writeup, and event page into a couple of hundred chunks; each chunk is embedded with a Workers AI model and stored in Vectorize, Cloudflare's vector database. When you ask a detail question, the Function embeds the question, retrieves the closest passages, and hands them to the model as ground truth alongside the brief.

So a broad question is answered from the summary, but "what exactly caused the 28-minute outage?" pulls the real paragraphs from that writeup. Retrieval is raced against a short timeout and fails silently, so it can only ever add depth, never slow the answer down or break it.

Questiona detail askEmbedWorkers AI modelVectorizeclosest passagesPassages + briefas ground truthModelHaiku, Llama fallback

An MCP server: the same brain, for agents

The concierge is not only for people typing in the chat box. The same retrieval stack is exposed as a Model Context Protocol (MCP) server, so any AI agent can discover this site and query it directly, as a tool. An agent reads the server card at /.well-known/mcp/server-card.json, which points it at a Streamable HTTP endpoint, /mcp, speaking JSON-RPC. There is no SDK and no separate service: it is one more Pages Function, sharing the exact same Vectorize index and Workers AI binding as the web concierge.

AI agentClaude, Perplexity…Discovery/.well-known/mcp/server-card.json/mcpJSON-RPC over HTTPThree toolsprofile · search · getVectorize RAGsame index as the chat

The server exposes three tools, so an agent can get oriented, search the portfolio, and read a whole page:

get_profile

Start here: years of experience, current and previous roles, scale metrics, plus an index of every page.

search_portfolio

Semantic search that returns verbatim passages, each tagged with its source URL.

get_page

A page's full Markdown (diagrams as captions, metrics as tables) plus its structured fields.

Every result also carries a resource link back to the live page, so an agent can cite its sources and a reader can click through to the real, interactive version. One knowledge base sits behind all of it: the chat box, the generative-UI components, and now any agent that speaks MCP all read the same grounded content, so the portfolio meets people, and their agents, wherever they already are.

Links