Platform Architecture
OpenInsure is built on a deliberately layered architecture that separates hot-path latency-sensitive operations (the Cloudflare edge) from authoritative system-of-record data (PlanetScale / Vitess 22.0). This document explains how those layers interact, how data flows from submission to bound policy, and how HIPAA-regulated data is isolated throughout.
Architecture Overview
Section titled “Architecture Overview” ┌────────────────────────────────────────┐ │ Cloudflare Global Network │ │ │ Browser / API Client │ ┌─────────────┐ ┌────────────────┐ │ ─────────────────────► │ │ CF Worker │ │ Durable Object │ │ │ │ (Hono API) │ │ (UW Workbench)│ │ │ └──────┬──────┘ └───────┬────────┘ │ │ │ │ │ │ ┌──────▼──────┐ ┌───────▼────────┐ │ │ │ CF D1 │ │ CF KV Cache │ │ │ │ (edge cfg) │ │ (sessions, │ │ │ └──────┬──────┘ │ non-PHI) │ │ │ │ └────────────────┘ │ │ ┌──────▼──────┐ │ │ │ CF Hyperdrive│ │ │ │(conn pool) │ │ └──┼─────────────┼──────────────────────┘ │ │ ┌──────▼─────────────▼──────┐ │ PlanetScale (Vitess 22.0) │ │ System of Record (PHI OK) │ │ App-level tenant isolation │ │ org_id scoping │ └───────────────────────────┘
┌──────────────────────────────┐ │ Cloudflare R2 │ │ (documents, COIs, loss runs) │ └──────────────────────────────┘
┌──────────────────────────────┐ │ Workers AI │ │ Llama 3 — ACORD extraction │ │ and loss run analysis │ └──────────────────────────────┘
┌──────────────────────────────┐ │ Auth Worker (oi-sys-auth) │ │ User auth, sessions, orgs │ │ Cloudflare D1 (oi-auth) │ └──────────────────────────────┘
┌──────────────────────────────┐ │ TigerBeetle (Fly.io) │ │ Double-entry financial ledger│ │ via HTTP proxy │ └──────────────────────────────┘Financial transactions (premium splits, claim payments, reserve adjustments) are recorded in TigerBeetle, a purpose-built double-entry accounting database. The API Worker calls TigerBeetle through LedgerClient and TigerBeetleRemoteClient, which communicate with an HTTP proxy running on Fly.io (openinsure-tigerbeetle, shared-cpu-2x, 4 GB RAM, iad region). All monetary amounts are stored and transmitted as integer cents for precision — no floating-point arithmetic touches financial data.
Auth Architecture
Section titled “Auth Architecture”The Auth Worker (oi-sys-auth) is a fully separate Cloudflare Worker from the API worker (oi-sys-api). This separation means authentication concerns never share compute or binding scope with business logic.
Browser / Portal │ │ POST /api/auth/sign-in/email (or Microsoft SSO redirect) ▼┌─────────────────────────────────────┐│ Auth Worker (oi-sys-auth) ││ Cloudflare D1: oi-auth ││ Tables: user, session, ││ account, verification ││ Deployed: auth-dev.openinsure.dev │└──────────────┬──────────────────────┘ │ Issues HS256 JWT │ { sub, org, role, iat, exp } 8 hr TTL ▼ Portal /api/auth/callback Sets portal-specific cookie (oi_uw_token / oi_admin_token / …) │ │ Authorization: Bearer <JWT> ▼┌─────────────────────────────────────┐│ API Worker (oi-sys-api) ││ Validates JWT → extracts org/role ││ Checks SpiceDB ReBAC permissions ││ Proxies to PlanetScale via Hyperdrive │└─────────────────────────────────────┘Microsoft SSO is handled inside the auth worker via an Azure AD single-tenant app registration (AzureADMyOrg, tenant ebd58a52-c818-4230-b150-348ae1e17975). The IaC for the Azure app registration lives in infra/azure-auth/main.tf (OpenTofu + azuread provider). Known mhcis.com users are automatically granted org_admin on first login.
For full details, see the Authentication reference.
Phase 2 Features — Deal Rooms & Authority Matrix
Section titled “Phase 2 Features — Deal Rooms & Authority Matrix”Phase 2 added real-time collaborative underwriting infrastructure on top of the base platform.
Deal Rooms (DealRoomDO)
Section titled “Deal Rooms (DealRoomDO)”DealRoomDO is a Durable Object (new_sqlite_classes) that backs the real-time underwriting deal room. Each deal room maintains a persistent WebSocket hub where multiple underwriters can collaborate on a single submission simultaneously.
Database tables added in Phase 2:
| Table | Purpose |
|---|---|
deal_rooms | Deal room records linked to a submission |
deal_room_participants | Participants and their roles |
deal_room_messages | Chat message history |
deal_room_annotations | Document annotations |
API routes: GET/POST /v1/deal-rooms, GET /v1/deal-rooms/:id (WebSocket upgrade).
UW Workbench pages: /deal-rooms, /deal-rooms/[id].
Authority Matrix & Referrals
Section titled “Authority Matrix & Referrals”The authority matrix defines per-user underwriting authority limits by line of business and limit tier. When a submission exceeds a user’s authority, the system automatically creates a referral request.
Database tables:
| Table | Purpose |
|---|---|
authority_profiles | Authority limit definitions per LOB/tier |
user_authority_assignments | Maps users to authority profiles |
referral_rules | Auto-escalation thresholds |
referral_requests | Active referral queue |
referral_activities | Referral audit trail |
API routes: /v1/authority, /v1/referrals.
UW Workbench pages: /referrals, /admin/authority.
The Edge Layer (Cloudflare Workers)
Section titled “The Edge Layer (Cloudflare Workers)”Every API request hits a Cloudflare Worker running the Hono web framework. Workers execute in V8 isolates at the Cloudflare PoP closest to the client — typically within 5–50ms of any location on Earth.
What the Worker Does
Section titled “What the Worker Does”- Authenticates the request — validates an OpenInsure JWT (issued by the auth worker, portal token endpoints, or API secret flow) and extracts
org_id,user_id, androle. - Authorizes the action — checks SpiceDB ReBAC policies for relationship-based permissions.
- Reads edge state — quota limits, rate tables, and DA agreement configs are cached in CF D1 (SQLite at the edge) for sub-millisecond reads without hitting origin PlanetScale.
- Runs business logic — policy state machine transitions, rating calculations, and compliance rule evaluations run entirely in the Worker.
- Persists to PlanetScale — authoritative state (policy records, claim records, PHI) is written to PlanetScale via CF Hyperdrive in deployed environments, with direct
DATABASE_URLconnections in local/CI workflows. - Emits events — domain events (
policy.bound,claim.filed) are published to a queue for async processing (webhooks, notifications, bordereaux updates).
Durable Objects — Real-Time Coordination
Section titled “Durable Objects — Real-Time Coordination”OpenInsure uses Durable Objects extensively for stateful, real-time workloads. All DOs use new_sqlite_classes (SQLite-backed state):
| Durable Object | Purpose |
|---|---|
SubmissionAgent | Per-submission AI triage and extraction state |
PolicyAgent | Policy event sourcing and real-time status |
ClaimAgent | Claim lifecycle state machine with WebSocket updates |
DealRoomDO | Real-time underwriting deal room — WebSocket chat + annotations |
NotificationAgent | Per-user notification queue with delivery tracking |
OpenInsureMCP | Model Context Protocol server for AI tool integrations |
Browser (UW 1) ─────────────────────┐ ▼Browser (UW 2) ──────────► DealRoomDO (room_id) │ SQLite stateBrowser (UW 3) ─────────────────────┘ + WebSocket broadcastData Flow: Submission → Quote → Bind
Section titled “Data Flow: Submission → Quote → Bind”Step 1 — Submission Intake
A producer uploads an ACORD 125 PDF (or submits a structured JSON payload) to POST /v1/submissions.
If a PDF is provided, the Worker forwards it to Workers AI (Llama 3 multimodal), which returns structured JSON matching the Submission TypeSpec schema. The extracted data is stored in PlanetScale with status: 'draft'.
POST /v1/submissionsContent-Type: multipart/form-data
file=@acord125.pdfResponse:
{ "id": "sub_01J8K3M4N5P6Q7R8S9T0", "status": "draft", "extracted": { "insuredName": "Acme Manufacturing Co.", "naicsCode": "332710", "annualRevenue": 4500000, "requestedLimit": 1000000, "effectiveDate": "2025-01-01" }, "confidence": 0.94}Step 2 — Rating
POST /v1/submissions/:id/quote triggers the rating engine in @openinsure/rating.
The engine:
- Looks up the applicable rate table by
line_of_business+program_idfrom CF D1. - Applies factors in order: base rate → state modifier → class modifier → revenue band → loss history → schedule rating.
- Logs every factor and intermediate result to
rating_steps[]in the response. - Returns a
QuotewithgrossPremium,netPremium, andfees.
The quote is stored in PlanetScale with status: 'quoted' and a quote_expires_at timestamp (typically 30 days).
Step 3 — Bind
POST /v1/submissions/:id/bind is the most guarded operation in the system.
Pre-bind checks (all must pass):
- Quote freshness —
quote_expires_atmust be in the future. - DA limit check — The Worker reads the organization’s DA agreement from CF D1 and verifies the quote does not exceed per-policy or aggregate limits.
- State eligibility — The risk state must be in the DA’s allowed states list.
- NAICS exclusion — The insured’s industry code must not appear in the excluded classes list.
- SpiceDB authorization — The requesting user must have the
bindpermission for this risk class and limit.
If all checks pass, the Worker atomically:
- Creates a
Policyrecord in PlanetScale withstatus: 'bound' - Creates the billing schedule (installment plan)
- Sends a
policy.bounddomain event - Generates the declarations page PDF via
@openinsure/documents - Triggers the COI generation workflow if applicable
HIPAA Data Isolation Layers
Section titled “HIPAA Data Isolation Layers”OpenInsure handles Protected Health Information (PHI) for health insurance programs, workers’ compensation, and FHIR-integrated workflows. PHI isolation is enforced at four distinct layers.
Layer 1 — Field Tagging
Section titled “Layer 1 — Field Tagging”Every database column that may contain PHI is tagged in the TypeSpec schema with @phi. The @openinsure/hipaa package maintains a registry of tagged fields:
export const PHI_FIELDS = new Set([ 'claims.claimant_name', 'claims.claimant_dob', 'claims.diagnosis_codes', 'claims.treatment_notes', 'members.ssn', 'members.health_conditions',]);Layer 2 — Storage Isolation
Section titled “Layer 2 — Storage Isolation”- PHI fields are never written to CF D1 (edge SQLite) or CF KV (in-memory cache).
- PHI lives exclusively in PlanetScale with app-level tenant isolation.
- Privileged write credentials are never exposed to browser clients. Worker/server paths use scoped secrets and direct database access with auditable service identities.
Layer 3 — Runtime Redaction
Section titled “Layer 3 — Runtime Redaction”When a Worker serializes a database record into an API response, the @openinsure/hipaa redact() middleware checks the requesting user’s role against the PHI access matrix:
| Role | PHI Access |
|---|---|
producer | No PHI — claimant fields are masked as [REDACTED] |
adjuster | Full PHI access for assigned claims only |
underwriter | Aggregate stats only (no individual PHI) |
claims_supervisor | Full PHI access for all org claims |
compliance_officer | Audit log access, no PHI values |
admin | Full access with mandatory MFA |
Layer 4 — Immutable Audit Log
Section titled “Layer 4 — Immutable Audit Log”Every read or write to a PHI-tagged field appends a record to the phi_audit_log table:
CREATE TABLE phi_audit_log ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), org_id uuid NOT NULL, user_id uuid NOT NULL, action text NOT NULL, -- 'read' | 'write' | 'redact' table_name text NOT NULL, record_id uuid NOT NULL, fields text[] NOT NULL, ip_address inet, user_agent text, created_at timestamptz NOT NULL DEFAULT now());This table is append-only — no UPDATE or DELETE is permitted even for admin roles. It is partitioned by month and retained for 7 years per HIPAA requirements.
Multi-Tenancy Model
Section titled “Multi-Tenancy Model”OpenInsure is a hard multi-tenant system. Every table that contains customer data has an org_id column, and app-level tenant isolation (orgId scoping) enforces that queries can only return rows matching the authenticated user’s organization.
All queries run inside a transaction that applies the orgId filter. The org_id is extracted from the validated JWT claim app_metadata.org_id and applied to every query by the Worker.
Edge vs. Origin Data Split
Section titled “Edge vs. Origin Data Split”| Data Type | Storage Location | Rationale |
|---|---|---|
| Rate tables | CF D1 | Hot-path read on every quote — must be sub-millisecond |
| DA agreement configs | CF D1 | Pre-bind check — must be co-located with Worker |
| Session tokens | CF KV | Fast validation, short TTL |
| Non-PHI policy metadata | CF D1 (cache) + PlanetScale (SOR) | Read from edge, write through to origin PlanetScale |
| PHI fields | PlanetScale only | HIPAA — never at edge |
| Policy records | PlanetScale | Authoritative SOR |
| Claims records | PlanetScale | Authoritative SOR |
| Documents / PDFs | Cloudflare R2 | S3-compatible, global CDN |
| Workers AI embeddings | Vectorize (CF) | Similarity search for ACORD extraction |
AI Integration
Section titled “AI Integration”OpenInsure uses two AI tiers for different workloads:
Workers AI (Edge GPU — Llama 3.1 8B)
Section titled “Workers AI (Edge GPU — Llama 3.1 8B)”-
ACORD Extraction — Converts unstructured PDF submissions into structured
Submissionobjects. The prompt is templated inpackages/acord/src/extractor.ts. Confidence scores below 0.85 trigger a manual review flag. -
Loss Run Analysis — Parses loss run PDFs from prior carriers and extracts structured claim history (date, paid, reserve, status) for use in rating. The
@openinsure/loss-runspackage handles normalization across different carrier formats.
Claude (Anthropic — via @openinsure/agents)
Section titled “Claude (Anthropic — via @openinsure/agents)”Higher-capability reasoning tasks are routed to Claude via the @openinsure/agents package using @tanstack/ai:
- Risk narrative generation —
computeAiRiskNarrative()produces structured underwriting summaries ({ narrative, severity, topSignal }) using Claude claude-haiku-4-5 for speed. - AI Copilot — Streaming chat interface in the Underwriting Workbench for policy analysis and referral drafting.
- Deal room intelligence — Context-aware suggestions based on deal room message history.
AI calls are traced in Langfuse via setupTanStackAiObservability() and evaluated with promptfoo in the evals/ directory.
Financial Ledger (TigerBeetle)
Section titled “Financial Ledger (TigerBeetle)”All monetary movements (premium payments, commission splits, reserve allocations) flow through a TigerBeetle double-entry ledger (TIGERBEETLE_URL + TIGERBEETLE_API_KEY). TigerBeetle provides ACID guarantees and sub-millisecond ledger writes. PlanetScale is the system of record for business data; TigerBeetle is the system of record for financial balances.
TigerBeetle runs on Fly.io (openinsure-tigerbeetle, shared-cpu-2x, 4 GB, iad) behind an HTTP proxy (infra/tigerbeetle/proxy.ts). The API Worker communicates with TigerBeetle via LedgerClient / TigerBeetleRemoteClient. Each organization is provisioned with 9 account types:
| Code | Account Type | Purpose |
|---|---|---|
| 1 | CARRIER_PAYABLE | Net premium owed to the carrier |
| 2 | MGA_FIDUCIARY | Hub account for premium receipts |
| 3 | MGA_REVENUE | MGA commission and fee income |
| 4 | PRODUCER_PAYABLE | Commissions owed to producers |
| 5 | TAX_AUTHORITY_PAYABLE | Premium taxes owed to state authorities |
| 6 | LOSS_FUND | Captive loss fund assets |
| 7 | CLAIMS_PAID | Cumulative claim disbursements |
| 8 | RESERVES | Outstanding loss reserves |
| 9 | REINSURER_PAYABLE | Reinsurance premiums owed to reinsurers |
Transfer codes classify every ledger movement: PREMIUM_PAYMENT(101), COMMISSION_SPLIT(102), TAX_SPLIT(103), FEE_SPLIT(104), and SETTLEMENT(201).
Operations exposed through the ledger API: splitPremiumPayment, getAccountBalance, lookupTransfers, getAccountTransfers, getTrialBalance, getJournalEntries, recordInstallmentPayment, and recordClaimTransaction (payment, recovery, reserve adjustment, reinsurance recovery).
Frontend Architecture
Section titled “Frontend Architecture”OpenInsure runs three distinct sites and five Next.js portals:
| App | Audience | Hosting | Stack |
|---|---|---|---|
openinsure.dev (landing) | Public | Cloudflare Pages (oi-sys-landing) | Astro 5 + React 19 + GSAP + Lenis |
oi-sys-preview.pages.dev (preview) | Public | Cloudflare Pages (oi-sys-preview) | Astro 5 + React 19 + Tailwind v4 |
pushdown.ai | Public | Vercel (team: pushdown) | Astro 5 + React 19 + GSAP + Lenis |
| Admin portal | MHC IS ops (mhcis_operator) | Cloudflare Workers (oi-sys-admin) | Next.js 16 + OpenNext Cloudflare |
| Finance portal | Finance team (finance_analyst) | Cloudflare Workers (oi-sys-finance) | Next.js 16 + OpenNext Cloudflare |
| Compliance portal | Compliance team (compliance_officer) | Cloudflare Workers (oi-sys-compliance) | Next.js 16 + OpenNext Cloudflare |
| Carrier portal | Carrier staff | Cloudflare Workers (oi-sys-carrier) | Next.js 16 + OpenNext Cloudflare |
| Producer portal | Producers/agents | Cloudflare Workers (oi-sys-producer) | Next.js 16 + OpenNext Cloudflare |
| Underwriting Workbench | Underwriters | Cloudflare Workers (oi-sys-uw) | Next.js 16 + OpenNext Cloudflare |
| Policyholder portal | Policyholders | Cloudflare Workers (oi-sys-portal) | Next.js 16 + OpenNext Cloudflare |
All Next.js apps use React Compiler (no manual useMemo/useCallback), React 19, and Tailwind v4.
Design system — unified across all portals: Bricolage Grotesque (headings) + Geist (body) + Geist Mono (data/code), @phosphor-icons/react for icons (9,000+ icons, 6 weights), and oklch-precise shadow/color tokens defined in packages/ui/src/globals.css. See the Design System reference for full details.