Multi-Worker Platform Services & Service Binding Topology Specification
Multi-Worker Platform Services & Service Binding Topology Specification
Section titled “Multi-Worker Platform Services & Service Binding Topology Specification”Document Status: Living True Specification (Single Source of Truth)
Authority: Core Systems & Infrastructure Architecture
Related Epics: #101 (Capability Governance & Platform Services), #34 (Client App Anatomy), #35 (Capability Package Anatomy)
Related Documents: HIGH_LEVEL_DESIGN.md, docs/DATA_ISOLATION_AND_STORAGE.md, docs/CAPABILITY_MANAGEMENT.md
1. Executive Summary & Design Rationale
Section titled “1. Executive Summary & Design Rationale”In SiteSwarm, client applications must never become monolithic, bloated services that bundle shared infrastructure, heavy background workers, or multi-tenant database credentials.
To preserve Maximum Server Uptime and Single-Tenant Blast Radius Containment, the platform explicitly rejects a monolithic “god worker”. Instead, SiteSwarm deploys specialized, domain-oriented platform workers connected via zero-latency Cloudflare Service Bindings:
flowchart TD subgraph ClientEdge["Client Edge Tier (Independent Workers)"] BakeryApp["apps/bakery\n(Worker A: Static Frontend)"] AgencyApp["apps/software-agency\n(Worker A: Static Frontend)"] AutoApp["apps/auto-repair\n(Worker A: Static Frontend)"] end
subgraph ServiceMesh["Cloudflare Service Binding Mesh (0ms In-Memory RPC)"] LeadsWorker["services/leads-service\n• Turnstile & Honeypot Validation\n• SMS / Email / Webhook Ingestion\n• Rate Limiting & Antispam"] CMSWorker["services/cms-service\n• Multi-Tenant D1 / KV Engine\n• Live Hours & Emergency Overrides\n• Fallback Seed Replicas"] MonitorWorker["services/synthetic-monitoring\n• Scheduled Cron Health Probes\n• Uptime Telemetry & PagerDuty Rollup"] AnalyticsWorker["services/analytics-service\n• Zero-Cookie Edge Pageview Stats\n• Privacy-Preserving Traffic Aggregation"] end
BakeryApp -->|"env.LEADS_SERVICE.fetch() (tenantId: 'bakery')"| LeadsWorker AgencyApp -->|"env.LEADS_SERVICE.fetch() (tenantId: 'software-agency')"| LeadsWorker AutoApp -->|"env.LEADS_SERVICE.fetch() (tenantId: 'auto-repair')"| LeadsWorker
BakeryApp -.->|"env.CMS_SERVICE.fetch()"| CMSWorker AutoApp -.->|"env.MONITOR_SERVICE"| MonitorWorker2. Decoupled Platform Service Boundaries
Section titled “2. Decoupled Platform Service Boundaries”Platform services are partitioned by domain responsibility, strictly isolating high-risk dependencies, external credentials, and database access:
2.1 services/leads-service
Section titled “2.1 services/leads-service”- Domain: Lead intake, antispam verification, routing, and delivery.
- Responsibilities:
- Validates Cloudflare Turnstile tokens against
challenges.cloudflare.com. - Enforces client-specific honeypot traps and silently absorbs bot requests without polluting storage.
- Rate-limits abusive client IPs at the edge before storage operations.
- Dispatches notifications via Twilio (SMS), SendGrid/Resend (Email), and HTTP webhooks.
- Stores leads into partitioned D1 lead repositories when
storeSubmissions: trueis configured inswarm.config.ts.
- Validates Cloudflare Turnstile tokens against
- Security Boundary: Third-party API keys (
TWILIO_AUTH_TOKEN,RESEND_API_KEY,TURNSTILE_SECRET_KEY) reside only inleads-serviceenvironment secrets, never in client frontend bundles.
2.2 services/cms-service
Section titled “2.2 services/cms-service”- Domain: Dynamic content, business hours overrides, emergency alerts, and daily announcements.
- Responsibilities:
- Serves multi-tenant dynamic configuration from dedicated Cloudflare D1 tables and KV caches.
- Provides zero-lockout fallback seeds: if D1 or KV is unreachable, returns the static snapshot declared in the client manifest.
- Provides administrative REST & RPC endpoints for EmDash Studio.
2.3 services/synthetic-monitoring
Section titled “2.3 services/synthetic-monitoring”- Domain: Edge probe scheduling, health aggregation, and alerting.
- Responsibilities:
- Scheduled Cloudflare Cron Triggers (
*/5 * * * *) pinging public client URLs. - Asserts expected HTTP status codes, response times (<500ms), and critical DOM elements.
- Directly pings PagerDuty or Slack webhooks upon 3 consecutive probe failures.
- Scheduled Cloudflare Cron Triggers (
2.4 services/analytics-service
Section titled “2.4 services/analytics-service”- Domain: Privacy-preserving edge traffic and performance telemetry.
- Responsibilities:
- Collects beacon telemetry without third-party tracking cookies or GDPR consent banners.
- Aggregates pageviews, device classes, and edge pop locations into Cloudflare Analytics Engine.
3. Worker vs. Package Decision Matrix
Section titled “3. Worker vs. Package Decision Matrix”When adding platform capabilities, engineers and AI agents must follow this strict decision heuristic:
| Characteristic | Headless Package (packages/capabilities/*) |
Dedicated Platform Worker (services/*) |
|---|---|---|
| Execution Context | In-process within client app build / SSR | Independent Cloudflare Worker isolate |
| Secrets / API Keys | Zero platform secrets (pure client configuration) | Holds sensitive credentials (Twilio, Resend, Turnstile) |
| Compute Overhead | Negligible CPU (math, schema generation, AST) | Async I/O, webhook retries, queue consumers |
| Storage Access | No direct database credentials | Owns partitioned D1 database or KV bindings |
| Examples | @siteswarm/seo, @siteswarm/quote-estimator |
services/leads-service, services/cms-service |
4. Cloudflare Service Binding Contracts & Topology
Section titled “4. Cloudflare Service Binding Contracts & Topology”4.1 Client Configuration (wrangler.jsonc)
Section titled “4.1 Client Configuration (wrangler.jsonc)”Client applications declare zero-latency service bindings in their edge configuration:
{ "name": "client-bakery", "services": [ { "binding": "LEADS_SERVICE", "service": "siteswarm-leads-service" }, { "binding": "CMS_SERVICE", "service": "siteswarm-cms-service" } ]}4.2 Standardized Tenant Context Injection
Section titled “4.2 Standardized Tenant Context Injection”Every cross-worker RPC or fetch call injected over a Service Binding must pass the authoritative tenantId extracted from swarm.config.ts:
// Client-side edge dispatchconst response = await env.LEADS_SERVICE.fetch(request, { headers: { "X-SiteSwarm-Tenant-Id": manifest.appId, "X-SiteSwarm-Capability-Version": manifest.capabilities["lead-capture"].version, },});4.3 Multi-Tenant Schema Partitioning
Section titled “4.3 Multi-Tenant Schema Partitioning”Shared services use tenant-isolated column keys in SQLite D1:
CREATE TABLE IF NOT EXISTS leads ( id TEXT PRIMARY KEY, tenant_id TEXT NOT NULL, payload JSON NOT NULL, turnstile_verified INTEGER NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP);CREATE INDEX IF NOT EXISTS idx_leads_tenant ON leads(tenant_id, created_at DESC);All queries executed by services/leads-service strictly scope their WHERE clauses by tenant_id = :tenantId.
5. Client SDK Architecture (@siteswarm/sdk)
Section titled “5. Client SDK Architecture (@siteswarm/sdk)”To decouple client application developers from raw HTTP/Service Binding mechanics, the platform exposes a strongly typed client SDK:
import { createPlatformClient } from "@siteswarm/sdk";import manifest from "../swarm.config";
export const platform = createPlatformClient({ appId: manifest.appId, bindings: env, // Local development mock provider when running outside Cloudflare workerd mockFallback: process.env.NODE_ENV === "development",});
// Strongly typed dispatchconst result = await platform.leads.submit({ name: "Jane Doe", email: "jane@example.com", turnstileToken: token,});6. Edge Route Delegation Pattern
Section titled “6. Edge Route Delegation Pattern”For high-throughput endpoints (e.g. /api/leads/*), client applications can delegate the entire route subtree to the dedicated capability worker directly in their edge routing middleware:
// apps/*/src/pages/api/leads/[...path].tsimport type { APIRoute } from "astro";
export const ALL: APIRoute = async ({ request, locals }) => { const leadsService = locals.runtime.env.LEADS_SERVICE; if (!leadsService) { return new Response(JSON.stringify({ error: "Service unavailable" }), { status: 503 }); } return leadsService.fetch(request);};This ensures client applications remain 100% focused on presentation and static assets, offloading all stateful logic to the platform mesh.