Architecture
Version 1.2.0
This page covers how content actually moves through the system, end to end — from a conversation with an agent to a live page.
Two repos
Code and content live in separate GitHub repositories. The app repo holds the site's build script and templates. The content repo holds nothing but entries — one directory per post or doc, each with a JSON metadata file and an HTML body. They're pulled together only at build time.
This split exists because GitHub's access controls apply to a whole repository or branch, not a folder inside one. Two repos get real write isolation — only the content agent can write to content, only a reviewed code change can touch the app — using permissions that already exist, with no custom enforcement required.
MCP is the only writer
There's no admin panel. Every content operation — create, edit, publish, roll back — is an MCP tool call, from whatever LLM or agent you're using. Each write commits as it happens, so history stays meaningful: one action, one commit.
A draft is a branch (draft/<collection>/<slug>). The branch existing is the lock — nothing else can start a competing draft on that slug while it's there, and the state can't drift out of sync with reality, because it isn't stored anywhere separate from git itself.
The allowlist
Content is stored as a small, explicit set of permitted HTML — never arbitrary markup. It's validated twice: once when it's written, and again every time it's read for rendering, so a bypass of the normal write path still can't introduce something unsafe. Growth happens by adding specific, named vocabulary — a new component, a new enumerated class — never by loosening what raw tags can already do. See the Style Guide Reference for the current allowed set.
A visitor's request never touches any of this. Publishing produces a static build; the build is what actually serves traffic. There's no live process standing between a request and the page it gets back.
Build and delivery
The site is a small, hand-rolled TypeScript static-site generator — no framework. It reads content directly from the pulled-in repository, renders it through plain template functions, and writes static HTML to disk. Publishing triggers a rebuild automatically; there's no separate deploy step to remember.
Hosting is Cloudflare Pages from the start, not a later migration. The published output is plain static files with nothing platform-specific baked in, so it stays portable to any static host — the operational features around it (previews, scheduling, instant rollback) are what's deliberately Cloudflare-coupled, because those are the pieces a CMS vendor would otherwise charge for.