Data Model
Illumera stores everything in PostgreSQL, accessed through Drizzle ORM. The schema
lives in lib/db/src/schema/ — one file per domain area — and is the single source of truth
for table shapes and enumerations.
Entity groups
| Area | Representative tables |
|---|---|
| Tenancy & identity | tenants, users, user_company_memberships, impersonation_tokens |
| Companies | company_profiles, company_pipeline_stages, company_candidate_notes |
| Talent | person_profiles, person_skills, person_certifications, education |
| Projects & staffing | projects, project_slots, slot_skill_requirements, role_templates |
| Engagements | engagements, engagement_events, engagement_ratings |
| Skills & culture | skills, certifications, skill_relationships, culture (CVF) |
| Matching & AI | match_scores, match_score_history, ai_assessments |
| Capability Intelligence | company_capability_goals, company_capability_recommendations, company_capability_goal_snapshots |
| Financial | invoices, project_budget_overrides, submission_reports |
| Catalogue | catalogue_submissions, catalogue_external_imports, catalogue_remediation_actions |
| Integrations & system | align, communication, notifications, audit_logs, email_logs |
Key enumerations
These pgEnum values are the vocabulary the whole platform shares. They are defined in the
schema and reflected through the OpenAPI contract into the frontend.
| Enum | Values |
|---|---|
User roles (users.roles, a text array) | platform_admin, company_admin, company_member, person (legacy company still honored by the rate limiter) |
Availability (availability_status) | available, limited, booked, not_looking |
Seniority (seniority_level) | junior, mid, senior, lead, principal, fellow |
Proficiency (proficiency_level) | aware, beginner, intermediate, advanced, expert |
Skill category (skill_category) | frontend, backend, data_analytics, devops_cloud, security, design_ux, domain_industry, leadership_management |
Project status (project_status) | draft, active, paused, completed, archived |
Slot status (slot_status) | open, filled, cancelled |
Engagement status (engagement_status) | shortlisted, invited, accepted, active, paused, completed, terminated, declined, withdrawn, archived |
Visibility (visibility) | private, tenant, federated, marketplace |
User roles are not a database enum — they're a text[] on users, so a single user can
hold several roles (a dual-role admin who also runs a company is common). Authorization
checks whether the user's roles array contains any of the roles a route requires.
Tenancy & isolation
Most rows carry a tenantId. Isolation is enforced in the API layer, not by trusting the
client:
- The tenant is derived server-side from the authenticated user's DB record. The raw
x-tenant-idrequest header is never honored as a tenant source. - A platform admin gains tenant context only through an active impersonation session; a dual-role admin who also owns a company falls back to their own tenant.
- Partnership agreements between tenants are what allow talent visibility to cross tenant boundaries in the federated marketplace.
Migrations
Schema changes are not applied ad hoc. After editing a file under lib/db/src/schema, run
pnpm --filter @workspace/db generate and commit the generated migration, snapshot, and
journal entry. CI regenerates migrations from the schema and fails on drift — a schema
edit without its migration will not merge.