Skip to main content
Version: v0.25.0 (Latest)

Tenant Model

The tenant is the central entity in the EDK. Every record that is not deployment-wide (configuration, integrations, credential designs, DCQL queries, AS clients, federation providers, signing key aliases, webhooks, sessions, audit events) belongs to exactly one tenant. The fields below are the ones that matter when you create, list, and manage tenants through the Platform Admin API.

Slugs

The slug is a globally unique, URL-safe label. It does a lot of work:

  • It is the platform subdomain label. acme is reachable at acme.<platform-base>.
  • It is the path segment for path-based routing. /acme/oid4vci/... resolves to the acme tenant.
  • It appears in the .well-known URL forms for OID4VCI (/.well-known/openid-credential-issuer/acme), OAuth AS metadata (/.well-known/oauth-authorization-server/acme), and OIDC Discovery (/acme/.well-known/openid-configuration).

Because slugs are globally unique, any tenant is reachable by direct subdomain regardless of where it sits in the hierarchy: tenantc.saas.com resolves to tenantc even when tenantc is a child of tenanta. Service-specific labels such as issuer, verifier, or auth may sit to the left of the tenant slug without changing the result: issuer.tenantc.saas.com still resolves to tenantc.

Validation rules:

  • Length 1 to 63 characters (the DNS label limit).
  • Character set [a-z0-9-], must start with [a-z].
  • No consecutive hyphens.
  • A small reserved-word denylist (admin, api, www, system, plus deployment-specific entries an operator can extend).

Slugs are globally unique. The admin API rejects a collision with a 409.

Hierarchy

A tenant is either a root (parentTenantId = null) or a child pointing at its parent. The hierarchy is used for two things:

  • Administration scope. A parent tenant's admin sees and manages its child tenants; a child tenant's admin sees only its own resources.
  • License limits. maxHierarchyDepth caps how deep the tree can go (a root is depth 1, one child is depth 2, and so on).

The hierarchy does not share data. A child tenant's configuration, integrations, signing keys, designs, queries, and sessions are independent of the parent's. Registration walks the proposed parent's ancestry and refuses a cycle or a tree deeper than maxHierarchyDepth before any change is made.

Status Lifecycle

Tenant status lifecycle
  • ACTIVE. Fully provisioned. The resolver returns the tenant; admin and protocol surfaces work.
  • SUSPENDED. The tenant's records remain (no data loss), but the resolver rejects requests with a tenant_suspended error. Use it to disable a tenant without deleting its data, for example during a billing dispute, a security incident, or a manual hold.
  • PENDING_VERIFICATION. Registered, but a required verification step (owner email verification, a custom-domain DNS challenge, or another deployment-specific gate) is still outstanding. The resolver returns the tenant so the verification flow can run, but commands gated by verification refuse until it clears.

Change a tenant's status with PATCH /api/platform-admin/v1/tenants/{tenantId}/lifecycle/status. The change propagates to every service replica without a restart, so resolution reflects it immediately. Soft delete is separate from status: a soft-deleted tenant disappears from listings and from resolution while its data remains in storage. Hard delete is not exposed through the API.

System Tenants

A system tenant is marked as not a customer tenant. System tenants:

  • Are excluded from tenant listings by default (pass an explicit flag to include them).
  • Are not returned by slug- or parent-based resolution.
  • Do not count toward maxRootTenants or maxTotalTenants (those limit customer tenants only).

The EDK does not assign any privilege to the flag; it is a marker that higher layers and operators use for their own administration patterns. The application tenant that administers an EDK deployment is a system tenant. The flag is set only at registration; there is no API to convert a customer tenant into a system tenant afterward, so tenant counts do not change meaning under the operator's feet.

Audit Fields

Every tenant carries createdAt/createdById, updatedAt/updatedById, and deletedAt/deletedById. The *ById fields name the principal who performed the operation, derived from the calling JWT. For a self-service signup registration, the creator is a synthetic signup principal (the owner does not exist as a principal at registration time). Every admin operation also emits a structured audit event carrying the tenant id, the principal, the operation, the result, and the relevant business identifiers.