# Phase 1 — Data Model (Spec 114: System Console Control Tower) This feature is primarily **read-only UI** over existing platform/ops metadata. ## Entities (existing) ### Workspace (`workspaces`) - Purpose: group tenants; scope boundary for tenant plane. - Relevant fields: `id`, `name`, `slug`, timestamps. - Relationships: - `Workspace::tenants()` - `Workspace::memberships()` ### Tenant (`tenants`) - Purpose: customer tenant inventory + onboarding/health metadata. - Relevant fields (high level): - `id`, `external_id`, `name`, `workspace_id`, `status`, `environment` - RBAC signals: `rbac_last_checked_at`, `rbac_last_setup_at`, `rbac_canary_results`, `rbac_last_warnings` - `metadata` (array) - Relationships: - `Tenant::providerConnections()` → `provider_connections` - `Tenant::permissions()` → `tenant_permissions` - `Tenant::auditLogs()` → `audit_logs` ### ProviderConnection (`provider_connections`) - Purpose: connectivity + health-check metadata for external provider. - Relevant fields: `provider`, `is_default`, `scopes_granted`, `last_health_check_at`, `metadata`. ### TenantPermission (`tenant_permissions`) - Purpose: cached/recorded permission checks. - Relevant fields: `key` (permission name), `status`, `details`, `last_checked_at`. ### OperationRun (`operation_runs`) - Purpose: canonical operations observability record (non-negotiable per constitution). - Relevant fields: - identity/scope: `workspace_id` (NOT NULL), `tenant_id` (nullable), `user_id` (nullable), `initiator_name` - lifecycle: `type`, `status` (`queued|running|completed`), `outcome` (`pending|succeeded|failed|canceled|…`) - audit UX: `summary_counts` (numeric-only keys), `failure_summary` (sanitized bounded array), `context` (sanitized/limited) - timing: `created_at`, `started_at`, `completed_at` ### AuditLog (`audit_logs`) - Purpose: security/audit trail. - Relevant fields: `workspace_id`, `tenant_id` (nullable), `actor_*`, `action`, `status`, `metadata` (sanitized), `recorded_at`. - System console relevant actions (already emitted): - `platform.auth.login` - `platform.break_glass.enter|exit|expired` ## Derived/Computed concepts (new, no new table) ### Time window - Enumerated: `1h`, `24h` (default), `7d`. - Used for Control Tower and for failures/stuck scoping. ### “Stuck” run classification - Definition: a run is “stuck” when: - `status=queued` and `created_at <= now() - queued_threshold_minutes` AND `started_at IS NULL`, OR - `status=running` and `started_at <= now() - running_threshold_minutes` - Thresholds are configurable (v1): - `system_console.stuck_thresholds.queued_minutes` - `system_console.stuck_thresholds.running_minutes` ### Tenant/workspace health badge - “Worst wins” aggregation over signals: - Tenant status (active/onboarding/archived) - Provider connection health/status - Permission status - Recent failed/stuck runs within the time window - Display-only; does not mutate state. ## Validation rules - Any operator-provided reason/note (break-glass, mark investigated): min length 5, max length 500. - Filters: only allow known enum values (time window, run status/outcome). ## Storage/indexing plan (Phase 2 tasks will implement) - `operation_runs`: - Indexes to support windowed queries and grouping: - `(workspace_id, created_at)` - `(tenant_id, created_at)` - optional: `(status, outcome, created_at)` and `(type, created_at)` depending on explain plans - `audit_logs`: - `(action, recorded_at)` and `(actor_id, recorded_at)` for Access Logs filters ## Notes on data minimization - Use `RunFailureSanitizer` + `SummaryCountsNormalizer` contracts. - Avoid rendering raw `context` by default; when displayed, cap size and redact sensitive keys.