# Multi-Tenancy {#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 {#tenant-model}



### What is a Tenant? {#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-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 {#how-tenant-isolation-works}



### Entity Base Classes {#entity-base-classes}


All tenant-scoped entities extend `TenantBaseEntity` or `TenantOrganizationBaseEntity`:

```typescript
// 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 {#automatic-tenant-filtering}


The `TenantAwareCrudService` base class **automatically injects** the current tenant's ID into all queries:

```typescript
// 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 {#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:

```typescript
const tenantId = RequestContext.currentTenantId();
```


## Tenant Lifecycle {#tenant-lifecycle}



### 1. Tenant Creation {#1-tenant-creation}


When a user registers publicly, a new tenant is created during onboarding:

```typescript
// 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 {#2-organization-creation}


Within a tenant, multiple organizations can be created:

```typescript
const organization = await this.organizationService.create({
  name: "Engineering Division",
  tenantId: RequestContext.currentTenantId(),
  currency: "USD",
  defaultValueDateType: "TODAY",
});
```


### 3. User-Tenant Association {#3-user-tenant-association}


Users are always associated with exactly one tenant:

```
User (id, email, tenantId, roleId)
  └── tenantId references Tenant(id)
```


## Tenant Filtering Bypasses {#tenant-filtering-bypasses}


Some scenarios intentionally bypass tenant filtering. These are documented in detail in the [Tenant Filtering](https://sourcey-gauzy.pages.dev/database/tenant-filtering/index.md) page.


### Categories of Bypass {#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 {#security-rules-for-bypasses}


1. Every bypass **must be documented** in the tenant filtering doc
2. Manual `createQueryBuilder` calls **must include** `tenantId` in WHERE clauses
3. Cross-tenant queries require **SUPER_ADMIN** role guard
4. New services must use `TenantAwareCrudService` unless explicitly justified


## Multi-Tenant Guards {#multi-tenant-guards}



### TenantPermissionGuard {#tenantpermissionguard}


The primary guard that enforces tenant isolation at the API level:

```typescript
@UseGuards(TenantPermissionGuard)
@Controller("employee")
export class EmployeeController {
  // All endpoints automatically scoped to the user's tenant
}
```


### OrganizationPermissionGuard {#organizationpermissionguard}


For endpoints that require organization-level access:

```typescript
@UseGuards(OrganizationPermissionGuard)
@Controller("project")
export class ProjectController {
  // Endpoints scoped to user's organization within their tenant
}
```


## Data Flow Example {#data-flow-example}






```mermaid
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 {#configuration}



### Allow Super Admin Role {#allow-super-admin-role}


```bash
# .env
ALLOW_SUPER_ADMIN_ROLE=true    # Allow creating Super Admin users
```

When disabled, no new SUPER_ADMIN users can be created, enhancing security for production multi-tenant deployments.


## Best Practices {#best-practices}



### DO {#do}


- ✅ Extend `TenantAwareCrudService` for all new services
- ✅ Use `TenantOrganizationBaseEntity` for new entities
- ✅ Document any tenant filtering bypasses
- ✅ Use `RequestContext.currentTenantId()` in manual queries
- ✅ Apply `TenantPermissionGuard` to all controllers


### DON'T {#dont}


- ❌ Use `Repository.find()` directly (bypasses tenant filtering)
- ❌ Create entities without extending `TenantBaseEntity`
- ❌ Allow users to specify `tenantId` in request bodies (use RequestContext)
- ❌ Query across tenants without SUPER_ADMIN authorization


## Related Pages {#related-pages}


- [Tenant Filtering](https://sourcey-gauzy.pages.dev/database/tenant-filtering/index.md) — detailed bypass documentation
- [Roles and Permissions](https://sourcey-gauzy.pages.dev/authentication/roles-and-permissions/index.md) — role hierarchy
- [Registration and Onboarding](https://sourcey-gauzy.pages.dev/authentication/registration-and-onboarding/index.md) — tenant creation flow
- [Backend Architecture](https://sourcey-gauzy.pages.dev/architecture/backend-architecture/index.md) — guard architecture
