# Install YourStories on Cloudflare and cPanel

Use **staging.yourstories.life** first. Keep `yourstories.life` on the current landing page until the complete test checklist passes.

## 1. Create the staging domain in cPanel

In **cPanel → Domains**, create:

```text
staging.yourstories.life
```

Give it a normal temporary document root. Application Manager will route the domain to Passenger after registration.

## 2. Add Cloudflare DNS temporarily as DNS only

Create:

| Type | Name | Target | Proxy initially |
|---|---|---|---|
| A | `staging` | cPanel origin IP | DNS only |

Wait for the record to resolve, then run cPanel AutoSSL for the staging hostname. Open `https://staging.yourstories.life` and confirm the origin certificate is valid before enabling the orange-cloud proxy.

After origin HTTPS works:

1. Enable the Cloudflare proxy.
2. Set **SSL/TLS encryption mode → Full (strict)**.
3. Do not enable a zone-wide Cache Everything rule.

## 3. Upload the application outside public_html

Upload and extract the package to a directory such as:

```text
/home/CPANEL_USER/yourstories-staging
```

The application root must contain:

```text
app.js
package.json
app/
components/
lib/
sql/
scripts/
public/
```

Do not put family uploads inside the application directory or `public_html`.

## 4. Create the MySQL database

Use **cPanel → Database Wizard**.

Example names after cPanel adds its account prefix:

```text
Database: CPANELUSER_yourstories_stage
User:     CPANELUSER_ystage
```

Grant the database user **All Privileges** only on the staging database.

Build the URL with URL-encoded credentials:

```text
mysql://CPANELUSER_ystage:ENCODED_PASSWORD@localhost/CPANELUSER_yourstories_stage
```

## 5. Create the environment file

In the application root:

```bash
cp .env.example .env
```

Generate four independent secrets:

```bash
openssl rand -hex 32
openssl rand -hex 32
openssl rand -hex 32
openssl rand -hex 32
```

Use a different output for each:

```text
AUTH_SECRET
OTP_SECRET
QR_ACTIVATION_SECRET
IP_HASH_SECRET
```

Set at least:

```text
NODE_ENV=production
APP_URL=https://staging.yourstories.life
ALLOWED_ORIGINS=https://staging.yourstories.life
DATABASE_URL=...
SMTP_HOST=...
SMTP_PORT=465
SMTP_SECURE=true
SMTP_USER=...
SMTP_PASSWORD=CHANGE_ME
SMTP_FROM=YourStories.life <noreply@yourstories.life>
ADMIN_EMAIL=your-real-admin-email
ADMIN_NAME=Madhu Kumar
ALLOW_CONSOLE_OTP=false
```

The `.env` file must not be web accessible or committed to Git.

## 6. Create the R2 staging bucket

In Cloudflare R2 create:

```text
yourstories-stage-private
```

Create an R2 API token restricted to this bucket with object read/write permission. Add the values to `.env`:

```text
R2_ACCOUNT_ID=
R2_ACCESS_KEY_ID=
R2_SECRET_ACCESS_KEY=
R2_PRIVATE_BUCKET=yourstories-stage-private
```

Apply the CORS policy in `r2-cors-staging.json` to the private bucket. The `ETag` response header must be exposed because the browser sends it to the completion endpoint.

A public bucket is not required for this release. Originals remain private and the server generates short-lived read links.

## 7. Configure Turnstile

Create a Turnstile widget for:

```text
staging.yourstories.life
```

Add:

```text
NEXT_PUBLIC_TURNSTILE_SITE_KEY=
TURNSTILE_SECRET_KEY=
TURNSTILE_REQUIRED=true
```

During initial private server setup, `TURNSTILE_REQUIRED=false` may be used briefly, but it must be enabled before external testers are invited.

## 8. Install, migrate, seed and build

From cPanel Terminal or SSH as the cPanel user:

```bash
cd "$HOME/yourstories-staging"
bash scripts/deploy-cpanel.sh
```

The script performs:

```text
npm install --omit=dev (or npm ci when package-lock.json exists)
node scripts/check-env.mjs
npm run build
node scripts/migrate.mjs
node scripts/seed-admin.mjs
touch tmp/restart.txt
```

The first install creates `package-lock.json`. Preserve it after the staging build is verified.

## 9. Register in cPanel Application Manager

Use **cPanel → Software → Application Manager → Register Application**:

```text
Application name: YourStories Staging
Deployment domain: staging.yourstories.life
Base application URL: /
Application path: yourstories-staging
Deployment environment: Production
Startup file: app.js
Node.js: ea-nodejs22
```

Where the interface supports environment variables, at minimum set:

```text
NODE_ENV=production
```

The included `app.js` also loads the protected `.env` file from the application root.

After registration or every deployment:

```bash
mkdir -p "$HOME/yourstories-staging/tmp"
touch "$HOME/yourstories-staging/tmp/restart.txt"
```

## 10. Confirm the application

Open:

```text
https://staging.yourstories.life/api/health
```

Expected core response:

```json
{
  "ok": true,
  "service": "yourstories.life",
  "version": "2026.08.27.1",
  "database": { "ok": true }
}
```

Then sign in through `/login` using `ADMIN_EMAIL`. The administrator uses the same one-time email-code flow as every Story Keeper.

## 11. Configure Cloudflare cache bypass

Create bypass rules for:

```text
/api/*
/q/*
/connect/*
/access/*
/login*
/onboarding*
/dashboard/*
/admin/*
/story/*
```

For the first staging release, bypass `/story/*` as well. Public story caching can be introduced later together with explicit cache purging whenever privacy or publication status changes.

Normal Cloudflare static caching may remain for:

```text
/_next/static/*
/assets/*
/favicon.svg
/manifest.webmanifest
```

## 12. Add the cleanup cron

In **cPanel → Cron Jobs**, run every 15 minutes:

```bash
cd /home/CPANEL_USER/yourstories-staging && /opt/cpanel/ea-nodejs22/bin/node scripts/cleanup-expired.mjs >> logs/cleanup.log 2>&1
```

This releases abandoned QR reservations and removes expired OTPs, claim sessions, rate-limit buckets and old sessions.

## 13. Production promotion

Do not copy the staging database into production. Create a fresh production database, fresh R2 bucket, fresh secrets and production Turnstile widget.

Recommended production paths:

```text
/home/CPANEL_USER/yourstories-app
yourstories_prod
yourstories-private
```

Production Cloudflare DNS:

| Type | Name | Target | Proxy |
|---|---|---|---|
| A | `@` | cPanel origin IP | Proxied |
| CNAME | `www` | `yourstories.life` | Proxied |

Run the same deployment and test process before switching the public landing page.
