Skip to main content

Multi-Tenant Architecture

Tenant Isolation Model

Cronozen uses row-level tenant isolation with Prisma scoped models. Every data query is automatically filtered by center_id.
// All queries scoped to current center
const scopedPrisma = createScopedPrisma(centerId);
const members = await scopedPrisma.centerMembership.findMany();
// → WHERE center_id = 'center_123' automatically applied

Actor-Workspace Separation

ActorData Access
InstructorWorkspace and center data completely isolated. Must explicitly enter a center.
ParentCan view data across all centers where children are enrolled. Cross-center aggregation supported.
AdminFull access within their center scope.

Membership-Based Access

There is no direct center access. All access flows through memberships:
Actor → CenterMembership (ACTIVE) → Center Data
The getActor center_id fallback has been removed. All center access must go through center_memberships. This was verified with zero affected rows in staging and production.

Scoped vs Base Prisma

ModeUse CaseAudit
scopedPrismaNormal operations within current centerAutomatic
basePrismaCross-center queries (parent aggregation, admin tools)Required
// ✅ Normal: scoped to current center
const schedules = await scopedPrisma.schedule.findMany();

// ⚠️ Cross-center: explicitly audited
const allCenters = await basePrisma.center.findMany({
  where: { id: { in: authorizedCenterIds } }
});

Center Scope Helpers

// Require center scope — throws if not in center context
const { centerId } = requireCenterScope(session);

// Get center scope or error
const result = getCenterScopeOrError(session);

Tenant Types

TypeDescription
CENTERStandard center tenant
WORKSPACEPersonal actor workspace
PROGRAMProgram within a center
WHITE_LABELPartner-branded instance