Bluekite is a single multi-tenant Cloudflare Worker that renders ~1000 customer sites from rows in a database. It is not a fleet of per-customer builds. Every architectural choice — bundling, versioning, deployment, observability — follows from that single shape.
| Concept | What it is | What it is not |
|---|---|---|
| Site | One row in the sites table + a chain of site_versions rows |
A separate Cloudflare Pages project |
| Version | One JSON snapshot of {layout_id, sections_json, business_data, theme, component_versions} at a point in time |
A Git branch, a deploy, or a bundle |
| Rollback | UPDATE sites SET active_version_id = $N — ~200ms, no rebuild |
A redeploy or git revert |
| Component | A TSX module in the Worker bundle, semver'd in source | A package on npm |
| Theme | A brand-token set referenced by business_data.theme |
A separate CSS bundle |
| Subsystem | Purpose | Status | Tech |
|---|---|---|---|
| Edge Render Worker | Resolves any tenant request (<tenant>.bluekite.co.za or custom domain) → DB row → rendered HTML |
Horizon 1 | Astro / Hono on Cloudflare Workers |
| Tenant Database | tenants, sites, site_versions, change_requests, traffic_buckets, users, magic_link_tokens |
Horizon 0 schema, Horizon 1 production | Cloudflare D1 |
| Asset Storage | Customer photos, snapshot thumbnails, brand assets | Horizon 0 scaffold | Cloudflare R2 |
| Component Library | All components in-Worker, additive prop contracts, source-level semver, snapshot migrations for breaking changes | Today: additive rule; Horizon 0: semver field + CI lint; Horizon 1: migrations | TSX in-repo, no npm |
| Admin Portal | Internal-only route. Magic-link auth. Customer queue, change-request workflow, manual deploys, metrics overview | Horizon 1 build | Astro + React islands |
| Customer Portal | Per-tenant authenticated route. Magic-link auth. Change-request log, traffic dashboards, rollback button, billing | Horizon 1–2 build | Same Astro app, scoped routes |
| Inbound WhatsApp Bridge | Receives customer messages, threads them to change-request queue, sends acknowledgements | Horizon 1 build | WhatsApp Business API + Worker webhook |
| Outbound Comms | Weekly traffic reports (WhatsApp primary, email fallback), change-request confirmations, magic-link delivery | Horizon 0: weekly report ships first | Resend (wired) + WhatsApp Business API |
| Analytics Pipeline | Cloudflare Web Analytics → D1 traffic buckets joined to version windows for per-version attribution | Horizon 0: read; Horizon 1: attribution | CF Web Analytics + cron Worker |
| CI / CD | GitLab CI → Cloudflare Workers. Layouts smoke test gates every preview before merge to main | Shipped | GitLab + wrangler + bun |
| Observability | Layouts smoke test, Worker logs, Cloudflare alerts → ops WhatsApp channel | Layouts smoke landed; expand Horizon 1 | bun + GitLab CI + WhatsApp |
The component library is not published to npm for the core platform. Reason: at multi-tenant scale, the consumer of the components IS the Worker itself. Exporting your own modules to import them right back adds friction without buying anything. Components stay in-repo, in-Worker, semver'd at source, pinned per-snapshot.
Three versioning concerns live separately:
| Concern | What it means | Decision | Cost |
|---|---|---|---|
| Version tracking (metadata) | Each site_versions row records {component_slug: version_at_snapshot_time} |
Day one. Cheap. Retrofitting later is brutal. | JSON field + CI assertion |
| Version discipline (source) | Each component has a version constant. Code change requires bump; CI enforces. |
Day one. ~50 lines of lint. | One hour to set up, ten seconds per change |
| Version coexistence (runtime) | hero@1.x AND hero@2.x both live, served per-tenant by snapshot pin |
Deferred. Use snapshot migrations instead — Worker always ships latest, migrations upgrade old snapshots at render time. | Reach for it only if customer demand for staying-pinned actually appears |
npm publication re-enters the conversation only if a separate agency-SDK product is launched (third-party developers building white-label Bluekite sites). That is a different product, not the core platform, and decisions about it should not constrain the multi-tenant Worker.
Bluekite is a managed-website service for South African SMBs. Customers pay a monthly fee; we build, host, run, and maintain their site. They WhatsApp changes; we apply them. The platform's job is to make that promise scale.
/florist, /school, /trades, /tutor — each themed and copy-customised for a sample business/api/quote lead-capture form with Resend email delivery to opsmain, preview deploy on every other branchbun run smoke:layouts --base <url>) — gates every merge by asserting hydrated chunks use the automatic JSX runtimeversion constant per component + a bun run validate-versions CI lint that fails on un-bumped changes. One hour of setup, day-one option-value preserved.site_versions table + one-click revert from the change-request logdata-theme attributes into a brand-token systemA Bluekite customer journey has three flows: prospect → paying customer → live site (onboarding), then live site ↔ change requests ↔ traffic insights (steady state), then optionally live site → rollback or version branch (correction). Each flow has a different mix of manual and automated touchpoints, and the mix shifts across horizons.
/api/quote form → Josh receives email/florist, /school, /trades, /tutor) into a new branchsrc/data/layouts.ts + business data, commits/api/quote → automated quote PDF + WhatsApp follow-uptenants row created/onboard/{token}site_versions[0] → Worker serves the live site instantly (no deploy, no CI)| Touchpoint | Today | Horizon 1 | Horizon 2 |
|---|---|---|---|
| Lead capture | manual triage | auto-quote, manual close | auto-close on low tier |
| Payment | manual EFT | webhook (Yoco / PayFast) | webhook |
| Site creation | manual code commit + deploy | self-serve form, instant publish | bulk API |
| Change requests | WhatsApp → manual code commit | WhatsApp → admin queue → one-click apply | inline approve / auto-apply for trusted patterns |
| Traffic insights | none | auto WhatsApp weekly report | per-segment reports |
| Rollback | git revert + redeploy | one-click in customer portal | one-click + scheduled (revert at 02:00) |
| Custom domain | manual DNS + Cloudflare config | guided self-serve | self-serve API |
| Billing | manual invoice | subscription auto-charge | seat-based agency billing |