An open-source, headless CMS β editor-first, type-safe, batteries included.
Paperboy ONE β Open, No Extras. Β· DPX β Don't Pay eXtra. A proper headless CMS without the enterprise "experience platform" invoice. Self-host it, own your content, ship.
Built for teams of humans and agents. Every write path β admin UI, REST, MCP β goes through the same validation, permissions and audit trail; agents are first-class editors, not an integration afterthought (the MCPβAPI parity is test-enforced). See docs/POSITIONING.md.
A TypeScript monorepo: a Fastify Management + Delivery API, a React admin with visual on-page editing, a Next.js reference frontend, and a stdio MCP server β on PostgreSQL.
- Data-driven content types β pages, blocks, and globals defined as data, not code.
- Content areas with inline + shared blocks and per-field "allowed types".
- No-leak Delivery API β one read chokepoint with a
perspective; public keys see only published content, private fields never serialize, preview keys see drafts. Lists support pagination, sorting and field filters (?limit=&offset=&sort=-data.publishDate&data.author=Jane), plus full-text search (/delivery/search?q=). - Image transforms β
?w=800&format=webp&q=75on any media URL; variants generated once with sharp and cached on disk (widths/qualities snap to fixed steps). - Visual on-page editing β click an element in the live preview to focus its field in the editor, and focus a field to highlight it in the preview (both ways).
- Multi-language (document-level i18n + fallback chain), hierarchical URLs from the page tree, version history + restore, trash/restore, duplicate.
- Secure by default β Argon2id + opaque server-side sessions + CSRF, passwordless TOTP 2FA, deny-by-default RBAC with object-level scope checks, append-only audit log.
- Typed client SDK β
@paperboycms/client:createClient({ baseUrl, key }), then typedgetBySlug/list/search+ media-variant helpers and an optional ETag cache. No codegen β types come from the shared Zod schemas. - Agent provenance & review β every version records whether a human or an agent wrote it (π€ badges), agent drafts carry a one-click-approve review flag, and an opt-in gate keeps agents from publishing unreviewed work.
- Integrations β HMAC publish webhooks, media uploads, SEO/OpenGraph metadata, an optional AI editorial assistant, and a full MCP server (drive the CMS from an AI client, with revocable tokens).
- Great admin β React 19 + Vite, light/dark themes, βK command palette, accessible (axe-clean), keyboard-operable drag-drop.
apps/api Fastify v5 β Management API (auth/RBAC) + Delivery API (read-only, key-scoped)
apps/admin React 19 + Vite β the editor (tree, content areas, live preview, on-page editing)
apps/web Next.js 15 β reference headless frontend with Draft Mode preview
apps/mcp stdio MCP server β operate the whole CMS over the Model Context Protocol
packages/shared Zod schemas + shared types (single source of truth)
packages/db Drizzle schema, migrations, query layer (object-level authz), seed
packages/client @paperboycms/client β the typed Delivery API SDK (see its README)
Any frontend (Next, Astro, SvelteKit, β¦) can be a Paperboy client β either with
@paperboycms/client (typed, ETag-aware) or by reading the Delivery API over plain HTTP;
it doesn't import the CMS, only the contract.
docker compose up -d # db β init(migrate+seed) β api β web β admin
# Admin http://localhost:8090 (admin@paperboy.test / Admin!Passw0rd)
# API http://localhost:8091 (OpenAPI UI at /docs)
# Web http://localhost:8092
β οΈ Redeploy one service withdocker compose up -d --no-deps --force-recreate <svc>. The seed CLI refuses to wipe a database that already holds content (setFORCE_SEED=1for a deliberate reseed) β but don't lean on the seatbelt. SeeCLAUDE.md.
Never used a terminal before? Follow these steps exactly β copy/paste each command.
1. Install Docker Desktop
- Download it: https://www.docker.com/products/docker-desktop/
- Run the installer, click through with the defaults, then restart your PC if it asks.
- Open Docker Desktop from the Start menu and wait until the whale icon (bottom-left) says Engine running. Leave it open in the background.
2. Get Paperboy onto your PC
- Easiest way (no extra tools): go to the project page on GitHub, click the green Code button β Download ZIP, then right-click the downloaded file β Extract Allβ¦.
- You now have a folder like
paperboycms. Remember where it is (e.g. your Downloads folder).
3. Open a terminal in that folder
- Open the
paperboycmsfolder in File Explorer. - Click the address bar at the top, type
powershell, and press Enter. A blue window opens β that's your terminal, already pointed at the right folder.
4. Start everything (one command)
- Copy this line, paste it into the blue window (right-click to paste), and press Enter:
docker compose up -d - The first run downloads things and takes a few minutes. When it finishes you'll get your prompt back.
5. Open the admin
- In your browser go to http://localhost:8090
- Log in with:
- Email:
admin@paperboy.test - Password:
Admin!Passw0rd
- Email:
That's it β you're running Paperboy. π
Everyday commands (run them in the same blue window, inside the paperboycms folder):
- Stop it:
docker compose stop - Start it again later:
docker compose start - See it running: check Docker Desktop, or run
docker compose ps
β οΈ Don't rundocker compose down -vor re-run the setup once you've added content β it wipes everything and resets the login. Use stop/start instead.π‘ If a page won't load, make sure Docker Desktop is open and says Engine running, then try again.
pnpm install
docker compose up -d db
export DATABASE_URL=postgresql://paperboy:paperboy@localhost:5433/paperboy
pnpm db:seed
pnpm dev # api :8091, admin :8090, web :8092pnpm --filter @paperboy/api test # integration tests (real Postgres)
pnpm --filter @paperboy/admin test:e2e # Playwright e2e + axe accessibility# Mint a token in the admin (Settings β MCP), then:
MCP_TOKEN=mcp_β¦ DATABASE_URL=postgresql://β¦ pnpm --filter @paperboy/mcp start # stdio (local clients)
# β¦or serve it over Streamable HTTP for remote clients (Bearer = MCP_TOKEN):
MCP_TOKEN=mcp_β¦ docker compose --profile mcp up -d --no-deps mcp # β http://<host>:8093/mcpThe MCP authenticates as a Paperboy user (token or email+password) and inherits its RBAC. The default transport is stdio; set MCP_HTTP_PORT (or use the compose mcp profile) to expose it over HTTP for remote clients.
The MCP surface is hardened against the ways LLM agents actually fail β every one of these came out of running real agent workloads against it:
- No silent damage β input is either coerced meaning-preservingly (a TipTap doc sent to a markdown field becomes real Markdown,
{en: "β¦"}locale wrappers unwrap, a resolved asset object collapses to its id) or rejected with a self-teaching error that names the field, the expected shape, and a copyable example. A write never destroys content and reports success. - Serialization-proof writes β
set_field(documentId, field, value)writes one field as a flat string parameter, because long strings nested inside record arguments don't survive some clients' tool-call JSON repair (they arrive as{}). - Safe defaults β
update_contentmerges over the draft by default (a full replace that drops required fields would brick the next publish), and pages auto-slug from their name so agent-created content is always reachable. - A diagnosable trail β tool errors are logged with their arguments (
docker logs), and every MCP write lands in the same append-only audit log as the admin/API (ip=mcp). - Discoverable contract β
get_content_typeannotates every field withvalueFormat+valueExample, so an agent learns the exact JSON shape from the schema instead of by trial and error.
The full rules (with the war stories behind them) live in CLAUDE.md.
admin@ Admin Β· editor@ Editor Β· author@ Author (section-scoped) Β· viewer@ Viewer β passwords follow <Role>!Passw0rd.
Seeded credentials, keys, and secrets are dev defaults β rotate them before exposing the CMS (
SESSION_SECRET,CSRF_SECRET, the delivery keys,PREVIEW_SECRET, and the admin password).
CLAUDE.mdβ how to develop & deploy safely (and how AI agents should work in the repo).STACK.mdβ the stack and the reasoning behind each choice.
MIT β do what you like. Don't Pay eXtra. π°