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.
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
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.
Query & Retrieval Layer
Converts natural-language questions into SQL via LangChain, then validates the generated query before execution.
Data Layer
PostgreSQL with row-level security policies enforcing tenant isolation at the database, independent of application code.
Deployment
Containerized services on AWS ECS Fargate behind an Application Load Balancer.
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
- 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.
Validating LLM-generated SQL before execution
- Execute LLM-generated SQL directly against the database.
- Insert a validation step between generation and execution that rejects unsafe or cross-tenant queries.
Production Stories
This section is planned but not yet documented.
Lessons Learned
Database-enforced isolation is worth the per-table schema overhead when the alternative depends on every engineer remembering a filter.
Validating generated queries before execution is cheaper than trusting model output, even when the model is usually correct.
Related Notes
This section is planned but not yet documented.