Architecture
The apps, the shared packages, and where each CMS ends and the shared front-end code begins.
apps/ Deployable apps: CMS installs, vinext front-ends, the MCP Worker, this docs site
packages/ Shared libraries: content model, UI, adapters, Access helpers, logging, MCP
wordpress-shared/ Patronage's WordPress code that both installs load, and the deploy kit
docs/ Engineering notes: decision records in docs/adr/, research in docs/spikes/
Apps
| App | What it is |
|---|---|
cms-wordpress-headless |
WordPress in Docker as an editor and a WPGraphQL API. It renders nothing public. |
web-wordpress |
The vinext front-end for headless WordPress, deployed as a Worker. |
payload |
Payload as one vinext app on Workers: the admin, plus front-end pages that read through the Local API. |
cms-wordpress-timber |
A traditional WordPress install with a Timber theme. It shares design tokens with the others, not components. See Traditional WordPress. |
mcp |
Launch’s deployment of the MCP gateway Worker, behind Access, with its WordPress and Payload connectors. See MCP server. |
docs-site |
This site. |
Shared packages
content-model: the CMS-neutral shape of a page and its sections.ui: shadcn/ui components on Tailwind 4, theSection,ContainerandTypographyprimitives, and one component per content-model block. They take plain props and know nothing about WordPress or Payload. See Shared UI.adapter-wordpressandadapter-payload: map each CMS’s data tocontent-model. See CMS-neutral sections.access: provisions the Access applications, calls the CMS through Access, and verifies Access tokens. See Lock the CMS behind Cloudflare Access.logging: the Worker wrapper that prints one wide event per request, and the script that provisions a site’s log bucket. WordPress’s side is thepatronage-loggingmu-plugin. See Logging.sections-preview: a parity test and a static page that render the same page from both CMSs side by side.demo-content: the placeholder images and their alt text that every CMS’s seed uses.images: serves CMS images through the Cloudflare Images binding, on one same-origin route.mcp-core,mcp-wordpressandmcp-payload: the MCP gateway and its two connectors, built for npm under@patronage.apps/mcpdeploys them. See MCP server.
Shared WordPress code
Both WordPress installs load the same Patronage code from wordpress-shared/:
patronage-fields: the ACF field groups, post types, rich-text shortcodes, the form model and the demo seed.patronage-access: the mu-plugin that verifies Cloudflare Access sign-ins. Only the headless install uses it, since the Timber install serves its public site from WordPress.patronage-logging: the mu-plugin that records WordPress events. See Logging.wp-deploy: the deploy kit. See Deploying WordPress.
Where the CMS stops
Each CMS has an adapter that maps its data to content-model. Rich text and editor field definitions stay CMS-specific: the adapter renders rich text and passes it to the shared components as children. That keeps ui free of any one editor’s format.
Locking the CMS down
The pattern every reference follows:
- The CMS admin and API are behind a Cloudflare Access application.
- The front-end Worker reaches the CMS with an Access service token, or (for Payload) through the Local API in the same Worker.
- The CMS tells the front-end what changed through a signed revalidation request, so public pages stay cached until an edit is published.