# Installer Overview

The Installer package is the temporary browser setup layer for a new Capell application. It collects install input, runs preflight checks, delegates install work to Core Actions, gives the user a progress/report surface, and can remove itself once the site is ready.

Core owns the install primitives. Installer owns the web routes, views, Filament setup pages, dashboard warning, install guide patches, installer session state, and post-install cleanup.

## Request Flow

| Step                 | Owner                                                                            | What happens                                                                                                                                                                                                                      |
| -------------------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Show `/install`      | `InstallController::show()`                                                      | Regenerates the CSRF token, builds page data with `BuildInstallerPageDataAction`, and shows active install status if the current session may see it.                                                                              |
| Build form state     | `BuildInstallerPageDataAction`                                                   | Reads package metadata from `CapellCore::getPackages()`, fetches downloadable package metadata when available, builds theme options, language options, existing user options, default package selections, and a preflight report. |
| Submit install       | `InstallController::store()`                                                     | Validates the form and converts it to `InstallInputData` through `InstallInputFactory`. The browser flow usually uses step-based JSON requests; non-AJAX and queued installs are still supported.                                 |
| Run preflight        | `InstallerPreflight`                                                             | Checks PHP extensions, process support, PHP/Composer/Git binaries, writable paths, database readiness, cache readiness, queue readiness, and Composer dry-runs when optional packages are selected.                               |
| Run install steps    | `RunInstallStepAction` in Core                                                   | Installer stores the input and plan in cache, then calls one step at a time so the browser can keep showing progress and retry around CSRF refreshes.                                                                             |
| Show progress/report | `InstallerSessionRepository`, `FileLogProgressReporter`, `CacheProgressReporter` | Progress lines and status are kept under `capell.install.{installId}.*` cache keys for two hours. Reports can be downloaded from the progress route by the same install session.                                                  |
| Finish cleanup       | `RemoveSetupPackageAction`                                                       | The success page enables installer removal for the current session. The delete route removes `capell-app/installer` from the host app.                                                                                            |

`EnsureNotInstalled` guards the main install routes. It treats the app as installed when the Admin package is installed, the `sites` table exists, and at least one site exists. Reinstall is allowed only when `CAPELL_SETUP_ALLOW_REINSTALL` is true, which defaults to `APP_DEBUG`.

## Install Guide Patches

Install guide patches are the installer-owned extension point. A patch implements `Capell\Installer\Support\InstallGuide\Patch`:

| Method                      | Purpose                                                                                 |
| --------------------------- | --------------------------------------------------------------------------------------- |
| `id()`                      | Stable machine key used by the install guide UI and tests.                              |
| `group()`                   | Groups related checks in the guide.                                                     |
| `label()` / `description()` | Human copy shown to the installer user.                                                 |
| `docUrl()`                  | Optional link to the longer setup guide.                                                |
| `defaultEnabled()`          | Whether the patch should be selected by default when it can run.                        |
| `probe()`                   | Returns a `PatchStatus` describing whether the host already matches the expected state. |
| `reason()`                  | Explains why a patch is needed or why it cannot run.                                    |
| `apply()`                   | Makes the smallest idempotent host-file change.                                         |

The service provider registers first-party patches in `registerPatches()` through `PatchRegistry`. Add new first-party patches there, keep them idempotent, and cover each patch with a focused test under `packages/installer/tests/Feature/InstallGuide/Patches`.

Patch code should use the installer patching helpers (`EnvFileEditor`, `PhpFileEditor`, `ConfigArrayEditor`) instead of raw string edits when possible. A patch that only documents manual work should stay explicit, as the `DocOnly*Patch` classes do.

## Theme And Package Selection

The installer always has safe theme choices from `ThemePackageCandidates`, including `none`, `foundation`, `corporate`, and `saas` when available. Installed and downloadable package metadata can add more choices with a theme key, package name, description, requirements, and preview image.

Default selected packages come from two places:

- trusted core package defaults from Core;
- `CAPELL_SETUP_DEFAULT_PACKAGES`, filtered to packages that are available to the installer.

Optional Composer packages are shown only when the Composer package can be resolved by the web PHP process. If a package is missing from the installer, check Composer repository access before changing UI code.

## Fresh Database Safety

Fresh apps may configure database-backed sessions or cache before the required tables exist. `InstallerServiceProvider::fallbackDatabaseDriversIfNeeded()` switches the current web request to file-backed session/cache when those database tables are missing. This is request-scoped and only runs for web requests.

The installer still refuses to start when the active cache store cannot store progress. If the progress request says the cache store is unavailable, either create the cache table, temporarily use `CACHE_STORE=file`, or run the CLI installer.

## Debugging

| Symptom                                                     | Likely cause                                                        | Check                                                                                                | Fix                                                                                                        |
| ----------------------------------------------------------- | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `/install` shows an already-installed notice on a fresh app | The Admin package, `sites` table, and a site row exist              | `php artisan tinker` then `Capell\Installer\Support\InstallerInstallationState::capellIsInstalled()` | Use the installed admin, or set `CAPELL_SETUP_ALLOW_REINSTALL=true` only for a controlled local reinstall. |
| `/install` 404s                                             | Package provider or route file is not loaded                        | `php artisan route:list --name=capell-installer`                                                     | Confirm `capell-app/installer` is installed/discovered and run `php artisan optimize:clear`.               |
| Preflight blocks on `proc_open`                             | Web PHP cannot run subprocesses                                     | Check PHP disabled functions for the web SAPI                                                        | Enable `proc_open` or run the CLI installer from an environment that can run Composer and Artisan.         |
| Preflight warns about PHP binary                            | `CAPELL_SETUP_PHP_BINARY` points at FPM or a missing binary         | `php artisan config:show capell-installer.php_binary`                                                | Set `CAPELL_SETUP_PHP_BINARY` to the CLI PHP executable and clear config cache.                            |
| Composer dry-run fails                                      | Web PHP cannot see Composer, Git, credentials, or path repositories | `composer config repositories` and the preflight report                                              | Set `CAPELL_SETUP_COMPOSER_BINARY`, fix Composer auth/repositories, then restart the installer.            |
| Progress page says the install session expired              | `capell.install.{installId}.*` cache keys expired or were cleared   | Check cache driver and `capell.install.lock`                                                         | Restart the installer. For long installs, use a persistent cache store and keep queue workers alive.       |
| Another install appears locked                              | `capell.install.lock` still points at a queued/running install      | Inspect the cache key or revisit `/install` from the same browser                                    | Cancel from the UI, wait for the lock TTL, or clear only the installer lock in a local/dev environment.    |
| Guide patch is unavailable                                  | `probe()` found an unsafe or already-satisfied host state           | Read the patch `reason()` in the UI/report                                                           | Fix the host file manually or update the patch probe/apply pair with a regression test.                    |
| Success page still exposes installer removal/report URLs    | Installer package remains installed after setup                     | `composer show capell-app/installer`                                                                 | Remove the package or restrict access. Treat progress/report URLs as bootstrap-only secrets.               |

## Verification

Run the package test suite after changing installer routes, validation, preflight checks, patching, session state, or cleanup:

```bash
vendor/bin/pest packages/installer/tests --configuration=phpunit.xml
```

Run the browser installer spec after changing the web flow or CSRF retry behavior:

```bash
npm run test:installer-browser
```

Run screenshot checks when installer pages or visual states change:

```bash
npm run screenshots
npm run screenshots:check
```