Domain Model

Ten core nouns, each small on purpose — the complexity in this system is in how they relate, not in any single one’s field list — plus four supporting join/credential records that make the many-to-many relationships and multi-method auth actually work without a bigger table.

Overview

Entity What it represents
User A person with an account on Substratal. One identity, used everywhere.
Organization A domain/grouping object — a company, or a group within one — for Users. Decoupled from infrastructure placement; see Tenancy. Pulled into Phase 2.
Tenant The infrastructure-placement and data-isolation boundary for one Application owner’s data — shared, isolated, or dedicated-region. See Decisions.
Application A catalog entry for a developer’s app, built on this platform for its user/org/tenancy/settings/storage layer.
Role / Permission A named bundle of capabilities, scoped to the platform or to one app.
Entitlement The on/off record: does a User own an Application, and is it currently switched on.
Profile / AppProfile Identity/display data — global, and per-app.
Setting / AppSettings Configuration — global, and per-app, layered over app defaults.
Order The commerce record an Entitlement traces back to.
Audit Event An immutable log of who changed what access, when.

Supporting entities

Each of these is a join or credential record behind one of the relationships above — real rows in the schema, just not independent concepts the way the ten above are:

Entity Joins What it represents
UserIdentity User ↔ login method How a User actually authenticates — password or sso — since a User can hold more than one at once. See Decisions → Identity provider.
SSOConnection Organization ↔ identity provider One Organization’s federated-login configuration, referenced by its members’ sso-method UserIdentity rows.
OrganizationMembership User ↔ Organization Which Organizations a User belongs to, and their standing (org_admin/member) in each.
UserRoleAssignment User ↔ Role Which Roles a User holds, platform-wide or scoped to one Application.

How they relate

erDiagram
    USER ||--o{ ENTITLEMENT : holds
    ORGANIZATION ||--o{ USER : "has members (many:many)"
    ORGANIZATION ||--o{ ENTITLEMENT : "holds (org-wide)"
    APPLICATION ||--o{ ENTITLEMENT : "granted via"
    USER ||--o{ APPLICATION : owns
    ORGANIZATION ||--o{ APPLICATION : "owns (alt.)"
    USER ||--o| TENANT : "owns (alt.)"
    ORGANIZATION ||--o| TENANT : "owns (alt.)"
    TENANT ||--o{ APPLICATION : places
    ENTITLEMENT ||--o| ORDER : "traces to"
    USER ||--|| PROFILE : "has (global)"
    USER ||--o{ APP_PROFILE : "has, per app"
    APPLICATION ||--o{ APP_PROFILE : scopes
    USER ||--|| SETTINGS : "has (global)"
    USER ||--o{ APP_SETTINGS : "has, per app"
    APPLICATION ||--o{ APP_SETTINGS : scopes
    USER }o--o{ PLATFORM_ROLE : assigned
    USER }o--o{ APP_ROLE : "assigned, per app"
    APP_ROLE }o--|| APPLICATION : scopes
    PLATFORM_ROLE ||--o{ PERMISSION : grants
    APP_ROLE ||--o{ PERMISSION : grants

The relationship worth internalizing before reading further: Entitlement and Role are independent axes. Entitlement answers “can this user reach this app at all, right now.” Role answers “once inside, what can they do.” See Access Control for how the two combine on every request.

ID format

Every entity has an opaque, stable id, prefixed by type for readability (a Stripe-style convention): usr_, uid_, ssc_, org_, tnt_, app_, role_, ent_, ord_, evt_, whk_, key_. IDs are never reused and never encode meaning beyond the type prefix (Role is a deliberate exception — see Conventions).

Building the actual database, not just calling the API? Database Schema has the Postgres-level types, constraints, and indexes behind every entity above.


Table of contents


Back to top

Substratal Apps Platform API — living specification. This site is the system of record; see git history for how it has changed over time.

This site uses Just the Docs, a documentation theme for Jekyll.