# gngpasse Namecheap VPS Migration Runbook

## Scope and migration strategy

This runbook moves the **gngpasse Node.js runtime** from Manus WebDev to a Namecheap VPS while preserving the existing React interface, Express/tRPC API, MySQL/TiDB-compatible database layer, Manus OAuth session flow, Paystack checkout and webhooks, mNotify SMS, EmailJS delivery, QR tickets, organizer dashboards, and event management.

The migration should be performed as a **staged runtime cutover**. Keep the current Manus deployment available until the Namecheap instance passes the production checklist. Do not point `gngpasse.online` at the VPS or change SMS links until the new instance is healthy.

> The current codebase is not a static website. Uploading the repository into `public_html` only exposes source folders and cannot run the Express server.

## Important architecture dependencies

The application runtime can move to Namecheap, but several existing services remain external dependencies unless they are deliberately replaced:

| Capability | Current dependency | Namecheap requirement | Migration decision |
|---|---|---|---|
| Authentication | Manus OAuth and local JWT sessions | Outbound HTTPS access and valid OAuth callback configuration | Preserve initially; confirm the custom callback URL is accepted by the Manus OAuth application. |
| Event-banner storage | Manus Forge presigned storage and `/manus-storage/*` proxy | Outbound HTTPS access plus the existing Forge credentials | Preserve initially, or migrate to portable S3-compatible storage before removing Forge access. |
| Database | `DATABASE_URL` MySQL/TiDB connection | Reachable database with SSL/firewall access | Use the existing managed database only after confirming Namecheap IP access, or migrate a backup to a managed MySQL service. |
| Payments | Paystack secret API and signed webhook | HTTPS public webhook endpoint | Update Paystack webhook URL after DNS and TLS are live. |
| SMS | mNotify API | `MNOTIFY_API_KEY`, sender ID, and `PUBLIC_APP_URL` | Set `PUBLIC_APP_URL=https://gngpasse.online` only during final cutover. |
| Email | EmailJS API | EmailJS service/template/public/private keys | Copy the existing values into the VPS environment. |

The current storage and authentication modules are Manus-coupled. A fully independent migration would require replacing Manus OAuth with another identity provider and replacing Forge storage with S3-compatible storage. This runbook preserves them initially to reduce risk and keep all current features working.

## Production build and startup contract

The repository already provides the required production scripts:

```bash
pnpm install --frozen-lockfile
pnpm build
NODE_ENV=production node dist/index.js
```

The build produces the browser assets under `dist/public` and bundles the Express server as `dist/index.js`. The server reads `PORT` from the hosting environment and serves the built React application and `/api/trpc` routes from the same process. Do not hardcode a VPS port.

For cPanel's **Setup Node.js App**, use the following values:

| cPanel field | Value |
|---|---|
| Node.js version | Node.js 22.x, matching the current project runtime |
| Application mode | Production |
| Application root | The uploaded repository root, not `public_html` itself |
| Application URL | `https://gngpasse.online` after DNS/TLS setup, or the temporary VPS hostname during staging |
| Startup file | `dist/index.js` after running the production build |
| Environment | `NODE_ENV=production` plus the variables listed below |

Namecheap documents that cPanel's **Setup Node.js App** supports selecting a Node.js version, application root, application URL, startup file, package installation, and environment variables.[1] The application must have a valid `package.json`; cPanel no longer creates one automatically in some setups.[1]

## VPS preparation

Create the VPS with a supported Linux distribution and install Node.js 22.x, Git, and the selected process management tools. If cPanel is used, create one Node.js application through **cPanel → Setup Node.js App** rather than exposing the repository through the default document root. Namecheap lists VPS Pulsar with 2 CPU cores, 2 GB RAM, and 40 GB SSD as a practical entry point for a growing project; cPanel licensing is a separate charge and should be confirmed during checkout.[2] [3]

Upload the repository through Git or an archive into the application root. Do not upload `.env` files to a public directory, and do not place source files directly in the domain's document root. Install dependencies, build the application, and start it through the cPanel Node.js application manager.

## Required production variables

Copy values securely from the current project configuration into the VPS environment. Never commit secret values or place them in browser-visible Vite variables unless they are intentionally public.

| Variable | Purpose |
|---|---|
| `NODE_ENV` | Must be `production`. |
| `PORT` | Supplied by cPanel/Node.js App; do not hardcode it. |
| `DATABASE_URL` | Production MySQL/TiDB connection string. Confirm SSL and firewall access. |
| `JWT_SECRET` | Signs local session cookies. Use the existing value to avoid invalidating sessions during a staged cutover, then rotate after migration. |
| `VITE_APP_ID` | Manus OAuth application identifier. |
| `OAUTH_SERVER_URL` | Manus OAuth backend URL. |
| `VITE_OAUTH_PORTAL_URL` | Browser OAuth portal URL. |
| `OWNER_OPEN_ID`, `OWNER_NAME` | Existing owner/admin identity configuration. |
| `BUILT_IN_FORGE_API_URL`, `BUILT_IN_FORGE_API_KEY` | Existing Forge OAuth/storage integration used by the current application. |
| `PAYSTACK_SECRET_KEY` | Server-side Paystack transaction initialization, verification, and webhook validation. |
| `MNOTIFY_API_KEY`, `MNOTIFY_SENDER_ID`, `MNOTIFY_MESSAGE_TEMPLATE` | Ghana SMS delivery. The sender ID is currently `GNGBASH`. |
| `PUBLIC_APP_URL` | Must become `https://gngpasse.online` only after DNS and HTTPS are working; it controls ticket links sent by SMS. |
| `VITE_EMAILJS_SERVICE_ID`, `VITE_EMAILJS_TEMPLATE_ID`, `VITE_EMAILJS_PUBLIC_KEY`, `EMAILJS_PRIVATE_KEY` | Ticket email delivery. |
| `VITE_ANALYTICS_ENDPOINT`, `VITE_ANALYTICS_WEBSITE_ID` | Existing analytics configuration, if retained. |

The `VITE_` prefix in this project does not by itself make a value safe for public exposure because some values are consumed by server code as well. Keep the full environment configuration in cPanel's server-side environment settings.

## Database migration and backup

Before changing DNS, export a verified database backup from the current production database. Restore it into the target database only if a separate database is required; otherwise, validate that the VPS can reach the existing database over TLS. Do not run destructive schema commands against the production database during cutover.

The database must retain orders, tickets, events, organizer ownership, Paystack references, payment records, and QR check-in state. After configuring `DATABASE_URL`, run a read-only health check and verify that event discovery, organizer authentication, ticket access, and order status can load before allowing new transactions.

## Paystack cutover

The current Paystack webhook route is `POST /api/paystack/webhook` and must receive the exact raw JSON body for HMAC signature verification. The VPS must expose it over HTTPS. After `https://gngpasse.online` resolves to the new VPS and the certificate is active, set the Paystack webhook URL to:

```text
https://gngpasse.online/api/paystack/webhook
```

Perform one low-value test transaction and confirm that the order changes to paid, tickets are issued exactly once, email and SMS notifications are attempted, and the public ticket URL resolves on the new hostname. Keep the previous Manus endpoint available until this verification is complete.

## DNS, TLS, and public URL sequence

First stage the VPS using its temporary hostname or IP. Then install the TLS certificate for `gngpasse.online`, configure the reverse proxy or cPanel application mapping, and verify that the application loads over HTTPS. Only then update `PUBLIC_APP_URL` and restart the Node.js application.

At Namecheap, edit the domain under **Domain List → Manage → Advanced DNS**. Use the exact A record or CNAME target supplied by the VPS/cPanel configuration. Remove conflicting parking redirects, but preserve required mail-related records if the domain is used for email. DNS values must be copied from the active hosting configuration rather than guessed.

Once DNS has propagated, verify all of the following on `https://gngpasse.online`:

| Verification | Expected result |
|---|---|
| `/` | Branded gngpasse home page loads over HTTPS. |
| `/api/trpc/auth.me` | Unauthenticated response is clean and does not expose errors. |
| `/api/paystack/webhook` | Route is reachable and rejects invalid signatures. |
| `/ticket/<real-token>` | Public ticket access works without login. |
| Organizer login | OAuth callback returns to the custom domain and establishes a session. |
| SMS confirmation | Links point to `https://gngpasse.online/ticket/...`. |
| Email confirmation | Links point to the custom domain where configured. |
| Dashboard | Event, guest, payment, export, and check-in views load correctly. |

## Rollback plan

Do not delete the Manus deployment during the first release. If authentication, database access, Paystack webhooks, or ticket links fail, revert DNS to the previous Manus target, restore `PUBLIC_APP_URL` to the previous hostname, and disable the new Paystack webhook target until the issue is corrected.

## References

[1]: https://www.namecheap.com/support/knowledgebase/article.aspx/10047/2182/how-to-work-with-nodejs-app/ "Namecheap: How to work with Node.js App"
[2]: https://www.namecheap.com/hosting/vps/ "Namecheap VPS Hosting Plans"
[3]: https://www.namecheap.com/support/knowledgebase/article.aspx/10158/21/cpanel-tier-pricing-for-vpsdedicated-servers/ "Namecheap cPanel VPS Tier Pricing"
