Why multi-tenancy matters
"We sell to hundreds of companies. Each one needs to feel like they have their own private CRM — but if we spin up a separate server and database per customer, our infrastructure bill and ops burden explode before we even hit our first hundred customers."
This is the core SaaS tension: customers want isolation, the business needs shared infrastructure. Get it wrong and either your infrastructure costs spiral, or — far worse — one customer's data becomes visible to another.
Anatomy of tenant isolation
SmartLite uses a shared-schema model: one database, one set of tables, serving every customer org. Isolation is enforced on every single request through a three-part chain:
| Step | Mechanism | What it guarantees |
|---|---|---|
| 1. Login | JWT issued with an embedded orgId claim | Every subsequent request proves which org it belongs to |
| 2. Request enters | JwtAuthFilter writes the token's orgId into TenantContext (a ThreadLocal) | The current org is known for the entire lifetime of the request |
| 3. Every query | Services scope reads/writes with TenantContext.getOrgId() | You can only ever operate on your own org's rows |
| 4. Every single-record fetch | OrgIsolationGuard.assertBelongsToCurrentOrg(entity.getOrgId()) | Even a guessed or leaked internal ID can't be used to read another org's record |
Org registration itself provisions a brand-new tenant in one transaction: it creates the organization row, the first Admin user, and seeds all 16 standard objects (Lead, Contact, Account, Opportunity, Case, and more) — so a new customer is productive immediately, with no manual setup step from your ops team.
Register a new org and prove the isolation
/org/register on your SmartLite frontend.orgId claim in each.Test what you learned
JwtAuthFilter write into TenantContext on every incoming request?orgId claim extracted from the JWTOrgIsolationGuard.assertBelongsToCurrentOrg() after every single-record fetch by ID?