# HostGator VPS Deployment Runbook

HostGator VPS boxes differ from generic cloud VMs in two ways: they usually
run **AlmaLinux/CentOS** (dnf, firewalld) instead of Ubuntu, and many plans
come with **cPanel/WHM**, which owns ports 80/443 via Apache. This runbook
handles both. Companion checklist: deployment/SECURITY.md.

## Step 0 — Prerequisites (before touching the server)

- [ ] Rotate credentials that ever appeared in chat: NEW Alpaca keys, NEW
      Gmail app password. They go only into the server's `.env`.
- [ ] Your domain ready; you'll point `app.` and `api.` subdomains at the
      VPS IP (registrar DNS or HostGator's DNS zone editor — either works).
- [ ] Project pushed to a private GitHub repo (SETUP.md step 4), or WinSCP
      installed to upload the folder (exclude node_modules, .venv, .next,
      *.db, .git).
- [ ] From HostGator's portal: your VPS IP + root SSH credentials.

## Step 1 — Identify what you have

SSH in (`ssh root@YOUR_IP`) and run:

```bash
cat /etc/os-release | head -2
ls -d /usr/local/cpanel 2>/dev/null && echo "CPANEL PRESENT" || echo "NO CPANEL"
```

- **NO CPANEL** → follow Path A below (simplest, recommended).
- **CPANEL PRESENT** → follow Path B (we run behind Apache instead of nginx).

Paste the output to Claude if unsure — the rest tailors from this.

---

## Step 2 — Common setup (both paths)

```bash
# App user
adduser trader || useradd -m trader
mkdir -p /home/trader/.ssh && cp ~/.ssh/authorized_keys /home/trader/.ssh/ 2>/dev/null
chown -R trader:trader /home/trader/.ssh

# Runtimes (AlmaLinux/CentOS)
dnf -y update
dnf -y install python3.11 python3.11-pip git
curl -fsSL https://rpm.nodesource.com/setup_20.x | bash - && dnf -y install nodejs
```

Get the code (as trader):

```bash
su - trader
git clone https://github.com/YOU/ai-trading-signal-lab.git app
# or upload to /home/trader/app with WinSCP
```

Backend:

```bash
cd ~/app/backend
python3.11 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python -m pytest -q          # must be green here too
cp ../.env.example .env && nano .env
```

Production `.env` essentials (the app refuses to boot if these are weak):

```
ENV=production
REQUIRE_AUTH=true
REGISTRATION_OPEN=true          # flip to false after you create YOUR account
SECRET_KEY=<openssl rand -base64 48>
FRONTEND_ORIGIN=https://app.yourdomain.com
DATA_PROVIDER=live
ALPACA_API_KEY=<NEW>  ALPACA_API_SECRET=<NEW>
AUTO_SCAN_ENABLED=true
EMAIL_ALERTS_ENABLED=true  SMTP_USER=...  SMTP_PASSWORD=<NEW>  ALERT_EMAIL_TO=...
```

```bash
chmod 600 .env
```

Frontend:

```bash
cd ~/app/frontend
echo "NEXT_PUBLIC_API_URL=https://api.yourdomain.com" > .env.local
npm ci && npm run build
exit   # back to root
```

Systemd services (both paths — apps bind to localhost only):

```bash
cat > /etc/systemd/system/lab-api.service <<'EOF'
[Unit]
Description=Signal Lab API
After=network.target
[Service]
User=trader
WorkingDirectory=/home/trader/app/backend
ExecStart=/home/trader/app/backend/.venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF

cat > /etc/systemd/system/lab-web.service <<'EOF'
[Unit]
Description=Signal Lab Web
After=network.target
[Service]
User=trader
WorkingDirectory=/home/trader/app/frontend
ExecStart=/usr/bin/npx next start -p 3000
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload && systemctl enable --now lab-api lab-web
systemctl status lab-api lab-web --no-pager
```

DNS (both paths): create **A records** `app.yourdomain.com` and
`api.yourdomain.com` → your VPS IP. If HostGator hosts your DNS: cPanel →
Zone Editor, or the portal's DNS manager.

---

## Path A — No cPanel (nginx, recommended)

```bash
dnf -y install nginx certbot python3-certbot-nginx
systemctl enable --now nginx

# firewalld (AlmaLinux default) — web + ssh only
firewall-cmd --permanent --add-service=http --add-service=https --add-service=ssh
firewall-cmd --reload

cat > /etc/nginx/conf.d/lab.conf <<'EOF'
limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s;
server {
    server_name app.yourdomain.com;
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
server {
    server_name api.yourdomain.com;
    location = /api/auth/register { return 403; }   # UNCOMMENT AFTER creating your account
    location / {
        limit_req zone=api burst=20 nodelay;
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
EOF
# NOTE: leave the register-block line commented out (add a # in front)
# until AFTER Step 3's account creation, then remove the # and reload.

nginx -t && systemctl reload nginx
certbot --nginx -d app.yourdomain.com -d api.yourdomain.com
```

## Path B — cPanel/WHM present (Apache proxy)

Do NOT install nginx (Apache owns 80/443). Instead:

1. In WHM/cPanel, create the two subdomains `app.` and `api.` on your
   domain (any docroot; it won't be served).
2. Run AutoSSL (WHM → SSL/TLS → Manage AutoSSL) so both subdomains get
   certificates.
3. Add proxy includes (as root):

```bash
mkdir -p /etc/apache2/conf.d/userdata/ssl/2_4/trader/app.yourdomain.com
cat > /etc/apache2/conf.d/userdata/ssl/2_4/trader/app.yourdomain.com/proxy.conf <<'EOF'
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
EOF

mkdir -p /etc/apache2/conf.d/userdata/ssl/2_4/trader/api.yourdomain.com
cat > /etc/apache2/conf.d/userdata/ssl/2_4/trader/api.yourdomain.com/proxy.conf <<'EOF'
ProxyPreserveHost On
ProxyPass /api/auth/register !
ProxyPass / http://127.0.0.1:8000/
ProxyPassReverse / http://127.0.0.1:8000/
EOF
# The register exclusion returns Apache's default (blocks signups).
# Comment it out until AFTER you create your account in Step 3.

/scripts/rebuildhttpdconf && systemctl restart httpd
```

4. Firewall on cPanel boxes is usually CSF: ensure only 22/80/443 (and
   cPanel's own ports if you use them) are open (WHM → ConfigServer
   Firewall), and confirm 3000/8000 are NOT publicly open.

---

## Step 3 — First-run verification (both paths)

1. `https://api.yourdomain.com/health` → `{"status":"ok","mode":"paper-trading-only"}`
2. `https://api.yourdomain.com/docs` → 404 (production mode confirmed)
3. `https://app.yourdomain.com` → redirects to the **login page**
4. Create YOUR account there (first account = admin), confirm the
   dashboard loads with the **Live API** badge
5. Close registration: set `REGISTRATION_OPEN=false` in `.env`,
   `systemctl restart lab-api`, AND enable the register block in your
   proxy config (Path A nginx line / Path B exclusion), reload the proxy
6. Reboot test: `reboot`, wait 2 min, confirm everything comes back

## Step 4 — Backups + monitoring

```bash
cat > /etc/cron.daily/lab-backup <<'EOF'
#!/bin/sh
mkdir -p /home/trader/backups
cp /home/trader/app/backend/trading_lab.db /home/trader/backups/trading_lab_$(date +%F).db
find /home/trader/backups -mtime +14 -delete
EOF
chmod +x /etc/cron.daily/lab-backup
```

External uptime monitor (UptimeRobot, free) on the /health URL. During
market hours, check `GET /api/scheduler/status` (with your login token) —
`last_run` should never lag more than ~30 minutes.

## Updating later

```bash
su - trader -c "cd ~/app && git pull && cd backend && .venv/bin/pip install -r requirements.txt && .venv/bin/python -m pytest -q && cd ../frontend && npm ci && npm run build"
systemctl restart lab-api lab-web
```

## If something fails

`journalctl -u lab-api -n 50 --no-pager` — the most common "failure" is the
intentional fail-closed boot check naming exactly which .env value is weak.
Fix the value; never weaken the check. Paste any error to Claude.
