# HostSeba NOC — Backend

Internal Network Operations Center for HostSeba. Built on **Laravel 13** (PHP 8.5), MariaDB, Redis, and Laravel Reverb for live WebSocket streams.

> **Internal tool.** Not customer-facing. Role-based access for HostSeba employees only.

---

## Stack

| Layer | Tech | Version |
|---|---|---|
| Framework | Laravel | 13.x |
| Language | PHP | 8.3+ (8.5 recommended) |
| DB | MariaDB | 10.6+ (cPanel default) |
| Cache / Queue | Redis | 7.x |
| WebSocket | Laravel Reverb | 1.x |
| Auth | Sanctum (SPA cookie mode) | 4.x |
| HTTP client | Guzzle | 7.x |
| SSH agent | phpseclib | 3.x |
| Frontend (separate) | React 19 + Vite | — |

---

## Project layout

```
app/
├── Console/
│   ├── Kernel.php               # Schedule
│   └── Commands/
│       ├── AggregateMetricsCommand.php
│       ├── PruneMetricsCommand.php
│       ├── CheckSslCommand.php
│       └── CheckDomainsCommand.php
├── Events/
│   ├── ServerStatusChanged.php
│   ├── MetricRecorded.php
│   └── IncidentOpened.php
├── Http/
│   ├── Controllers/
│   │   ├── Auth/AuthController.php
│   │   └── Api/
│   │       ├── ServerController.php
│   │       ├── WebsiteController.php
│   │       ├── IncidentController.php
│   │       ├── NotificationController.php
│   │       ├── UserController.php
│   │       ├── ActivityLogController.php
│   │       └── DashboardController.php
│   └── Resources/
│       ├── UserResource.php
│       └── ServerResource.php
├── Jobs/
│   ├── ScrapeServer.php
│   └── CheckWebsite.php
├── Listeners/
│   ├── EvaluateAlertRules.php
│   └── NotifyAssigneeOfIncident.php
├── Models/                      # 19 models (User, Server, Incident, ...)
├── Providers/
│   ├── AppServiceProvider.php
│   ├── EventServiceProvider.php
│   └── RouteServiceProvider.php
└── Services/
    ├── ServerScraperService.php
    ├── WebsiteCheckerService.php
    ├── IncidentManagerService.php
    ├── AlertEvaluator.php
    └── NotificationDispatcher.php

database/
├── migrations/                  # 9 migration files, 21 tables total
├── seeders/DatabaseSeeder.php   # Initial admin + alert rules + settings
└── factories/

routes/
├── api.php                      # Full REST API
├── web.php                      # CSRF cookie endpoint
└── channels.php                 # Reverb private channels

config/
├── noc.php                      # NOC-specific tunables
├── sanctum.php
└── cors.php

tests/
├── Feature/                     # Auth, ServerCrud, Incident
└── Pest.php
```

---

## Setup (cPanel server)

```bash
# 1. Clone & install
cd /home/hostseba/repos
git clone git@github.com:hostseba/noc-backend.git
cd noc-backend
composer install --no-dev --optimize-autoloader

# 2. Environment
cp .env.example .env
# Edit DB_*, REDIS_*, SANCTUM_STATEFUL_DOMAINS, REVERB_*, MAIL_*
php artisan key:generate

# 3. Database
mysql -u root -p
> CREATE DATABASE hostseba_noc CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
> CREATE USER 'hostseba_noc'@'localhost' IDENTIFIED BY '...';
> GRANT ALL ON hostseba_noc.* TO 'hostseba_noc'@'localhost';
> FLUSH PRIVILEGES;

php artisan migrate
php artisan db:seed   # creates admin@hostseba.com / changeme-noyon (CHANGE IT)

# 4. Reverb
php artisan reverb:install
# Start the daemon (use systemd in production):
php artisan reverb:start --host=0.0.0.0 --port=8080

# 5. Queue worker (systemd unit recommended)
php artisan queue:work --queue=alerts,notifications,default --tries=3

# 6. Scheduler (cron entry)
* * * * * cd /home/hostseba/repos/noc-backend && php artisan schedule:run >> /dev/null 2>&1
```

### systemd units (recommended)

`/etc/systemd/system/noc-queue.service`:
```
[Unit]
Description=HostSeba NOC queue worker
After=mariadb.service redis.service

[Service]
User=hostseba
Group=hostseba
Restart=always
ExecStart=/usr/bin/php /home/hostseba/repos/noc-backend/artisan queue:work --queue=alerts,notifications,default --sleep=1 --tries=3 --max-time=3600

[Install]
WantedBy=multi-user.target
```

`/etc/systemd/system/noc-reverb.service`:
```
[Unit]
Description=HostSeba NOC Reverb WebSocket
After=network.target

[Service]
User=hostseba
Group=hostseba
Restart=always
ExecStart=/usr/bin/php /home/hostseba/repos/noc-backend/artisan reverb:start --host=0.0.0.0 --port=8080

[Install]
WantedBy=multi-user.target
```

---

## API surface

All endpoints sit under `/api/` and require Sanctum cookie auth except `/api/auth/login`. JSON only.

### Auth
| Method | Route | Notes |
|---|---|---|
| POST | `/api/auth/login` | Throttled 5/min per email+IP |
| POST | `/api/auth/logout` | |
| GET | `/api/auth/me` | Returns user + permission map |

### Dashboard
| Method | Route |
|---|---|
| GET | `/api/dashboard/summary` (15s cache) |
| GET | `/api/dashboard/recent-incidents` |
| GET | `/api/dashboard/top-servers` |

### Servers
| Method | Route |
|---|---|
| GET | `/api/servers` |
| POST | `/api/servers` |
| GET | `/api/servers/{server}` |
| PATCH | `/api/servers/{server}` |
| DELETE | `/api/servers/{server}` |
| GET | `/api/servers/{server}/metrics?minutes=60` |
| POST | `/api/servers/{server}/services/restart` |

### Websites, Incidents, Notifications, Users, Activity Logs
All standard REST verbs — see `routes/api.php`.

---

## Permission matrix

| Permission | super_admin | admin | server_admin | support | viewer |
|---|---|---|---|---|---|
| View dashboard | ✓ | ✓ | ✓ | ✓ | ✓ |
| Manage servers | ✓ | ✓ | ✓ | – | – |
| Acknowledge incidents | ✓ | ✓ | ✓ | ✓ | – |
| Resolve incidents | ✓ | ✓ | ✓ | ✓ | – |
| Schedule maintenance | ✓ | ✓ | ✓ | – | – |
| Configure alert rules | ✓ | ✓ | – | – | – |
| Manage users | ✓ | ✓ | – | – | – |
| Activity logs | ✓ | ✓ | ✓ | – | – |
| Generate reports | ✓ | ✓ | ✓ | ✓ | – |
| Modify settings | ✓ | – | – | – | – |
| API key management | ✓ | ✓ | – | – | – |

---

## How monitoring runs

1. **Scheduler** (`app/Console/Kernel.php`) dispatches one `ScrapeServer` job per active server every 30s.
2. `ServerScraperService` SSHes into the box (key auth via phpseclib), runs a small bash script, parses key=value output.
3. A new `server_metrics` row is inserted, the parent `servers` row is updated with the snapshot.
4. `MetricRecorded` event is broadcast on `metrics.{id}` and `servers` Reverb channels — frontend updates live.
5. `EvaluateAlertRules` listener runs all enabled `AlertRule`s against the new metric. If a rule trips beyond its `duration` and isn't on cooldown, an incident is opened via `IncidentManagerService` and notifications fan out via `NotificationDispatcher`.
6. `IncidentOpened` event broadcasts on the `incidents` channel.
7. Hourly: `noc:aggregate-metrics` rolls raw rows into `server_metric_hourly`.
8. Daily at 03:00: `noc:prune-metrics` deletes raw rows older than 30 days.
9. Daily at 04:00 / 04:30: `noc:check-ssl` and `noc:check-domains` refresh certificate / WHOIS metadata.

---

## Tests

```bash
php artisan test
# or
./vendor/bin/pest
```

Covers: auth, rate limiting, server CRUD permission matrix, incident lifecycle.

---

## Security notes

- All SSH private keys are stored encrypted via Laravel's `encrypted` cast (uses `APP_KEY`).
- API keys are stored as SHA-256 hashes; the plaintext token is shown to the user once at creation time.
- Sessions live in the DB (`sessions` table) so logout-everywhere works server-side.
- Login throttle: 5 attempts per email+IP per minute. Lockout fires the `Lockout` event so it shows up in Activity Logs as `failed login`.
- 2FA: TOTP-based (Google Authenticator compatible). `users.two_fa_secret` is encrypted.
- Channel auth: every Reverb channel verifies `user.status === 'active'` via `routes/channels.php`.

---

## Next steps (not yet built in backend)

1. **Reverb event integration in frontend** (Echo client wiring) — mostly frontend work.
2. **Maintenance windows** controller + suppression logic in `AlertEvaluator`.
3. **Reports** — actual PDF/CSV generation jobs (uses `dompdf` or `barryvdh/laravel-dompdf`).
4. **API key controller** + `auth:api-key` middleware for external integrations.
5. **Two-factor enrollment endpoints** (`pragmarx/google2fa`).
