CMSCMSby SZ

Your own CMS that grows with extensions. Content, auth, search and scheduling — all yours.

View on GitHubIn development · Apache-2.0
Cloudflare Workers · Passkey login

Grows with extensions

Declarative, declarative + code, or pure code — plus a private registry. Features are added, not rebuilt from scratch.

Passkey-first

WebAuthn login. No password to leak or to forget. It's sz.ws's technical statement, not an option.

Runs at the edge

Next.js + OpenNext on Cloudflare Workers, D1 / R2 for data, responding close to everyone.

Features are added, not rebuilt

Three kinds of extension, from a single manifest file to a full package of code. Install one and it shows up in the admin — the core doesn't change.

Declarative

One manifest.json is one feature: content types, admin pages and public routes, all described as data — the contact form on the right is a real example from the registry.

Declarative + code

A manifest that also ships its own admin components and SQL, published and versioned together in the registry.

Pure code

A whole package living in extensions/<id>/ — its own admin, public pages, API and kit, compiled into the core.

{
  "kind": "declarative",
  "id": "contact",
  "coreApi": "^1.3.0",
  "contentTypes": [{
    "name": "submission",
    "fields": [
      { "key": "name",    "type": "text", "required": true },
      { "key": "email",   "type": "text", "required": true },
      { "key": "message", "type": "text", "required": true }
    ]
  }],
  "adminPages":   [{ "view": "collection", "layout": "table" }],
  "publicRoutes": [{ "pattern": "/contact", "view": "form" }]
}

Excerpt from the contact extension's manifest in the registry — a declarative form whose anonymous submissions land straight in shared content.

One command to install

cms add fetches from the registry, materialises extensions/<id>/ and patches registry.ts. Private registries work over a token, and re-running is idempotent — no duplicate lines.

~/cms $ npx @sz.ws/cms add contact

registry — fetched manifest.json

materialised extensions/contact/

patched extensions/registry.ts

--dry-run shows what it would do; private registries use SZWS_REGISTRY_TOKEN.

An AI agent that always asks first

A chat panel built into the admin, wired to your content, orders and settings. It can answer questions, draft changes and run real calculations — but every write comes back as a proposal. Nothing changes until you say yes.

Confirms before it acts

A new post, a status change, a refund — every write is held up as a proposal first. There is no setting that skips this.

Answers with a chart, not a paragraph of numbers

Ask how many orders came in this month and it renders the same chart components the dashboard uses — real numbers pulled from your data, not a guess dressed up as one.

Computes, doesn't guess

A QuickJS sandbox running in a Web Worker does the arithmetic — the model runs real code instead of doing math in its head.

How the admin feels

None of this is a screenshot — these are the same components that ship in the admin. Select the text, hover the rows. They play one shared timeline on a loop: a ⌘K search, the numbers filling in, the save bar rising and dropping away.

Search content…
Recent searches
cloudflare workersextension registry
⌘K open · esc close navigate · go
Most read · last 30 days

Writing your first extension

0views
Post

Release notes for v0.4

DraftPublished
Deploying to Cloudflare Workers3,140
Blog
How scheduling and drafts work2,260
Blog
The private registry guide1,180
Docs