---
title: "Core Concepts"
url: https://docs.growamal.com/en/core-concepts/
language: en
---

# 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), an `academicYear`
  (`YYYY-YYYY`), and a display `name`.

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 |

> **Note:** **`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](https://docs.growamal.com/en/guides/classes-and-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,
25-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](https://docs.growamal.com/en/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.
