Core Concepts
The hierarchy: Organization → School → Class
Amal’s tenancy is a three-level tree:
- An Organization owns many Schools (chain support).
- A School belongs to exactly one Organization.
- A Class belongs to exactly one School, and has a
grade(1-4 in Wave 1), anacademicYear(YYYY-YYYY), and a displayname.
Organization, School, and Class IDs are cuid strings (^c[a-z0-9]{24}$).
Tenant isolation
organizationId is the canonical isolation key. It is denormalized onto every tenant-scoped row, and
middleware filters every query by it automatically. Two response patterns enforce privacy:
| Situation | Response | Why |
|---|---|---|
| Resource in another organization | 404 | Existence is never disclosed across org boundaries |
| Resource in your org, but your role can’t reach it | 403 | The resource exists; your permission set doesn’t reach it |
404 does not mean “doesn’t exist”. Across organizations, a real resource is reported as 404
on purpose. Within your own org, an out-of-reach resource is 403. Build your error handling around
this two-pattern rule.
No teacherId on a Class
A class has no teacher foreign key. Teacher ↔ class is many-to-many through the ClassTeacher
junction. A teacher can teach multiple classes, and authorization always asks “is there an active
ClassTeacher row?”, not “does this class’s teacherId match?”. See
Manage Classes & Rosters.
The six personas
The PersonaKey enum has six values:
| Persona | Level | What they do |
|---|---|---|
STUDENT | — | Practices, answers diagnostics; logs in by quickCode |
TEACHER | class | Decides on every recommendation; teaches one or more classes |
PARENT | — | Sees their own child’s growth |
PRINCIPAL | school | Leads exactly one school; reviews school health |
MANAGER | org | Org-level role, scoped to specific schools (or org-wide) |
ADMIN | operator | Provisions organizations, schools, classes, and users |
RBAC: permissions gate endpoints, not roles
Authentication (who you are) is separate from authorization (what you can do). Identity comes from
requireAuth; capability comes from requirePermission(code). Permission codes are a closed,
24-value enum. The codes most relevant to an integrator:
| Permission | Unlocks |
|---|---|
VIEW_ORG | Read an organization and its rollup |
MANAGE_ORG | Create / update organizations |
MANAGE_SCHOOLS | Create / update schools and classes |
MANAGE_TEACHERS | Assign teachers to classes (ClassTeacher) |
MANAGE_PRINCIPALS | Create / assign principals |
MANAGE_MANAGERS | Create / assign managers |
MANAGE_CLASS_STUDENTS | Manage class rosters |
SUPER_ADMIN | Cross-org operator capability (the bootstrap admin holds this) |
The API Reference lists the exact security requirement on each operation.
Approval gates
Some sensitive actions require an approval role (the APP_* rules). In Wave 1, Tier-3 activation
is admin-approved (APPROVE_TIER_3_ACTIVATION), and the reading-specialist role is admin-seated
via a specialistRole flag. This is mostly product-internal; it is mentioned here so you can see the
platform carries a real approval model.