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

Entity: group ​

Entity Type: Database table

Description: A named, tenant-scoped collection of users used as the primary recipient of permissionSet grants for tenant-side users (see permissionSetGroupAssignment). A group belongs to exactly one tenant for its entire lifetime — it cannot be reassigned to a different tenant. Within a given tenant, a user's group membership is exclusive: a user can belong to at most one group per tenant (see groupMembership), though the same user can belong to a different group in a different tenant.

Groups exist only for tenant-side access. Porsenna platform users are never granted access via a group — they are always granted directly (see permissionSetUserAssignment), including when a single Porsenna user needs access across several tenants (one direct grant per tenant). A group-based, one- click, cross-tenant grant for Porsenna staff, and a Platform-managed "template" mechanism for pre-populating a new tenant's groups, were both considered and explicitly deferred — see Users & Access.

Some groups are auto-created, not administrator-created. When a permissionSet is created with visibility = organization and restrictedToTenantId left null (or when a tenant later becomes active while such a set already exists), one group per relevant tenant is provisioned automatically — named after the Permission Set, empty, holding a fresh permissionSetGroupAssignment for that set — see permissionSet ("Auto-provisioned across every tenant when created unrestricted"). This is narrower than, and distinct from, the deferred group-templating idea referenced above: it reuses one shared Permission Set across tenants (exactly the existing restrictedToTenantId = null reuse case), it doesn't clone Permission Set content per tenant. Once created, an auto-provisioned group is an ordinary group in every respect — nothing tracks that it was system-created, and its members are added the same way as any other group's (see Users & Access). There is no group-management UI in MVP, for a tenant admin or anyone else — a group can't be renamed or deleted through the product at all until that screen exists (🚫 not in MVP).

Data Attributes Table ​

Attribute NameDescriptionData TypeDefault ValueRequired (= Nullable)UniqueFormatValidationsIndexExample
idPrimary key of the entity.UUIDGenerated in code (app layer)YesYesUUID v7-Primary Key018ed0b3-c298-7c7a-96d5-8b36f5a7f8d2
tenantIdThe tenant this group belongs to. Set once at creation and never changed.UUID-YesNoUUID v7Foreign Key → tenant; must exist. Immutable after insert — a group cannot be moved between tenants.name: idx_groups_tenant, type: btree (active records)018fa51f-fda1-79f4-8461-2cb8f1cabc10
nameHuman-readable name of the group, shown in the UI (e.g. "Admins", "Accountants").String-YesYes (per tenant)-Non-empty-Accountants
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.-2026-09-01T00:00:00Z
updatedAtTimestamp of the last update. Set on insert (equal to createdAt) and updated on every change.Timestamp with time zonenow() — set in codeYesNoISO 8601Cannot be null.-2026-09-01T00:00:00Z
deletedAtTimestamp of soft deletion. Null means the group is active. Once set, immutable.Timestamp with time zone-NoNoISO 8601Immutable once set. Active records: WHERE deletedAt IS NULLname: idx_groups_deleted_at, type: btree (active records)-
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.-user:018e...

Uniqueness: (tenantId, name) is unique among active (non-deleted) records, enforced by unique index uidx_groups_tenant_name, type: btree — two groups in the same tenant cannot share a name. This constraint is an analysis-added convention (not stated in the original requirement) to keep a tenant's group list unambiguous; flag for confirmation if a different rule is wanted.

Deletion: A group with any live member cannot be deleted — see DELETE /v1/groups/{groupId} — so deleting a group (soft delete) never touches its members' user records, their groupMembership rows, or their derived tenantUser membership: by the time a group can be deleted, it has none. The group's own permissionSetGroupAssignment rows are soft-deleted along with it.

Audited fields ​

Recorded (created/deleted: full set; updated: changed fields only): name.

Excluded:

  • id, createdAt, updatedAt, deletedAt, createdBy, updatedBy — redundant with the audit entry's own entityId/createdAt/createdBy, which already identify the record and the write.
  • tenantId — immutable after insert, and redundant with the audit entry's own tenantId (RLS scope column).