The premise
cimisy keeps the editor and refuses the trade. Everything a hosted CMS asks you to give up stays exactly where it already is.
Their database
Your repo
Content is MDX + YAML frontmatter, committed to the repo you already have — local disk in development, real commits on GitHub in production.
Their login
Your domain
The editor mounts at /admin inside your own app's deploy — no second app, no separate account for your teammates.
Their export feature
Nothing to export
Leaving is deleting a config file and two routes. What remains is a normal Next.js app with normal files — because that's all it ever was.
How it works
A typed cimisy.config.ts declares collections and fields. That file is the whole content model — versioned, reviewable, greppable.
One API route, one admin page — inside the Next.js app you already deploy. There is no third thing to run, host, or pay for.
Local disk while you develop; commits, branches, and pull requests on GitHub once you ship. Same config, same editor, higher stakes.
The editor
A block editor with a slash menu, image upload with a media picker, and live preview via Next.js Draft Mode. When a non-admin hits submit, their draft becomes a branch and a pull request — they never have to know.
Paragraph, heading, code, image, callout — each block declares how it round-trips to clean, human-readable MDX.
Non-admin saves land on a deterministic branch with an auto-opened PR. Repeated saves update the same PR — review happens on GitHub or in the admin, whichever the reviewer prefers.
Next.js Draft Mode renders the draft branch on your actual site, side by side with the editor — no rebuild, no staging environment.
Security
No adjectives here — mechanisms. Each claim below maps to code and tests in THREAT_MODEL.md, written down before v1 shipped, not after.
Every read, write, and delete passes one centralized permission check before storage is touched. Deny by default; a forged client-side role has zero effect — there's a regression test for exactly that.
import · export · {expressions} · unregistered JSX · spread attrs — rejected
Content is validated as an AST on both write and read, so a hand-edited malicious file on disk is rejected too — against a permanent corpus of 24 attack fixtures.
Files are checked for what they actually are, not what their extension claims. PNG, JPEG, GIF, and WEBP only — no SVG — capped at 5MB. Link URLs are scheme-validated at multiple independent layers.
Assets, trust boundaries, and specific attack scenarios — each mapped to the code and tests that mitigate it. SECURITY.md · THREAT_MODEL.md
The contrast
Other git-based CMSes keep your files in your repo too. The difference is everything around that fact: server-side roles, a validated content pipeline, drafts as real pull requests, and a written threat model — inside your own app, not a hosted editor pointing at it.
Content is MDX + YAML frontmatter in your own repo. Remove the config file and two route files, and what's left is a normal Next.js app with normal content. There is no export step because the content was never stored anywhere else.
No. Local disk in development; the GitHub API (commits, branches, pull requests) in production. Your git history is the version history.
Yes — that's most of the point. They sign in on your app's own domain, write in a block editor, and hit submit. The branch and pull request happen behind the scenes; they never touch git or GitHub directly.
Direct-publish roles commit straight to the default branch. Everyone else's drafts land on a per-user branch with an auto-opened PR. Approval and merging are GitHub's own PR review and branch protection — deliberately not reimplemented.
The package is Apache 2.0 licensed. There's no hosted service to pay for — it runs inside your app's existing deploy, against the repo you already have.
An existing Next.js app. Storage is local disk in development and a GitHub App integration in production — setup for the GitHub App is a documented, one-time walkthrough.