Skip to main content
A Membership represents a user’s participation in a workspace, binding a Person (identified by their Firebase auth identity) to a Workspace with a specific role and lifecycle state. It is the central RBAC artifact in the multi-tenant architecture: every permission check, invitation flow, and workspace-access decision resolves through this record. Each Membership carries a role (owner, admin, member, guest), a status (pending or active), and a provenance flag that marks which workspace is the user’s default. A soft-deleted Membership means access has been revoked; the row is retained for audit purposes.

API operations

Data model

Attributes

Relationships

System-computed

  • membership_id is generated via gen_random_uuid() as a database default and also seeded via randomUUID() in the entity constructor, ensuring the UUID is available before the first flush.
  • created_at is set by the @Property onCreate lifecycle hook and is never subsequently modified.
  • updated_at is set by both the @Property onCreate and onUpdate lifecycle hooks; it reflects the most recent mutation to the row.
  • deleted_at is the soft-delete sentinel. Setting it to a non-null timestamp revokes access. Queries scoped to active memberships always filter deleted_at IS NULL.
  • is_default invariant: at most one active (status = active, deleted_at IS NULL) membership per firebase_id may have is_default = true. MembershipService.acceptInvitation computes isFirstWorkspace based on the firebase_id scope (not the Person scope) to determine whether to set is_default = true on the new membership. Migration20260424100000_backfill_membership_default_flag repairs any existing rows where this invariant was violated.
  • invite_token is a UUID generated at invitation creation and serves as a single-use bearer credential. The invitation flow is idempotent: re-sending an invite to the same email reuses the existing pending membership row rather than inserting a duplicate.
  • status transitions: pending (created at invite issuance) -> active (set atomically at acceptance). A failed mid-flow acceptance must leave the row in pending status with no membership access granted.
  • Two composite database indexes are maintained for hot-path queries: idx_memberships_firebase_workspace_deleted (firebase_id, workspace_pk, deleted_at) for per-user workspace resolution, and idx_memberships_workspace_role_deleted (workspace_pk, membership_role, deleted_at) for owner/admin lookup within a workspace (added in Migration20260416000000).

Example

Source: apps/api/src/database/entities/Membership.ts · domain: workspace · tier: Platform