# Deploy guide — `expert.hostseba.com` on cPanel shared hosting

Specific configuration for **THIS** deploy:

| Setting | Value |
|---|---|
| Target subdomain | **`expert.hostseba.com`** |
| Target path | **`/home/hostseba/expert.hostseba.com`** |
| cPanel user | `hostseba` |
| Server type | cPanel shared hosting |
| WebSocket / custom ports | **None** — pure HTTPS over 443. Realtime fallback = 30s polling |

Bundle ready to upload: **`f:\antigravity\noc\noc-deploy-*.tgz`** (~33 MB)

---

## Why no ports?

Frontend's Echo WebSocket client (`App.jsx` line ~520) is wired so that
when no Reverb / Pusher key is configured, it silently no-ops and
disconnects. The dashboard then falls back to the 30-second polling
loop that's already in every `useApi()` call — operators see incident
state changes within ~30s instead of instant push.

This means:
- ✅ No port 8080 (Reverb) — disabled
- ✅ No port 6001 (Soketi) — disabled
- ✅ No port 5173 / 5174 (Vite dev) — production uses built static files
- ✅ Only port 443 (HTTPS) — same as every other site on the shared box
- ✅ Outbound only: agent metric pushes, Anthropic API, mail SMTP — all standard 443/587

Trade-off: ~30s lag on dashboard updates vs instant push. Tomorrow you
can flip a single `.env` value to upgrade to Pusher SaaS (free tier, also
port 443) and get instant push back — no code change needed.

---

## Two ways to map `expert.hostseba.com` → Laravel `public/`

### Option A — change subdomain document root (RECOMMENDED)

Cleanest. The Laravel app structure lives entirely inside
`/home/hostseba/expert.hostseba.com/`, and cPanel serves from the
`public/` subfolder:

```
/home/hostseba/expert.hostseba.com/        ← upload here
├── app/   bootstrap/   config/   etc.
├── public/                                 ← cPanel docroot points HERE
│   ├── index.php
│   ├── .htaccess
│   ├── noc/   (SPA)
│   └── ai/    (SPA)
├── storage/
├── vendor/
├── .env
└── artisan
```

In cPanel **Domains** panel:
1. Find `expert.hostseba.com` in the list
2. Click **Manage** / **Edit** next to it
3. Change "Document Root" from `expert.hostseba.com` to
   **`expert.hostseba.com/public`**
4. Save

If your cPanel version doesn't let you edit the document root (some
restrictive hosts hide this option), use Option B below.

### Option B — public_html-style overlay (if Option A unavailable)

Keep the doc root at the default. Put Laravel app code OUTSIDE the
web-accessible folder, and put `public/` contents directly into the
subdomain folder:

```
/home/hostseba/expert.hostseba.com/        ← docroot stays here (web-visible)
├── index.php                              ← from Laravel public/index.php (edited)
├── .htaccess                              ← from Laravel public/.htaccess
├── favicon.svg
├── noc/                                   ← SPA build
├── ai/                                    ← SPA build
└── storage/                               ← symlink to ../laravel-app/storage/app/public

/home/hostseba/laravel-app/                ← OUTSIDE web root (NOT accessible publicly)
├── app/   bootstrap/   config/   etc.
├── storage/
├── vendor/
├── .env                                   ← secrets safe here
└── artisan
```

Edit `index.php` to fix the paths:
```php
require __DIR__.'/../laravel-app/vendor/autoload.php';
$app = require_once __DIR__.'/../laravel-app/bootstrap/app.php';
```

Detailed steps for Option B are at the bottom of this guide.

**Recommendation:** Try Option A first. It's one cPanel click. Only
fall back to Option B if cPanel won't let you change document root.

---

## Step-by-step deploy (Option A)

### 1. Create the subdomain (if not already)

cPanel → **Domains** → **Create A New Domain** → enter
`expert.hostseba.com`. cPanel will auto-create
`/home/hostseba/expert.hostseba.com/` as the document root.

### 2. Create the database

cPanel → **MySQL Databases**:
- Create database: e.g. `hostseba_noc` (cPanel will prefix it,
  becoming `hostseba_noc` or `hostseba_noc1` depending on cPanel version)
- Create user: e.g. `hostseba_nocadm` with a strong password
- Add user to database with **ALL PRIVILEGES**

Note the EXACT database name + user — you'll paste them in `.env`.

### 3. Upload the bundle

**Via cPanel File Manager:**
1. cPanel → **File Manager** → navigate to `/home/hostseba/`
2. Upload `noc-deploy-20260511-XXXXXX.tgz`
3. Right-click the tarball → **Extract**
4. After extraction you'll see `/home/hostseba/hostseba-noc-backend/` —
   rename it to `expert.hostseba.com` (replacing the empty auto-created
   subdomain folder if cPanel made one):
   ```
   # If a subdomain folder was auto-created and is empty:
   rm -rf /home/hostseba/expert.hostseba.com
   mv /home/hostseba/hostseba-noc-backend /home/hostseba/expert.hostseba.com
   ```

**Or via SSH (faster):**
```bash
# From your laptop:
scp -P 22 noc-deploy-*.tgz hostseba@<cpanel-server>:~/

# On the cPanel server:
ssh hostseba@<cpanel-server>
cd ~
tar -xzf noc-deploy-*.tgz
rm -rf expert.hostseba.com   # delete the auto-created empty folder
mv hostseba-noc-backend expert.hostseba.com
cd expert.hostseba.com
```

### 4. Configure `.env`

Inside `/home/hostseba/expert.hostseba.com/`:

```bash
cp deploy/.env.expert.hostseba.com .env
nano .env
```

Fill in every `FILL IN` placeholder. The critical four:
- `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD` — from step 2
- `ANTHROPIC_API_KEY` — your Anthropic API key (or leave blank to skip AI features)

You can leave `MAIL_*` blank for now if mail alerts aren't set up yet.

### 5. Generate APP_KEY + run migrations

Find your PHP binary path first:
```bash
which php                    # output something like /usr/local/bin/ea-php83
ls /usr/local/bin/ea-php*    # alternative listing
```

Then with that path (replace `ea-php83` with whatever you found):
```bash
PHP=/usr/local/bin/ea-php83

$PHP artisan key:generate --force
$PHP artisan migrate --force
$PHP artisan storage:link
$PHP artisan config:cache
$PHP artisan route:cache
$PHP artisan view:cache

chmod -R 755 storage bootstrap/cache
```

If `composer` is available, slim the vendor dir by removing dev deps:
```bash
composer install --no-dev --optimize-autoloader
```

### 6. Point the subdomain at `public/`

cPanel → **Domains** → click **Manage** next to `expert.hostseba.com` →
change "Document Root" to `/home/hostseba/expert.hostseba.com/public` →
**Save**. Wait a few seconds for the change to propagate.

### 7. Add cron entries

cPanel → **Cron Jobs**. Add TWO entries (both every minute):

**Laravel scheduler** (drives periodic NOC monitoring + AI tasks):
```
* * * * * /usr/local/bin/ea-php83 /home/hostseba/expert.hostseba.com/artisan schedule:run >> /dev/null 2>&1
```

**Queue worker** (processes background jobs like AI ticket drafting):
```
* * * * * /usr/local/bin/ea-php83 /home/hostseba/expert.hostseba.com/artisan queue:work --stop-when-empty --max-time=55 >> /dev/null 2>&1
```

Replace `ea-php83` with your PHP binary path from step 5.

### 8. Verify

From your laptop:
```bash
curl -fsSL https://expert.hostseba.com/health
# Expected: {"app":"HostSeba NOC","version":"1.0.0","status":"ok"}

curl -fsSL https://expert.hostseba.com/install.sh | head -3
# Expected: #!/usr/bin/env bash
#           # ... and BACKEND_URL inside should be https://expert.hostseba.com
```

In browser: `https://expert.hostseba.com/` → redirects to `/noc/` →
the NOC dashboard loads. First time you'll need to seed an admin user
or use the registration flow (depends on how your seeding is set up).

### 9. First agent install

Generate a registration token from the dashboard
("Servers → Add Server"), then on the target server you want to monitor:
```bash
TOKEN="paste-here"
curl -fsSL https://expert.hostseba.com/install.sh | sudo bash -s -- --token "$TOKEN"
```

Within 30s the new server should appear in the dashboard.

---

## Step-by-step deploy (Option B fallback)

Use ONLY if cPanel won't let you change the document root.

After uploading + extracting the bundle:

```bash
cd ~
mv hostseba-noc-backend laravel-app                       # app outside web root
rm -rf expert.hostseba.com                                # empty default folder
mkdir expert.hostseba.com                                 # fresh docroot

# Move public/ contents into the web-visible folder:
cp -rp laravel-app/public/. expert.hostseba.com/
rm -rf laravel-app/public                                 # avoid duplication
ln -s ../expert.hostseba.com laravel-app/public           # so artisan storage:link works

# Fix the index.php paths in the public folder:
nano expert.hostseba.com/index.php
```

Edit `expert.hostseba.com/index.php`. Replace these two lines:
```php
require __DIR__.'/../vendor/autoload.php';
$app = require_once __DIR__.'/../bootstrap/app.php';
```
With:
```php
require __DIR__.'/../laravel-app/vendor/autoload.php';
$app = require_once __DIR__.'/../laravel-app/bootstrap/app.php';
```

Configure `.env` and run migrations from the `laravel-app/` dir
(same as step 5 above, but cd into `laravel-app/` first).

Cron entries point at `laravel-app/artisan` instead.

---

## Troubleshooting

### `expert.hostseba.com` shows "Index of /" or a default cPanel page
Document root not pointing at `public/`. Re-check step 6.

### 500 error: "No application encryption key has been specified"
You forgot `php artisan key:generate`. Run it again. After that:
```bash
$PHP artisan config:cache
```

### 500 error: "Could not open input file: artisan"
You're running artisan from the wrong directory. Cron entries MUST use
the absolute path to artisan
(`/home/hostseba/expert.hostseba.com/artisan`).

### `/install.sh` downloads HTML instead of bash
The Apache mod_rewrite rule isn't matching. Verify
`/home/hostseba/expert.hostseba.com/public/.htaccess` exists (it ships
in the bundle).

### Sessions don't persist; gets logged out on every refresh
`SESSION_DOMAIN` mismatch. Either:
- Make sure `APP_URL=https://expert.hostseba.com` exactly
- And `SESSION_DOMAIN=.hostseba.com` (or `expert.hostseba.com` without the dot if you want strict scoping)
- After changing, run `php artisan config:cache`

### "Permission denied" on storage/logs
```bash
chmod -R 755 /home/hostseba/expert.hostseba.com/storage
chmod -R 755 /home/hostseba/expert.hostseba.com/bootstrap/cache
```
If still denied, check the cPanel user owns the directory:
```bash
chown -R hostseba:hostseba /home/hostseba/expert.hostseba.com
```

### Realtime updates feel slow
Expected — without WebSocket, dashboard polls every 30s. To upgrade to
instant push:
1. Sign up at https://dashboard.pusher.com (free tier)
2. Create an app in the `ap2` (Mumbai) cluster
3. Set in `.env`:
   ```
   BROADCAST_CONNECTION=pusher
   PUSHER_APP_ID=<from pusher dashboard>
   PUSHER_APP_KEY=<from pusher dashboard>
   PUSHER_APP_SECRET=<from pusher dashboard>
   PUSHER_APP_CLUSTER=ap2
   ```
4. Set VITE_* equivalents (same key) and rebuild the frontend (or upload
   a fresh bundle from your laptop)
5. `php artisan config:cache`

### Agent install on real server fails with SSL error
cPanel's Let's Encrypt cert must be active. Check cPanel → SSL/TLS Status
→ **AutoSSL** should show `expert.hostseba.com` as covered. If not:
- cPanel → SSL/TLS Status → click "Run AutoSSL"
- Wait 2-5 min, then retry the agent install
