Angel Campa
Colour theme

WorkShared infrastructure, then teardown

Ventora CRM

Tracked every customer who touched any product across the portfolio, collected their testimonials into embeddable widgets, and made sure two competing products could never share a customer.

Ran on
Cloudflare Workers
Ran
May 2026 to July 2026
Domain
Internal CRM and embeddable testimonials, shared across a product portfolio
Source
multi-product-crm-ventora-snapshot

Built with

  • Cloudflare Workers
  • Hono 4
  • D1
  • R2
  • React 19
  • Vite
  • Tailwind

Hosted services

  • D1
  • R2
  • Cron Triggers
  • Cloudflare Access
Admin overview running against the local demo database: per-product readiness cards for four products with customer, testimonial and feedback counts, and a work queue of items awaiting moderation, under an orange banner reading DEMO DATA, fictional companies, local database, not real customers
The admin overview, captured from the local stack against seeded demo data: every company and testimonial visible anywhere in this repository is fictional. The orange banner is stamped onto every screenshot by the capture harness itself.

The decision

Ventora CRM’s hardest rule was that a customer could never be reachable from two competing products at once. One product in the portfolio worked a commercial lease dispute from the tenant’s side and another worked the same kind of dispute from the landlord’s side, so a mixed record in one shared customer database was a real conflict of interest.

The obvious fix was a check in the service layer, but a customer could become associated with a product through four different tables (an explicit link, a testimonial, a review, or a feedback item), and a single service-layer check was one forgotten write path away from being quietly wrong forever.

So the invariant moved into the database itself: thirteen BEFORE INSERT / BEFORE UPDATE triggers across five tables enforced the conflict-of-interest firewall on every write path, in the database rather than in the service layer, including one on the products table itself that closed the gap the other four would otherwise leave open when a product’s group changed rather than its associations. Nothing could violate the rule after that: not a migration, not a repair script, not a stray manual query.

The design was verified rather than asserted: a script wrote real SQL to a real database, attempted an actual violation, and asserted that it aborted. TypeScript still checked the rule too, because a database trigger cannot produce an error message a non-engineer can act on. The trigger was the invariant; the TypeScript was the user experience.

Architecture

One Cloudflare Worker served four route buckets across three authentication mechanisms, and the order requests were matched in was itself load-bearing: because Static Assets was configured with an SPA fallback, a request to a path that didn’t exist would otherwise have returned the admin app’s HTML with a 200. Ten explicit guards closed that gap, forcing the correct 404 on every reserved prefix.

Five embeddable widgets were server-rendered to HTML and CSS strings inside the Worker, then injected into a Shadow DOM by a small loader script: no framework shipped to the host page, and the host page’s own CSS could not reach inside the widget. The same brand-token system that drove the admin app’s own theme drove every widget’s appearance per product, so adding a product to the portfolio was a data change.

Verification

verify:rollout chained fourteen checks into one command ahead of every deploy. It ran lint, typecheck, the coverage suite, a full local migrate/seed/retire/origins cycle, the firewall and media verifiers, the end-to-end suite, a dry-run deploy, and an audit pass. deploy was defined as verify:rollout followed by the retired-products cleanup and the staged rollout itself, so the gate sat directly in the path to production rather than beside it.

The coverage floor inside that gate was scoped rather than global: 95% applied to the seven modules where a bug would cause an auth bypass, a data-isolation violation, or silent data loss, so a well-tested module elsewhere could not compensate for an untested one in scope. Admin component tests ran on a separate command, test:admin, outside verify:rollout.

By the numbers

Each figure carries where it came from and when it was measured. Open one to see the command behind it, where the repository recorded a command.

Lines of code
35,870 across 192 files
Provenance

Source portfolio/METRICS.md § Size

node scripts/repo-metrics.mjs

Measured

Application vs test code
13,971 / 13,844 lines (1.01:1)
Provenance

Source portfolio/METRICS.md § By role

Measured

Tests
573 cases across 60 files (450 unit, 83 admin component, 40 e2e), plus a 29-spec screenshot suite run outside the gate
Provenance

Source portfolio/METRICS.md § Tests

Measured

Coverage gate
95% lines/functions/branches/statements on 7 security-critical modules, where a bug causes an auth bypass, a data-isolation violation, or silent data loss
Provenance

Source portfolio/METRICS.md § Tests

Measured

Database
14 tables, 17 triggers, 20 indexes in the live schema, counted by replaying migrations/ in order and asserted against a real database by scripts/verify-migration-state.ts
Provenance

Source portfolio/METRICS.md § Database

Measured

API surface
53 endpoints, 10 explicit 404 guards, 1 SPA fallback route
Provenance

Source portfolio/METRICS.md § HTTP surface

Measured

Deploy gate
14 automated checks (lint, typecheck, coverage, a full local migrate/seed/retire/origins cycle, seven verifier scripts, e2e, a dry-run deploy, npm audit) run in one command
Provenance

Source README.md § Testing

npm run verify:rollout

Measured

Commit history
179 commits across roughly two months
Provenance

Source README.md, status banner

Measured

Screens

Customers table: twelve fictional people at companies including Acme, Globex, Initech, Vandelay, Hooli and Cyberdyne, each row showing a lifecycle badge of Lead, Active or Champion and a PRODUCTS column of per-product tags such as GrantPipe, Ventora CRM and CAMAudit
One row per person, with a PRODUCTS column listing every product they touched: exactly what the conflict-of-interest firewall constrained.
Feedback board filtered to CAMAudit, a six-column board scrolled to show New, Triaged, Planned and In Progress, holding cards tagged Feature Request, Bug or General, each with a vote count and the requesting customer's name
Feature requests and bugs moved across a six-column drag-and-drop board, filtered per product.
wall-grid widget embedded on a plain third-party-style host page (tests/fixtures/embed-sandbox.html): five fictional testimonials in a grid, each with an attribution and a source tag reading EMAIL, MANUAL or WIDGET, most also carrying a star rating. No admin chrome, no preview banner
The widget with no sandbox around it: a plain host page, the real script loader tag, and a Shadow DOM the host page's CSS could not see into.
Settings page: a product table listing CAMAudit, GrantPipe, Ventora CRM and Floriva with masked widget keys and origin counts, a widget preview picker, and the generated one-line script tag for embedding wall-grid
Everything a product needed to embed a widget lived on one row: the widget key, the origin allowlist, and the generated script tag.
Read the repositorymulti-product-crm-ventora-snapshot

Its portfolio/ directory holds the architecture, metrics and testing documents these figures were taken from.