Files
infra/ai-memory/project_novacpx.md
myron 52f6073593 Add Claude Code AI memory files
AI context/memory from Claude Code sessions covering all
infrastructure: JARVIS, NovaCPX, DO sites, Proxmox, FusionPBX,
MediaStack, and project feedback/preferences.
2026-06-26 03:06:26 +00:00

5.7 KiB

name, description, metadata
name description metadata
project-novacpx NovaCPX — custom Linux web hosting control panel (cPanel alternative) built from scratch
node_type type originSessionId
memory project c454fc50-f93d-4ddd-b9f3-f3f442e89fb9

NovaCPX is a full web hosting control panel built from scratch with 4 dedicated ports: Admin :8882, Reseller :8881, User :8880, Webmail (Roundcube) :8883.

Why: User wants a cPanel/Plesk alternative they own outright, with distinctive UI, auto-installer, and extensible feature system.

How to apply: Direct SSH to VM works (no PVE hop needed). All code in /tmp/novacpx locally + pushed to GitHub myronblair/novacpx.

VM Details

  • Host: PVE1 VM 120, name HostPanel-110
  • IP: 10.48.200.110 (static)
  • OS: Ubuntu 24.04 LTS
  • SSH (direct — works from Claude Code): sshpass -p 'Joker1974!!!' ssh -o StrictHostKeyChecking=no root@10.48.200.110
  • SCP (direct): sshpass -p 'Joker1974!!!' scp -o StrictHostKeyChecking=no FILE root@10.48.200.110:/path
  • PVE1 hop NOT needed and currently broken (permission denied on orbisne.fortiddns.com SSH)
  • Panel web root: /srv/novacpx/public
  • Repo on VM: /opt/novacpx-src
  • Source of truth for deploy scripts: /opt/novacpx-src/deploy/

GitHub

  • Repo: myronblair/novacpx (private)
  • Branches: main (stable releases, PATCH auto-bump) and beta (beta releases, -beta.N auto-bump)
  • Auto-deploy: push to main or beta → GitHub webhook → VM pulls (deploy-runner.sh cron every min)
  • PAT: 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

Ports

  • 8880 — User panel
  • 8881 — Reseller panel
  • 8882 — Admin/datacenter panel
  • 8883 — Roundcube webmail

Architecture

  • Backend: PHP 8.3 REST API (/api/{endpoint}/{action})
  • Auth: session cookie ncpx_session (login returns token, set as cookie) + Bearer tokens for API. Both use SHA256 hash lookup in sessions/api_tokens tables.
  • DB: SQLite at /var/lib/novacpx/panel.db (migrated from MySQL 2026-06-09)
  • DB.php has translate() layer: ON DUPLICATE KEY→ON CONFLICT, DATE_ADD/DATE_SUB→datetime(), NOW()→datetime('now')
  • novacpx_version table: tracks install history; columns: id, version, installed_at, notes, git_commit
  • settings table: panel_version key tracks current version; update_channel (stable/beta) controls which GitHub branch to check/pull
  • Feature registry: 70+ pluggable features in 20 categories
  • Update channels: stable=origin/main (major/minor), beta=origin/beta (patch/pre-release)

VM Credentials

  • Admin panel: admin / Admin2026!
  • Root SSH: root / Joker1974!!!
  • MySQL (customer sites): novacpx_user / 6fyWj6vYnJDKEQvNANzj
  • WP provisioning MySQL user: novacpx_wp / tCXAU1K2WX31xUAMY9EM
  • Config: /etc/novacpx/config.ini (root:www-data 640)

JARVIS Agent

  • Installed 2026-06-09; agent_id: novacpx_e3b07264
  • Status: online, reporting heartbeats to JARVIS every 10s
  • Config: /opt/jarvis-agent/config.json
  • Key config fields: server_url, host_header, api_key, agent_type: linux, heartbeat_interval: 10

Versioning (as of 2026-06-10)

  • Current git version: 1.0.27 (GitHub main)
  • Active deploy channel: beta (VM settings)
  • GitHub Actions auto-bumps VERSION on push to main (PATCH) or beta (-beta.N)
  • deploy-runner.sh writes new version to novacpx_version + settings.panel_version after every webhook deploy
  • deploy-runner.sh also copies VERSION to web root (/srv/novacpx/public/VERSION) — fixed 2026-06-10
  • apply-novacpx-update endpoint does the same for manual updates
  • Updates page shows installed version, latest remote version, and active channel badge

Settings Page

  • Loads current values from DB via server-options API (panel_name, default_php, nameservers, update_channel)
  • Saves each field via save-option API individually
  • Update channel dropdown shows stable vs beta with explanatory labels

Docker App Catalog (as of 2026-06-10)

  • 140 apps across 15 categories in DockerManager.php
  • Categories: Web/CMS, Productivity, Dev, Analytics, Wiki/Docs, Messaging/Chat, Security/Auth, Business, Design/Collab, AI/LLM, Developer Tools, Databases, Monitoring, Networking/Security, CMS/E-commerce, Project Mgmt, Communication, File/Storage, ERP/Business, Media, Smart Home, Dashboards/Admin
  • Security fixes applied (code review 2026-06-09): shell injection fixes in WordPressManager, PHPManager, install.sh sudoers hardening (removed ufw * wildcard, removed curl * and env * NOPASSWD), SQLite syntax fix, WP-CLI download size validation
  • Per-account uninstall (uninstall-account API action), per-stack Reinstall button
  • Admin Docker page has App Catalog tab to launch apps on behalf of accounts

nginx / phpMyAdmin (fixed 2026-06-12)

  • Apache2 was holding port 80 — stopped and disabled: systemctl stop apache2 && systemctl disable apache2
  • nginx now running and enabled on boot
  • phpMyAdmin accessible at http://10.48.200.110/phpmyadmin (returns 200)
  • nginx config: /etc/nginx/sites-enabled/default with /phpmyadmin location block
  • phpmyadmin.conf in /etc/nginx/conf.d/ is empty (just a comment) — all config in sites-enabled/default

Known Quirks

  • PVE1 SSH (orbisne.fortiddns.com) currently permission-denied — use direct IP 10.48.200.110 instead
  • api/.htaccess needed for URL routing
  • NOVACPX_ROOT in api/index.php is dirname(DIR) = /srv/novacpx/public (NOT 2 levels up)
  • SQLite only — never use MySQL syntax (ON DUPLICATE KEY, NOW(), etc.)
  • sudo tee pattern needed for writing /etc/postfix/* files as www-data
  • Docker stacks backed by docker_compose_stacks table (separate from docker_containers)
  • Panel reachable at https://10.48.200.110:8882 (self-signed cert with SAN for IP)
  • PHP-FPM pools are root-owned; www-data cannot file_exists()/unlink() them — must use sudo rm -f
  • git cherry-pick to beta required after each main push (GitHub Actions version bumps create divergence)