Platform Design

Identity & Tenancy

How customers, users, and data are modeled from day one: a global control-plane registry of organizations, one isolated data plane per customer, and delegated enterprise SSO that customers configure themselves through Auth0.

Status: proposed design

The decisions on this page are tracked formally as ADR-0028 through ADR-0032 on the Architecture Decisions page, and grounded in Auth0's Self-Service Enterprise Configuration and Organizations documentation.

Two data planes

The platform keeps two very different kinds of data apart. A small, highly available control plane is our customer registry and the thing our IaC reads and writes — it never holds PHI. Each customer then gets its own data plane: a Postgres deployed where their cluster lives, holding users, sessions, their forked lesson library, and PHI.

Control plane — global Postgres (Azure home region)

organizations · infra/provisioning state · Auth0 mappings · billing — no PHI

Mercy Health

Azure · us-east

Dedicated
Tenant Postgresusers · sessions · content · PHI

Charité Berlin

GCP · europe-west3

Dedicated
Tenant Postgresusers · sessions · content · PHI

Shared pool

Azure · us-east

Shared
Tenant Postgresusers · sessions · content · PHI

One control-plane registry routes every org to its own data plane. Residency follows the tenant DB; no tenant holds another tenant's data.

The organizations registry

organizations is the top-level entity and the system of record for every customer — the central control point where placement, infrastructure, identity, and compliance all hang off one row. It is the seam between our IaC and the running platform.

Identity

  • name
  • slug
  • status (trial · active · suspended)

Placement

  • tier (dedicated · shared)
  • cloud (azure · gcp)
  • region
  • version_pin

Infra binding

  • pulumi_stack
  • cluster_endpoint
  • tenant_db_ref (secret)

BYO-cloud

  • cloud_credentials_ref (secret store, never raw)

Auth0 binding

  • auth0_org_id
  • connection_ids
  • verified_domains

Compliance

  • data_residency
  • baa_signed
  • isolation notes

Onboarding is a pipeline keyed off the org row

Adding a customer — today by us, later via a sign-up or trial button — is an ordered, auditable pipeline. The org row is the durable orchestration record: each step records status and is idempotent on retry.

1 · Create org row

Tier, cloud, region, version chosen in the control-plane registry.

2 · Trigger Pulumi stack

Parameterized stack provisions the cluster + tenant Postgres.

3 · Fork starter content

Lumeto template scenarios are copied into the tenant DB.

4 · Auth0 Org + SSO ticket

Mint the Auth0 Organization and a self-service access ticket.

5 · Write outputs back

Endpoints, DB ref, auth0_org_id, domains saved onto the org row.

Self-serve enterprise SSO

Customers are hospitals and medical schools that authenticate against their own directories and want to wire up SSO themselves. We use Auth0 Self-Service Enterprise Configuration so the setup is documentation-driven and hands-off on our side.

Profile — a reusable whitelist

A profile is a template (max 20 per tenant) listing which IdPs the assistant offers and which attributes to capture. It is not per customer — it's the cookie-cutter that many customers are stamped from.

Ticket — one per customer

Onboarding mints a one-time access ticket from a profile, bound to the customer's Auth0 Organization. We send the URL plus our docs; it grants their admin scoped access to set up exactly one connection.

Connection + Org — the result

The finished Enterprise connection lands in our tenant under the customer's Auth0 Organization (1:1 with the org row) — the membership and org-aware login anchor.

Start with one permissive profile

Because a profile can offer several providers and let each customer choose, we start with a single permissive profile that enables Entra ID (OIDC), Generic SAML, and Google Workspace — most customers are Microsoft Entra and can pick OIDC or configure Entra as SAML themselves. We only add a second profile when we need to restrict rather than enable — e.g. a regulated profile that forces SAML and requires SCIM + domain verification. The per-customer variation lives in the ticket and connection, never in new profiles.

What the customer admin does in the setup assistant

  1. 1Select identity provider (Entra ID, Okta, Google, ADFS, Ping, OIDC/SAML)
  2. 2Create the application in their IdP (guided instructions)
  3. 3Configure the connection (domain, client id/secret)
  4. 4Map claims to our User Attribute Profile
  5. 5Assign user/group access
  6. 6Test SSO — then enable

Routing and provisioning users

Email-based routing

Organization Domain Discovery syncs verified domains to both the Auth0 Org and the connection's matching_domains. A user types their work email and is routed straight to their company's IdP — no tenant picker.

SCIM lifecycle

SCIM (offered in the assistant) is the primary path for create/update/deactivate, so disabling a user in the customer's IdP deprovisions them here. JIT-on-login is the fallback where SCIM isn't configured.

User Attribute Profile

A defined UAP fixes the claims we depend on — email, name, and a role/group signal for instructor vs learner — so the tenant users table is populated reliably.

Identity boundaries

The control plane stores only the auth0_org_id, connection ids, and verified domains. Actual user records, sessions, and PHI live exclusively in the tenant data plane, keyed by the Auth0 user id (sub). Enterprise customers are SSO-only; database connections are reserved for Lumeto staff and self-serve trials.

Lumeto is its own organization

We model Lumeto exactly like a customer: its own Auth0 Organization backed by a Google Workspace enterprise connection (our daily-driver directory and single source of truth for staff identity). Eating our own dogfood keeps one consistent model and gives every Lumeto employee SSO with proper deprovisioning.

Google Workspace connection

One directory of record for Lumeto staff — not split across Google and Azure, so a departing employee is removed in one place. Our own Entra directory stays available only as an internal test IdP.

Staff are control-plane operators

Lumeto staff identities live in the control plane with elevated roles (provisioning, support, cross-tenant access) — they are not users inside any customer's tenant DB.

Authn vs authz

Google Workspace answers who a staff member is; control-plane roles answer what they may do to a given customer's org and infrastructure.