Skip to main content
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

                         ┌────────────────────────────────────────┐
                         │          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

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

Phase 2 added real-time collaborative underwriting infrastructure on top of the base platform.

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:
TablePurpose
deal_roomsDeal room records linked to a submission
deal_room_participantsParticipants and their roles
deal_room_messagesChat message history
deal_room_annotationsDocument 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

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:
TablePurpose
authority_profilesAuthority limit definitions per LOB/tier
user_authority_assignmentsMaps users to authority profiles
referral_rulesAuto-escalation thresholds
referral_requestsActive referral queue
referral_activitiesReferral audit trail
API routes: /v1/authority, /v1/referrals. UW Workbench pages: /referrals, /admin/authority.

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

  1. 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, and role.
  2. Authorizes the action — checks SpiceDB ReBAC policies for relationship-based permissions.
  3. 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.
  4. Runs business logic — policy state machine transitions, rating calculations, and compliance rule evaluations run entirely in the Worker.
  5. Persists to PlanetScale — authoritative state (policy records, claim records, PHI) is written to PlanetScale via CF Hyperdrive in deployed environments, with direct DATABASE_URL connections in local/CI workflows.
  6. 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

OpenInsure uses Durable Objects extensively for stateful, real-time workloads. All DOs use new_sqlite_classes (SQLite-backed state):
Durable ObjectPurpose
SubmissionAgentPer-submission AI triage and extraction state
PolicyAgentPolicy event sourcing and real-time status
ClaimAgentClaim lifecycle state machine with WebSocket updates
DealRoomDOReal-time underwriting deal room — WebSocket chat + annotations
NotificationAgentPer-user notification queue with delivery tracking
OpenInsureMCPModel Context Protocol server for AI tool integrations
Browser (UW 1) ─────────────────────┐

Browser (UW 2) ──────────► DealRoomDO (room_id)
                                    │  SQLite state
Browser (UW 3) ─────────────────────┘  + WebSocket broadcast

Data Flow: Submission → Quote → Bind

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

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:
// packages/hipaa/src/phi.ts
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

  • 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

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:
RolePHI Access
producerNo PHI — claimant fields are masked as [REDACTED]
adjusterFull PHI access for assigned claims only
underwriterAggregate stats only (no individual PHI)
claims_supervisorFull PHI access for all org claims
compliance_officerAudit log access, no PHI values
adminFull access with mandatory MFA

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

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. :::caution Never bypass tenant isolation in user-facing code paths. Elevated credentials are reserved for controlled migration/ops workflows. :::

Edge vs. Origin Data Split

Data TypeStorage LocationRationale
Rate tablesCF D1Hot-path read on every quote — must be sub-millisecond
DA agreement configsCF D1Pre-bind check — must be co-located with Worker
Session tokensCF KVFast validation, short TTL
Non-PHI policy metadataCF D1 (cache) + PlanetScale (SOR)Read from edge, write through to origin PlanetScale
PHI fieldsPlanetScale onlyHIPAA — never at edge
Policy recordsPlanetScaleAuthoritative SOR
Claims recordsPlanetScaleAuthoritative SOR
Documents / PDFsCloudflare R2S3-compatible, global CDN
Workers AI embeddingsVectorize (CF)Similarity search for ACORD extraction

AI Integration

OpenInsure uses two AI tiers for different workloads:

Workers AI (Edge GPU — Llama 3.1 8B)

  1. ACORD Extraction — Converts unstructured PDF submissions into structured Submission objects. The prompt is templated in packages/acord/src/extractor.ts. Confidence scores below 0.85 trigger a manual review flag.
  2. 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-runs package handles normalization across different carrier formats.
:::tip Workers AI runs on Cloudflare’s GPU infrastructure with no cold start. Extraction latency for a typical ACORD 125 (3–5 pages) is under 2 seconds. :::

Claude (Anthropic — via @openinsure/agents)

Higher-capability reasoning tasks are routed to Claude via the @openinsure/agents package using @tanstack/ai:
  • Risk narrative generationcomputeAiRiskNarrative() 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)

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:
CodeAccount TypePurpose
1CARRIER_PAYABLENet premium owed to the carrier
2MGA_FIDUCIARYHub account for premium receipts
3MGA_REVENUEMGA commission and fee income
4PRODUCER_PAYABLECommissions owed to producers
5TAX_AUTHORITY_PAYABLEPremium taxes owed to state authorities
6LOSS_FUNDCaptive loss fund assets
7CLAIMS_PAIDCumulative claim disbursements
8RESERVESOutstanding loss reserves
9REINSURER_PAYABLEReinsurance 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

OpenInsure runs three distinct sites and five Next.js portals:
AppAudienceHostingStack
openinsure.dev (landing)PublicCloudflare Pages (oi-sys-landing)Astro 5 + React 19 + GSAP + Lenis
oi-sys-preview.pages.dev (preview)PublicCloudflare Pages (oi-sys-preview)Astro 5 + React 19 + Tailwind v4
pushdown.aiPublicVercel (team: pushdown)Astro 5 + React 19 + GSAP + Lenis
Admin portalMHC IS ops (mhcis_operator)Cloudflare Workers (oi-sys-admin)Next.js 16 + OpenNext Cloudflare
Finance portalFinance team (finance_analyst)Cloudflare Workers (oi-sys-finance)Next.js 16 + OpenNext Cloudflare
Compliance portalCompliance team (compliance_officer)Cloudflare Workers (oi-sys-compliance)Next.js 16 + OpenNext Cloudflare
Carrier portalCarrier staffCloudflare Workers (oi-sys-carrier)Next.js 16 + OpenNext Cloudflare
Producer portalProducers/agentsCloudflare Workers (oi-sys-producer)Next.js 16 + OpenNext Cloudflare
Underwriting WorkbenchUnderwritersCloudflare Workers (oi-sys-uw)Next.js 16 + OpenNext Cloudflare
Policyholder portalPolicyholdersCloudflare 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.