Skip to main content

Tenancy

A tenant is the isolation boundary. Users can belong to more than one tenant through Membership. The JWT tenantId is the active tenant for the session, not the only membership.

Checked in tenancy.extension.ts.

Classification​

ClassBehavior
DirectdirectTenantModels adds where.tenantId
IndirectindirectTenantFilters filters through a parent
GlobalIntegrationRegistry, Permission, PlatformRolePermission, Currency, FxRate

Adding a model without updating the extension fails test/architecture/backend-architecture.spec.ts.

What the extension does not rewrite​

$queryRaw is not rewritten the way the Prisma query API is. Flows that were read (FOR UPDATE on an order, outbox claim) include tenantId in the WHERE. A full raw-SQL audit was not completed.

System jobs must enter runWithSystemContext or runWithTenantContext. An anonymous Prisma call from a worker is not tenant-scoped by a request.

Subscription and sync​

SyncJobScheduler skips channel sync unless tenant.isActive and subscription.isActive are true.

Unknown: whether an inactive subscription also blocks HTTP writes, or only the scheduler. Commercial meaning of SubscriptionTier is not established.

Channel ownership​

A Channel belongs to a Seller and a Tenant. Secrets live on IntegrationConnection, which is tenant-scoped and vault-encrypted. IntegrationRegistry is the global catalog of connector types.

WarehouseGuard confines an authenticated user to warehouses allowed by membership. A client header cannot widen that set.

Auth endpoints that are not the active tenant​

The tenant picker (GET /v1/auth/my-tenants) is documented in the controller comment as not filtered by the active tenant. Switching tenant is POST /v1/auth/switch-tenant.