apps/growth is a small internal SPA (Vite + React + Clerk, plain Tailwind — no
Catalyst) for the PolicyCodex team’s own sales operations: website leads, demo-call
bookings, and the state of the Resend nurture sequences. It is not customer-facing
and holds no tenant data — leads and email sends are internal sales-ops data.
C2 slice
Access model — the important part
- The tables growth reads (
website_leads,email_*) are service-role-only with no RLS policies. The SPA has no direct Supabase path to them;growth-apiis the one door. growth-apiverifies the Clerk session token in-function (the platform gateway cannot validate Clerk JWTs;verify_jwt = falseinconfig.toml), then requires the caller to be adomain_ownerof the internal org identified by theGROWTH_INTERNAL_CLERK_ORG_IDsecret.- Client-side,
SignedInonly means “has a Clerk session” — an unauthorized signed-in user gets a 403 from every fetch. This is deliberate and verified: sign-in cannot be app-restricted on the shared Clerk instance; the function is the boundary.
Surfaces
Deployment: git-connected Cloudflare Pages project
policycodex-growth, same pattern
as the other three SPAs — git push origin main is the deploy, nothing else. Local
dev: pnpm dev:growth (port 5175).
The email_* tables are filled by Resend — for the full inventory of templates,
automations and their triggering events, see
Resend environment (as-built).
