Skip to main content

The system at a glance

The shape (unusual: no app server)

BROWSER (React SPA, Vite + TS + shadcn/ui)

│ hooks (useWorkflow, useChecklistOperations, useInquiryActions…)
│ hold the WORKFLOW LOGIC and query Postgres DIRECTLY

├──────────────── supabase-js (anon key) ───────────────┐
│ ▼
│ ┌───────────────────────────┐
│ │ SUPABASE │
│ │ │
│ Auth (JWT, email hooks) ◀───────────────│ Postgres (RLS policies) │
│ │ ~30 tables, enums, │
│ │ SECURITY DEFINER fns, │
│ │ triggers, PGMQ queues │
│ │ │
│ Storage (case-attachments, │ Storage buckets │
│ logos, avatars, email-assets) │ │
│ │ Edge Functions (Deno): │
└──── functions.invoke() ─────────────────▶│ generate-dossier, │
│ portal-access, │
EXTERNAL PARTY (no login) ──── /portal/:token ─▶│ invite-user, │
token → portal-access edge fn │ setup-company, │
│ process-email-queue, │
│ auth-email-hook, … │
└───────────────────────────┘

Lovable email API (proprietary)

The defining architectural fact: there is no traditional backend. The browser is the application layer. It runs the workflow engine and talks to the database directly with the public anon key. Edge functions exist only for the handful of operations that need the service-role key (bypassing RLS): assembling the dossier, provisioning tenants/users, the external portal, and the email queue.

This is fast to prototype and the single biggest structural problem to fix — see Client-side enforcement is the whole trust model.

Tech stack

LayerChoice
FrontendReact 18, Vite, TypeScript, React Router, shadcn/ui (Radix + Tailwind)
Server stateTanStack Query (React Query)
Forms / validationReact Hook Form + Zod
BackendSupabase — Postgres, Auth, Storage, Realtime, Edge Functions (Deno/TS)
Async / queuespg_cron + PGMQ queues; email via Lovable's API
Email templatesReact-Email (Portuguese)
MarketingRemotion (a 5-scene explainer video in remotion/)
TestsVitest + Testing Library — one placeholder test, no real coverage
OriginGenerated by Lovable; forked from a whistleblower template

The subsystems (each has a map)

SubsystemOne-lineDetail
Workflow engineClient-side 8-phase FSM with guards, gates, deadlines
TenancyCompany-scoped via company_memberships + RLS
AuditTwo parallel trails + auth log
DeliverablesTwo subsystems (witness vs generic) writing JSONB
DossierEdge function assembles a ZIP with hashes
External portalToken access for the arguido
EmailPGMQ queue, Lovable-coupled
Storage4 buckets; case-attachments holds everything

Data model in one breath

~30 Postgres tables around a central cases row: identity/tenancy (profiles, companies, company_memberships), the case and its children (messages, attachments, team, response), workflow (workflow_transitions, case_checklist_items, case_gates, case_deadlines, inquiry_actions, case_phase_deliverables), evidence + audit + dossier, the portal tables, and email infrastructure. Full map: Key tables.

How a typical action flows (example: advance a phase)

user clicks "Advance" → useWorkflow.executeTransition (browser)
├─ validate: allowed transition? checklist approved? gate passed? deadlines? (all JS)
├─ INSERT workflow_transitions ┐
├─ UPDATE cases (status + dates) │ seven separate writes,
├─ complete old deadlines │ NOT in a transaction
├─ create next-phase checklist │ (see non-atomic-transitions)
├─ auto-create gates │
├─ upsert next-phase deadlines │
└─ INSERT case_audit_events ┘ (fire-and-forget)

That this critical operation is seven un-transacted, un-authorized-server-side writes is the kind of thing the Tacit Assumptions section catalogues.