Skip to main content

Data Model

Cronozen manages 200+ tables across its hub-and-spoke ecosystem. This page documents the core entities that define the platform’s multi-tenant architecture.

Entity Relationship

                    ┌──────────────────┐
                    │  unified_actors  │
                    │  (identity)      │
                    └────────┬─────────┘
                             │ 1:N
                    ┌────────▼─────────┐
                    │ center_memberships│
                    │ (access control)  │
                    └────────┬─────────┘
                             │ N:1
              ┌──────────────▼──────────────┐
              │           centers            │
              │  (tenant / organization)     │
              └──────┬──────────────┬───────┘
                     │              │
          ┌──────────▼───┐  ┌──────▼────────┐
          │   schedules  │  │  dpu_records   │
          │   sessions   │  │  (audit trail) │
          │   invoices   │  └───────────────┘
          └──────────────┘

Core Entities

unified_actors

The identity layer. Every person in the system is a unified_actor.
FieldTypeDescription
idUUIDPrimary key
emailStringLogin email (unique within center context)
phoneString?Phone number
nameStringDisplay name
password_hashStringbcrypt hash
is_verifiedBooleanEmail/phone verified
created_atDateTimeAccount creation
One person can have multiple actors (e.g., parent at Center A and instructor at Center B). These are linked via actor familyfindActorFamilyIds() groups actors by shared email or phone.

centers

The tenant entity. Each center is an isolated organization.
FieldTypeDescription
idIntegerPrimary key
nameStringCenter name
center_domainStringURL slug (unique)
path_slugStringInternal routing path
tenant_typeEnumCENTER, WORKSPACE, PROGRAM, WHITE_LABEL
verticalStringrehabilitation, welfare, education, etc.
statusEnumACTIVE, SUSPENDED, INACTIVE
partner_idUUID?FK to whitelabel_agreements (if white-label)
settingsJSONCenter-specific configuration
Tenant types:
TypePurposeExample
CENTERPhysical facilityRehabilitation center, welfare center
WORKSPACEPersonal actor spaceInstructor freelancer workspace
PROGRAMSpecific program instanceSummer therapy program
WHITE_LABELPartner-branded instanceslowpace.co.kr

center_memberships

The access control layer. Actors access centers through memberships.
FieldTypeDescription
idUUIDPrimary key
actor_idUUIDFK to unified_actors
center_idIntegerFK to centers
roleEnumADMIN, INSTRUCTOR, PARENT, CHILD
statusEnumINVITED, PENDING, ACTIVE, SUSPENDED, REJECTED, ENDED
invited_atDateTime?When invitation was sent
ended_atDateTime?When membership ended
status_reasonString?Reason for status change
Lifecycle:
INVITED ──▶ PENDING ──▶ ACTIVE ──▶ ENDED
                │          │
                ▼          ▼
            REJECTED   SUSPENDED
Security guarantees:
  • No center access without active membership
  • No center_id fallback — always explicit membership check
  • All transitions are audited
Never query center data by center_id directly. Always verify membership status first via requireCenterScope() or getCenterScopeOrError().

center_tenant_mapping

Cross-service linking between OPS hub and spoke services.
FieldTypeDescription
idIntegerPrimary key
center_idIntegerFK to centers (unique)
center_domainStringDomain slug
center_nameStringDisplay name
cms_tenant_idUUID?FK to CMS tenant
lms_tenant_idUUID?FK to LMS tenant
cms_enabledBooleanCMS service active
lms_enabledBooleanLMS service active
provisioning_statusEnumCOMPLETED, FAILED, PROVISIONING
provisioning_refStringIdempotency key (order ID)

whitelabel_agreements

Partner/white-label contract and branding configuration.
FieldTypeDescription
idUUIDPrimary key
partner_domainStringUnique brand slug
partner_nameStringPartner organization name
contract_statusEnumACTIVE, INACTIVE
business_modelEnumPUBLIC, PRIVATE
revenue_share_rateDecimalRevenue share percentage (0-100)
branding_logoString?Logo URL
branding_primary_colorString?Brand color hex
branding_faviconString?Favicon URL
landing_hero_titleString?Landing page headline
seo_titleString?SEO page title
seo_descriptionString?SEO meta description
featuresJSONEnabled features (survey, voucher, matching, etc.)
enabled_domainsString[]Active verticals (rehab, edu, etc.)
public_signup_enabledBooleanAllow public registration

partner_memberships

Partner organization access control.
FieldTypeDescription
idUUIDPrimary key
actor_idUUIDFK to unified_actors
partner_idUUIDFK to whitelabel_agreements
roleEnumPARTNER_ADMIN, PARTNER_OPERATOR
statusEnumACTIVE, INVITED, PENDING, SUSPENDED
Partner Admin ≠ Tenant Admin. Partner admins manage centers (create, configure, monitor). Tenant admins manage data within a specific center.

DPU Entities

dpu_records

Decision Proof Unit — tamper-evident audit trail.
FieldTypeDescription
idUUIDPrimary key
contentTextDecision content
evidence_levelEnumDRAFT(0), DOCUMENTED(1), AUDIT_READY(2)
chain_hashStringSHA-256 hash
chain_indexIntegerPosition in hash chain
previous_hashString?Hash of previous record (null for genesis)
reference_typeStringSource context (workflow, lms-attendance, etc.)
reference_idStringSource record ID
created_byUUIDActor who created the decision
center_idIntegerTenant scope
tagsString[]Classification tags
metadataJSONAdditional context
created_atDateTimeCreation timestamp
Hash computation:
chainHash = SHA-256(content | previousHash | timestamp)
Evidence level transitions:
DRAFT ──▶ DOCUMENTED ──▶ AUDIT_READY (LOCKED)
Once AUDIT_READY, the record is sealed. Any modification breaks the chain.

LMS Entities (learn.cronozen.com)

Key Models

ModelDescriptionKey Fields
TenantLMS tenant (training institution)subdomain, centerId, features, settings
ProductCourse, workshop, webinarproductType, title, price, hrdEnabled
ChapterCourse structure unitproductId, title, order
ContentLesson (차시)chapterId, title, duration, videoUrl
RegistrationEnrollmentactorId, productId, expiresAt, status
ProgressLearning progress per contentregistrationId, contentId, progressPercent
AssessmentExam/assignmenthrdType, hrdWeight, timeLimitMinutes
AttendanceSessionWorkshop event instancedate, location, capacity, qrTokenTTL
AttendanceRecordIndividual check-insessionId, actorId, dpuChainHash, gps

Tenant Configuration (JSON)

{
  "hrdEnabled": true,
  "hrdPassingScore": 60,
  "hrdMinProgressPercent": 50,
  "hrdMaxDailyLessons": 8,
  "emonEnabled": true,
  "emonApiUrl": "https://emon.hrd.go.kr/api/v1",
  "emonInstitionId": "INST-001"
}

Data Isolation Pattern

Scoped Prisma

All center-specific queries go through scoped Prisma, which automatically filters by center_id:
// Scoped: only returns data for the current center
const schedules = await scopedPrisma.schedule.findMany();

// Base: cross-center query (admin/audit only)
const allCenters = await basePrisma.centers.findMany({
  where: { partner_id: partnerId }
});

Row-Level Isolation

Every data table includes center_id as a foreign key. The scoped Prisma middleware injects WHERE center_id = ? on every query automatically.
LayerMechanismScope
ApplicationScoped Prisma middlewareAutomatic per-request
APIrequireCenterScope()Endpoint-level guard
Databasecenter_id FK constraintSchema-level
basePrisma bypasses tenant isolation. It must only be used for:
  • Admin cross-center reporting
  • Actor family lookups
  • Partner platform aggregation
  • System-level operations (cron, migration)