# XC_VM Build System (MAIN vs LB)

How XC_VM produces two build variants from a single codebase: a full MAIN server and a lightweight Load Balancer (LB) server.

---

## Build Variants

XC_VM supports two deployment roles from a single source tree:

| Variant | Archive | Purpose |
| --- | --- | --- |
| **MAIN** | `xc_vm.tar.gz` | Full application — admin panel, streaming, all modules, cron jobs |
| **LB** (Load Balancer) | `loadbalancer.tar.gz` | Streaming-only server — no admin panel, no user management |

**MAIN** is the primary server that manages everything: admin UI, database writes, user/device management, EPG processing, backups, etc.

**LB** is a lightweight streaming node that receives streams from MAIN (or other sources) and delivers them to clients. It connects to the master database in read-only mode and has no admin panel or management capabilities.

---

## Makefile Targets

| Target | Output | Description |
| --- | --- | --- |
| `make main` | `dist/xc_vm.tar.gz` | Full MAIN build |
| `make lb` | `dist/loadbalancer.tar.gz` | LB build (streaming-only subset) |
| `make main_update` | `dist/update.tar.gz` | Incremental MAIN update |
| `make lb_update` | `dist/loadbalancer_update.tar.gz` | Incremental LB update |
| `make new` | Both full builds | Shortcut: `main` + `lb` |

Additional outputs:

- `XC_VM.zip` — installer package (`install/` + `xc_vm.tar.gz`)
- `hashes.md5` — MD5 checksums for integrity verification

---

## Composer Dependencies

`src/vendor/` (the Composer PSR-4 autoloader plus the production dependencies) is
**committed** and shipped as-is — the deploy path has no Composer and never runs
`composer install`. It is kept production-only via `composer install --no-dev`, so
both build variants ship a lean vendor with no dev tooling.

- `src/composer.lock` is committed so `composer install` is reproducible.
- Dev tools (PHPStan, PHP-CS-Fixer) are `require-dev` and are **not** in the
  committed vendor or the archives. Developers and CI add them with `make dev-tools`
  (`composer install`); the `check-vendor-prod-only` gate fails if a dev package is
  ever committed under `src/vendor/`.
- There is no build-time vendor step — `make main` / `make lb` copy the committed
  `vendor/` directly into the archive.

---

## What Goes Into Each Build

### MAIN Build

The MAIN build contains the **entire** `src/` directory.

### LB Build — Included Directories

Only these directories are copied into the LB archive:

```text
bin/        Cli/        config/     content/    Core/
Domain/     Infrastructure/         public/     resources/
signals/    Streaming/  tmp/        www/
```

Plus root files: `bootstrap.php`, `console.php`, `service`, `update`.

### LB Build — Excluded Content

After copying, admin-specific content is **removed** from the LB build:

**Directories removed:**

| Path | Reason |
| --- | --- |
| `bin/install/` | Installer scripts (not needed on LB) |
| `bin/redis/` | Redis binary (LB doesn't run its own Redis) |
| `bin/nginx/conf/codes/` | Error code pages (admin UI) |
| `Public/Controllers/Admin/` | Admin panel controllers |
| `Public/Controllers/Player/` | Player panel controllers |
| `Public/Controllers/Reseller/` | Reseller panel controllers |
| `Public/Views/` | Panel templates |
| `Public/assets/` | Panel static assets |
| `Public/routes/` | Panel route maps |
| `Domain/User/` | User management |
| `Domain/Device/` | Device registration |
| `Domain/Auth/` | Auth management (panel auth) |
| `resources/langs/` | Language resource files |
| `resources/libs/` | Admin library resources |

**Files removed:**

| File | Reason |
| --- | --- |
| `Public/Controllers/Api/AdminApiController.php` | Full admin API removed from LB |
| `Public/Controllers/Api/ResellerRestApiController.php` | Reseller API removed from LB |
| `Infrastructure/legacy/reseller_api.php` | Legacy reseller API bootstrap not needed on LB |
| `www/xplugin.php`, `www/probe.php`, `www/playlist.php` | Admin endpoints |
| `www/player_api.php`, `www/epg.php`, `www/enigma2.php` | Client API endpoints (served by MAIN) |
| `www/stream/auth.php` | Auth endpoint removed from LB package |
| `www/admin/api.php`, `www/admin/proxy_api.php` | Admin API |
| `bin/maxmind/GeoLite2-City.mmdb` | GeoIP DB shipped separately |
| `config/rclone.conf` | Backup config |
| `Domain/Epg/EPG.php` | EPG processing class |
| `bin/nginx/conf/gzip.conf` | Gzip config (LB uses own) |

**CLI commands removed:**

| File | Reason |
| --- | --- |
| `Cli/Commands/MigrateCommand.php` | Migration is MAIN-only |
| `Cli/Commands/CacheHandlerCommand.php` | Cache handler is MAIN-only |
| `Cli/Commands/ServerInstallCommand.php` | Server installer (not needed on LB itself) |
| `Cli/Commands/LbInstallFlow.php` | LB install helper (not needed on LB itself) |
| `Cli/Commands/ProxyInstallFlow.php` | Proxy install helper (not needed on LB itself) |

**Cron jobs removed:**

| File | Reason |
| --- | --- |
| `Cli/CronJobs/RootMysqlCronJob.php` | DB maintenance (MAIN-only) |
| `Cli/CronJobs/BackupsCronJob.php` | Backups (MAIN-only) |
| `Cli/CronJobs/CacheEngineCronJob.php` | Full cache rebuild (MAIN-only) |
| `Cli/CronJobs/EpgCronJob.php` | EPG processing (MAIN-only) |
| `Cli/CronJobs/UpdateCronJob.php` | Update check (MAIN-only) |
| `Cli/CronJobs/ProvidersCronJob.php` | Provider sync (MAIN-only) |
| `Cli/CronJobs/SeriesCronJob.php` | Series metadata (MAIN-only) |

> **Note:** Module-related crons (TMDB, Plex, Watch) now live inside `modules/<name>/` and are excluded from LB builds automatically since `modules/` is not in `LB_DIRS`.

### LB Build — Replaced Configs

These files from `lb_configs/` **replace** the MAIN versions:

| Source | Target | Purpose |
| --- | --- | --- |
| `lb_configs/nginx.conf` | `bin/nginx/conf/nginx.conf` | Performance-tuned nginx for streaming |
| `lb_configs/live.conf` | `bin/nginx_rtmp/conf/live.conf` | RTMP callback hooks |

---

## MAIN vs LB — Key Differences

| Aspect | MAIN | LB |
| --- | --- | --- |
| Admin panel | ✅ Full UI | ❌ Not included |
| Database role | Read + Write | Read-only consumer |
| User/device management | ✅ | ❌ |
| EPG processing | ✅ | ❌ |
| Backups | ✅ | ❌ |
| Migration tool | ✅ | ❌ |
| Stream delivery | ✅ | ✅ |
| RTMP ingestion | ✅ | ✅ |
| Transcoding (FFmpeg) | ✅ | ✅ |
| CLI commands | 26 | ~15 (admin-only removed) |
| Cron jobs | 25 | ~16 (admin-only removed) |
| Module system | ✅ | ❌ |

---

## LB Nginx Configuration

The LB build uses a specialized nginx config optimized for high-throughput streaming:

| Setting | Value | Purpose |
| --- | --- | --- |
| Worker processes | `auto` | Scale to CPU cores |
| Worker connections | 16,000 | High concurrency per worker |
| Max file descriptors | 300,000 | System resource limit |
| Thread pool | `pool_xc_vm` (32 threads) | Async I/O for streaming |
| Gzip | OFF | Streaming data is already compressed |
| Access logs | OFF | Reduce I/O overhead |
| Rate limiting | 20 req/s per IP | DDoS mitigation |
| Send timeout | 20 min | Support long-running streams |

RTMP hooks (`lb_configs/live.conf`) route authentication through local HTTP callbacks instead of the admin panel:

```nginx
on_play http://127.0.0.1:8080/stream/rtmp;
on_publish http://127.0.0.1:8080/stream/rtmp;
on_play_done http://127.0.0.1:8080/stream/rtmp;
```

---

## Runtime Behavior on LB

### Conditional Command Loading

`console.php` uses `file_exists()` guards for commands that may not exist on LB servers:

```php
if (file_exists(__DIR__ . '/Cli/Commands/CacheHandlerCommand.php')) {
    $rRegistry->register(new CacheHandlerCommand());
}
```

This prevents crashes when LB attempts to register a command whose file was removed during the build.

### Streaming Dependency Chain

LB servers retain the full streaming pipeline:

```text
www/stream/*.php
  ├── www/stream/init.php
  ├── vendor/autoload.php (Composer PSR-4 autoloader)
  ├── bootstrap.php (lightweight stream/bootstrap path)
  ├── Core/* (Config, Database, Cache, Auth, Http, Logging, Util)
  ├── Domain/Stream, Domain/Server, Domain/Vod, Domain/Bouquet
  ├── Streaming/* (Auth, Delivery, Codec, Protection)
  ├── Infrastructure/Redis, Infrastructure/Database
  └── resources/data
```

---

## Adding New Code to Builds

### New streaming-relevant directory under `src/`

Add it to `LB_DIRS` in the Makefile:

```makefile
LB_DIRS = bin cli config content core domain ... your_dir
```

### New admin-only directory

Add it to `LB_DIRS_TO_REMOVE`:

```makefile
LB_DIRS_TO_REMOVE = ... your_dir/admin_stuff
```

### New admin-only file

Add it to `LB_FILES_TO_REMOVE`:

```makefile
LB_FILES_TO_REMOVE = ... your_dir/admin_file.php
```

### New CLI command (admin-only)

1. Add `file_exists()` guard in `console.php`
2. Add the file to `LB_FILES_TO_REMOVE`

---

## Build Verification

After modifying the build, verify both variants:

```bash
# Build both
make new

# Check LB contains streaming code
tar -tzf dist/loadbalancer.tar.gz | grep -cE "Core/|Domain/Stream|Streaming/"
# Expected: > 0

# Check LB does NOT contain admin code
tar -tzf dist/loadbalancer.tar.gz | grep -cE "admin/|player/|ministra|reseller"
# Expected: 0

# Compare sizes (LB should be significantly smaller)
ls -lh dist/xc_vm.tar.gz dist/loadbalancer.tar.gz
```
