# Control Plane Architecture

## Overview

The Owner Control Plane is the global management layer for all Supplier Pulse tenants. It is logically isolated from all tenant data and handles provisioning, configuration, licensing, and platform operations.

## Placement (V1 Decision)

The control plane lives at `/owner/` within the existing Next.js app, protected by a dedicated middleware guard. Control plane database tables use the `cp_` prefix in the same Supabase project.

This avoids a premature monorepo migration while maintaining clean domain boundaries. See `docs/adr/001-owner-portal-placement.md`.

---

## Domain Modules

### A. Tenant Catalog
Maintains the authoritative registry of all companies and their lifecycle state.

**Tables**: `cp_tenants`, `cp_tenant_environments`

**Responsibilities**:
- Create, read, update tenant metadata
- Manage lifecycle state transitions (draft → provisioning → active → suspended → archived)
- Map each tenant to its environment, plan, and branding profile
- Validate state transition legality

**State machine**:
```
DRAFT → PROVISIONING → ACTIVE → SUSPENDED → ARCHIVED
                ↓
        PROVISIONING_FAILED
```

---

### B. Provisioning Engine
Orchestrates environment setup for new tenants asynchronously.

**Tables**: `cp_provisioning_jobs`, `cp_provisioning_job_steps`

**Steps per job**:
1. `create_supabase_project` — provision new Supabase project (or configure schema)
2. `apply_schema_migration` — run tenant database migrations
3. `configure_storage` — create storage bucket with tenant namespace
4. `set_secrets` — store runtime credentials in secret manager
5. `seed_branding` — apply initial branding configuration
6. `seed_modules` — apply plan-based module enablement
7. `create_tenant_admin` — create the initial admin user
8. `activate_tenant` — flip lifecycle to ACTIVE

**Idempotency**: Each job has a stable `idempotency_key`. Each step records its completion status. Retries skip completed steps.

---

### C. License and Plan Management
Defines commercial packages and their feature entitlements.

**Tables**: `cp_plans`, `cp_features`, `cp_plan_feature_entitlements`, `cp_tenant_feature_overrides`

**Plan examples**:
- `starter`: CALENDAR, REPOSITORY, basic REPORTING
- `professional`: + LSC, OEE, PROJECTS
- `enterprise`: + ASSESSMENT, VSM, advanced REPORTING, branding customization

---

### D. Branding Engine
Stores and resolves tenant-specific UI configuration.

**Tables**: `cp_branding_profiles`

**Configuration fields**:
- `company_name` — displayed in header and documents
- `logo_url` — storage URL for company logo
- `primary_color` — overrides `--color-primary` CSS variable
- `primary_dark_color` — overrides `--color-primary-dark`
- `default_language` — `de` | `en` | `zh`
- `email_sender_name` — used in outbound emails
- `terminology_overrides` — JSON map of platform term → tenant term
- `pdf_header_text` — appears on all exported documents

---

### E. Owner Identity and Access
Manages users who operate the platform (not tenant users).

**Tables**: `cp_owner_users`, `cp_owner_roles`
**Extension**: `user_profiles.is_platform_owner boolean DEFAULT false`

**Owner roles**:
| Role              | Capabilities                                              |
|-------------------|-----------------------------------------------------------|
| PLATFORM_OWNER    | All actions, including destructive ops                    |
| PLATFORM_ADMIN    | Create/manage tenants, plans, modules — no destructive ops|
| SUPPORT_ENGINEER  | Read tenant data, impersonation (time-limited)            |
| BILLING_ADMIN     | Plan assignment, license management only                  |
| AUDITOR           | Read-only across all control plane domains                |

---

### F. Audit Log
Immutable append-only log of all owner-initiated actions.

**Table**: `cp_audit_events`

**Captured for every mutation**:
- `actor_id` — owner user performing the action
- `action` — verb (e.g., `tenant.created`, `module.enabled`, `plan.changed`)
- `entity_type` + `entity_id` — target of the action
- `before_snapshot` (jsonb) — previous state
- `after_snapshot` (jsonb) — new state
- `ip_address`, `user_agent`
- `created_at`

---

### G. Operational Controls
Short-lived runtime toggles for emergency use and staged rollout.

**Table**: `cp_release_rollouts`

**Types**:
- `KILL_SWITCH` — globally disable a module (overrides all entitlements)
- `BETA_ROLLOUT` — enable a feature for a subset of tenants
- `PREVIEW` — opt-in preview per tenant

---

## API Route Structure

```
/api/owner/
  tenants/
    GET, POST
    [tenantId]/
      GET, PATCH, DELETE
      provision/   POST — start provisioning job
      suspend/     POST
      archive/     POST
      modules/     GET, PATCH
      branding/    GET, PATCH
  provisioning/
    GET — list all jobs
    [jobId]/
      GET — job detail + steps
      retry/ POST
  plans/
    GET, POST
    [planId]/
      GET, PATCH, DELETE
      features/ GET, PATCH
  audit/
    GET — paginated event log with filters
  settings/
    GET, PATCH — owner portal settings
```

---

## Security Boundaries

- All `/api/owner/` routes require `is_platform_owner = true` claim verified server-side
- Owner users cannot access tenant data API routes
- Support impersonation is separately gated, time-limited, and logged
- Control plane tables have RLS policies that restrict access to service role only
- No cross-tenant query is possible from within a tenant deployment
