Skip to content

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.

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

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.

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.

Extension points, in the order most people need them:

How the platform behaves, and what to read when it misbehaves:

Reference:

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.

You know Astro, you know Zod, and you have a Cloudflare account. Every code sample here is complete and runnable, not a fragment.