Skip to content
Updated Sep 26, 2026 by Barča Dvořáková · Owner: analysisactiveentity Edit on GitHub

Entity: tenant ​

Entity Type: Database table (platform.tenants)

Description: A single provisioned organisation-space in EM3 — one tenant maps to one dedicated database schema. tenant is referenced by tenantId on the great majority of tenant-scoped tables across the whole system (this catalog included — see e.g. group, groupMembership, tenantUser, currently under analysis on a separate Users & Access branch not yet merged to main), but until now had no entity page of its own in this catalog. This page documents tenant as it exists today in EM3, sourced directly from the platform-backend migrations rather than from any prior written spec.

Relationship to client: the pre-existing, Confluence-migrated client page describes client as corresponding 1:1 to tenant ("one client = one tenant schema"). The current DB schema instead models this the other way round and with an explicit uniqueness constraint: tenant.clientId is a required, unique foreign key to client — i.e. a 1:1 relationship enforced from the tenant side, with client as the referenced (not referencing) table. This is a known, pre-existing inconsistency between the legacy client write-up and the real schema; per prior direction on this analysis, reconciling it is out of scope here and is left unaddressed.

Data Attributes Table ​

Attribute NameDescriptionData TypeDefault ValueRequired (= Nullable)UniqueFormatValidationsIndexExample
idPrimary key.UUIDGenerated in code (app layer)YesYesUUID v7-Primary Key018ed0b3-c298-7c7a-96d5-8b36f5a7f8d2
tenantDatabaseIdThe logical database this tenant's schema lives in. Platform infrastructure concern (Tiger Cloud service connection → logical database), not modeled further by this analysis.UUID-YesNoUUID v7FK → tenant_database (platform infrastructure table, not in this catalog); ON DELETE RESTRICT; must exist.-018fa51f-fda1-79f4-8461-2cb8f1cabc10
schemaNameName of the tenant's dedicated database schema. Always tenant_<id without dashes> — see Tenant Provisioning.String-YesYes-Non-empty-tenant_018ed0b3c2987c7a96d58b36f5a7f8d2
displayNameHuman-readable name of the tenant, shown in the UI.String-YesNo-Non-empty-Město Brno
statusLifecycle state of the tenant.EnumprovisioningYesNoSee Enum - TenantStatusMust be a valid TenantStatus valuename: idx_tenants_status, type: btreeactive
clientIdThe client this tenant belongs to. Required and unique — a tenant has exactly one client, and a client has at most one tenant (see relationship note above).UUID-YesYesUUID v7FK → client; ON DELETE RESTRICT; must exist. Unique constraint uq_tenants_client_id.-018ed0b3-c298-7c7a-96d5-8b36f5a7f8d2
provisioningStartedAtThe lease timestamp for the current or most recent provisioning attempt — fixed once when the attempt is claimed, timestamptz(3) (millisecond precision, matched to what a JS Date round-trips, so every terminal write can compare it for equality). Null once the tenant is active, suspended, deleted, or has never been claimed. See Tenant Provisioning.Timestamp with time zone (ms precision)-NoNoISO 8601--2026-09-16T12:00:00.000Z
provisioningAttemptsNumber of provisioning attempts started (claimed) so far for this tenant, capped by the sweep's maxAttempts policy.Integer0YesNo---2
lastProvisioningErrorThe error message from the most recent failed provisioning step, or a converge failure recorded after the tenant went active. Cleared (null) the moment status successfully flips to active.Text-NoNo---connect ECONNREFUSED 127.0.0.1:5432
createdAtTimestamp of when the record was created. Immutable after insert.Timestamp with time zonenow() — set in codeYesNoISO 8601Cannot be null; cannot be modified after creation.-2025-05-25T13:11:00Z
updatedAtTimestamp of the last update.Timestamp with time zonenow() — set in codeYesNoISO 8601Cannot be null.-2025-05-25T13:11:00Z
deletedAtTimestamp of soft deletion. Null means the tenant is active. Once set, immutable.Timestamp with time zone-NoNoISO 8601Immutable once set. Active records: WHERE deletedAt IS NULLname: idx_tenants_deleted_at, type: btree (partial: WHERE deletedAt IS NULL)-
createdByIdentifier of the actor who created the record.String-YesNotype:actorNon-empty.-user:018e...
updatedByIdentifier of the actor who last updated the record.String-YesNotype:actorNon-empty.-system:migration

Notes on status vs. deletedAt: the table carries both a status value of deleted and a standard soft-delete deletedAt column. Whether these are kept in lock-step by application logic, or can diverge (e.g. status = suspended with deletedAt still null), is not resolved by the migrations alone — see Enum - TenantStatus.

Provisioning: documented in full at Tenant Provisioning — a periodic sweep claims each not-yet-active tenant under a lease and runs it through schema creation, migration, seeding, and (once active) a platform-admin access converge; failed is the terminal state if the retry budget runs out first.

Audited fields ​

Recorded on created (in full) and updated (changed only) — no deleted, since a tenant is never hard- or soft-deleted by this flow (see status/deletedAt note above): tenantDatabaseId, schemaName, displayName, status, clientId, provisioningStartedAt, provisioningAttempts, lastProvisioningError.

Excluded: none.

Not part of the entityName registry — a tenant row is named through a separate mechanism: every audit entry already carries its own tenantName, filled directly from the resolved tenant scope rather than looked up per row (architecture 61-audit-log.md §7.1–§7.2 in the code repo).