A Git-native CMS for the AI age. The repository is the database, static files are the API, and AI is the admin.
Your whole website is a folder of plain files: Markdown for content, JSON for settings. Git gives you versioning, collaboration, and hosting hooks for free. The build turns it into a fast static site — HTML pages plus a read-only JSON API — that deploys anywhere for $0/month.
No database, no server, nothing to patch. All state lives in this repo.Vanilla by design. No frameworks, no bundlers. One dependency:. The entire engine is a few small, readable files.marked
Works without JavaScript. JS is progressive enhancement only.AI-operable. Deterministic layout, machine-readable content model, and aso agents (or Claude Code) can edit content, add collections, and write plugins safely.CLAUDE.md
Get a copy: clickUse this template(or fork) to create your own repo.** Enable hosting:in your repo, go to Settings → Pagesand set Sourceto GitHub Actions**.** Make it yours:**editsite.config.json
— set yourtitle
,description
, andurl
— and commit.- Push (or edit on github.com and commit). About 30 seconds later, your site is live.
Every later change is the same loop: edit → commit → live in ~30s. Nothing is ever lost; any version of any page can be restored from Git history.
Your live site includes an editor at ** /admin/** — a clean writing screen with Save draft / Publish buttons, live preview, image uploads, and per-page History with one-click restore. No Git knowledge needed.
Sign in once with a GitHub access token (it stays on that device):
- On GitHub: Settings → Developer settings → Fine-grained tokens → Generate new token. - Repository access: Only select repositories→ pick your site's repo. - Permissions → Repository permissions: Contents: Read and write,** Actions: Read-only**. - Generate, copy, and paste it into the admin's sign-in screen.
The token never leaves the browser except to api.github.com. Editors who prefer files can keep editing files — the admin and direct edits coexist happily.
Prefer a button to a paste? Deploy the optional ~60-line OAuth Worker (free on Cloudflare, backed by a free GitHub App) and the sign-in screen gains "Sign in with GitHub" — one click, and each writer gets a token scoped to just that repo's contents.
AI assist (optional, BYOK): paste an Anthropic API key in Settings and the editor grows ✨ buttons — improve writing, suggest titles, generate the meta description, write image alt text, translate a page into a new draft. Every suggestion shows a before/after and asks before applying. The key stays in your browser and is sent only to Anthropic.
Every build also publishes a read-only JSON API: /api/site.json
, /api/posts/index.json
, /api/posts/<slug>.json
— plain static files any script, app, or AI agent can consume. No keys, no rate limits, cached by the CDN.
A post is a Markdown file in content/posts/
. The filename is the URL: hello-world.md
→ /blog/hello-world/
.
---
title: Hello world
date: 2026-07-05
description: One sentence for search engines and link previews.
tags:
- launch
draft: false
---
Body in **Markdown**. Images by path: 
Set draft: true
and the post is saved but not published. Pages work the same in content/pages/
(about.md
→ /about/
; index.md
is the homepage). Menus live in data/navigation.json
; renamed URLs get an entry in data/redirects.json
.
First sign in and the admin greets you with a five-step wizard: pick what you're building, name it, see it (a live preview already wearing your name), write your first words, and go live. Under the hood it applies a starter — a theme plus the right content types, menu, and example content.
Fifteen starters ship in the box: Journal (blog), Toolbox (trades & local services), Studio (portfolio), Bistro (restaurant), Manual (documentation), Terminal (developer blog), Letters (newsletter & essays), Launch (startup/SaaS), Gazette (local news & magazine), Folio (résumé/CV), Keys (real estate), Cause (nonprofit), Practice (clinic), Form (fitness studio), and Encore (band & artist). The Appearance screen lets you try any of them on your own pages — with device widths and a light/dark toggle — before committing, and a customizer exposes each theme's colors and fonts as live controls. Your tweaks survive theme updates.
A plugin is a folder — install one by copying it into plugins/
and adding its name to "plugins"
in site.config.json
. Ships with search (enabled: a /search/
page over a prebuilt index, no services involved) and contact-form (disabled reference: write [[contact-form]]
in any page, point it at a Formspree-style endpoint). The full hook API is documented in CLAUDE.md — it's small enough that "write me a plugin that adds reading time" is a one-prompt job for an AI agent. Also included:
reading-time(enabled) — written by an AI agent from the docs alone, in one prompt, as proof of that claim — plus
api-form(forms declared in config, POSTing to your own backend via the
"services"
map) and goatcounter(opt-in page-view counts). Good first plugins: giscus comments, image gallery, table of contents.
Already have a site? The importers under tools/migrate/
convert it — content, media, and a complete old→new redirect map so your URLs (and your SEO) survive the move. Run one on your machine:
node tools/migrate/jekyll.js /path/to/your-jekyll-site
It writes content/
, media/
, and data/redirects.json
into ./plain-import/
(never touching your working tree) plus a migration report of anything that needs a human eye. Copy those folders into your plain repo and build. Jekyll ships today; Hugo, Eleventy, and WordPress are on the roadmap (§15).
The full step-by-step walkthrough — keeping your URLs, wiring forms and analytics, the cutover checklist — is tools/migrate/README.md.
plain follows semver. Engine files (build.js
, lib/
, admin/
, themes/default/
) are upstream-owned; your content, config, and custom themes are yours. When a new version ships, the admin shows an Update available banner: click it and a pull request appears with the changelog and any files you'd customized flagged for review. Merge to upgrade, revert to roll back — never a surprise. An optional weekly workflow opens that PR on its own, so even an unattended site keeps getting security fixes as reviewable PRs. See §14 for the mechanism.
npm install
node build.js # build into dist/
node build.js --watch # serve on http://localhost:4000, rebuild on change
node --test tests/ # run the test suite
node tools/engine-manifest.js # regenerate engine.json before a release
site.config.json all configuration: site info, collections, plugins
config.defaults.json engine-owned defaults, merged under your config
content/ your words (Markdown, one file per page/post)
data/ navigation, redirects (JSON)
media/ images and files
themes/ fifteen starters ship in the box; add your own
plugins/ a plugin is a folder; install = copy + enable in config
admin/ the browser editor (static, vanilla ES modules)
tools/migrate/ importers (Jekyll today)
workers/oauth/ optional "Sign in with GitHub" worker (v1 uses a token)
build.js + lib/ the whole engine — under 2,500 lines, one dependency, MIT
The full product specification lives in cms-spec.md; instructions for AI agents (and how to add collections, plugins, themes) in
CLAUDE.md
CONTRIBUTING.md
MIT — open source from day one. Themes, plugins, and importers are the adoption engine; the core will never be closed.