# Kualifi — deployment runbook (cPanel, no SSH, no Terminal)

Ordered checklist for putting kualifi.com live. Every step is doable from the
cPanel UI; CLI scripts run via one-shot cron jobs (this host has no Terminal).

**Actual deployed layout** (differs from the original template): the project
root is `/home/dalecarn/public_html/addons/kualifi/` and the kualifi.com
addon-domain docroot is its `public/` folder. That works — `config/`, `sql/`,
`src/`, `bin/`, `uploads/` are above the docroot so kualifi.com cannot serve
them, and the parent domain returns 404 for those paths (verified 29 Jul 2026).
The deny-all `.htaccess` files in each of those folders are the backstop.

## 1. Domain + files

1. Register **kualifi.com** and point its nameservers/DNS at the hosting
   account.
2. cPanel → **Addon Domains** → add `kualifi.com`. Set the document root to a
   fresh directory, e.g. `/home/dalecarn/public_html/addons/kualifi/public`.
3. Upload the repo so the layout on the server mirrors the repo, with ONLY
   `public/` inside the docroot:

   ```
   /home/dalecarn/public_html/addons/kualifi/
     config/        <- outside webroot (has credentials)
     src/
     sql/
     bin/
     uploads/esg/   <- evidence files, outside webroot, must be writable
     public/        <- the addon domain's document root
   ```

4. Confirm `public/.htaccess` uploaded (dotfiles are easy to miss in File
   Manager — enable "Show Hidden Files").

## 2. Database

1. cPanel → **MySQL Databases**: create database `dalecarn_kualifi` and user
   `dalecarn_kualifi_app` with a strong generated password; grant ALL on the DB.
2. phpMyAdmin → select the database → **Import tab** (never paste into the SQL
   box — large pastes truncate silently) → import `sql/schema.sql`, then
   `sql/seed_standard_sirim55.sql`. If the file is too big, gzip it first —
   the Import tab accepts `.sql.gz`.
3. Sanity check: `standards` has the SIRIM55 row, `std_requirements` /
   `assessment_questions` are populated, `plans` has the `free` plan.

## 3. Config

Edit `config/config.php` on the server:
- `DB_NAME` / `DB_USER` / `DB_PASS` — the real values from step 2 (cPanel
  prefixes the names with the account name).
- `APP_BASE_URL` — confirm `https://kualifi.com` (no trailing slash).
- Leave everything else as committed.

Enable **AutoSSL / Let's Encrypt** for the addon domain (cPanel → SSL/TLS
Status) before testing — the session cookie is marked secure on HTTPS.

## 4. Anthropic key

1. In the Anthropic Console, create a dedicated API key for Kualifi under a
   funded account. Decide a **monthly spend cap** while you're there — every
   tenant's document generation bills this key (quota default: 20 docs/tenant/
   month, padmin-adjustable).
2. Create the file at the exact path configured in `ANTHROPIC_KEY_FILE`
   (currently `.../addons/kualifi/config/kualifi-anthropic.key`), containing
   the key on one line; set permissions to 600 in File Manager. The folder is
   above the kualifi.com docroot and carries a deny-all `.htaccess`, and
   `*.key` is gitignored — keep it out of OneDrive/local copies regardless.

## 5. First platform admin (via one-shot cron)

No Terminal on this host — use a temporary cron job:

1. cPanel → **Cron Jobs** → new job, schedule **Once Per Minute**.
2. Command (password ≥ 12 chars; avoid `%` and `!` — cron/shell mangle them):

   ```bash
   php /home/dalecarn/public_html/addons/kualifi/bin/create_padmin.php "Your Name" you@example.com "a-long-password" >> /home/dalecarn/padmin-setup.log 2>&1
   ```

3. After a minute, check `/home/dalecarn/padmin-setup.log` in File Manager —
   it should say `Created platform admin #1 (...)`.
4. **Delete the cron job** (the command line contains the password) and the
   log file, then sign in at `https://kualifi.com/padmin/`.

Re-running the same command later with a new password resets that admin's
password — that is also the recovery path if it's ever forgotten.

## 6. Reminder cron

cPanel → **Cron Jobs** → daily, early MYT (e.g. `0 7 * * *`):

```bash
php /home/dalecarn/public_html/addons/kualifi/bin/reminders.php >> /home/dalecarn/kualifi-cron-debug.log 2>&1
```

The `>>` capture means a wrong path or fatal error lands in
`kualifi-cron-debug.log` instead of vanishing. On a healthy run it records
`START: lead time 14 day(s).` and the script's own log appears at
`/home/dalecarn/public_html/addons/kualifi/kualifi-reminders.log`. The job
also prunes expired email tokens.

## 6b. Applying updates (30 Jul 2026 — brand redesign)

For the Kualifi brand redesign + four-tier readiness release.
**Run the database migrations BEFORE uploading files** — the new code queries
columns the migrations add and every page 500s until they exist, while the
currently-deployed code is entirely unaffected by the new columns. Migration
first = zero downtime; upload first = minutes of outage.
1. phpMyAdmin → the kualifi database → Import → run
   `sql/migrate_01_four_tier.sql` ONCE (renames outcome bands, recomputes
   stored scores; safe on existing data), then
   `sql/migrate_02_state_holidays.sql` ONCE (adds per-state columns; existing
   companies stay national/Mon–Fri until an Owner picks a state in Team), then
   `sql/migrate_03_ai_cost_tracking.sql` ONCE (adds token + USD cost columns
   to ai_usage; padmin → Tenants then shows per-client AI spend. Rows from
   before the migration show US$0.00 — cost unknown).
2. Upload every changed file (`public/`, `src/`, `sql/`) **including the new
   `public/assets/img/` logo folder** — mirror the repo layout.
3. Mirror the one changed config line on the server:
   `const APP_TAGLINE = 'Making ESG readiness achievable.';`
4. Hard-refresh the site — the stylesheet name is unchanged, so browsers may
   cache the old theme.
5. **Pre-rebrand AI documents**: documents generated before the redesign may
   name SIRIM in their body. Check with
   `SELECT id, company_id, title FROM esg_documents WHERE content LIKE '%SIRIM%';`
   and regenerate (or hand-edit) any hits — the interim rule covers generated
   documents too.

## 7. Email deliverability (do not skip)

Verification emails are the front door — if they land in spam, nobody can
register.

1. cPanel → **Email Deliverability** → kualifi.com → install the suggested
   **SPF** and **DKIM** records (if DNS is at the registrar, copy the records
   there).
2. Create the mailbox or forwarder for `noreply@kualifi.com` (some hosts
   refuse to send from an address that doesn't exist).
3. Send a test registration to a Gmail address and check "Show original" —
   SPF and DKIM should both say PASS.

## 8. Smoke test (end-to-end)

1. Register a test company → verification email arrives (not spam) → verify →
   sign in.
2. Take the public Readiness Assessment as a guest, then register with the
   claim link — the score should appear on the dashboard.
3. Run the 8-step setup wizard; generate at least one AI document (proves the
   key file + model work); approve it as Owner.
4. Dashboard: duties appear with dates; complete one and revert it.
5. Attach an evidence file to a requirement; download it back.
6. Print-preview the Certification Pack.
7. `/padmin/`: sign in, impersonate the test company (banner appears), stop
   impersonating, check the audit log recorded both.
8. Password reset round-trip.

## 9. Before PUBLIC launch (not needed for a private pilot)

- **SIRIM 55:2023 content licensing** — explicitly deferred; resolve before
  charging customers or marketing publicly (see `DECISIONS.md`).
- Payment gateway + pricing when billing switches on.
- A backup plan: cPanel backup schedule must cover the DB **and**
  `/home/dalecarn/public_html/addons/kualifi/uploads/` (tenant evidence lives there, not in git).
