Skip to main content
OpenInsure uses PlanetScale (Vitess 22.0) as its primary database, accessed from Cloudflare Workers via Hyperdrive connection pooling. The ORM is Drizzle 0.45 with a schema-first approach — all tables are defined in TypeScript and migrations are generated by the Drizzle CLI.

Package

Database schema, migrations, and query utilities live in packages/db:
packages/db/
├── src/
│   └── supabase/
│       └── schema/           # Table definitions (Drizzle)
│           ├── index.ts      # Central re-export
│           ├── submissions.ts
│           ├── policies.ts
│           └── ...
├── migrations/
│   └── postgres/             # SQL migration files (0000–0081+)
│       ├── 0000_kind_famine.sql
│       ├── ...
│       └── meta/             # Drizzle migration journal
├── seeds/
│   └── demo-data.sql         # Demo seed data
└── drizzle.config.ts         # Drizzle CLI config

Database Connection

import { createDb } from '@openinsure/db';

// In a Cloudflare Worker handler:
const db = createDb(env.HYPERDRIVE);
// env.HYPERDRIVE is the Cloudflare Hyperdrive binding
// configured in wrangler.toml under [[hyperdrive]]
Hyperdrive pools TCP connections to PlanetScale, giving Workers the connection efficiency of a traditional server. Max 5 connections per client; Hyperdrive handles pooling across Worker instances.

Multi-Tenant Isolation (orgId Scoping)

All queries run inside a transaction that applies the orgId filter, ensuring every SELECT, INSERT, UPDATE, and DELETE is scoped to the authenticated user’s organization.
await db.transaction(async (tx) => {
  await setOrgContext(tx, orgId); // MUST be called inside transaction
  const policies = await tx.select().from(schema.policies);
  // Only returns policies for this org
});
Danger: Never call setOrgContext() outside a transaction. In pooled connections, context can leak between requests if not scoped to a transaction boundary. Failing to call setOrgContext() at all allows cross-org data access — a critical security violation.

Schema — Table Groups

The schema is organized into domain groups. All 78+ tables are in packages/db/src/supabase/schema/.

Core Platform

TableDescription
organizationsTenant organizations
usersAuthenticated users (synced from oi-sys-auth D1)
api_keysAPI key credentials with role and org scoping
activitiesActivity diary — all user actions
notificationsNotification queue (in-app, email, SMS)
webhooksWebhook event delivery log
saved_viewsUser-defined data view configurations
chat_historyAI agent conversation transcripts

Producers & Programs

TableDescription
producersProducer/agency profiles and license status
programsUnderwriting program definitions
delegated_authorityDA agreement terms and authority limits

Submissions & Underwriting

TableDescription
submissionsSubmission lifecycle (new → quoted → bound → declined)
submission_conditionsConditions attached to a submission
submission_notesNotes with visibility control (internal/external)
submission_eventsActivity timeline per submission
submission_documentsDocument attachments to submissions
quote_optionsQuote variants (coverage options, pricing tiers)
rulesUnderwriting rules configuration
underwriting_rulesRules engine evaluation results
approvalsApproval queue items with audit trail

Aspire Application Detail (Commercial Auto)

These tables store the detailed application data entered through the Aspire form tab system. Each table has an application_id FK to submissions.
TableDescription
application_insuredInsured company info (name, DBA, tax ID, contact details)
application_contactsContact records (corporate, inspection) with name, email, phone, cell
application_addressesMailing and physical addresses
application_coverageCoverage config (CSL limit, deductibles, hired auto, waivers)
application_operationsOperations (max radius, mileage distribution %, Canada/Mexico)
application_vehiclesVehicle schedule (VIN, year, make, model, body type, ownership, ACV)
application_vehicle_coveragesPer-vehicle comp/collision limits and deductibles
application_driversDriver roster (name, DOB, license, MVR points, violations, accidents)
application_driver_general_infoAggregate driver info (total employees, hiring rates, MVR review)
application_driver_violationsIndividual driver violation records
application_prior_carriersPrior carrier history (carrier, dates, coverage type, reason for change)
application_loss_recordsLoss history (per-year accidents, claims, incurred, loss ratio)
application_work_experienceWork experience (new ventures — company, role, dates, supervisor)
application_officersOfficers/stockholders (title, name, ownership %)
application_underwriting_qaKnockout questions (~23 yes/no fields for referral triggers)
application_cargoCargo coverage (limit, deductible, commodities)
application_physical_damagePhysical damage (comp/collision limits and deductibles)
application_trailer_interchangeTrailer interchange (limit, deductible, trailer types)
application_additional_coverageMisc coverages (rental reimbursement, towing, medical payments)
application_additional_interestsAdditional interests (mortgagee, loss payee, lienholder)
application_filingsRegulatory filings (BMC-91, BMC-34, Form E/H, MCS-90)
application_attachmentsFile attachments stored in Cloudflare R2

Policies

TableDescription
policiesPolicy header (number, status, dates, premium)
policyExtended policy detail (jacket data)
coveragesLine-level coverage records per policy
endorsementsPolicy modification history
interestsAdditional insureds, mortgagees, loss payees
driversDriver records (commercial auto)
vehiclesVehicle/equipment schedule
vehicle_coveragesPer-vehicle coverage elections
documentsPolicy documents with R2 key references
coiCertificate of Insurance records
esignE-signature audit trail (Documenso)

Rating

TableDescription
rate_tablesRate table definitions and factor matrices
ratingRating results with canonical payload and audit trail
rating_resultsSource-tagged rating outputs (manual vs system)
market_benchmarksPeer market pricing benchmarks

Claims

TableDescription
claimsClaim header (number, status, loss date)
claim_lifecycleClaim status transition history
claim_notesClaim notes with visibility
claim_tasksClaim work items and diary entries
claimantsClaimant party information
claim_transactionsFinancial transactions (payments, recoveries)
litigation_timersStatute of limitations countdown
disbursementsClaim payment disbursements
subrogationSubrogation tracking and recovery
fraud_feedbackFraud model feedback and outcome labels

Financials

TableDescription
billingInvoice and billing records
invoice_line_itemsLine-item detail for invoices
chart_of_accountsGeneral ledger account hierarchy
general_ledgerDouble-entry ledger transactions
reconciliationLedger reconciliation state
disbursementsPayment disbursement records
ap_transactionsAccounts payable transaction log
reinsuranceReinsurance placement, cession, and recovery
premium_financePremium finance contracts
loss_reservesActuarial loss reserve records
bordereauxBordereaux submission records
bordereaux_reviewsCarrier bordereaux review outcomes

Compliance

TableDescription
complianceCompliance filing calendar
filingsIndividual regulatory filing records
blacklistBlocked entities and IP addresses
report_schedulesScheduled automated report configuration
statutory_reportingStatutory exhibit data

Captive / RRG

TableDescription
captive_accountingCaptive fund accounting records
loss_runsLoss history import and analysis
inspectionsRisk inspection records
smart_contractsBlockchain integration hooks

Infrastructure

TableDescription
vectorsEmbedding vectors for semantic search
territoriesGeographic territory and radius-of-operations
commodity_mixCommodity exposure schedule
motor_carriersDOT/FMCSA motor carrier detail
telematicsGPS and driver safety score data
formsForm template library
document_requirementsRequired document checklists by program

Migrations

Schema files live in packages/db/src/schema/ and are pushed to PlanetScale via Drizzle:
# Generate a new migration from schema changes
cd packages/db
pnpm drizzle-kit generate

# Apply migrations to the database
pnpm drizzle-kit push

# Or run via the migration job (CI/CD)
DATABASE_URL=$DATABASE_URL pnpm drizzle-kit migrate
The CircleCI db-migrate workflow runs on master push and applies pending migrations to the PlanetScale production branch.

Key Migrations

MigrationPurpose
0010_row_level_security.sqlOrg-scoped tenant isolation setup
0023_performance_scaling_indexes.sqlComposite indexes for common query patterns
0071_rls_comprehensive.sqlComprehensive tenant isolation gap closure
0072_foreign_keys.sqlFK constraints across all domain tables
0074_general_ledger.sqlDouble-entry ledger table
0080_documents_r2_key.sqlR2 object key column on documents
0081_submissions_carrier_id.sqlCarrier ID on submissions for gateway routing

Seed Data

Demo data seeds a realistic policy portfolio for development and demos:
pscale shell openinsure main < packages/db/seeds/demo-data.sql
Seeded records:
TypeCountNotable IDs
Organization100000000-0000-4000-a000-000000000001
Programs2GL ...000011, WC ...000012
Rate Tables2GL ...000021, WC ...000022
Users4matt, michael, sean, jd (10000000-...000X)
Policies2WC Metro ...000201, GL Acme ...000202
Claims3Open ...000301, Closed ...000302, Pending ...000303

PlanetScale Branching

PlanetScale’s database branching lets developers create isolated database branches for feature development:
# Create a branch for a feature
pscale branch create openinsure feat/my-feature

# Run migrations against the branch
DATABASE_URL=$(pscale connect openinsure feat/my-feature --execute 'echo') pnpm drizzle-kit push

# Delete branch when done
pscale branch delete openinsure feat/my-feature
Manage branches via pscale CLI or the PlanetScale dashboard at app.planetscale.com.

Packages Overview

All @openinsure/* packages, exports, and consumers.

Architecture

Edge/origin split, Hyperdrive, and data flow.