# Data Model: Managed Tenant Onboarding Draft Identity & Resume Semantics ## Primary Entity: Onboarding Draft ### Backing Model - Existing model: `App\Models\TenantOnboardingSession` - Existing table: `managed_tenant_onboarding_sessions` - UI language: `Onboarding draft` ## Ownership & Authorization Model - The onboarding draft is a workspace-scoped workflow-coordination record, not a generic tenant-owned domain record. - `workspace_id` is mandatory for every draft. - `tenant_id` remains nullable under the constitution-approved onboarding exception because drafts can exist before tenant identification. - Once `tenant_id` is attached, authorization must enforce both workspace entitlement and tenant entitlement for draft access. - Before `tenant_id` is attached, workspace entitlement is sufficient. ## Entity Fields ### Existing or Required Core Fields | Field | Type | Purpose | |---|---|---| | `id` | bigint | Stable draft identity used in canonical route binding | | `workspace_id` | bigint | Workspace isolation boundary | | `tenant_id` | bigint nullable | Managed tenant database record when identified | | `entra_tenant_id` | string nullable | External tenant identity used before or during tenant materialization | | `started_by_user_id` | bigint nullable | Attribution for who created the draft | | `updated_by_user_id` | bigint nullable | Attribution for who last confirmed a change | | `current_step` | string nullable | Diagnostic and audit marker only | | `state` | json nullable | Confirmed non-secret draft state | | `completed_at` | timestamp nullable | Marks the draft as completed | | `cancelled_at` | timestamp nullable | Marks the draft as cancelled | | `created_at` | timestamp | Created timestamp | | `updated_at` | timestamp | Last persisted update timestamp | ### Recommended State Payload Keys Confirmed draft state should continue to allow only non-secret, confirmed keys such as: - `entra_tenant_id` - `tenant_id` - `tenant_name` - `environment` - `primary_domain` - `notes` - `provider_connection_id` - `selected_provider_connection_id` - `verification_operation_run_id` - `verification_run_id` - `bootstrap_operation_types` - `bootstrap_operation_runs` - `bootstrap_run_ids` ## Lifecycle Semantics ### Resumable A draft is resumable when all of the following are true: - It belongs to the current workspace. - It is authorized for the current actor. - It is not completed. - It is not cancelled. ### Non-Resumable A draft is non-resumable when any of the following are true: - It is completed. - It is cancelled. - It is outside the current workspace. - It is inaccessible due to authorization. ## Derived Projections ### Draft Stage Derived from confirmed state, not only from `current_step`: 1. No identified tenant data: `identify` 2. Tenant identified but no provider connection selected: `connect-provider` 3. Provider connection selected but verification cannot proceed or is incomplete: `verify-access` 4. Verification complete but bootstrap pending: `bootstrap` 5. Bootstrap confirmed and ready for final review or activation: `review` 6. Completed: `completed` 7. Cancelled: `cancelled` ### Draft Summary Metadata Used by the landing picker and header banner: - Tenant display name - External tenant identifier - Environment - Current stage label - Started by display name - Last updated by display name - Last updated at - Draft age - Verification stale or blocked hint ## Sensitive Data Rule The following values must never be stored in the draft payload for UI rehydration: - Client secrets - Raw credential material - Temporary authorization tokens - Any secret provider credential value The draft may store references to related provider connections or verification runs, but not secrets.