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
That platform choice is part of a longer migration: Azure, then Railway, then Cloudflare, across the whole estate. Read how and why.
Built with
- Cloudflare Workers
- Hono 4
- D1
- R2
- React 19
- Vite
- Tailwind
Hosted services
- D1
- R2
- Cron Triggers
- Cloudflare Access

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.mjsMeasured
- 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:rolloutMeasured
- Commit history
- 179 commits across roughly two months
Provenance
Source README.md, status banner
Measured
Screens




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