# Data Model: Provider Foundation v1 **Branch**: `061-provider-foundation` **Date**: 2026-01-24 **Spec**: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/spec.md` ## Entities ### ProviderConnection Represents a tenant-scoped connection to an external provider (v1: Microsoft). **Identity** - Scoped to a Suite Tenant (`tenant_id`). - Canonical target scope identifier for Microsoft: `entra_tenant_id` (GUID). - Uniqueness: `(tenant_id, provider, entra_tenant_id)` must be unique. **Fields (suggested)** - `id` (UUID) - `tenant_id` (FK → tenants) - `provider` (string; v1: `microsoft`) - `entra_tenant_id` (string; GUID) - `display_name` (string) - `is_default` (boolean; exactly one default per `(tenant_id, provider)`) - `status` (string enum): `connected | needs_consent | error | disabled` - `health_status` (string enum): `ok | degraded | down` - `scopes_granted` (json/jsonb; optional; stores observed granted scopes/permissions metadata) - `last_health_check_at` (timestamp nullable) - `last_error_reason_code` (string nullable; stable reason code) - `last_error_message` (string nullable; sanitized short message) - `metadata` (json/jsonb nullable; optional non-sensitive provider metadata) - timestamps **Indexes (suggested)** - Unique index: `(tenant_id, provider, entra_tenant_id)` - Partial unique index: `(tenant_id, provider)` where `is_default = true` - Indexes for filtering: `(tenant_id, provider, status)`, `(tenant_id, provider, health_status)` **State transitions (v1)** - `status`: typically `needs_consent`/`error` → `connected` after successful health check; `disabled` is an explicit admin action. - `health_status`: `ok` on successful check; `degraded/down` based on categorized failures (throttling vs outage vs auth). --- ### ProviderCredential Represents securely stored credentials for exactly one provider connection. **Identity** - 1:1 with ProviderConnection (`provider_connection_id` unique). **Fields (suggested)** - `id` (UUID) - `provider_connection_id` (FK → provider_connections, unique) - `type` (string; v1: `client_secret`) - `payload` (encrypted JSON/array) - required keys (v1): `client_id`, `client_secret` - optional key: `tenant_id` (should match ProviderConnection `entra_tenant_id` if stored) - timestamps **Constraints (suggested)** - `payload` must be encrypted at rest. - `payload` must never be exposed via UI/API serialization. - If `payload.tenant_id` is stored, validate it matches `provider_connections.entra_tenant_id`. --- ### DefaultProviderConnection (concept) Not a separate table by default; represented by `provider_connections.is_default = true`. Rules: - Multiple Microsoft connections per Suite Tenant are allowed. - Exactly one default connection exists per `(tenant_id, provider)` and is used when starting operations without an explicit connection selection. --- ### OperationRun (existing) Provider operations are tracked as `operation_runs` with provider context stored in the `context` JSON. **Provider context fields (suggested)** - `provider`: `microsoft` - `provider_connection_id`: UUID - `target_scope`: `{ "entra_tenant_id": "" }` - `module`: `health_check | inventory | compliance` - `selection`: optional selectors/filters for the operation (DB-only) - `idempotency`: fingerprint/hash inputs used for dedupe **Concurrency rules (from spec clarifications)** - Same operation type + same scope: dedupe → return the active run. - Different operation type while any run is active for the same scope: block as “scope busy” and link to the active run.