Multi-Tenancy
Ever Gauzy is built as a multi-tenant platform where every piece of data is scoped to a specific tenant. This ensures complete data isolation between different organizations using the platform.
Tenant Model
What is a Tenant?
A tenant represents a top-level organizational boundary β typically a company or business entity that uses the Gauzy platform. Each tenant has:
- Its own users, employees, and organizations
- Isolated data (invoices, time logs, expenses, etc.)
- Independent configuration and settings
- Separate roles and permissions
Tenant Hierarchy
Tenant (Company)
βββ Organization A (Branch / Division)
β βββ Department 1
β β βββ Team Alpha
β β βββ Team Beta
β βββ Department 2
β βββ Projects
β βββ Project X
β βββ Project Y
βββ Organization B
β βββ ...
βββ Users
βββ Super Admin (tenant-wide)
βββ Admin (per organization)
βββ Employee (per organization)How Tenant Isolation Works
Entity Base Classes
All tenant-scoped entities extend TenantBaseEntity or TenantOrganizationBaseEntity:
// Tenant-scoped entity
export abstract class TenantBaseEntity extends BaseEntity {
@MultiORMManyToOne(() => Tenant, { nullable: false })
tenant: ITenant;
@MultiORMColumn({ relationId: true })
tenantId: string;
}
// Tenant + Organization scoped entity
export abstract class TenantOrganizationBaseEntity extends TenantBaseEntity {
@MultiORMManyToOne(() => Organization, { nullable: true })
organization?: IOrganization;
@MultiORMColumn({ relationId: true, nullable: true })
organizationId?: string;
}Automatic Tenant Filtering
The TenantAwareCrudService base class automatically injects the current tenant's ID into all queries:
// In TenantAwareCrudService
async findAll(options?: FindManyOptions<T>): Promise<IPagination<T>> {
const tenantId = RequestContext.currentTenantId();
// Automatically adds WHERE tenant_id = :tenantId
return super.findAll({
...options,
where: {
...options?.where,
tenantId,
} as any,
});
}This means:
- SELECT queries only return data for the current tenant
- INSERT operations automatically set
tenantId - UPDATE and DELETE operations are scoped to the current tenant
- No tenant data leaks are possible through the service layer
Request Context
The tenant is resolved from the JWT token on every request:
JWT Token β Auth Guard β Tenant Resolve β RequestContext.currentTenantId()The RequestContext makes the tenant ID available throughout the request lifecycle:
const tenantId = RequestContext.currentTenantId();Tenant Lifecycle
1. Tenant Creation
When a user registers publicly, a new tenant is created during onboarding:
// TenantService.onboardTenant()
async onboardTenant(user: IUser, input: TenantInput): Promise<ITenant> {
// Create tenant
const tenant = await this.tenantRepository.save({
name: input.name,
id: uuidv4(),
});
// Create default roles for the tenant
await this.roleService.createBulk(tenant.id);
// Assign SUPER_ADMIN role to the creating user
await this.userRepository.update(user.id, {
tenantId: tenant.id,
roleId: await this.findSuperAdminRole(tenant.id),
});
return tenant;
}2. Organization Creation
Within a tenant, multiple organizations can be created:
const organization = await this.organizationService.create({
name: "Engineering Division",
tenantId: RequestContext.currentTenantId(),
currency: "USD",
defaultValueDateType: "TODAY",
});3. User-Tenant Association
Users are always associated with exactly one tenant:
User (id, email, tenantId, roleId)
βββ tenantId references Tenant(id)Tenant Filtering Bypasses
Some scenarios intentionally bypass tenant filtering. These are documented in detail in the Tenant Filtering page.
Categories of Bypass
| Category | Justification | Example |
|---|---|---|
| Cross-Tenant by Design | Must query across all tenants | TenantService.findAll(), UserService.findByEmail() |
| Manual QueryBuilder | Complex queries using createQueryBuilder | Aggregation queries, reporting |
| Plugin Services | Integration plugins that sync external data | GitHub sync, Upwork import |
| Base Class Internal | Internal ORM operations | CrudService base methods |
Security Rules for Bypasses
- Every bypass must be documented in the tenant filtering doc
- Manual
createQueryBuildercalls must includetenantIdin WHERE clauses - Cross-tenant queries require SUPER_ADMIN role guard
- New services must use
TenantAwareCrudServiceunless explicitly justified
Multi-Tenant Guards
TenantPermissionGuard
The primary guard that enforces tenant isolation at the API level:
@UseGuards(TenantPermissionGuard)
@Controller("employee")
export class EmployeeController {
// All endpoints automatically scoped to the user's tenant
}OrganizationPermissionGuard
For endpoints that require organization-level access:
@UseGuards(OrganizationPermissionGuard)
@Controller("project")
export class ProjectController {
// Endpoints scoped to user's organization within their tenant
}Data Flow Example
Diagram source
graph TB
A["User (Tenant A) β GET /api/employees"] --> B["JWT decoded β tenantId = 'tenant-a'"]
B --> C["TenantPermissionGuard"]
B --> D["RequestContext stores tenantId"]
C --> E["EmployeeController.findAll()"]
E --> F["EmployeeService.findAll()"]
F --> G["TenantAwareCrudService<br/>adds WHERE tenantId = 'tenant-a'"]
G --> H["Database returns ONLY<br/>Tenant A employees"]Configuration
Allow Super Admin Role
# .env
ALLOW_SUPER_ADMIN_ROLE=true # Allow creating Super Admin usersWhen disabled, no new SUPER_ADMIN users can be created, enhancing security for production multi-tenant deployments.
Best Practices
DO
- β
Extend
TenantAwareCrudServicefor all new services - β
Use
TenantOrganizationBaseEntityfor new entities - β Document any tenant filtering bypasses
- β
Use
RequestContext.currentTenantId()in manual queries - β
Apply
TenantPermissionGuardto all controllers
DON'T
- β Use
Repository.find()directly (bypasses tenant filtering) - β Create entities without extending
TenantBaseEntity - β Allow users to specify
tenantIdin request bodies (use RequestContext) - β Query across tenants without SUPER_ADMIN authorization
Related Pages
- Tenant Filtering β detailed bypass documentation
- Roles and Permissions β role hierarchy
- Registration and Onboarding β tenant creation flow
- Backend Architecture β guard architecture