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)