# Provisioning Go-Live: Credential Checklist and Operations Guide

Every provisioning step is credential-gated: when credentials are present, real API calls
are made. When credentials are absent, the step completes with a stub note and manual fallback.
The workflow is resumable — add credentials and retry the job.

---

## Architecture: What Each Step Does End-to-End

```
create_supabase_project
  ├─ POST /v1/projects                  → creates Supabase project
  ├─ GET  /v1/projects/{ref}  (polls)   → waits for ACTIVE_HEALTHY
  ├─ GET  /v1/projects/{ref}/api-keys   → fetches anon_key + service_role_key
  └─ stores ref, url, anon_key, service_role_key → cp_tenant_environments

apply_schema_migration
  ├─ GET  /v1/projects/{ref}/database/migrations  → checks if v001_initial_schema applied
  └─ POST /v1/projects/{ref}/database/migrations  → applies lib/provisioning/migrations/v001-initial.ts

configure_storage
  └─ no API call — Supabase auto-provisions default storage on project creation

set_secrets
  ├─ POST /v9/projects/{id}/env         → pushes env vars to Vercel (5 vars)
  ├─ GET  /v6/deployments               → finds latest production deployment
  ├─ POST /v13/deployments              → triggers redeployment with new env vars
  └─ stores vercel_deployment_id → step output (read by activate_tenant)

seed_branding                           → inserts cp_branding_profiles record
seed_modules                            → validates plan assignment

create_tenant_admin
  └─ POST {supabase_url}/auth/v1/admin/users  → creates masteradmin user in tenant Auth

activate_tenant
  ├─ GET  /v13/deployments/{id}         → DEPLOYMENT GATE: verifies state === READY
  └─ UPDATE cp_tenants SET status = ACTIVE
```

**Critical invariant**: `activate_tenant` will not set the tenant ACTIVE unless the
Vercel deployment that carries the provisioned env vars has reached `state: READY`.
This prevents activating a tenant whose app is still running on old/missing config.

---

## Required Environment Variables (Owner Portal Deployment)

### Supabase Management API

| Variable | Where to get it | Purpose |
|---|---|---|
| `SUPABASE_MANAGEMENT_API_KEY` | app.supabase.com → Account → Access tokens | Creates projects, fetches API keys, applies migrations |
| `SUPABASE_ORGANIZATION_ID` | app.supabase.com → Organization settings → General | Which org new projects are created in |
| `SUPABASE_DEFAULT_REGION` | Default: `eu-central-1` | Region for new projects |

### Vercel

| Variable | Where to get it | Purpose |
|---|---|---|
| `VERCEL_API_TOKEN` | vercel.com → Account → Tokens | Pushes env vars and triggers redeployment |
| `VERCEL_TEAM_ID` | vercel.com → Team Settings → General | Required for team-scoped projects (optional for personal) |

### No TENANT_SCHEMA_SQL Required

The schema migration is embedded in `lib/provisioning/migrations/v001-initial.ts` and
applied via the Supabase Management API migrations endpoint. No env var needed.
When the schema changes, add a new file `v002-*.ts` and extend `STEP_ORDER` if a new
step is needed, or apply it as a continuation of the migration chain.

---

## Required DB Values per Tenant

Table: `cp_tenant_environments`

| Column | Set by | Required by | Notes |
|---|---|---|---|
| `supabase_project_ref` | `create_supabase_project` | `apply_schema_migration`, `configure_storage` | Short project ID, e.g. `dvvlyqlvaxcn` |
| `supabase_url` | `create_supabase_project` | `set_secrets`, `create_tenant_admin` | `https://{ref}.supabase.co` |
| `supabase_anon_key` | `create_supabase_project` (via API keys endpoint) | `set_secrets` | Publishable — safe to expose to browser |
| `supabase_service_role_key` | `create_supabase_project` (via API keys endpoint) | `create_tenant_admin` | **Privileged** — see Security Notes |
| `vercel_project_id` | Pre-set manually before provisioning | `set_secrets` | The Vercel project for this tenant's deployment |

**`vercel_project_id` must be set before triggering provisioning.** It cannot be created
automatically — the Vercel project must exist and be connected to the tenant app repository.

---

## Secret Security Model

### supabase_anon_key
- Publishable. Designed to be used client-side.
- Stored in `cp_tenant_environments` and pushed to Vercel as `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY`.
- No special handling needed.

### supabase_service_role_key
- **Highly privileged**: bypasses RLS, full DB admin access.
- Stored in `cp_tenant_environments` under RLS: only the control plane service_role can read it.
- Used once by `create_tenant_admin` to create the initial admin user.
- **Recommended post-provisioning**: rotate this key in Supabase and update the DB, or delete it
  from `cp_tenant_environments` if no further automated admin operations are needed.
- For ongoing admin automation (future steps), prefer Supabase's role-specific tokens or
  a secret manager (Vault, AWS Secrets Manager, Vercel Environment Secrets).

```sql
-- After provisioning is complete and key is no longer needed:
UPDATE cp_tenant_environments
SET supabase_service_role_key = NULL
WHERE tenant_id = '{tenantId}';
```

### SUPABASE_MANAGEMENT_API_KEY
- Owner-portal-level secret. Store in Vercel project settings for the owner portal deployment.
- Never stored in the DB or pushed to tenant projects.
- Rotate periodically via app.supabase.com → Account → Access tokens.

---

## Go-Live Sequence (Automated Path)

**Prerequisites:**
- [ ] All env vars above set in owner portal Vercel project settings
- [ ] Tenant record created in `cp_tenants` with `plan_id` and `contact_email`
- [ ] Vercel project created for tenant and `vercel_project_id` set in `cp_tenant_environments`

**Trigger:**
```
POST /api/owner/tenants/{tenantId}/provision
```

**Workflow execution (automatic, sequential):**
1. `create_supabase_project` — creates project, polls until ready, fetches + stores API keys
2. `apply_schema_migration` — applies `v001_initial_schema` via migrations endpoint
3. `configure_storage` — confirms default storage (no API call)
4. `set_secrets` — pushes 5 env vars to Vercel, triggers redeployment, stores `vercel_deployment_id`
5. `seed_branding` — creates initial branding profile
6. `seed_modules` — validates plan assignment
7. `create_tenant_admin` — creates masteradmin user using stored service_role_key
8. `activate_tenant` — **verifies Vercel deployment is READY**, then sets status = ACTIVE

**Typical timeline:** 5–10 minutes total (dominated by Supabase project provision + Vercel build).

**If `activate_tenant` fails with "deployment not ready":**
The deployment is still building. Wait 2–3 minutes, then retry the job:
```
POST /api/owner/provisioning/{jobId}/retry
```
All prior steps are COMPLETED and will be skipped. Only `activate_tenant` re-runs.

---

## Manual Fallback Procedure

If any required credential is missing, the step completes with a stub note.
Complete the step manually, then mark it COMPLETED in the DB:

```sql
UPDATE cp_provisioning_job_steps
SET status = 'COMPLETED',
    output_data = '{"note": "completed manually", "by": "ops-{name}"}'::jsonb,
    completed_at = now()
WHERE job_id = '{jobId}'
  AND step_name = 'create_supabase_project';
```

### Step 1: create_supabase_project (manual)
1. app.supabase.com → New project (org: KADi, region: eu-central-1)
2. Copy: Project Reference, Project URL, `anon` key, `service_role` key (Settings → API)
3. Store in control plane DB:
```sql
INSERT INTO cp_tenant_environments
  (tenant_id, supabase_project_ref, supabase_url, supabase_anon_key, supabase_service_role_key)
VALUES
  ('{tenantId}', '{ref}', 'https://{ref}.supabase.co', '{anonKey}', '{serviceRoleKey}')
ON CONFLICT (tenant_id) DO UPDATE SET
  supabase_project_ref = EXCLUDED.supabase_project_ref,
  supabase_url = EXCLUDED.supabase_url,
  supabase_anon_key = EXCLUDED.supabase_anon_key,
  supabase_service_role_key = EXCLUDED.supabase_service_role_key;
```

### Step 2: apply_schema_migration (manual)
```bash
# Option A: Supabase CLI (preferred)
supabase db push --project-ref {ref}

# Option B: SQL editor
# Open Supabase dashboard → SQL editor → run supabase/bootstrap/supabase-schema.sql
```

### Step 3: configure_storage
No action needed — auto-configured.

### Step 4: set_secrets (manual)
In Vercel project settings → Environment Variables:
```
NEXT_PUBLIC_SUPABASE_URL            = https://{ref}.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY = {anonKey}
TENANT_ID                           = {tenantId}
NEXT_PUBLIC_APP_ENV                 = production  (for Production env)
NEXT_PUBLIC_APP_ENV                 = staging     (for Preview env)
```
Then trigger a deployment: `vercel deploy --prod` or push to main branch.

**IMPORTANT**: Do not mark this step complete and do not activate the tenant until the
Vercel deployment that carries these vars is `READY`. Check status:
```bash
vercel ls --project {projectId}
```
Or in the Vercel dashboard → Deployments → confirm the latest production deployment is `Ready`.

### Steps 5–6: seed_branding, seed_modules
Run automatically when provisioning job is retried.

### Step 7: create_tenant_admin (manual)
Supabase dashboard → {project} → Authentication → Users → Add user:
- Email: tenant contact email
- Email confirm: checked

### Step 8: activate_tenant
Run automatically when job is retried — after confirming deployment is READY.

---

## Environment Verification Checklist (before triggering provisioning)

- [ ] `SUPABASE_MANAGEMENT_API_KEY` set in owner portal Vercel env
- [ ] `SUPABASE_ORGANIZATION_ID` set in owner portal Vercel env
- [ ] `VERCEL_API_TOKEN` set in owner portal Vercel env
- [ ] `VERCEL_TEAM_ID` set (if using a team project)
- [ ] `cp_tenants`: tenant has `plan_id` assigned
- [ ] `cp_tenants`: tenant has `contact_email` set
- [ ] Vercel project exists for tenant deployment (linked to repo + branch)
- [ ] `cp_tenant_environments`: `vercel_project_id` set for tenant
