Products•5 min read

Inside Foundation CMS: the platform this website runs on

Every website we look after has to let someone change it without ringing us first. For years that meant WordPress, and with it a server, a stack of plugins and a theme where the words and the layout are tangled together. Foundation CMS is what we built instead. It is the content management system behind the sites we now build, and since 26 September 2026 it has run this one. It sits on our applications page alongside the rest of the fleet.

Foundation CMS is a multi-tenant content management system on Cloudflare Workers, made for agencies and the clients they look after. One control plane serves many sites as data, not code. Each client gets an admin on their own hq. address that already knows their content types, their fields and their brand, and each site gets its content as whole, typed records. The rule is in its own strapline: the content layer, never the layout. The CMS hands over data and a way to fetch it, and never markup.

Why not a CMS that draws the page?

A traditional CMS renders the page itself, so the design lives inside the CMS’s templates and a change to one risks the other. Content points at addresses rather than records, which is why moving a WordPress site so often breaks every image on it. And when one server answers for many sites, one bad afternoon takes them all down together. We wanted the opposite on each count: content held as records, the front end owned entirely by the site, and nothing shared on the path a visitor takes.

Content, contract, presentation

The Foundation CMS mark has three layers and so does the product. Content is written in an admin built from configuration, so nothing about it is bespoke; what differs between two sites is data. The contract turns every field into a generated TypeScript type, produced on request from the live schema rather than stored, so it can never describe a model the site no longer has. Presentation is the site’s own code, and nobody else’s.

Every read returns the whole record. There are no projections and no selection sets, so a field added in the CMS today is in the site’s payload today: nothing lists fields at fetch time, so nothing goes stale.

A field added in the CMS is in the contract and the payload straight away, because every read returns the whole record
In the CMS
Titletext
Excerpttext
Categoryterm
Reading timenumber
Generated contract
interface Post {  title: string;  excerpt: string;  category: string;  readingTime: number;}
What the site receives
{  "title": "Inside Foundation CMS",  "excerpt": "Foundation CMS is…",  "category": "products",  "readingTime": 7}

Every site is its own Worker

Each site runs as its own Cloudflare Worker, holding its own D1 database and its own R2 bucket for media. The only shared piece on a visitor’s path is a small dispatcher that maps a hostname to its site through a cached lookup. Switch the CMS off and every site keeps serving: editors cannot edit until it is back, and visitors never notice it was gone.

The same separation makes leaving simple. A client who moves on takes their content as a database file, their stored files and their content model, because those were only ever theirs.

The CMS goes offline and every site keeps serving: each one is its own Worker, with its own database and media bucket
The CMShq. adminonlineoffline
Dispatchhostname → site
swarmlabs.ioWorkerD1R2serving
foundationcms.orgWorkerD1R2serving
demo-trades-sheetWorkerD1R2serving
demo-nursery-journalWorkerD1R2serving

A new site is an API call, not a deploy

Setting up a site creates a database and a bucket, records the site, binds it, publishes the lookup and registers the hostname. Nothing redeploys and no other site is touched. It is written on the assumption that it will fail partway, because five services with no shared transaction eventually will, so each step asks whether it is already done and a re-run picks up where the last one stopped.

The client’s part is one DNS record. Their own domain is served through Cloudflare for SaaS, so their DNS never has to move to us. Sites start from templates, which are themselves sites: fourteen design directions across five industries are live on foundationcms.org, and a new site begins as a copy of the one the client picks.

A new site, step by step. Each step checks whether it is already done, so a re-run resumes instead of duplicating
1Create the databaseD1
2Create the media bucketR2
3Record the siteregister
4Bind itWorker
5Publish the lookupKV
6Register the hostnameSSL
The client adds one recordCNAME  hq.yourclient.co.uk  →  cname.foundationcms.org

The same door for people and automations

The content API gives builders and automations the schema, the generated contract and the published content. Keys are scoped to one site and to named abilities, such as writing posts, so a leaked key costs one site and nothing more. A write through the API is an editor’s save reached by key: a revision is recorded first, categories and related entries are filed, and search engines that accept IndexNow are told about the change.

This website is the proof. Waggle, our search product, researches, writes and publishes this blog’s posts through that API, our n8n workflows publish the integration pages, and Time Hive’s running total of hours saved reaches our home page the same way.

An automation publishes through the same door as an editor: a key scoped to one site and one type, a revision, and search engines told
WaggleArticle writtenkey: content:write:post
POST /api/v1/content/post
Foundation CMS201 Createdrevision recorded, category filed
IndexNow
swarmlabs.io/news/…live, search engines told

What else comes with it

  • Forms defined as one document that the admin edits, the site renders and the server checks, with delivery by email, a signed webhook or a mapped call into a CRM, and Turnstile or reCAPTCHA per site.
  • A media library where every file is a record referenced by id, with web sizes made when it is uploaded.
  • Search and AI visibility from one register of crawler rules, with structured data, llms.txt and an IndexNow push after every publish.
  • Revisions, a bin and scheduled publishing, so nothing is lost to a bad save or a slip of the finger.
  • AI drafts on Workers AI, such as a first search description, which a person accepts, edits or discards. Nothing AI-written goes live on its own.
  • Access by scoped grants, and a platform plane that signs in with passkeys only, with no password to phish.
  • Health that only shows green when a check has proved it. A result too old to stand behind turns to “unknown” rather than staying green.

How it is built

The platform is TypeScript on Cloudflare: Workers for the admin, the sign-in and each site, a D1 database and an R2 bucket per site, KV for the hostname lookup and Workers AI for drafts. The admin is written in Svelte. When we measured site speed in September, every database query took under a millisecond; the time went on round trips from data centres far from the database, so reads are now batched into as few trips as possible.

Foundation CMS leans on the rest of the studio. Apiary supplies the domain and certificate picture its health checks read, and Barb put it through a full security audit in September 2026.

Why we built it

A website should be easy to change for the person who owns it and fast for the person reading it, and those two goals should not fight. Keeping content and layout apart lets us design each site properly, hand editing to the people who know the business, and add a site without adding a server. If you are an agency weighing a move away from WordPress, or a business whose site has become hard to change, read more on foundationcms.org or see the services we build around it.

Want this wired up
for you?