@openinsure/hipaa package implements privacy-by-design architecture: PHI is isolated, tagged, redacted, and audited at every layer of the stack.
HIPAA Applicability in Insurance
PHI Field Taxonomy
The@openinsure/hipaa package maintains a registry of every database field that may contain PHI, mapped to HIPAA’s 18 identifiers:
| HIPAA Identifier | OpenInsure Fields |
|---|---|
| Names | claims.claimant_name, members.full_name, patients.name |
| Geographic data | claims.claimant_address, members.address |
| Dates (birth, admission, discharge) | members.date_of_birth, claims.treatment_start_date, claims.treatment_end_date |
| Phone numbers | claims.claimant_phone, members.phone |
| Email addresses | claims.claimant_email, members.email |
| Social Security Numbers | members.ssn, claims.claimant_ssn |
| Medical record numbers | claims.medical_record_number |
| Account numbers | members.health_plan_id |
| Certificate / license numbers | members.provider_npi |
| IP addresses | phi_audit_log.ip_address (not exposed in API responses) |
| Biometric identifiers | Not stored in current schema |
| Full-face photographs | Stored in R2 with PHI-tagged metadata |
| Diagnosis codes | claims.diagnosis_codes[] (ICD-10) |
| Procedure codes | claims.procedure_codes[] (CPT) |
| Treatment notes | claims.treatment_notes (free text) |
packages/hipaa/src/phi.ts. Adding a new PHI field requires a PR that updates this registry — enforced by a CI lint rule.
Storage Isolation Architecture
PHI protection is enforced at the storage layer:What Lives Where
| Data | Storage | PHI |
|---|---|---|
| Policy records | PlanetScale + CF D1 cache | No |
| Claim records (non-PHI fields) | PlanetScale + CF D1 cache | No |
| Claim claimant information | PlanetScale only | Yes |
| Medical records / treatment notes | PlanetScale only | Yes |
| Workers’ comp payroll data | PlanetScale only | Yes |
| FHIR resources | PlanetScale only (FHIR schema) | Yes |
| PHI audit log | PlanetScale only (append-only) | Meta |
| Session tokens | CF KV | No |
| Rate tables | CF D1 | No |
App-Level Tenant Isolation for PHI
PHI tables are protected by app-level tenant isolation (orgId scoping) that restricts access beyond the standardorg_id filter. The API Worker enforces that queries to PHI tables are scoped to the authenticated user’s organization and, for claims data, further restricted to the assigned adjuster or supervisor.
Field-Level Encryption
For the most sensitive PHI fields (SSN, DOB), OpenInsure uses field-level encryption at the application layer. The per-organization PHI encryption key is stored in Cloudflare Secrets and used by the Worker to encrypt/decrypt sensitive fields before writing to or reading from PlanetScale.Audit Log Structure
Every read or write to a PHI-tagged field is logged tophi_audit_log. This table is append-only — no updates or deletes are permitted by any role.
Querying the Audit Log
Runtime Redaction Engine
Theredact() middleware in @openinsure/hipaa runs on every API response that may contain PHI:
Role-Based PHI Access Matrix
| Role | PHI Access | Scope |
|---|---|---|
producer | None — all PHI redacted | — |
adjuster | Full PHI | Claims assigned to them only |
claims_supervisor | Full PHI | All org claims |
underwriter | Aggregate stats only | Cohort-level, no individual PHI |
compliance_officer | Audit log only | No PHI values |
admin | Full PHI | Requires MFA + session purpose declaration |
actuarial | Anonymized data only | SSN/name replaced with pseudonymous IDs |
Enforcement Middleware
The@openinsure/hipaa package includes Hono middleware that enforces PHI access logging and role-based redaction at the API layer. Both middleware functions run after the route handler, inspecting JSON responses before they reach the client.
PHI Access Logger
ThephiAccessLogger middleware intercepts successful JSON responses, detects any PHI fields present in the response body, and writes an audit entry to a configurable store. Audit logging is fire-and-forget — failures never block the response.
c.get('user') (expecting { sub, role }) and extracts the client IP from the CF-Connecting-IP or X-Forwarded-For header. The audit action (read, write, delete, export) is inferred from the HTTP method and path.
In a Cloudflare Workers environment, the audit write is passed to executionCtx.waitUntil() so it survives beyond the response lifecycle.
PHI Redaction
ThephiRedaction middleware automatically applies minimum-necessary redaction to JSON responses based on the authenticated user’s role. Fields that are PHI but not permitted for the role are replaced with '[REDACTED]'.
MINIMUM_NECESSARY mapping defined in the package to determine which PHI fields each role is allowed to see:
| Role | Allowed PHI Fields |
|---|---|
admin / org_admin / system / clinical_admin | All PHI fields |
underwriter | firstName, lastName, dob, dateOfBirth, ein, address |
producer | firstName, lastName, email, phone |
adjuster | firstName, lastName, treatmentDate |
auditor | firstName, lastName |
member | No PHI fields (fully redacted) |
member role (most restrictive).
The middleware handles both flat JSON objects and { data: ... } wrapper patterns common in API responses.
Combining Both Middleware
For production routes that serve PHI, apply both middleware together — redaction first (so only permitted fields are visible), then audit logging (so the log reflects what the user actually received):Implementing PhiAuditStore for Production
The PhiAuditStore interface requires a single method:
InMemoryAuditStore— stores entries in an array. Suitable for tests and development only; entries are lost on process exit.DbAuditStore— delegates to aninsertfunction you provide, allowing integration with any database layer (Drizzle + PlanetScale, Prisma, raw SQL, etc.).
DbAuditStore backed by the append-only phi_audit_log table described in the Audit Log Structure section above.
BAA (Business Associate Agreement) Obligations
Before processing PHI on OpenInsure, you must execute a Business Associate Agreement:- OpenInsure Cloud: The BAA is included in the Enterprise subscription agreement. Contact
legal@openinsure.devto execute. - Self-Hosted: If you deploy OpenInsure on your own infrastructure, you are the BA processing PHI. You must execute BAAs with your database provider (PlanetScale), Cloudflare, and any other subprocessors.
Subprocessor BAAs for Self-Hosted Deployments
| Vendor | BAA Available | Notes |
|---|---|---|
| PlanetScale | Yes | Confirm terms for your selected plan/provider |
| Cloudflare | Yes | Available via Cloudflare Data Processing Addendum |
| Stripe | Yes | Stripe does not process PHI — BAA not required for billing |
Minimum Necessary Standard
HIPAA’s Minimum Necessary rule requires that PHI is disclosed only to the extent necessary to accomplish the intended purpose. OpenInsure enforces this by:- Field-level scope — API responses only include PHI fields that are necessary for the operation being performed. A
GET /v1/claims/:idby a producer returns no claimant PHI, only coverage verification. - Role scoping — Adjusters see only claims assigned to them.
- Export controls — Bulk PHI exports require admin approval and a documented business purpose, which is logged to the audit trail.