# Going live — deployment runbook

This is the concrete, step-by-step path from "runs on my machine" to "reachable on a real
domain." It's written so you can follow it top to bottom.

**Honest limits of what I (the assistant) can do here:** I can't create accounts on your
behalf, enter payment details, register a domain, or obtain real PayPal/AdSense credentials
— those need your own accounts and your own decisions about payment, and I'm not able to
verify an actual deploy from this environment (no Docker, no hosting credentials). Everything
below is the exact configuration to use; I'd recommend doing a first pass on a throwaway
subdomain before pointing your real domain at it.

## 0. What's already production-ready in the code

No further code changes are needed for any of this — it's already implemented:

- `secure` session cookies, `helmet`, CORS as an explicit allowlist (never `*`), a global
  rate limit, graceful shutdown on SIGTERM/SIGINT, `TRUST_PROXY` support for correct
  per-visitor rate limiting behind a real reverse proxy.
- `GET /api/v1/health` → `{status:"ok"}` for a host's health check.
- Every secret (PayPal, AdSense, admin bootstrap, analytics key) is env-only, never in code.

## 1. Pick where things run

Recommended split (matches this repo's shape — a Next.js app and a separate Fastify API):

| Piece | Where | Why |
|---|---|---|
| `apps/web` (Next.js) | **Vercel** | Built for Next.js, zero-config for this framework. |
| `apps/api` (Fastify) + Postgres | **Railway** or **Render** | Simple Node + managed Postgres in one place, no Dockerfile needed (buildpack). |

You don't have to use these two specifically — any Node host works for the API, any static/
edge host works for the Next.js app — but the steps below assume this split.

## 2. Switch the database to Postgres

Local dev intentionally uses SQLite (zero setup). Production uses Postgres
(`apps/api/prisma/schema.prisma`'s own comment already says this). Once you've provisioned a
Postgres database (Railway/Render both offer one with one click, or use Neon/Supabase):

1. In `apps/api/prisma/schema.prisma`, change:
   ```prisma
   datasource db {
     provider = "sqlite"      // change to:
     provider = "postgresql"
     url      = env("DATABASE_URL")
   }
   ```
2. **Delete `apps/api/prisma/migrations/`** — the existing migration files contain
   SQLite-specific SQL and will not apply to Postgres. This is expected, not a mistake.
3. With `DATABASE_URL` pointed at your real Postgres instance, run:
   ```bash
   cd apps/api
   npx prisma migrate dev --name init
   ```
   This generates a fresh, Postgres-native set of migrations and applies them. Commit the new
   `prisma/migrations/` folder.
4. From here on, every future schema change uses `prisma migrate dev` locally against a local
   Postgres (or `prisma migrate deploy` in CI/production) — the SQLite/Postgres split is a
   one-time switch, not something you toggle back and forth.

If you'd rather keep local dev exactly as it is today (SQLite) while only production uses
Postgres, do steps 2–3 in a separate branch/checkout used only for generating the production
migrations, then merge just the new `migrations/` folder and the provider line back — Prisma
schemas don't support a provider that varies by environment.

## 3. Deploy the API (Railway or Render)

Both platforms deploy directly from a Git repo without a Dockerfile — set:

- **Root directory**: the repo root (needed so npm workspaces can resolve `@praxao/core`)
- **Build command**:
  ```bash
  npm ci && npm run build -w packages/core && npx prisma generate --schema apps/api/prisma/schema.prisma && npm run build -w apps/api
  ```
- **Start command**:
  ```bash
  node apps/api/dist/index.js
  ```
- **Release / one-off command** (run once after the first deploy, and again after any future
  schema change): `npx prisma migrate deploy --schema apps/api/prisma/schema.prisma`

Environment variables to set on the API host (see `apps/api/.env.example` for the full list
with explanations) — at minimum:

```
DATABASE_URL=<your Postgres connection string>
NODE_ENV=production
CORS_ORIGINS=https://yourdomain.com
TRUST_PROXY=true
ADMIN_BOOTSTRAP_EMAIL=<a real email you control>
ADMIN_BOOTSTRAP_PASSWORD=<a real, strong password — change it from this account's own Profile page the moment you first log in>
ANALYTICS_ADMIN_KEY=<generate one: openssl rand -base64 24>
PAYPAL_API_BASE=https://api-m.paypal.com      # live, not sandbox
PAYPAL_CLIENT_ID=<from developer.paypal.com>
PAYPAL_CLIENT_SECRET=<from developer.paypal.com>
PAYPAL_WEBHOOK_ID=<from developer.paypal.com>
```

`PORT`/`HOST` don't need setting — Railway/Render inject `PORT` automatically and this code
already reads it.

## 4. Deploy the web app (Vercel)

- Import the repo, set **Root Directory** to `apps/web` (Vercel's npm-workspaces detection
  handles the monorepo install automatically from there).
- Environment variables (see `apps/web/.env.example`):
  ```
  NEXT_PUBLIC_API_URL=https://api.yourdomain.com      # wherever step 3 ends up living
  NEXT_PUBLIC_SITE_URL=https://yourdomain.com
  NEXT_PUBLIC_PAYPAL_CLIENT_ID=<same Client ID as PAYPAL_CLIENT_ID above — this one is public>
  NEXT_PUBLIC_ADSENSE_PUBLISHER_ID=<ca-pub-XXXXXXXXXXXXXXXX, from your approved AdSense account>
  ```

## 4b. Alternative: cPanel shared hosting ("Setup Node.js App")

If you're deploying to a cPanel host instead of Vercel/Railway (e.g. hosting that offers
**Setup Node.js App** in its control panel), the same two apps run as two separate Node.js
app entries there, and there's no reason to switch off SQLite for a temporary/soft launch —
it needs no separate database service at all, which sidesteps whether the plan even
includes PostgreSQL.

**What to upload**: a source-only archive (no `node_modules`, no `.next`, no `dist`, no
`.env`, no `dev.db`) — `npm install` and the build run *on the host*, because Prisma's
query-engine binary is platform-specific: a `node_modules` built on this dev machine
(Windows) will not run on the host's Linux server. Upload via FTP/File Manager, then:

1. In cPanel → **Setup Node.js App**, create two apps, both with **Application root**
   pointed at the folder you uploaded (the repo root):
   - **API**: Application URL → `api.yourdomain.com` (create that subdomain first).
     Application startup file: `apps/api/dist/index.js`.
   - **Web**: Application URL → `yourdomain.com`.
     Application startup file: `apps/web/server.js` — a small custom entry point already in
     this repo for exactly this case (Passenger needs a plain JS file, not the `next start`
     CLI command; `npm run start:custom` runs the same file locally if you want to try it
     first).
2. For **each** app, open its "Enter to the virtual environment" terminal (or use its `npm
   install` button) and run, from the **repo root** (not from inside `apps/api`/`apps/web` —
   npm workspaces need the root to resolve `@praxao/core`):
   ```bash
   npm install
   npm run build -w packages/core
   npx prisma generate --schema apps/api/prisma/schema.prisma
   npm run build -w apps/api
   npm run build -w apps/web
   npx prisma migrate deploy --schema apps/api/prisma/schema.prisma
   ```
   (Only needs doing once total, not once per app — both apps share the same uploaded
   folder and `node_modules`.)
3. Set environment variables in each app's cPanel config screen (not a `.env` file — the
   code already falls back to reading `process.env` directly when no `.env` file exists):
   - **API app**: `DATABASE_URL=file:./prod.db`, plus the same `CORS_ORIGINS`,
     `ADMIN_BOOTSTRAP_EMAIL`/`PASSWORD`, `ANALYTICS_ADMIN_KEY`, `NODE_ENV=production`, and
     PayPal vars as the Railway/Render path above (skip `TRUST_PROXY` unless your host's
     docs say its proxy is trustworthy — check with them rather than guessing).
   - **Web app**: same `NEXT_PUBLIC_*` vars as the Vercel path above, pointed at
     `https://api.yourdomain.com`.
4. Restart both apps from cPanel after setting env vars — they don't pick up changes live.

**Caveat, stated plainly**: this path is genuinely less proven than Vercel/Railway for
Next.js specifically (cPanel's Node.js support via Passenger is more commonly used for
plain Express-style apps). `apps/web/server.js` is verified to work locally (`npm run build
-w apps/web && npm run start:custom -w apps/web` serves the site correctly), but the
Passenger-specific wiring on your actual host isn't something this assistant can test —
watch cPanel's application logs after first start, and your host's own Node.js docs are the
tie-breaker if something doesn't come up.

## 5. Domain and DNS

- Buy the domain (any registrar).
- Point it at Vercel for the main site (Vercel gives you the exact A/CNAME records once you
  add the domain in its dashboard).
- Point a subdomain (e.g. `api.yourdomain.com`) at the Railway/Render API service the same way.
- Once both are live on the real domain, update `CORS_ORIGINS` (API) and
  `NEXT_PUBLIC_API_URL`/`NEXT_PUBLIC_SITE_URL` (web) to the real domain if you used a
  placeholder during setup, and redeploy both.

## 6. Real PayPal and AdSense credentials

Neither can be obtained by this assistant — both require your own accounts:

- **PayPal**: a PayPal Business account → developer.paypal.com → create a live REST app →
  Client ID/Secret go server-side (`apps/api`), Client ID also goes to `apps/web` (it's the
  public half of the pair). Set up the webhook (URL: `https://api.yourdomain.com/api/v1/support/webhook`,
  events: `PAYMENT.CAPTURE.COMPLETED`, `PAYMENT.CAPTURE.REFUNDED`, `PAYMENT.CAPTURE.REVERSED`)
  and copy its Webhook ID into `PAYPAL_WEBHOOK_ID`.
- **AdSense**: apply for an AdSense account against your live domain (it has to already be
  live and reachable — this is Google's own requirement, not something to work around),
  wait for approval, then set `NEXT_PUBLIC_ADSENSE_PUBLISHER_ID`.

Until both are set, the site works exactly the same for every visitor — Support PRAXAO shows
a clear "not configured" state instead of a broken button, and no ad script loads at all.
Nothing is blocked on either being ready.

## 7. First-login checklist once live

1. Log in to `/admin/login` with `ADMIN_BOOTSTRAP_EMAIL`/`ADMIN_BOOTSTRAP_PASSWORD`.
2. Immediately change that password from `/admin/profile`.
3. Add any other real admins you need from `/admin/users`.
4. Set the real Support PRAXAO config (`/admin/support`) once live PayPal is wired up.
5. Set real ad placements (`/admin/advertising`) once AdSense is approved.
6. Spot-check `/robots.txt` and `/sitemap.xml` on the real domain.

## 8. What's still not built (V3 and beyond)

No subscriptions/billing exist yet (by design — V2 stays free). No CI/CD pipeline is set up;
each deploy above is manual (Railway/Render/Vercel all support "deploy on push to main" as a
one-click setting in their dashboard if you want that later).
