Multi-Tenancy Architecture

Status: ✅ Production Ready Namespace: src/tenancy Verification: src/tenancy/tenancy-verification.spec.ts

Overview

The Multi-Tenancy module provides strict data isolation between different customers (tenants) sharing the same application instance. It ensures that every request, query, and background job runs within a specific TenantContext.

Core Components

1. TenantService

The central service responsible for:

  • Resolving tenant from headers (x-tenant-id) or JWT.
  • Managing the lifecycle of TenantContext.
  • Applying isolation policies to database queries.

2. AsyncLocalStorage (Context Propagation)

We use Node.js AsyncLocalStorage to store the tenant context for the duration of a request. This avoids "prop drilling" (passing tenantId manually to every function).

// Example usage
this.tenantService.runWithTenant(context, async () => {
  // context is implicitly available here
  const id = this.tenantService.getCurrentTenantId(); 
});

Isolation Strategies

The system supports two isolation modes, configured via JORVIS_TENANT_ISOLATION.

Strategy A: ROW (Row-Level Security)

Best for: High scale, many small tenants.

  • All tenants share the same database tables.
  • Every table has a tenant_id column.
  • TenantService.applyTenantFilter(sql) injects WHERE tenant_id = ? automatically.

Strategy B: SCHEMA (Schema-Level Isolation)

Best for: Enterprise, strict security compliance.

  • Each tenant has their own Postgres SCHEMA (tenant_123).
  • search_path is set to the tenant's schema at the start of the transaction.
  • Complete data isolation at the database level.

Implementation Details

Tenant Resolution

  1. Header: x-tenant-id (Development/API)
  2. Domain: Subdomain mapping (e.g., tenant1.jorvis.com)
  3. Token: Extracted from JWT org_id claim.

Security

  • Context Integrity: Once a context is set in AsyncLocalStorage, it cannot be overridden by user input within that execution scope.
  • SQL Guard: The SqlGuardService calls TenantService to enforce isolation on raw SQL queries generated by LLMs.

Verification

Run the verification suite to ensure isolation integrity:

npx jest src/tenancy/tenancy-verification.spec.ts