Skip to content

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.

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

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.

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].

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.

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.

  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

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 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
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'.

Terminal window
POST /v1/submissions
Content-Type: multipart/form-data
file=@acord125.pdf

Response:

{
"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:

  1. Looks up the applicable rate table by line_of_business + program_id from CF D1.
  2. Applies factors in order: base rate → state modifier → class modifier → revenue band → loss history → schedule rating.
  3. Logs every factor and intermediate result to rating_steps[] in the response.
  4. Returns a Quote with grossPremium, netPremium, and fees.

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):

  1. Quote freshnessquote_expires_at must be in the future.
  2. 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.
  3. State eligibility — The risk state must be in the DA’s allowed states list.
  4. NAICS exclusion — The insured’s industry code must not appear in the excluded classes list.
  5. SpiceDB authorization — The requesting user must have the bind permission for this risk class and limit.

If all checks pass, the Worker atomically:

  • Creates a Policy record in PlanetScale with status: 'bound'
  • Creates the billing schedule (installment plan)
  • Sends a policy.bound domain event
  • Generates the declarations page PDF via @openinsure/documents
  • Triggers the COI generation workflow if applicable

OpenInsure handles Protected Health Information (PHI) for health insurance programs, workers’ compensation, and FHIR-integrated workflows. PHI isolation is enforced at four distinct layers.

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',
]);
  • 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.

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

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.

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.

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

OpenInsure uses two AI tiers for different workloads:

  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.

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 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.

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).

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.