How this site works
Static pages, three small server functions, one notification service and one language model. Everything measurable on it is measured, and the measurements are on the page.
- Framework
- Astro 7, static output, React islands only where there is state (the palette, the contact trace, the ledger). Deployed on Vercel; the API routes run as serverless functions.
- Content
- MDX case studies with a Zod schema. A project marked measured must carry at least one metric with a source; the build fails otherwise. Diagrams are data (nodes and edges) rendered to SVG.
- Type and colour
- Satoshi, Newsreader for display, Geist Mono for numbers and labels, all self-hosted. Neutral paper, one accent for links and measured marks, green only for live status. Light and dark from the same tokens.
- Avatar
- The voxel figure behind the photo is 2,051 cubes described in code and drawn by a software renderer under 3 KB gzipped on a 2D canvas: surface faces only, greedy-merged to 211 quads, painter's sort, three-tone shading. No WebGL, no model file, loaded after the page is idle and only on pointer devices.
- Findability
- Person, WebSite and ProfilePage JSON-LD on every page, CreativeWork per case study, a sitemap, llms.txt, and a robots file that allows AI crawlers.
Ask this site
Press ⌘K and type a question. The palette is a small retrieval-augmented pipeline over this site's own content. It answers only from what it retrieved and refuses when nothing qualifies.
Evaluated on 56 questions on 15 Sept 2026: expected citations, expected refusals and expected facts, scored by scripts/run-evals.ts. The threshold came from a calibration run on the current embedding model: on-topic questions score at or above 0.44, off-topic ones at or below 0.37. The palette footer repeats this line so the claim travels with the feature. The full benchmark, per-question results, retrieval metrics, adversarial cases and run history are on the evaluation lab.
Contact and the ledger
The contact form does not use a mail provider directly. It posts an event to Notify, the notification service I built, as tenant portfolio. Notify commits the event, fans it out by its rules table into an in-app job and an email job, delivers each through its worker, and the page polls the status endpoint to show every attempt as it lands. Only delivery state reaches the browser; the message stays inside Notify.
The reliability ledger reads GET /api/v1/metrics with a tenant-scoped key, cached five minutes in the function. A daily GitHub Action commits a snapshot so the numbers survive an outage, and the footer's heartbeat is the same call in its smallest form.
| Channel | Jobs | Sent | Failed | Rate |
|---|---|---|---|---|
| Email, all tenants | 295 | 285 | 10 | 96.6% |
| In-app, all tenants | 713 | 713 | 0 | 100.0% |
| Push, all tenants | 340 | 325 | 15 | 95.6% |
| This site, tenant portfolio | 2 | 2 | 0 | 100.0% |
| 1 | 1 | 0 | 100.0% | |
| In-app | 1 | 1 | 0 | 100.0% |
Source: GET /api/v1/metrics?tenantId=portfolio&days=30 on 2026-09-14T18:35:23.038910606Z (1650 ms)
Security posture
- No secrets in the browser. The Groq, Cloudflare, Gemini and Notify keys live in server functions; the palette and the contact form talk only to /api/* on this domain.
- Only delivery state comes back from Notify. Message content, recipients and API keys never appear in a response; the metrics endpoint returns counts.
- Rate limits: 20 questions a minute per address on /api/ask with a one-hour answer cache; three messages per ten minutes on /api/contact, plus a honeypot field.
- Prompt injection is tested, not assumed: seven adversarial cases run with every evaluation and are published on the lab.
- Response headers: nosniff, frame denial, strict referrer policy, permissions policy, HSTS (vercel.json). A security.txt says where to report anything.
- Measurement: Vercel Web Analytics and Speed Insights, which are cookieless and keep no identifier across visits. Microsoft Clarity also loads, and sets first-party cookies. What is measured is page views, Core Web Vitals and a handful of named interactions (a question asked, a lab used, an outbound link); never the text of a question or a message.
House rules
- Every number links to the query, run file or command that produced it, with a date.
- No invented metrics, no rounded-up claims, no client screenshots where the work was confidential.
- Failures are published next to the wins, with what changed because of them.
- No em dashes, no emoji, no skill bars, no logo walls.
- The site is its own project: Astro, a RAG palette, a contact form on Notify, and a published evaluation set. The stack and the decisions are on this page; the numbers are on the lab.