Skip to content
Engineering Atlas
SystemsEazAgent — Multi-Tenant Insurance CRM

EazAgent — Multi-Tenant Insurance CRM

A multi-tenant CRM for an insurance brokerage, hardened for OWASP compliance and extended with a natural-language query layer over tenant-isolated data.

Status
Active
Timeline
Jan 2026 – Present
Reading Time
4 min
Domains
Security, AI/LLM, Multi-tenancy
Technologies
Next.js, LangChain, PostgreSQL, AWS ECS Fargate, AWS ALB
Contents

Executive Summary

EazAgent is a multi-tenant CRM used by an insurance brokerage. Work centered on two things: closing an external OWASP security audit gap (fixing XSS, CSRF, and SQL injection issues, and redesigning tenant isolation at the database layer to 9.8/10), and building a retrieval-augmented, natural-language query layer over the same tenant-isolated data using LangChain, so non-technical staff can ask questions in plain English without a generated query ever being able to cross a tenant boundary.

Business Context

Problem
EazAgent serves multiple insurance-brokerage tenants from one shared application and database. An external security audit had flagged the existing implementation as non-compliant, and tenant data (client PII, policy and claims records) needed to be structurally impossible to leak across tenants — not just filtered correctly in application code.
Users
Internal brokerage staff across multiple tenant organizations, each scoped to their own tenant's data.
Business Goals
Pass the external OWASP audit. Let staff query CRM data in plain English instead of requesting custom reports for every question.
Success Metrics
OWASP audit score of 9.8/10. Every natural-language query validated against unsafe or cross-tenant execution before it reaches the database.
Environment
Production system on AWS ECS Fargate behind an Application Load Balancer, PostgreSQL as the primary datastore.
Stakeholders
Engineering, accountable for the audit outcome; brokerage staff, as end users of both the CRM and the new query feature.

Constraints

Tenant isolation

Data access had to be structurally prevented from crossing tenant boundaries — a single missed WHERE clause in application code should not be able to leak another tenant's records.

No downtime for existing tenants

Security remediation had to ship against a live system already in use by paying tenants.

External audit deadline

OWASP compliance fixes were scoped and sequenced against a fixed external audit timeline.

Untrusted query generation

LLM-generated SQL could not be trusted to respect tenant boundaries or avoid destructive operations — it needed validation, not faith.

System Blueprint

Requests move from the frontend through a validation boundary before touching tenant data, with isolation enforced at the database layer rather than in application code.

Frontend

Next.js application used by brokerage staff.

Next.js

Query & Retrieval Layer

Converts natural-language questions into SQL via LangChain, then validates the generated query before execution.

LangChain

Data Layer

PostgreSQL with row-level security policies enforcing tenant isolation at the database, independent of application code.

PostgreSQL

Deployment

Containerized services on AWS ECS Fargate behind an Application Load Balancer.

AWS ECS FargateAWS ALB
  • Frontend → Query & Retrieval Layer → Data Layer (row-level security enforced regardless of caller)
  • ECS Fargate tasks → ALB → health checks

Architecture

Tenant isolation at the database layer

Isolation is enforced with PostgreSQL row-level security policies rather than per-query application filters, so a query missing a tenant filter fails closed instead of leaking rows.

Retrieval-augmented query layer

Natural-language questions are converted to SQL through a LangChain pipeline, then checked by a validation step that rejects unsafe or cross-tenant queries before they run.

Role-based access control

RBAC supports multiple roles per tenant, structurally preventing a role in one tenant from reaching another tenant's resources.

Deployment topology

Services run on AWS ECS Fargate behind an Application Load Balancer, with health checks tied to that routing layer.

Engineering Decisions

Row-level security vs. application-level tenant filtering

Problem
Relying on every query in application code to include a correct tenant filter meant a single missed WHERE clause could leak another tenant's data.
Options
  • Keep filtering by tenant_id in application code at every query site.
  • Enforce tenant isolation at the database layer with PostgreSQL row-level security policies.
Decision
Moved isolation into PostgreSQL row-level security policies.
Trade-offs
Every table needs an explicit RLS policy as part of schema review, adding upfront design and testing overhead per table.
Outcome
Tenant isolation no longer depends on every engineer remembering a filter — it's enforced independent of the calling code.

Validating LLM-generated SQL before execution

Problem
Letting staff query CRM data in plain English meant turning natural language into SQL through an LLM, but generated SQL can't be trusted to respect tenant boundaries or avoid unsafe operations.
Options
  • Execute LLM-generated SQL directly against the database.
  • Insert a validation step between generation and execution that rejects unsafe or cross-tenant queries.
Decision
Added the validation step; no generated query reaches the database unchecked.
Trade-offs
Adds latency per query and requires maintaining a validation ruleset independent of the LLM prompt.
Outcome
Unsafe or cross-tenant queries are rejected before execution, regardless of what the model produces.

Production Stories

This section is planned but not yet documented.

Lessons Learned

  1. Database-enforced isolation is worth the per-table schema overhead when the alternative depends on every engineer remembering a filter.

  2. Validating generated queries before execution is cheaper than trusting model output, even when the model is usually correct.

This section is planned but not yet documented.



Command Palette

Search for a command to run...