Overview
Ounce is a CMS engine that ships as one npm package, @ouncepage/core, and one
Astro integration. You describe your content in a config file. It gives you an
admin at /admin, the routes that serve your pages, the middleware that guards
them, and a typed API for reading content back out.
It runs on Cloudflare Workers. D1 stores content, R2 stores media, Cloudflare Access authenticates editors. Those three are not pluggable.
A project that uses it has no src/pages/ and no src/middleware.ts unless it
wants them. What is left in your own source tree is your content model and your
design, which is the point.
Who writes what
Section titled “Who writes what”| You write | Ounce provides |
|---|---|
| A Zod schema and a field list per content type | The forms, validation, sanitising and save path |
| Astro components that render your sections | The loop that renders them, the page model and the preview |
ounce.config.ts |
Every route under /admin, plus /[...path], /404 and /media/[...key] |
A csp block, if your templates embed anything |
Authentication, security headers and the policy |
| Migrations for your own tables, if any | The settings, pages, page_sections, revisions, media, media_uses, redirects, editors and mcp_tokens tables |
Content model
Section titled “Content model”Three kinds of thing, and the distinction decides which API you use.
Settings are global singletons keyed by a string: site, footer,
navigation. One row each, no page owns them.
Pages are routes. A page owns a slug, a title, SEO and Open Graph fields, and an ordered list of sections.
Sections are typed blocks owned by exactly one page. One instance per type
per page, enforced by PRIMARY KEY (page_id, key). That constraint is what
makes a hardcoded anchor id inside a section component legitimate.
Extension points
Section titled “Extension points”These are the only ones. Everything else is closed.
| Point | Adds | Page |
|---|---|---|
| Field type | A new type usable in any field list |
Field types |
| Section type | A renderable block | Section types |
| Plugin | A bundle of the below, with its own settings form | Plugins |
| Page tab | Fields on every page’s editor | Page tabs |
| Admin screen | A route under /admin/x/ |
Admin screens |
| Analytics provider | A dashboard source | Analytics providers |
| Assistant provider | An AI backend for the content assistant | Assistant providers |
Authentication and media storage are deliberately not extension points. There is one implementation of each and it is not swappable.
Every page
Section titled “Every page”Extension points, in the order most people need them:
How the platform behaves, and what to read when it misbehaves:
Reference:
Where to start
Section titled “Where to start”Installing: Install.
Understanding it: Architecture. It traces one field from the schema you declare to the HTML a visitor gets, which is the fastest way to know which file to open.
Something behaving strangely rather than erroring: Silent failures. Most of what goes wrong here produces a page that renders and a save that looks like it worked.
Assumptions
Section titled “Assumptions”You know Astro, you know Zod, and you have a Cloudflare account. Every code sample here is complete and runnable, not a fragment.