# First-run setup wizard

The browser wizard at `setup/index.php` provisions a new deployment: database, migrations, **deployment profile**, **mail transport**, **branding**, Stripe, admin user, and starter plans.

Tracked in [Ticket 215](../tickets/concluded/215-setup-wizard-profile-selection-and-mail.md) (profile + mail), and [Ticket 218](../tickets/concluded/218-setup-wizard-branding-configuration.md) (branding step). Profile semantics: [deployment profiles](./deployment-profiles.md). Branding keys and `Portal_SiteBranding`: [site branding](./site-branding.md).

## Step order

| Step | Purpose |
|------|---------|
| 1. Database | Connection string, optional socket, runs `php api/bin/migrate.php up`. Writes `db_*` keys to `api/config.local.php`. |
| 2. Profile & mail | **Site profile** (`site_profile`: `content_gateway`, `licensing`, or `tool_management`), **sender identity** (`from_name` from site display name, `from_address`), **mail transport** (`email_transport` and provider fields), **`disabled_modules`** defaults, and for **licensing** a generated **`solar_capture_api_key`**. |
| 3. Branding | **`site_public_name`** (the brand name shown in the navbar, login screen, and email templates) and optional **`site_logo_path`**. Defaults are profile-aware: `content_gateway` → "Portal"; `licensing` → "Solar Capture"; `tool_management` → "GuardGeese". Operators can pick "Use profile defaults" or "Custom" (override either field). Both keys land in `api/config.local.php` and are read by `Portal_SiteBranding` (Ticket 216). |
| 4. Stripe & admin | Stripe secret, optional webhook signing secret, **`admin_email`**, optional **`support_email`**. |
| 5. Admin user | First `is_admin` user. |
| 6. Plans | Creates Starter / Growth / Scale via `ScApi_PlanService::savePlan()` with **profile-aware** `plan_features` rows (machine `value` keys such as `solar_capture` or `monitoring_sites` where applicable). Sets **`setup_complete`** and copies **`support_email`** from **`admin_email`** if still empty. |
| 7. Done | Shows **recommended follow-ups** (cron, workers, Stripe price linkage, branding edits, etc.) and, for licensing, the **Solar Capture API key** once. |

## Automatic versus manual

**Written automatically during setup**

- Database credentials and migration application.
- `site_profile`, `disabled_modules`, mail transport fields, `from_name`, `from_address`.
- `site_public_name` and `site_logo_path` — either the per-profile default or the operator's custom values (Ticket 218).
- `license_key_pepper`, `bearer_secret`, `payment_webhook_secret`, rate-limit defaults, `stripe_secret_key`, `admin_email`, optional `support_email`.
- `solar_capture_api_key` when profile is **licensing** (random hex).
- Default plans and entitlement rows for the selected profile.

**Still manual (listed on the Done step and in operator runbooks)**

- Mapping Stripe products/prices to portal plans.
- Setting **`public_site_base_url`** for stable links in email (strongly recommended).
- External clients: WordPress / workers / cron host configuration.
- Optional modules (for example `enquiries`) and any change to **`disabled_modules`** after setup via **Admin → Extensions**.
- VM provisioning: remains off by default for **tool_management** until you explicitly enable the module — confirmed as an optional companion module by [Decision 2026-05-23](./decisions/2026-05-23-scalable-tool-management-scope.md), closing [Ticket 217](../tickets/concluded/217-scalable-tool-management-scope-reset.md).

## Mail transports

Supported `email_transport` values match `ScApi_EmailService` / `api/config.example.php`:

| Value | Config keys | Notes |
|-------|-------------|--------|
| `mail` | (none extra) | PHP `mail()`; depends on server MTA. |
| `smtp` | `smtp_host`, `smtp_port`, `smtp_username`, `smtp_password`, `smtp_encryption` (`tls`, `ssl`, `none`) | SMTP path in core may still delegate to `mail()` in MVP builds; transport choice is still persisted for production MTA configuration. |
| `sendgrid` | `sendgrid_api_key` | HTTP API. |
| `postmark` | `postmark_api_key` | Server token. |
| `resend` | `resend_api_key` | HTTP API. |

Re-running the profile step with an empty provider password/API field **preserves** the previous value in `config.local.php` when the same transport is selected (merge behaviour).

## Config keys introduced by setup

| Key | Meaning |
|-----|---------|
| `site_profile` | One of `content_gateway`, `licensing`, `tool_management`. |
| `support_email` | Optional operator support address; defaults to `admin_email` at end of plan step if unset. |

Existing keys unchanged: `disabled_modules`, `email_transport`, `from_name`, `from_address`, SMTP/API fields, Stripe secrets, etc.

## Related

- [Deployment profiles](./deployment-profiles.md)
- [Extension integration](./extension-integration.md) — `disabled_modules` semantics
- [Module contract](./module-contract.md) — cron / mail expectations
