The Cloud CRM is built on four architectural decisions that, together, define every other choice downstream. Each one is unusual enough on its own to deserve its own page. Together they explain why this CRM behaves differently than what the trades industry is used to from ServiceTitan, Jobber, or HouseCallPro.
The sixth idea — that any record type can be rendered as a table, kanban, gantt, card, or knowledge base — falls out of the first three. If the data model is flexible, the storage is isolated, and the API is the same for every consumer, then the rendering can be flexible too. ClickUp and Monday have proven the pattern; we apply it to a trades CRM.
How the pillars stack: the Data Model defines what a record is. The Per-Tenant D1 defines where it lives. The Headless API defines how every consumer reaches it. Offline Sync defines what happens when reaching it isn't possible right now. RBAC defines who's allowed to ask. Views define how the answer is rendered. Each pillar builds on the one above it; together they produce a system where adding a new client, a new field, or a new role is a small change instead of a project.
The diagram below traces one request from a mobile tech submitting a job photo all the way to the per-tenant D1 write. Most of the moving parts on this page are explained in detail on the other architecture docs — this is the map.
Trace of a single sync request:
Mobile collects local changes since last successful sync (entries in its SQLite changes table after last_acked_change_id).
It posts them to the Edge Worker along with its current head ID.
The Worker decodes the JWT to find org_id, queries the Control Plane D1 for the tenant's D1 binding, and routes the request to the Sync Worker.
The Sync Worker writes the incoming changes into the tenant D1's changes table, runs CRDT-style merge per record, flags any irreconcilable conflicts.
It responds with the list of server changes the mobile hasn't seen — same protocol, opposite direction.
A Durable Object holds a list of active web sessions for the tenant and pushes the merged state down to anyone watching in the Cloud CRM UI.
Trades businesses are paranoid — for good reason — about their customer lists. Physical database isolation means no possibility of cross-tenant data leakage from a forgotten WHERE clause. It also means tenant-level backups, restores, and exports are one-line operations. Scaling cost is roughly the same on Cloudflare; isolation is dramatically higher.
Why a flexible base-record model?
Every trades business has slightly different fields they care about. Pool guys track chemistry; roofers track pitch and material; HVAC tracks tonnage and refrigerant. Rather than ship a 60-column contacts table and let each tenant ignore most of it, the model lets each tenant define exactly the fields they need — and we ship sensible defaults per industry.
Why Git-style sync instead of last-write-wins?
Techs work on roofs without signal. They edit a job at 10am. The dispatcher edits the same job at 10:15. They both come back online at 11am. Last-write-wins means one of those edits is silently lost. Append-only change logs with field-level CRDT merges mean both edits survive in 95% of cases, and the 5% that genuinely conflict are surfaced for human resolution instead of silently destroyed.
Why team-based billing only?
Trades companies are teams. The owner pays, the techs use, the dispatcher coordinates, the bookkeeper invoices. A single-user license model creates orphan accounts and access-control nightmares every time someone is hired or quits. Teams-only with RBAC makes onboarding and offboarding a single admin action.
The other architecture pages dive into each pillar. Read in any order, but if you're new to the stack, the recommended path is Data Model → Multi-Tenant D1 → Headless API → Offline Sync → Teams & RBAC → Views. Each builds vocabulary that the next page uses freely.