Cloudflare Architecture — CAC (LOCKED) Intent

Operator input → resolve config → generate PDFs → send email → write DB

Zero code change for new course/event/TP (data-only via KV/R2)

One worker serves infinite configs.

Workflow Resolve domain (Host → KV) Resolve event (URL → KV) Load all config (KV) Load templates/assets (R2) Decide docs Render PDFs (Puppeteer) Send email (Resend) Write D1

Execution Context domain:{host} → {tp_key, payment_paths[], grant_tp_key, owner_key} event:{course_id}:{slug} → full event course:{course_id} → course

Null → stop (404). No fallback.

Config Loads TP → from event or domain (grant path enforced) Owner → from domain Trainer → R2 PDF (required) Templates → R2 (required) Content → content:{course_id}.html (required)

Missing → throw

form hrdc output
corporate direct/grant proforma + schedule + trainer
corporate provider + HRDC guide
public grant same as corporate
public direct Stripe only

inject(html, vars) no undefined/null → use '' all vars resolved BEFORE inject

POST uses hidden fields Worker does NOT re-fetch KV on POST

PDF @cloudflare/puppeteer single browser sequential pages close before email save → proformas/{invoice}-{type}.pdf Email Resend API from: courses@claritysystems.work ONLY copy from KV (course.email_copy.*) never hardcode D1

Tables:

enquiries id, form_type, training_type, hrdc_option, status, name, email, phone, company, company_address, course_id, event_id, tp_key, pax, tentative_date, venue, message, created_at

email_events id, enquiry_id, resend_email_id, event_type, metadata_json, created_at

KV (LOCKED)

Patterns NEVER change:

domain:{host} event:{course_id}:{slug} course:{course_id} tp:{id} owner:{id} trainer:{id} invoice:last_number

Single namespace: CAC_KV

No new patterns without explicit decision

R2 (LOCKED) templates (html) content:{course_id} trainer PDFs HRDC guide output PDFs

Binding: ASSETS

Site Main (Eleventy) static reads KV at build all payment paths Subdomains (Worker) runtime render grant only minimal UI no Stripe Additions (NO CODE)

TP

add KV domain + tp add DNS

Course

add KV course upload content add Eleventy page

Event

add KV event Worker

Single worker

Routes:

GET /{slug} GET /{slug}/enquire POST /{slug}/enquire POST uses hidden fields only Constraints (ABSOLUTE) zero business data in worker KV patterns immutable one KV, one D1 never edit _site/ only counter: invoice:last_number always attach docs (grant path) email sender fixed no KV fetch on POST Open Decisions (BLOCK BUILD) KV field schema (inside objects) Eleventy KV fetch mechanism build trigger (CI/CD) worker merge Quality Gate

Reject output if:

requires code for data change adds hardcoded value breaks KV pattern needs multiple file edits for config asks user something already known

AI Behavior (MANDATORY) no explanations of options no unnecessary questions decide + execute minimal words, maximum action build at correct abstraction (data > code) prioritize compounding system design Deploy Constraints (LOCKED) Local machine: Node v18 — wrangler deploy blocked. Dashboard editor: blocks save on npm imports — never use for worker code. curl API: cannot bundle npm deps — does not work.

Worker deploy (ONLY valid path): git push to main → GitHub Actions → auto-deploys via Node 20 Workflow: .github/workflows/trainingbiz-admin-clerk.yml

Template/HTML deploy: Upload to R2 directly via Cloudflare dashboard. No wrangler needed.

Never suggest wrangler deploy locally, curl deploy, or dashboard editor for index.js.