System Architecture & Flows

The platform is organised into four logical planes. Each maps onto a set of serverless components defined in src/main/serverless/.

Plane
Responsibility
Key components
Management (admin) plane
Authenticated authoring, configuration and workflow
Admin_Tool (React UI), CMS_API, cms-functions
Delivery (public) plane
Serving content to visitors
Public_API, public-functions, per-site CloudFront + Publish_Store
Rendering plane
Turning page data into static HTML
publisher-function, shared-renderer, Component_Store
Data & platform services
Durable state and shared services
Content_Store (DynamoDB), S3 stores, Auth_Service, Email_Service, SMS_Service

High-level diagram

CMS Architecture

The management plane

  • Admin_Tool — a React 18 + TypeScript single-page application built with Vite and Tailwind, deployed as a website component onto S3 + CloudFront at admin.<rootDomain>. It talks only to the CMS API.
  • CMS_API — an API Gateway (cms-api.<rootDomain>) with ~90 routes. CORS is restricted to the admin origin, access logging is on, and a pre-process authorizer (cms-pre-process) runs before routes to validate the caller.
  • cms-functions — the control-plane Lambda group (Node.js 22). Each API route maps to a dedicated handler exported from handler.js, which delegates to a module under handlers/ with shared helpers in lib/.

The delivery plane

  • Each site is served from its own CloudFront distribution over the static objects in the shared Publish_Store (S3). This is provisioned automatically when a site is created.
  • Public_API (api.<rootDomain>) + public-functions provide dynamic read endpoints for published content, navigation, metadata, on-site search, and form submissions — without exposing the admin API.
  • The delivery plane is unauthenticated and read-optimised; it reads from Publish_Store and Content_Store only.

The rendering plane

  • publisher-function is a dedicated Lambda (1024 MB) that composes a page's component tree into a single static HTML document and writes it to Publish_Store. It is invoked asynchronously by the publish-page, unpublish-page, rebuild-site and bulk-publish handlers.
  • shared-renderer is a pure, dependency-light rendering library shared by the publisher and the live preview handler, guaranteeing preview and published output match. See Rendering & publishing.
  • Component_Store (S3) holds the HTML/CSS/JS bundles for every component the renderer can use.

Data & platform services

  • Content_Store — a single DynamoDB table (with two GSIs) holding all authoring state for every site and the platform. See the Data model.
  • S3 stores — Asset_Store (media & fonts), Component_Store (component bundles), Publish_Store (rendered sites), Backup_Store (backup archives), and deploy-artifacts (Lambda zips).
  • Auth_Service — Amazon Cognito user pool (email login, optional MFA, strong password policy, groups).
  • Email_Service / SMS_Service — transactional email (SES) and SMS (SNS), wired into Cognito for notifications and MFA.

Request flow: editing a page

  1. The author works in Admin_Tool; the SPA calls CMS_API with a Cognito JWT access token.
  2. cms-pre-process validates the token and resolves the caller's permissions, attaching them to the request context.
  3. The route's handler in cms-functions authorises the action, then reads or writes items in Content_Store.
  4. Draft changes are persisted immediately; a prior version snapshot is kept for history.

Request flow: publishing a page

  1. The author triggers Publish; publish-page validates permissions and marks the page published in Content_Store.
  2. publish-page invokes publisher-function (async).
  3. The publisher loads the page, its template and referenced component bundles, renders the full component tree to static HTML, and writes the object(s) to Publish_Store. It also regenerates the site's sitemap and robots.txt as needed.
  4. The site's CloudFront cache is invalidated so the new HTML is served.

Request flow: serving a visitor

  1. A visitor requests a page on the site's domain.
  2. CloudFront serves the pre-rendered HTML from Publish_Store (cache hit for most requests).
  3. For dynamic reads (navigation, search, metadata) or form posts, the page calls Public_API, which runs a public-functions handler against the published data.

Request flow: provisioning a site

  1. sites-create writes the site record and invokes provision-site.
  2. provision-site creates the DNS records, requests/validates a TLS certificate, creates the CloudFront distribution and configures storage — polling to completion across retries.
  3. Deleting a site runs delete-site-step to reverse each of these.