Kenresoft CMSOpen source · MIT
Your content.Your infrastructure.Your CMS.
A self-hosted, API-first content management system that runs in your own Cloudflare account. A Hono API on Workers, content in D1, media in R2, and a separate React admin.
Workers · D1 · R2 · Hono · React · Astro
Philosophy
Content is part of your infrastructure. It should live where the rest of it does.
Kenresoft CMS stores and serves content. It doesn’t decide what your website looks like, and it doesn’t sit on someone else’s platform. You deploy it, you own it, and your frontend reads from it over HTTP like any other service.
- 01
Self-hosted
It deploys into your Cloudflare account. The database, the media bucket and the Workers are yours, and so is the data in them.
- 02
API-first
Content is served over a versioned REST API. The CMS never renders your website, so the frontend is free to be whatever it needs to be.
- 03
Independently deployable
The API, the admin and your website are separate deployments. Updating one never requires redeploying the others.
Why Kenresoft CMS
For developers who want to own the stack.
Built on Cloudflare primitives
A Hono API on Workers, content in D1, media in R2. No servers or containers to run.
One command to deploy
Setup provisions the database and bucket, applies migrations and deploys both Workers.
A typed Astro client
@kenresoft-cms/astro reads content, pages, media and settings, with Live Preview built in.
Documented public API
Every deployment serves its own OpenAPI spec and an interactive reference at /api/v1/docs.
Extensible without forking
Plugins add tables, routes and admin pages without touching the core.
MIT licensed
Read it, run it, change it. The source is on GitHub.
Features
Everything a content team needs. Nothing it has to rent.
Content
Content types and fields
Define your own models with 14 field types, from text and rich text to media and references.
Entries
Draft and published states, with bulk export and import per content type.
Scheduled publishing
Set a time and the entry publishes itself.
Revisions
Revision history for entries and pages, with restore.
Live Preview
See a draft on your real site before it is published.
Site building
Pages and blocks
Compose pages from blocks, with templates and dynamic routing.
Reusable blocks
Build a block once and use it on any page.
Structured settings
Navigation, footer, contact, social and SEO settings, served over the API.
Media and forms
Media library
Uploads stored in R2, organised in folders. Image uploads are checked byte by byte.
Forms
Public submissions with spam protection and rate limits, an inbox, email notifications and replies from the admin.
Integration
Public REST API
Versioned, documented with OpenAPI, and edge-cached with invalidation on publish.
Webhooks
Signed deliveries notify your other systems when content changes.
Authentication
Staff and site-visitor accounts through Better Auth, with a same-origin proxy for first-party cookies.
The admin
A real editor for real content.
The admin is a React and Vite app, deployed as its own Worker. It talks to the API over HTTPS and never touches the database directly.
Drawn from the admin’s own layout, field types and controls.
Developer experience
Four commands to a running CMS.
You need Node 20 or newer, pnpm, git and a Cloudflare account. pnpm run setupwalks you through the rest, then prints the URLs of your API and admin. The first account you create becomes the owner.
npm create @kenresoft-cms@latest my-site # Scaffold the CMScd my-sitepnpm installpnpm run setup # Provision and deployWhat setup does
- Deploys the API Worker and the Admin Worker
- Creates the D1 database and R2 bucket, if you don’t have them
- Applies the database migrations
- Generates the auth secret
- Adds the admin’s origin to the API’s CORS allow-list
Later, pnpm run update pulls the latest version, applies new migrations and redeploys.
Astro integration
First-class support for Astro.
Astro is the frontend we build for and test against. The public API is plain REST, so any application that can make an HTTP request can read from it too.
---import { createKenresoftClient } from '@kenresoft-cms/astro';const cms = createKenresoftClient({ url: import.meta.env.PUBLIC_KENRESOFT_CMS_URL });const posts = await cms.entries.list({ contentType: 'blog' });---{posts.map((post) => ( <a href={`/blog/${post.slug}/`}>{post.data.title}</a>))}Typed client
Entries, pages, reusable blocks, media, forms, settings and auth, from one package.
Same-origin proxy
Sign-in cookies stay first-party to your site, even in browsers that block third-party cookies.
Live Preview
Render a draft on your real site from a short-lived preview token.
Connect an existing site
npx @kenresoft-cms/create astro adds the integration to an Astro 5+ project, and --update keeps it current.
Architecture
Three deployments. One API in the middle.
Your website and the admin are both clients of the API Worker. Only the API Worker reads and writes storage.
The website never connects to D1 or R2.
It holds no database credentials, and it can be hosted anywhere that can reach the API.
The CMS
pnpm run update
API, admin and migrations.
The integration
npx @kenresoft-cms/create astro --update
The managed files in your Astro site.
Your website
your own pipeline
Deploys whenever you choose.
Each updates on its own schedule. None of them modifies another.
Security and control
Access you define. Data you hold.
The API checks roles on every request, and your content never leaves the Cloudflare account you deployed it to.
Five roles
Owner, Admin, Editor, Author and Viewer, enforced by the API on every request.
Two-factor authentication
TOTP for staff accounts, with recovery codes.
Verified staff accounts
Email verification, password recovery and session monitoring.
Audit log
Admin actions are recorded, with who made them and when.
Rate limits and CORS
Per-IP limits on sign-in and public forms, and an explicit origin allow-list.
Your data, your account
D1 and R2 live in your Cloudflare account. D1 has 30-day Time Travel, and a script backs up media.
Extensibility
Extend it with plugins, not forks.
A plugin is a TypeScript package built against @kenresoft-cms/plugin-sdk. It brings its own tables, API routes, permissions and admin pages. Plugins are bundled at deploy time and switched on or off from the admin. The core never contains plugin logic.
Plugin shape
packages/plugin-hello/
- manifest.tsidentity, permissions, capabilities
- config-schema.tsvalidated settings
- repository.tsits own tables
- routes.tsits own API routes
plugin-hello is the reference plugin: every extension point, no business logic.
First-party plugin
Commerce
- Catalog: products, variants, categories
- Cart and customer accounts
- Checkout with stock enforcement
- Orders
- Paystack payments
- A storefront client in @kenresoft-cms/astro
Open source
MIT licensed. All of it.
The API, the admin, the CLI, the Astro integration and the plugins live in one public repository. Run it as it is, change what you need, or send the change back. The contributing guide, governance and security policy are in the repo.
- License
- MIT
- Hosting
- Self-hosted
- Language
- TypeScript
- On npm
- @kenresoft-cms/*
Kenresoft
Not a demo. This system runs kenresoft.com.
- 01
We built it.
Designed and written by Kenresoft, from the API to the admin.
- 02
We use it.
kenresoft.com is a Kenresoft CMS deployment. Its pages, navigation, portfolio, blog and forms come from it.
- 03
We maintain it.
Fixes found running that site go back into the product.
- 04
We open-source it.
The whole system is MIT licensed on GitHub.
How we designed it, what we changed along the way and what we learned is in the engineering case study.
Read the engineering storyDeploy your own CMS.
The documentation covers installation, deployment, the Astro integration and plugins.