Colophon / the engine room, open
The right architecture is the one sized to the problem.
The problem here is precise: a personal archive that must be fast, cost zero, be readable by humans, crawlers and agents, and let itself be maintained on autopilot. Everything else on this page follows from that.
Requirements → decisions
Static HTML served from a CDN, client JS only where there's interaction. The bottleneck must be the reading, never the loading.
No server, no database, no CMS: nothing to patch, nothing that gets breached, nothing that calls at 3am. A personal archive must not demand operational attention.
Static build on a hobby plan: the marginal cost of a visit is ~zero, and so is the fixed cost. The money goes into the content, not the hosting.
The site maintains itself: I say what's needed, code-os (si apre in una nuova scheda) builds it, the gate decides whether it ships. agent-memory (si apre in una nuova scheda) holds the rules and the definition of done, decisions are recorded as ADRs: a fresh agentic session is productive at the first prompt, without me acting as its memory.
The data architecture is for agents too: typed data in src/content with predictable paths, one component per responsibility, names that say what they do. Anything is findable without burning tokens on exploration: minimum sufficient context is a property of the repo, not of the prompt.
Static is crawlable by definition, and I use Next for what it's good at: Metadata API for canonicals and OpenGraph, typed sitemap and robots, OG images generated at build time with ImageResponse, self-hosted fonts via next/font (zero layout shift), JSON-LD in the layouts. For AI-mediated search: llms.txt and /api/dossier, from the same source as the pages.
No funnels, no popups, no third-party trackers (cookieless analytics and that's it). It's an archive to read, not a landing page to convert.
// this page too: I said what I wanted, the harness built it, the gate approved it.
Architecture
CONTENT / SSoT
src/content/*.ts — essays, principles, work: typed data, 3 languages
REACT SERVER COMPONENTS
thin routes, SRP sections, client JS only where you interact
STATIC BUILD
every route is HTML at build time — OG, llms.txt, JSON too
EDGE / CDN
Vercel — zero runtime compute, ~0 marginal cost
the same content exits via /llms.txt and /api/dossier
GATE
- eslint
- tsc --noEmit
- next build
green, or it doesn't get committed
INVARIANTS
- a11y AA
- reduced motion
- one accent
- SSoT-derived
- i18n parity
regression = bug
Decision record
Zero dependencies, plus one
Runtime: Next + React + react-aria-components (widget accessibility: focus management, press semantics, focus traps — things you get wrong if you rewrite them). No Tailwind, CMS, UI kits, animation libraries. Consequence: minimal update surface, every line in the bundle is justifiable.
Everything static, always
Every route — HTML, OG images, llms.txt, /api/dossier — is generated at build time and served from a CDN. No runtime compute: no cold starts, attack surface reduced to a minimum, ~zero marginal cost per request. The constraint is accepted: new content = new deploy.
Content is a type, not a page
Essays, principles, work live in src/content as typed objects (SSoT). Archive, homepage, sitemap, OG, llms.txt and /api/dossier all derive from the same source. Consequence: a piece of content is written once and cannot diverge across formats.
One accent
#E35B2D, everywhere; on the snow section a darker shade (#B8481C) for AA contrast on small text — same hue, not a second color. It holds in the easter eggs too: every snowfall has exactly one orange flake.
Motion as decoration, never as a gate
The hidden state of the scroll reveal exists only with JS enabled (@media scripting) and motion allowed. JS off or prefers-reduced-motion: content intact. Animations are a layer above the content, not a precondition for reading it.
Accessibility as an invariant
Skip link with a focusable target, visible focus ring, aria-current on navigation, AA contrast, external links announced, aria-live even for the easter eggs. Treated like tests: a regression is a bug, not a trade-off.
Machine-readable
/llms.txt for agents, /api/dossier for curl, JSON-LD for crawlers — all generated from the same SSoT as the pages, so never out of sync. I serve the context about me myself, in the format of whoever consumes it.
Three languages, one SSoT
IT, EN, PL as static routes (/it /en /pl), per-page hreflang, shared slugs. No middleware: detection is a static redirect. Cross-language parity is asserted at build time: a missing translation breaks the build, not the trust.
For the machines
I serve the content about me myself, in the format of whoever consumes it. Agents read /llms.txt; if you prefer the terminal, there's a JSON endpoint generated from the same SSoT as the pages:
curl simonebova.com/api/dossier
// same source, three formats: HTML for you, llms.txt for agents, JSON for curl.