Skip to content

Custom Domain Ingress, SSL Lifecycle & Ephemeral Preview Routing True Specification

Custom Domain Ingress, SSL Lifecycle & Ephemeral Preview Routing True Specification

Section titled “Custom Domain Ingress, SSL Lifecycle & Ephemeral Preview Routing True Specification”

Document Status: 🟢 Active True Specification
Target Audience: Core Engineering, Platform SRE, AI Agents, Client Success
Primary Maintainer: SiteSwarm Architecture Council
Related Documents: HIGH_LEVEL_DESIGN.md, docs/PRD.md, docs/CAPABILITY_MANAGEMENT.md, docs/CLIENT_CMS.md, docs/DATA_ISOLATION_AND_STORAGE.md, ADR-0003
Implementation Tracking: Issue #57 (Supersedes Issue #30; Part of Epic #52 alongside #53, #54, #55, and #56)


1. Executive Summary & Core Architectural Invariants

Section titled “1. Executive Summary & Core Architectural Invariants”

SiteSwarm delivers bespoke, high-performance web presences for local small businesses while enabling software engineers to operate the platform with zero daytime distractions. For this business model to scale sustainably across dozens of independent client applications in a monorepo, the edge ingress and routing infrastructure must solve two conflicting operational pressures:

  1. Production Custom Ingress Without DNS Migration Friction: Non-technical local business owners (bakeries, boutique agencies, medical clinics) own custom domains registered at fragmented third-party registrars (GoDaddy, Namecheap, Google Domains / Squarespace, Network Solutions). They have existing business email (Google Workspace, Microsoft 365) configured on their domain. Forcing a full authoritative nameserver transfer to Cloudflare introduces high customer friction, risk of broken email delivery, and customer support emergencies. Production ingress must route custom domains to edge workers purely via CNAME records with fully automated SSL provisioning.
  2. Ephemeral PR Preview Routing Without Resource Bloat: In a monorepo with multiple independent applications (apps/bakery, apps/software-agency, etc.), developers and AI agents routinely push feature branches. Validating changes requires live mobile verification on real smartphone devices by stakeholders before merging. However, deploying every app on every commit exhausts CI minutes, inflates edge worker counts, and causes cloud sprawl. Ephemeral PR previews must deploy only for impacted applications, expose instant mobile inspection links (with QR codes), and automatically decommission upon PR closure.
flowchart TD
subgraph ClientDNS["Third-Party Client DNS (GoDaddy, Namecheap, Google Domains)"]
ClientApex["client.com (Apex)\n(URL Forwarder or ALIAS)"]
ClientSub["www.client.com (Subdomain)\n(CNAME Record)"]
end
subgraph PreviewDNS["SiteSwarm Staging & Preview DNS (*.siteswarm.dev)"]
PRPreviewDNS["pr-55-bakery.preview.siteswarm.dev\n(Ephemeral Route)"]
StagingDNS["staging-bakery.siteswarm.dev\n(Staging Route)"]
end
subgraph EdgeIngress["Cloudflare Edge Fleet (SSL & Routing Layer)"]
CF_SaaS["Cloudflare for SaaS (SSL for SaaS)\nCustom Hostname Engine + DCV"]
CF_WAF["Cloudflare Edge WAF & Security\n(Turnstile, HSTS, Rate Limiting, CSP)"]
CF_Router["Dynamic Edge Ingress Router\n(Host Header Evaluation)"]
CF_Access["Cloudflare Access / Zero Trust\n(Gates /admin* and /_emdash/*)"]
end
subgraph EdgeWorkers["Application Compute Layer (Cloudflare Workers)"]
subgraph AppBakery["Client App: Green Leaf Bakery"]
WorkerProdBakery["Worker A (Public Frontend)\napps/bakery\n(Zero Session KV, Sub-20ms TTFB)"]
WorkerProdBakeryCMS["Worker B (Isolated CMS & Admin)\nservices/cms-bakery\n(EmDash Studio, D1 Database)"]
WorkerProdBakery -->|Service Binding: /admin*| WorkerProdBakeryCMS
end
WorkerProdAgency["Production Worker\napps/software-agency (apex-labs)"]
WorkerPRBakery["Ephemeral Worker A\ngreen-leaf-bakery-preview-pr-55"]
end
ClientSub -->|CNAME fallback.siteswarm.dev| CF_SaaS
ClientApex -.->|301 Redirect to www| ClientSub
PRPreviewDNS --> CF_WAF
StagingDNS --> CF_WAF
CF_SaaS --> CF_WAF
CF_WAF --> CF_Router
CF_Router -->|Host: www.greenleafbakery.com| WorkerProdBakery
CF_Router -->|Host: www.apexlabs.io| WorkerProdAgency
CF_Router -->|Host: pr-55-bakery.preview.siteswarm.dev| WorkerPRBakery
CF_WAF -.->|Protected Admin Paths| CF_Access
  1. The Zero-Nameserver-Migration Invariant: Clients are never required to change authoritative nameservers. All production routing is orchestrated via Cloudflare for SaaS custom hostnames pointing to a designated SiteSwarm fallback origin CNAME target. Client MX, TXT (SPF/DKIM/DMARC), and other operational DNS records remain untouched at their existing registrar.
  2. The Zero-Touch Renewal & Fully Automated SSL Invariant: Domain Control Validation (DCV) and TLS certificate generation (via Let’s Encrypt or Google Trust Services) must execute automatically through Cloudflare’s edge pipeline without manual certificate issuance, renewal tickets, or server reboots.
  3. The Ephemeral Isolation & Zero-Orphan Invariant: Ephemeral PR preview environments are deployed strictly for client apps impacted by git changes (via monorepo change detection). When a PR is closed or merged, preview workers and routing routes are automatically deleted with zero residual compute or DNS records left behind.
  4. The 30-Second Mobile Inspection Benchmark: Pinned PR comments must provide live, edge-probed preview URLs with mobile-ready QR codes so stakeholders can inspect live changes on physical mobile devices in under 30 seconds from deployment.

2. Ingress Topology & Architecture Evaluation Matrix

Section titled “2. Ingress Topology & Architecture Evaluation Matrix”

Before selecting Cloudflare for SaaS custom hostnames and dynamic worker-based ephemeral previews, we evaluated four candidate ingress models against SiteSwarm’s operating constraints: near-$0 baseline costs, zero daytime maintenance distractions, client autonomy, and preview velocity.

Ingress & Routing Pattern Contender 1: Full Nameserver Transfer to Cloudflare Contender 2: Traditional Reverse Proxy VPS (Nginx / Caddy) Contender 3: Cloudflare Pages Preview Environments Contender 4: Cloudflare for SaaS + Dynamic Worker Previews (SiteSwarm Canonical)
Client DNS Friction 🔴 High friction: Client must transfer authoritative nameservers; high risk of breaking email 🟢 Low friction: Standard CNAME record 🟢 Low friction: Standard CNAME for custom domains 🟢 Zero friction: Standard CNAME to fallback origin; email untouched
SSL / TLS Provisioning 🟢 Automatic (Universal SSL) 🟡 Let’s Encrypt certbot with manual renewal cron 🟢 Automatic Cloudflare SSL 🟢 Zero-touch automated SSL for SaaS (Let’s Encrypt / GTS DCV)
Multi-App Monorepo Previews 🔴 Single preview per repo; monorepo collisions 🔴 Complex manual Docker container reverse proxy orchestration 🟡 Single Pages project per repo; requires $N$ Pages projects 🟢 Dynamic per-app preview workers (<app>-preview-pr-${PR_NUM}) scoped by change detection
Ephemeral Teardown N/A 🔴 Fragile container pruning scripts 🟡 Manual branch deployment retention policies 🟢 Automated PR-closed workflow runs wrangler delete --force
Mobile Stakeholder Review 🔴 Must manually find preview URL in commit log 🔴 Custom bot scripts 🟡 Cloudflare bot posts generic commit link 🟢 Idempotent pinned PR comment bot with inline QR codes & edge health probe
Baseline Infrastructure Cost 🟢 Free tier 🔴 $10–$40/mo per VPS 🟢 Free tier (500 builds/mo limit) 🟢 $0 Free / $5/mo Workers Paid (includes 100 custom SaaS hostnames)
Architectural Verdict ❌ REJECTED (High client risk) ❌ REJECTED (High server upkeep) ❌ REJECTED (Monorepo friction) 🟢 ADOPTED AS CANONICAL STRATEGY

3. Production Ingress: Cloudflare for SaaS Architecture

Section titled “3. Production Ingress: Cloudflare for SaaS Architecture”

SiteSwarm utilizes Cloudflare for SaaS (also known as Custom Hostnames / SSL for SaaS). The architecture separates the platform infrastructure zone from the client’s custom domain:

  • SiteSwarm Platform Zone: siteswarm.dev is an authoritative Cloudflare zone owned by the platform.
  • Fallback Origin Hostname: A designated CNAME target configured on the platform zone:
    fallback.siteswarm.dev -> Cloudflare Worker Ingress Router (or specific client worker)
  • Client Custom Domain: www.greenleafbakery.com configured at GoDaddy/Namecheap with a CNAME record pointing to fallback.siteswarm.dev.

When an end user visits https://www.greenleafbakery.com:

  1. Client DNS resolves www.greenleafbakery.com to fallback.siteswarm.dev.
  2. Cloudflare Edge receives the request, identifies www.greenleafbakery.com in its Custom Hostname registry for siteswarm.dev, and terminates TLS using the auto-provisioned certificate.
  3. Cloudflare executes edge WAF, Turnstile, and caching policies.
  4. Cloudflare forwards the request to the Fallback Origin Worker with the original Host: www.greenleafbakery.com header preserved.
  5. The Worker inspects the Host header to dispatch to the appropriate client Astro SSR application.
sequenceDiagram
autonumber
actor Customer as Local Customer Browser
participant Registrar as Client Registrar DNS (e.g. GoDaddy)
participant CF_Edge as Cloudflare Anycast Edge (SaaS)
participant Worker as SiteSwarm Ingress Router / Client Worker
participant Storage as Cloudflare D1 / R2 / KV
Customer->>Registrar: Resolve www.greenleafbakery.com
Registrar-->>Customer: CNAME fallback.siteswarm.dev (Cloudflare Anycast IPs)
Customer->>CF_Edge: TLS 1.3 ClientHello (SNI: www.greenleafbakery.com)
CF_Edge->>CF_Edge: Match Custom Hostname & Terminate SSL (Let's Encrypt / GTS)
CF_Edge->>CF_Edge: Apply Ingress Security (WAF, HSTS, Rate Limits, Cache)
CF_Edge->>Worker: Route request with Host: www.greenleafbakery.com
Worker->>Storage: Query tenant data (D1 / KV Cache)
Storage-->>Worker: Tenant configuration & content
Worker-->>CF_Edge: HTML response + Security Headers + Cache-Control
CF_Edge-->>Customer: HTTP/3 response (Brotli/Zstd compressed)

3.2 Automated Custom Hostname Provisioning via Cloudflare API

Section titled “3.2 Automated Custom Hostname Provisioning via Cloudflare API”

Custom hostnames are provisioned automatically via the Cloudflare REST API. This allows onboarding scripts, CLI utilities, or admin interfaces to configure domains without manual dashboard navigation.

Terminal window
curl -X POST "https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/custom_hostnames" \
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"hostname": "www.greenleafbakery.com",
"ssl": {
"method": "cname",
"type": "dv",
"settings": {
"http2": "on",
"min_tls_version": "1.2",
"ciphers": []
}
}
}'
{
"success": true,
"errors": [],
"messages": [],
"result": {
"id": "e4b3c2a1-5d6e-4f8a-9b1c-7e8f9a0b1c2d",
"hostname": "www.greenleafbakery.com",
"status": "pending",
"verification_errors": [
"custom hostname does not CNAME to this zone."
],
"ownership_verification": {
"type": "cname",
"name": "www.greenleafbakery.com",
"value": "fallback.siteswarm.dev"
},
"ssl": {
"id": "f5c4b3a2-6e7f-4a9b-0c2d-8f9a0b1c2d3e",
"type": "dv",
"method": "cname",
"status": "pending_validation",
"dcv_delegation_records": [
{
"cname": "_cf-custom-hostname.www.greenleafbakery.com",
"cname_target": "www.greenleafbakery.com.e4b3c2a1-5d6e.dcv.cloudflare.com"
}
]
},
"created_at": "2026-09-29T20:00:00.000Z"
}
}

3.3 Domain Control Validation (DCV) & Zero-Touch Renewal

Section titled “3.3 Domain Control Validation (DCV) & Zero-Touch Renewal”

Cloudflare for SaaS supports three Domain Control Validation methods:

  1. CNAME Validation (Recommended Default):
    • When the client creates CNAME www.greenleafbakery.com -> fallback.siteswarm.dev, Cloudflare uses the routing CNAME to automatically perform both hostname ownership verification and SSL certificate validation.
    • For pre-validation before traffic switchover, an optional DCV delegation record (_cf-custom-hostname.www...) can be created to validate the certificate with zero downtime before changing the primary CNAME.
  2. HTTP Validation (/.well-known/cf-custom-hostname-challenge/):
    • Useful when the domain is already pointing to SiteSwarm and automated token requests are served by the edge worker.
  3. TXT Record Validation:
    • Explicit TXT ownership record: _cf-custom-hostname.www.greenleafbakery.com with value provided by the API.
  • Certificates are issued by Let’s Encrypt or Google Trust Services (DV - Domain Validated).
  • Cloudflare automatically revalidates and renews certificates 30 days prior to expiration via the existing CNAME target.
  • No developer intervention or certbot cron job is ever required.

3.4 Client Registrar DNS Onboarding Runbooks

Section titled “3.4 Client Registrar DNS Onboarding Runbooks”

Local small business owners need clear, step-by-step instructions. Below are copy-paste onboarding templates for the four most common registrar configurations.

Architecture Note: Apex (@) vs. Subdomain (www)

Section titled “Architecture Note: Apex (@) vs. Subdomain (www)”
  • DNS Specification RFC 1034/1035: An apex/root domain (example.com) cannot have a CNAME record if other records (like MX or SOA) exist on @.
  • SiteSwarm Best Practice: The canonical site lives at www.client.com. The apex client.com is configured with a 301 Permanent Redirect to https://www.client.com at the registrar level, or uses registrar CNAME flattening / ALIAS records when available.

Scenario: Client purchased greenleafbakery.com on GoDaddy. They use Google Workspace or Microsoft 365 for email.

Step-by-Step Instructions for Client:

  1. Log into your GoDaddy Domain Portfolio.
  2. Select your domain, click DNS, and navigate to DNS Records.
  3. Locate the existing CNAME record with Name www. Click Edit and set:
    • Type: CNAME
    • Name: www
    • Value: fallback.siteswarm.dev
    • TTL: 1/2 Hour (or Default)
  4. In the DNS settings page, scroll down to Forwarding > Domain Forwarding:
    • Click Add Forwarding on the root domain (@).
    • Destination URL: https://www.greenleafbakery.com
    • Redirect Type: Permanent (301)
    • Forward Settings: Forward Only
  5. Click Save. Do not alter any MX, TXT, or SPF records.

Scenario: Client domain on Namecheap. Namecheap supports ALIAS (ANAME) flattening at the root apex.

Step-by-Step Instructions for Client:

  1. Log into your Namecheap Dashboard and click Manage next to your domain.
  2. Select the Advanced DNS tab.
  3. In the Host Records section, add or update the following records:
    • Record 1 (Subdomain):
      • Type: CNAME Record
      • Host: www
      • Value: fallback.siteswarm.dev
      • TTL: Automatic
    • Record 2 (Apex ALIAS):
      • Type: ALIAS Record
      • Host: @
      • Value: fallback.siteswarm.dev
      • TTL: Automatic
  4. If Namecheap ALIAS is not desired, configure Redirect Domain from @ to https://www.greenleafbakery.com.
  5. Click Save all changes.

3. Google Domains / Squarespace Domains Runbook

Section titled “3. Google Domains / Squarespace Domains Runbook”

Scenario: Domain registered with Google Domains (migrated to Squarespace Domains).

Step-by-Step Instructions for Client:

  1. Log into your domain management dashboard at Squarespace Domains.
  2. Click on your domain and navigate to DNS Settings.
  3. Under Custom Records, create:
    • Record Type: CNAME
    • Host: www
    • Data: fallback.siteswarm.dev
    • TTL: 3600 (or 300)
  4. Under Domain Forwarding / Website Redirect Rules:
    • Forward from greenleafbakery.com to https://www.greenleafbakery.com.
    • Select 301 Permanent Redirect and enable SSL for Forwarding.
  5. Save changes.

4. Cloudflare DNS (Client Already on Cloudflare) Runbook

Section titled “4. Cloudflare DNS (Client Already on Cloudflare) Runbook”

Scenario: Client’s domain is already managed on Cloudflare DNS.

Step-by-Step Instructions for Client:

  1. Log into the Cloudflare Dashboard and select the domain zone.
  2. Navigate to DNS > Records.
  3. Add a CNAME for www and @ (Cloudflare automatically flattens CNAMEs at the apex root):
    • Record 1:
      • Type: CNAME
      • Name: www
      • Target: fallback.siteswarm.dev
      • Proxy status: DNS only (Grey Cloud) — Crucial: Must be Grey Cloud to prevent Cloudflare-on-Cloudflare proxy conflict (“Orange to Orange”) unless using Cloudflare SaaS CNAME delegation.
    • Record 2:
      • Type: CNAME
      • Name: @
      • Target: fallback.siteswarm.dev
      • Proxy status: DNS only (Grey Cloud)
  4. Save records.

3.5 Worker Splitting Ingress Topology & Routing Rules (/admin & /_emdash/*)

Section titled “3.5 Worker Splitting Ingress Topology & Routing Rules (/admin & /_emdash/*)”

As established in ADR-0003, SiteSwarm enforces physical isolation between public static delivery and dynamic CMS administration. This resolves the Astro session KV auto-provisioning problem and isolates the database blast radius.

flowchart TD
subgraph IngressGateway["Edge Ingress Gateway (Cloudflare Anycast)"]
Request["Incoming HTTPS Request\nHost: www.greenleafbakery.com"]
WAF["Edge WAF & DDoS Shield"]
HostMatch{"Route Matcher\nPath Inspection"}
AccessCheck{"Path in /admin* or /_emdash/*?"}
CFAccess["Cloudflare Access / Zero Trust\n(JWT Assertion Verification)"]
end
subgraph WorkerFleet["Decoupled Compute Layer"]
subgraph WorkerA["Worker A: Public Static Frontend (@siteswarm/app-bakery)"]
EdgeAssets["Cloudflare Assets (SSG Prerender)\nSub-20ms TTFB | 0 KB Client JS | Zero Session KV"]
ProxyMiddleware["Service Binding Proxy Handler\n(env.CMS_SERVICE)"]
end
subgraph WorkerB["Worker B: Isolated CMS & Admin (@siteswarm/service-cms-bakery)"]
EmDashStudio["EmDash CMS Engine\n(Astro + React Admin Studio)"]
AdminAuth["Passkey & Session Cookie Engine"]
MCPEndpoint["MCP Agent Endpoint (/_emdash/api/mcp)"]
D1Storage[("Cloudflare D1 Database\n(Atomic SQLite)")]
R2Storage[("Cloudflare R2 Bucket\n(Zero-Egress Media)")]
end
end
Request --> WAF --> HostMatch
HostMatch --> AccessCheck
AccessCheck -- "No (Public Paths: /, /menu, /about, /assets/*)" --> WorkerA
WorkerA --> EdgeAssets
AccessCheck -- "Yes (/admin*, /_emdash/*)" --> CFAccess
CFAccess -- "Authenticated" --> WorkerA
WorkerA --> ProxyMiddleware
ProxyMiddleware ==>|"Service Binding\n0ms In-Memory V8 Hop"| WorkerB
WorkerB --> EmDashStudio
WorkerB --> AdminAuth
WorkerB --> MCPEndpoint
EmDashStudio --> D1Storage
EmDashStudio --> R2Storage
Requested Path Target Runtime Delivery Mechanism Session KV Required? D1 Binding Exposed? Security Tier
/ (Homepage) Worker A (Frontend) Edge Cached / Static Asset ❌ No ❌ No Public Anonymous
/menu, /about, /contact Worker A (Frontend) Edge Cached / Static Asset ❌ No ❌ No Public Anonymous
/assets/*, /favicon.svg Worker A (Frontend) Cloudflare Assets (CDN) ❌ No ❌ No Public Anonymous
/api/contact (Lead Form) Worker A (Frontend) Turnstile Edge Validator ❌ No ❌ No Public Rate-Limited
/admin, /admin/* Worker B (CMS) Proxied via env.CMS_SERVICE ❌ (Cookie) ✅ Yes Cloudflare Access + Admin Auth
/_emdash/* (CMS Assets & API) Worker B (CMS) Proxied via env.CMS_SERVICE ❌ (Cookie) ✅ Yes Cloudflare Access + Admin Auth
/_emdash/api/mcp (AI Agent) Worker B (CMS) Proxied via env.CMS_SERVICE ❌ (Token) ✅ Yes Bearer Token / Service Auth

3.5.3 Worker A Service Binding Implementation

Section titled “3.5.3 Worker A Service Binding Implementation”

In Worker A’s wrangler.jsonc, the CMS worker is declared as a native Service Binding:

{
"name": "green-leaf-bakery",
"compatibility_date": "2026-09-29",
"compatibility_flags": ["nodejs_compat"],
"services": [
{
"binding": "CMS_SERVICE",
"service": "green-leaf-bakery-cms"
}
]
}

Worker A dispatches administrative paths to Worker B with zero network latency:

export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
// Route admin and CMS requests directly to Worker B
if (url.pathname.startsWith('/admin') || url.pathname.startsWith('/_emdash')) {
return env.CMS_SERVICE.fetch(request.clone());
}
// Serve public pages from static assets with edge cache headers
return env.ASSETS.fetch(request);
}
};

4. Multi-App Ephemeral PR Preview Routing Lifecycle

Section titled “4. Multi-App Ephemeral PR Preview Routing Lifecycle”

4.1 Subdomain & Cloudflare Worker Naming Conventions

Section titled “4.1 Subdomain & Cloudflare Worker Naming Conventions”

SiteSwarm enforces deterministic naming conventions across the monorepo to guarantee collision-free preview routing and predictable cleanup:

Concept Format / Pattern Example
Monorepo App Path apps/<app-folder> apps/bakery, apps/software-agency
Application ID / Slug Defined in package.json / swarm.config.ts green-leaf-bakery, apex-labs
Worker Preview Name <app-name>-preview-pr-${PR_NUM} green-leaf-bakery-preview-pr-55
Preview Edge Hostname https://pr-${PR_NUM}-${app-slug}.preview.siteswarm.dev https://pr-55-bakery.preview.siteswarm.dev
Fallback Workers URL https://${app-name}-preview-pr-${PR_NUM}.siteswarm.workers.dev https://green-leaf-bakery-preview-pr-55.siteswarm.workers.dev
flowchart LR
GitBranch["Feature Branch\nPR #55 touches apps/bakery"] --> Detector["Change Detector (#53)\nimpacted_apps: ['bakery']"]
Detector --> CIBuild["Selective Build (#54)\npnpm --filter=@siteswarm/app-bakery build"]
CIBuild --> Deploy["Wrangler Deploy (#55)\n--name green-leaf-bakery-preview-pr-55"]
Deploy --> Probe["Edge Health Probe\nGET /_emdash/api/health (200 OK)"]
Probe --> Comment["PR Comment Bot\nPost Pinned Comment + QR Code"]
PRClose["PR #55 Merged or Closed"] --> Teardown["Teardown Workflow (#56)\nwrangler delete --name green-leaf-bakery-preview-pr-55"]
Teardown --> DecomComment["Update PR Comment\n🧹 Preview Decommissioned"]

4.2 Integration with Monorepo Change Detection (#53, #54)

Section titled “4.2 Integration with Monorepo Change Detection (#53, #54)”

Ephemeral preview deployments consume outputs generated by @siteswarm/change-detector (Issue #53):

{
"has_runtime_changes": true,
"is_platform_wide": false,
"impacted_apps": ["bakery"],
"matrix": ["apps/bakery"]
}
  1. Documentation-Only PRs (has_runtime_changes: false):
    • Fast-pass exit within 20 seconds.
    • Zero preview deployments triggered.
    • No worker or DNS changes made.
  2. Single-App Impact (impacted_apps: ['bakery']):
    • Deploy only green-leaf-bakery-preview-pr-${PR_NUM}.
    • apps/software-agency and other client apps are untouched.
  3. Platform-Wide Impact (is_platform_wide: true):
    • Triggered when shared platform libraries (packages/*, root configurations, shared styles) are modified.
    • Generates a preview deployment matrix across all registered client applications in the fleet so regression testing can be conducted across all sites.

4.3 Preview Deployment Workflow Architecture (preview-deploy.yml #55)

Section titled “4.3 Preview Deployment Workflow Architecture (preview-deploy.yml #55)”

The preview deployment workflow executes on pull_request events (opened, synchronize, reopened):

# .github/workflows/preview-deploy.yml (Specification)
name: Ephemeral PR Preview Deployment
on:
pull_request:
types: [opened, synchronize, reopened]
concurrency:
group: preview-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
detect:
name: Evaluate Monorepo Changes
runs-on: ubuntu-latest
outputs:
has_runtime_changes: ${{ steps.detector.outputs.has_runtime_changes }}
impacted_apps: ${{ steps.detector.outputs.impacted_apps }}
matrix: ${{ steps.detector.outputs.matrix }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- id: detector
run: pnpm --filter=@siteswarm/change-detector run detect
deploy-previews:
name: Deploy Ephemeral Worker Preview
needs: detect
if: needs.detect.outputs.has_runtime_changes == 'true'
strategy:
fail-fast: false
matrix:
app: ${{ fromJson(needs.detect.outputs.matrix) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 12.4.1
- uses: actions/setup-node@v4
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile
- name: Build Application
run: pnpm --filter=${{ matrix.app }} run build
- name: Graceful Zero-Secrets Check & Edge Deploy
id: deploy
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: |
if [ -z "$CLOUDFLARE_API_TOKEN" ] || [ -z "$CLOUDFLARE_ACCOUNT_ID" ]; then
echo "::warning::Cloudflare secrets not configured. Skipping remote edge deployment (zero-secrets fallback active)."
echo "skipped=true" >> $GITHUB_OUTPUT
exit 0
fi
APP_NAME=$(node -p "require('./${{ matrix.app }}/package.json').name.replace('@siteswarm/app-', '')")
WORKER_NAME="${APP_NAME}-preview-pr-${{ github.event.pull_request.number }}"
# Deploy ephemeral worker
pnpm --filter=${{ matrix.app }} exec wrangler deploy \
--name "$WORKER_NAME" \
--compatibility-date 2026-09-29
PREVIEW_URL="https://${WORKER_NAME}.${{ secrets.CF_WORKERS_DOMAIN || 'workers.dev' }}"
echo "preview_url=$PREVIEW_URL" >> $GITHUB_OUTPUT
echo "worker_name=$WORKER_NAME" >> $GITHUB_OUTPUT

4.4 Cloudflare Credential Enforcement & Strict Failure Protocol

Section titled “4.4 Cloudflare Credential Enforcement & Strict Failure Protocol”

SiteSwarm enforces strict build failures when Cloudflare deployment tokens (CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID) are absent for runtime preview deployments:

  1. Hard CI Failure: If credentials are missing when deployable client applications are modified, .github/workflows/preview-deploy.yml exits with code 1 and emits an error annotation.
  2. Actionable Failure Diagnostics: The failure message in CI logs and the pinned PR comment explicitly detail the missing credentials and provide exact setup steps and required API token permissions.
  3. Required Cloudflare API Token Setup:
    • CLOUDFLARE_ACCOUNT_ID: Found in Cloudflare Dashboard (Workers & Pages Overview).
    • CLOUDFLARE_API_TOKEN: Created at Cloudflare API Tokens with the Edit Cloudflare Workers template, or custom permissions:
      • Account: Workers Scripts: Edit
      • Account: Workers KV Storage: Edit
      • Account: Workers Routes: Edit
      • Account: D1: Edit
      • Account: Account Settings: Read
      • User: Memberships: Read

4.5 Edge Health Probing & Synthetic Smoke Verification

Section titled “4.5 Edge Health Probing & Synthetic Smoke Verification”

Before broadcasting a preview URL to stakeholders, the deployment pipeline executes an automated HTTP probe against the deployed edge worker:

Terminal window
# Edge Health Verification Loop
PROBE_TARGET="${PREVIEW_URL}/_emdash/api/health"
MAX_ATTEMPTS=12
INTERVAL_SEC=5
for i in $(seq 1 $MAX_ATTEMPTS); do
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$PROBE_TARGET" || echo "000")
if [ "$HTTP_STATUS" -eq 200 ]; then
echo "✅ Edge health probe passed (HTTP 200) after ${i} attempts."
exit 0
fi
echo "Attempt $i/$MAX_ATTEMPTS: HTTP $HTTP_STATUS — waiting ${INTERVAL_SEC}s..."
sleep $INTERVAL_SEC
done
echo "❌ Error: Edge preview failed to reach healthy state within 60s."
exit 1
  • Health Probe Endpoints:
    • /_emdash/api/health for Astro applications with EmDash CMS integrated.
    • /api/health for lightweight client apps.
    • Validates edge worker initialization, D1 database binding connectivity, and asset routing.

4.6 Pinned PR Comment Bot & Mobile QR Code Inspection

Section titled “4.6 Pinned PR Comment Bot & Mobile QR Code Inspection”

Reviewing a local business website on a desktop monitor does not reflect how >75% of local bakery or restaurant customers interact with the site. The PR comment bot generates a responsive preview table with an embedded QR code for mobile inspection:

<!-- siteswarm-preview-bot -->
## 🐝 SiteSwarm Ephemeral PR Previews
| Client Application | Ephemeral Preview URL | Edge Health | Mobile Inspection |
| :--- | :--- | :--- | :--- |
| **Green Leaf Bakery** (`apps/bakery`) | [View Live Preview](https://green-leaf-bakery-preview-pr-55.siteswarm.workers.dev) | 🟢 `200 OK` (Edge Healthy) | ![Scan on Phone](https://api.qrserver.com/v1/create-qr-code/?size=120x120&data=https%3A%2F%2Fgreen-leaf-bakery-preview-pr-55.siteswarm.workers.dev) |
*Commit: `a1b2c3d` • Deployed: 2026-09-29T21:45:00Z • Managed by SiteSwarm Fleet Automation*
  • Idempotency Guarantee: The bot searches existing comments for the <!-- siteswarm-preview-bot --> HTML comment marker. If found, it updates the existing comment in-place via octokit.rest.issues.updateComment, avoiding comment spam across commits.

4.7 Ephemeral Teardown Lifecycle (preview-teardown.yml #56)

Section titled “4.7 Ephemeral Teardown Lifecycle (preview-teardown.yml #56)”

To prevent resource sprawl and avoid hitting Cloudflare account limits, preview environments must be destroyed as soon as their pull request lifecycle concludes.

  • Trigger Event: pull_request: [closed] (handles both merges and manual closes).
  • Cleanup Actions:
    1. Computes the worker names associated with the PR number:
      Terminal window
      wrangler delete --name green-leaf-bakery-preview-pr-${PR_NUM} --force
    2. The --force flag ensures the command succeeds even if the worker was never provisioned or already removed.
    3. Updates the pinned PR comment to reflect decommissioned status:
      <!-- siteswarm-preview-bot -->
      ## 🐝 SiteSwarm Ephemeral PR Previews
      > 🧹 **Ephemeral Preview Decommissioned**
      > PR #${PR_NUM} has been merged/closed. All associated edge workers and preview routes were cleaned up at `2026-09-29T22:15:00Z`.

5. Edge Ingress Policies, Performance & Security Layer

Section titled “5. Edge Ingress Policies, Performance & Security Layer”

Every request traversing SiteSwarm’s edge ingress layer is governed by strict caching, transport, and security policies enforced at Cloudflare edge nodes before compute execution.

5.1 Cache Control & Dynamic Invalidation Matrix

Section titled “5.1 Cache Control & Dynamic Invalidation Matrix”

SiteSwarm employs a multi-tiered edge cache strategy designed for sub-10ms TTFB on static assets while preserving sub-second propagation for CMS updates:

Asset / Route Category Cache-Control Header Edge Cache TTL Invalidation Mechanism
Vite / Astro Bundled Assets (/_astro/*, /assets/*) public, max-age=31536000, immutable 1 Year Content-hash fingerprinting (instant on build)
Static Images & Fonts (/fonts/*, /favicon.ico) public, max-age=604800, stale-while-revalidate=86400 7 Days Stale-while-revalidate background refresh
Public HTML & SSR Pages (/, /menu, /about) public, max-age=0, must-revalidate, s-maxage=60, stale-while-revalidate=300 60 Seconds Edge Cache Purge via Cache-Tag on CMS publish
CMS Studio & Admin Routes (/_emdash/*, /admin/*) private, no-cache, no-store, must-revalidate 0 Seconds (Bypass) Always bypassed; dynamic D1 session validation
Dynamic Form APIs (/api/submit-inquiry) no-store, no-cache 0 Seconds (Bypass) Edge WAF & Turnstile evaluation

Astro SSR endpoints emit cache tags representing the tenant and application:

Cache-Tag: tenant-bakery, app-bakery-prod

When an owner clicks “Publish Now” in EmDash CMS, an internal event triggers a Cloudflare API call to purge the associated cache tag instantly:

Terminal window
curl -X POST "https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/purge_cache" \
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"tags": ["tenant-bakery"]}'

Cloudflare automatically inspects client Accept-Encoding request headers and negotiates next-generation compression algorithms:

  • Zstandard (zstd): Preferred for modern browsers supporting Zstandard compression (offering higher compression ratios and faster decompression times than gzip).
  • Brotli (br): Enabled globally for all text, HTML, CSS, JavaScript, and JSON payloads.
  • Gzip (gzip): Automatic fallback for legacy clients.

5.3 Modern Transport Protocols & TLS Configuration

Section titled “5.3 Modern Transport Protocols & TLS Configuration”

All Custom Hostnames and preview environments enforce modern, zero-latency transport standards:

  • HTTP/3 (QUIC): Enabled globally. Eliminates head-of-line blocking on flaky mobile cellular networks, ensuring instant page loads for restaurant customers on mobile data.
  • 0-RTT Connection Resumption: Supported with TLS 1.3 for repeat visitors.
  • Minimum TLS Version: TLS 1.2 (TLS 1.0 and 1.1 are permanently blocked at the edge).
  • Automatic HTTPS Rewrites: Automatically converts insecure http:// asset references to secure https://.

The SiteSwarm Ingress Router injects the following security headers on all responses:

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https: blob:; connect-src 'self' https://challenges.cloudflare.com; frame-src https://challenges.cloudflare.com;

Content Security Policy (CSP) Design Rationale:

Section titled “Content Security Policy (CSP) Design Rationale:”
  • Cloudflare Turnstile Support: Allows script, connect, and frame resources from https://challenges.cloudflare.com for spam-free form validation.
  • Google Fonts Support: Whitelists Google Fonts styles and fonts.
  • Strict Frame Sandboxing: Blocks clickjacking by restricting frame embedding to SAMEORIGIN.

Local business websites are prime targets for automated spam bots submitting spam inquiries via contact and quote forms. SiteSwarm deploys three edge defense layers:

  1. Cloudflare Turnstile Integration:
    • Frictionless, privacy-preserving CAPTCHA replacement.
    • Embedded seamlessly on contact and quote submission forms (/api/submit-inquiry).
    • Server-side token verification executed in Astro API endpoints against https://challenges.cloudflare.com/turnstile/v0/siteverify.
  2. Edge Rate Limiting Rules (Cloudflare WAF):
    • Configured on API endpoints: /api/* limited to 10 requests per 10 seconds per IP.
    • Violating IPs receive HTTP 429 Too Many Requests at the edge before hitting Worker compute.
  3. Cloudflare Managed Ruleset & Bot Fight Mode:
    • Automatically drops known bad bot signatures, malicious user agents, and credential-stuffing crawlers.

6. Operational Diagnostics, Edge Troubleshooting & Failure Modes

Section titled “6. Operational Diagnostics, Edge Troubleshooting & Failure Modes”

When domain routing or preview deployments fail, engineers and AI agents consult the following diagnostics matrix:

Failure Symptom Probable Cause Diagnostic Command / Action Remediation Runbook
ERR_SSL_VERSION_OR_CIPHER_MISMATCH on custom domain DCV validation pending or DNS CNAME not yet propagated. curl -Iv https://www.client.com
dig CNAME www.client.com
Check Cloudflare Custom Hostname status via API. If pending DCV, verify CNAME target matches fallback.siteswarm.dev. Wait 15-30 mins for DNS propagation.
Error 1000: DNS points to prohibited IP Client pointed domain to Cloudflare IP instead of CNAME, or Cloudflare-on-Cloudflare proxy loop. Inspect client DNS records via dig. Ensure client registrar has CNAME pointing to fallback.siteswarm.dev. If client uses Cloudflare DNS, ensure record is set to DNS Only (Grey Cloud).
Apex domain client.com not loading while www works Client registrar lacks CNAME flattening at apex root (@). Test root URL: curl -I https://client.com Configure registrar 301 URL redirect from @ to https://www.client.com, or switch to Namecheap/Cloudflare ALIAS record.
Ephemeral preview returns HTTP 404 Not Found Preview deployment failed or worker name mismatch in PR bot. Run wrangler tail <worker-name> or check GitHub Actions step logs. Verify change detection output: ensure app is listed in impacted_apps. Re-run preview-deploy.yml.
Edge Health Probe Timeout (HTTP 500) D1 database binding missing or migration not applied in preview environment. wrangler d1 info <db-name> Ensure preview environment configuration in wrangler.jsonc includes required D1/KV bindings or runs preview schema migrations.

7. Architectural Governance & Verification Gates

Section titled “7. Architectural Governance & Verification Gates”

Any pull request or architectural modification altering domain ingress, edge routing, or preview CI must pass the following verification gates:

  1. RFC-Compliant DNS Verification Gate: DNS onboarding runbooks must never instruct users to create an invalid apex CNAME without specifying ALIAS/ANAME flattening or 301 forwarding.
  2. Zero-Lockout & Non-Interference Gate: Changes to fallback origins or custom hostname API configurations must never impact existing active client domains.
  3. Resource Leak Gate: All ephemeral preview workflows (preview-deploy.yml) must have a corresponding, verified teardown step in preview-teardown.yml triggered on PR close.
  4. Zero-Secrets Safety Gate: CI workflows must cleanly handle unconfigured Cloudflare tokens without failing pull request builds for open-source contributors or local branches.