Multi-Tenancy
Lexigram provides a first-class Multi-Tenancy engine (lexigram-tenancy) designed to scale from simple shared databases to complex, high-security multi-regional deployments.
1. The Tenancy Lifecycle
Section titled “1. The Tenancy Lifecycle”Multi-tenancy in Lexigram revolves around three pillars:
- Resolution: Identifying which tenant a request belongs to.
- Isolation: Separating data at rest (Schema, Table, or Database).
- Enforcement: Ensuring the current security context is bound to the resolved tenant.
2. Tenant Resolution
Section titled “2. Tenant Resolution”Resolution happens at the edge of the request pipeline. Lexigram supports several strategies:
| Strategy | Source | Use Case |
|---|---|---|
header | X-Tenant-ID | API Integrations, Mobile Apps |
jwt_claim | sub or tid | OAuth2 / OIDC authenticated requests |
subdomain | tenant.app.com | Traditional SaaS platforms |
path | /api/v1/:tenant/... | Multi-organization public portals |
Configuration
Section titled “Configuration”tenancy: resolution: strategy: subdomain domain: lexigram.dev3. Data Isolation Strategies
Section titled “3. Data Isolation Strategies”Lexigram can isolate data in three ways, depending on your scalability and compliance needs.
A. Row-Level Isolation (Shared Table)
Section titled “A. Row-Level Isolation (Shared Table)”All tenants share the same table; every row has a tenant_id column.
- Pros: Simplest to manage, cheapest to run.
- Cons: Highest risk of data leakage if queries are not properly filtered.
B. Schema Isolation (Same DB, Different Schema)
Section titled “B. Schema Isolation (Same DB, Different Schema)”Each tenant has its own Postgres schema (e.g., tenant_a.users, tenant_b.users).
- Pros: Stronger isolation, easy migrations.
- Cons: Moderate management overhead.
C. Database Isolation (Different DBs)
Section titled “C. Database Isolation (Different DBs)”Each tenant has a physically separate database instance.
- Pros: Maximum security and performance isolation. Required for some compliance standards (SOC2/HIPAA).
- Cons: Most complex to manage and scale.
4. Tenant-Aware Services
Section titled “4. Tenant-Aware Services”Once resolved, the TenantContext is available throughout the DI container. You don’t need to pass tenant_id manually to your functions.
from lexigram.tenancy import TenantContext
class DocumentService: def __init__(self, context: TenantContext) -> None: self.context = context
async def list_documents(self): # The context automatically knows the current tenant ID print(f"Listing docs for tenant: {self.context.tenant_id}") ...5. Middleware & Enforcement
Section titled “5. Middleware & Enforcement”The TenancyGuard ensures that if a route is marked as tenant-scoped, a valid tenant must be resolved, or the request is rejected with a 401 Unauthorized (if missing) or 403 Forbidden (if the user doesn’t belong to that tenant).
from lexigram.tenancy.enforcement import tenant_scopedfrom lexigram.web import Controller, get
@tenant_scoped()class OrganizationController(Controller): @get("/settings") async def get_settings(self): # This route is only accessible if a tenant is resolved ...[!CAUTION] When using Row-Level Isolation, ensure you use Lexigram’s built-in Query Interceptors. These automatically inject
WHERE tenant_id = :current_tenantinto every SQL statement, preventing accidental cross-tenant data exposure.