# Data Model — RBAC Troubleshooting & Tenant UI Bugfix Pack v1 **Spec**: [spec.md](spec.md) No new persistent tables are required for v1. Diagnostics findings are computed at runtime from existing tables. ## Entities ### Tenant (`tenants`) **Purpose**: Tenant-plane scope boundary, lifecycle state, and tenant-scoped configuration root. **Key fields (existing)**: - `id` (bigint, PK) - `tenant_id` (string GUID, external Microsoft Entra tenant identifier) - `external_id` (string, used as Filament tenancy slug; often mirrors `tenant_id`) - `status` (string, e.g. `active`, `archived`) - `deleted_at` (nullable timestamp, soft delete) - `name`, `environment`, `is_current`, `metadata` (assorted) **Lifecycle rules (existing)**: - Soft delete sets `status='archived'`. - Restore sets `status='active'`. ### TenantMembership (`tenant_memberships`) **Purpose**: Tenant membership boundary + role assignment. **Key fields (existing)**: - `id` (uuid, PK) - `tenant_id` (bigint, FK → `tenants.id`) - `user_id` (bigint, FK → `users.id`) - `role` (enum: `owner`, `manager`, `operator`, `readonly`) - `source` / `source_ref` (provenance) - `created_by_user_id` (nullable FK) **Constraints (existing)**: - Unique `(tenant_id, user_id)` - Index `(tenant_id, role)` ### DiagnosticsFinding (computed, not persisted) **Purpose**: Represent a detected integrity/operational issue for the current tenant. **Proposed shape (runtime DTO / array)**: - `id` (string, stable key like `missing_owner`) - `severity` (string, e.g. `warning`/`critical`) - `title` (string) - `description` (string) - `repair_actions` (array of available actions given actor capabilities) ## Derived rules / invariants - **Missing owner**: `tenant_memberships` has zero rows with `role='owner'` for the tenant. - **Duplicate membership**: more than one membership row exists for a given `(tenant_id, user_id)` (should be prevented by DB uniqueness; diagnostics treats this as “historical/edge-case”). - **Identifier misuse risk**: any query for membership/tenant scoping must use internal tenant key (`tenants.id`) and not the GUID. ## State transitions - Tenant status: - `active` → `archived` on soft delete - `archived` → `active` on restore - Membership role transitions: - Allowed transitions are existing (`owner`, `manager`, `operator`, `readonly`) but last-owner demotion/removal is prohibited.