The bundled ffmpeg/ffprobe 8.0 has a hard NEEDED dependency on
libogg.so.0. The debian13, ubuntu20 and ubuntu24 package lists (in
both the MAIN installer and the LB install flow) omitted libogg0,
so ffmpeg would fail to start with "error while loading shared
libraries: libogg.so.0" on those distributions.
Add libogg0 next to libnuma1 in all three lists, in both
install (PACKAGES) and LbInstallFlow::getPackages(), keeping MAIN
and LB in sync. redhat already ships the equivalent libogg RPM.
Refs: #152
Add a `cron:proxy --force` step to the installer right after `cron:maxmind --force`
(same "no longer bundled — fetch on install" pattern), run as xc_vm and non-fatal,
so a fresh panel downloads proxy.tar.gz and writes proxy_version.json at install time.
Drop the StartupCommand background prefetch — redundant now that install + the daily
cron:proxy + the ServerInstallCommand self-heal cover every path, matching how
cron:maxmind is wired.
'console.php status' and 'cron:maxmind' refuse to run as non-root, so the
previous sudo -u xc_vm invocations in the installer and service broke the
post-install steps ('Please run as root'). Revert them: startup/status are
root by design (root crontab, system limits, DB migrations) and startup
already delegates 'cron:cache' to xc_vm.
Fix the actual root-owned-cache leak instead:
- StartupCommand::generateCacheIfNeeded() spawned cron:cache_engine as the
current user (root at boot/install) — drop to xc_vm via sudo -u.
- FileCache::set() now chowns the written file to xc_vm when running as
root (same pattern as Logger), covering the remaining root-context
writers such as 'console.php status'.
A root-run 'console.php startup' (service boot) and 'status 1'/'startup'/
'cron:maxmind' (installer) created root-owned cache files in tmp/cache that
xc_vm daemons could not overwrite until the next boot chown, producing
'Permission denied' warnings and silently stale caches. Run them all via
sudo -u xc_vm. The CacheCronJob wipe branch loses its sudo accordingly —
xc_vm has no sudoers entry and the tmp tree is xc_vm-owned at that point.
- Install apt packages one at a time, each under a printc frame naming the
package with the raw apt log shown below it (_apt_install_framed); MariaDB
packages likewise.
- Frame the xc_vm user creation and capture adduser/useradd output so its raw
"info:" lines don't leak past the frame.
- Write credentials to a single /root/credentials.txt (now incl. Admin Access
Code); drop the duplicate /opt/xcvm-install copy and the duplicate path print.
Switch GeoIP/ISP lookups from the native maxminddb C extension to the
pure-PHP maxmind-db/reader, pulled in via geoip2/geoip2. This removes a
compiled-extension runtime dependency — the panel now works on any stock
PHP 8.1+ without building or installing maxminddb.so.
Dependencies:
- Add geoip2/geoip2 ~3.0 (brings maxmind-db/reader, maxmind/web-service-common)
- Rebuild committed vendor/ with `composer install --no-dev` (production-only)
Code fixes:
- Resolve MaxMind\Db\Reader against the global namespace (leading backslash)
in namespaced classes — `new MaxMind\Db\Reader(...)` previously resolved to
a non-existent XcVm\...\MaxMind\Db\Reader and would fatal once the extension
was gone. Fixed in GeoIPService, ResellerApiDispatcher, admin/api.php view.
- Update GeoIP util docblock to reflect the Composer-based source.
Installer / runtime cleanup:
- bin/php/lib/php.ini: comment out `extension=maxminddb.so`
- install (main installer) and LbInstallFlow: drop libmaxminddb0/-dev,
libmaxminddb/-devel and the now-unused mmdb-bin from all distro package
lists (Debian/Ubuntu/RHEL)
Behavior, the raw `->get()` array format and the on-disk GeoIP file cache
are unchanged; legacy libGeoIP packages are left untouched.
The distribution-specific binaries (PHP, nginx, ffmpeg, ...) are mandatory — the
panel will not start without them. Previously install_distribution_binaries()
downloaded them with a single urlretrieve() and no retry, and the caller treated
a failure as a non-fatal warning and continued. A transient DNS/network blip
(e.g. systemd-resolved not ready yet -> "[Errno -5] No address associated with
hostname") therefore left a "successful" install with no binaries.
- Retry the download up to 4 times with a 5s delay on any network/DNS error.
- On supported distros (ubuntu 20/22/24, debian 11/12/13, rhel/rocky/alma 8/9)
a binary install failure is now FATAL (sys.exit 1) with a clear message,
instead of a warning + continue.
The archive ships a placeholder bin/nginx/conf/server.{crt,key}; without this
every installation would run nginx with the SAME private key until certbot issues
a real one. The installer now overwrites the placeholder with a freshly generated
unique RSA-2048 self-signed pair (CN = hostname, 10y) right after extraction and
before nginx is first started, so each server has its own key. CertbotCronJob
still replaces it with a real Let's Encrypt certificate afterwards.
generate_self_signed_cert(): openssl req -x509 -newkey rsa:2048 -nodes; the key is
chowned xc_vm:xc_vm and chmod 640, the cert 644; a generation failure aborts the
install (nginx could not start without a cert anyway).
Follow-ups (out of scope here): rotate the still-shared key on existing installs
via update, and generate on the LoadBalancer install flow.
GeoLite2-City/Country are no longer bundled in the repo/release archive,
so fetch them (plus ASN) from the XC_VM_Update release:
- getGeolite(): include GeoLite2-ASN.mmdb
- install: run cron:maxmind --force after setup
- UpdateCommand post-update: background GeoLite refresh on MAIN
Reuses the existing MaxMindCronJob mechanism; version.json is updated by it.
- Updated `install` script to use dynamic thread pool max threads configuration.
- Simplified the check for MariaDB version in `secure_mariadb_installation`.
- Enhanced `generate_mysql_config` function to adjust max connections based on total RAM.
- Improved error handling in `BalancerCommand` for SFTP transfers.
- Refactored `CacheEngineCronJob` to ensure proper handling of cache validity checks.
- Updated `StreamsCronJob` to utilize Redis manager more effectively.
- Enhanced `UsersCronJob` to handle Redis connections safely.
- Refactored `portal.php` to improve handling of series and favorite channels.
- Updated `PortalHandler` to streamline category ID checks.
- Improved `EpgApiController` and `PlaylistApiController` to handle legacy actions more robustly.
- Refactored `StreamRedirector` to enhance server handling logic.
Root cause of 502: PHP compiled with sodium but libsodium.so.23 not installed.
- Add libsodium23 to all debian/ubuntu package lists
- Add libsodium to redhat package list
- Fix libpng16-dev → libpng-dev for Ubuntu 24.04
- Split apt install into system + MariaDB phases with fallback
- Verify MariaDB installed after package step
- Improve SSH2 library search (t64 suffix on 24.04+)
- Remove unsafe chars from generated root passwords
CLI consolidation:
- Delete 13 legacy CLI scripts from includes/cli/ (ondemand, proxy, queue, record, scanner, signals, startup, thumbnail, tools, update, watchdog, plex_item, watch_item)
- Convert status and tools entry points to thin proxies that delegate to console.php
- Update all shell_exec() calls across admin controllers, views, and API layer to use console.php command syntax instead of direct CLI file paths
- Update src/service to launch daemons via console.php (signals, watchdog, queue, cache_handler, startup)
- Update Python src/update script to call console.php update instead of includes/cli/update.php
- Update test_installer to use console.php startup
Streaming deduplication:
- Extract StreamAuthMiddleware — common response headers and token decryption shared by live/vod/timeshift
- Extract ShutdownHandler — unified shutdown logic replacing 3 duplicate function shutdown() blocks
- Refactor live.php, vod.php, timeshift.php to use new middleware classes
- Add streaming micro-router to www/stream/index.php as fallback entry point
Routing fixes:
- Fix admin index.php redirect to use relative path (supports access code prefixes)
- Add access code root redirect in public/index.php to prevent broken CSS/JS asset resolution
- Fix init.php for CLI compatibility: guard $_SERVER access, define PHP_ERRORS safely
Migrations:
- 001_update_crontab_filenames.sql — strip .php suffix from crontab filenames
- Fix cache.php view query to match new filename format (cache_engine instead of cache_engine.php)