Files
do-server-config/infra/CLAUDE.md
T

24 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Environment

This is a home-lab / managed-hosting environment. There is no local codebase to build or test — work consists of editing PHP/JS files on remote servers via SSH and managing infrastructure across several machines. All tool calls use sshpass with password auth.

Tailscale Network

All key hosts are on Tailscale (myronblair@gmail.com). Use Tailscale IPs for SSH — no relay or port forwarding needed.

Host Tailscale IP LAN IP Password
Claude VM (this) 100.69.120.58 10.48.200.29
PVE1 100.80.188.8 10.48.200.90 Joker1974!!!
PVE2 100.87.186.12 10.48.200.91 Joker1974!!!
DO server (orbis) 100.121.13.34 165.22.1.228 Gonewalk1974!@#
FusionPBX 100.74.46.120 134.209.72.226 Joker1974!@#
JARVIS VM 100.77.178.42 10.48.200.211 Joker1974!!!
NPM VM 100.110.239.71 10.48.200.201 Joker1974!!!
Ollama VM 100.96.100.113 10.48.200.210 Joker1974!!!
NovaCPX (hostpanel-110) 100.86.51.18 10.48.200.110 Joker1974!!!
HomeBridge 100.124.182.18 10.48.200.18
WireGuard CT 100.122.55.10 10.48.200.19
Synology NAS 100.118.175.5 10.48.200.249
mini-it12 (Windows) 100.98.151.120 10.48.200.87

DNS note: FortiGate blocks outbound port 53. All PVE1 VMs must use 10.48.200.90 (PVE1 dnsmasq → 100.100.100.100) as their DNS server, not 8.8.8.8 directly.

Server Map

Host IP SSH Purpose
DO (main) 165.22.1.228 root / Gonewalk1974!@# CyberPanel/OLS — all websites (not JARVIS after migration)
FusionPBX 134.209.72.226 root / Joker1974!@# (via Tailscale 100.74.46.120) FreeSWITCH PBX
PVE1 (Proxmox) orbisne.fortiddns.com (10.48.200.90) root / Joker1974!!! (via Tailscale 100.80.188.8) Primary hypervisor
PVE2 (Proxmox) 10.48.200.91 root / Joker1974!!! Secondary hypervisor
JARVIS VM 10.48.200.211 root / Joker1974!!! (via Tailscale 100.77.178.42) JARVIS dashboard — PVE1 VM 211, 8c/16GB
NPM VM 10.48.200.201 root / Joker1974!!! (via Tailscale 100.110.239.71) Nginx Proxy Manager — PVE1 VM 105 (was VM200 pre-2026-06-22 restore; cloud-init says .200 but runs at .201)
Ollama VM 10.48.200.210 root / Joker1974!!! (via Tailscale 100.96.100.113) Local LLM — PVE1 VM 106 (was VM210 pre-2026-06-22 restore), 4c/8GB
Home Assistant 10.48.200.97 myron → sudo HA VM 101
NetworkBackup 10.48.200.99 myron → sudo Backup VM (PVE2 VM 302)
MediaStack 10.48.200.35 root via PVE1 key Sonarr/Radarr/Prowlarr/qBittorrent (PVE1 VM 103, was VM113 pre-2026-06-22 restore)
NovaCPX 10.48.200.110 root / Joker1974!!! (direct SSH — Tailscale 100.86.51.18 needs re-auth) Custom hosting control panel (PVE1 VM 120)
NPM 10.48.200.201 root / Joker1974!!! (via Tailscale 100.110.239.71) Nginx Proxy Manager — PVE1 VM 200 · NPM API: POST http://localhost:81/api/tokens identity=myronblair@outlook.com

SSH password order (try in sequence if first fails): Joker1974!@#Joker1974!!!Joker1974!

SSH pattern for all remote work:

sshpass -p 'Gonewalk1974!@#' ssh -o StrictHostKeyChecking=no root@165.22.1.228 'commands here'

For PVE1 from anywhere (FortiGate DDNS, survives IP changes):

sshpass -p 'Joker1974!!!' ssh -o StrictHostKeyChecking=no root@orbisne.fortiddns.com 'commands here'

For commands inside VMs on PVE1:

sshpass -p 'Joker1974!!!' ssh -o StrictHostKeyChecking=no root@orbisne.fortiddns.com \
  'qm guest exec <VMID> -- bash -c "commands here"'

Websites on DO (165.22.1.228)

All sites live at /home/<domain>/public_html/ on DO. CyberPanel/OpenLiteSpeed serves them.

Site Path GitHub
jarvis.orbishosting.com removed from DO myronblair/jarvis (now on PVE1 VM 211)
tomsjavajive.com /home/tomsjavajive.com/public_html/ myronblair/tomsjavajive
epictravelexpeditions.com /home/epictravelexpeditions.com/public_html/ myronblair/epictravelexpeditions
parkerslingshotrentals.com /home/parkerslingshotrentals.com/public_html/ myronblair/parkerslingshotrentals
orbishosting.com /home/orbishosting.com/public_html/ myronblair/orbishosting
orbis.orbishosting.com /home/orbis.orbishosting.com/public_html/ myronblair/orbis-hosting-portal
tomtomgames.com /home/tomtomgames.com/public_html/ myronblair/tomtomgames

Parker Slingshot is served from epictravelexpeditions.com, not its own domain:

  • URL: https://parkerslingshot.epictravelexpeditions.com
  • Path: /home/epictravelexpeditions.com/parkerslingshot/
  • GitHub: myronblair/parkerslingshot (own repo, auto-deploy active)
  • db.php and config.php are gitignored (credentials); db.php.example is the reference template

Deployment Workflow

Auto-deploy is active. Push to main on any site repo → GitHub webhook → server pulls automatically within 1 minute. PHP syntax is validated before deploy; bad commits are auto-reverted.

Two separate webhook handlers:

  • JARVIS repohttp://jarvis.orbishosting.com:1972/webhook.php — deploys to JARVIS VM (/var/www/jarvis/). Deploy log: /var/www/jarvis/logs/deploy.log
  • **All 6 website repos → https://tomtomgames.com/webhook.php on DO — deploys to /home/<site>/public_html/ on DO. Deploy log: /home/tomtomgames.com/logs/deploy.log. Deploy log: /home/<site>/logs/deploy.log

HMAC secret (both handlers): 4c8805f0285214ff0a0602b5880270b935f36a896946c7f1 Deploy queue: /tmp/jarvis-deploy-queue.txt | Runner: /usr/local/bin/jarvis-deploy.sh (cron every min, on both DO and JARVIS VM)

For hotfixes that can't wait 1 min, SCP directly:

# JARVIS VM
scp -o StrictHostKeyChecking=no /tmp/changed.php root@100.77.178.42:/var/www/jarvis/public_html/changed.php
# DO websites
sshpass -p 'Gonewalk1974!@#' scp -o StrictHostKeyChecking=no /tmp/changed.php \
  root@165.22.1.228:/home/site.com/public_html/changed.php

GitHub PAT (embedded in remote URLs): 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 — expires ~2026-08-20. Infra repo: myronblair/infra — cloned at /opt/infra on DO server.

DO server backup: myronblair/do-server-config — scripts, systemd units, WireGuard, OLS vhosts, cron, MySQL creds + 8-phase restore wizard. Weekly Sunday 4am. Launcher: /usr/local/bin/do-server-backup.

Gitignored credentials (never in GitHub): api/config.php (JARVIS, epictravelexpeditions), config/database.php (tomsjavajive).

JARVIS System

Iron Man-style AI dashboard at http://jarvis.orbishosting.com:1972. Migrated from DO to PVE1 VM 211 (2026-06-18). All files on JARVIS VM at /var/www/jarvis/.

Access:

  • Dashboard: http://jarvis.orbishosting.com:1972
  • Admin: http://jarvis.orbishosting.com:1972/admin
  • Internal (LAN): http://10.48.200.211 or via Tailscale http://100.77.178.42
  • FortiGate VIP: external port 197210.48.200.211:80
  • Cloudflare: DNS only (grey cloud) — no CF proxy, no SSL overhead on origin

Stack on JARVIS VM:

  • nginx + PHP 8.3-FPM (replaces OLS/lsphp on DO)
  • MariaDB (jarvis_db local, jarvis_user / J4rv1s_Pr0t0c0l_2026!)
  • Redis (redis-server)
  • Python 3 + Arc Reactor daemon

Architecture:

  • public_html/api.php — API router; session_start() skipped only for machine-agent sub-actions (heartbeat/metrics/ha_state/command_result/register); browser-facing agent routes (list/status/myip) need session. Has session_write_close() guard (must skip for auth endpoint to prevent LSAPI session deadlock).
  • api/config.php — all credentials/constants (gitignored)
  • api/endpoints/chat.php — 4-tier AI: KB intent → Groq (compound-beta-mini) → Claude API; includes Tier 0.7 planner intents (tasks/appointments/briefing). Ollama at http://10.48.200.210:11434.
  • api/endpoints/agent.php — push-based agent registration/heartbeat/metrics; browser actions (list/status/myip) auth via $_SESSION, machine actions auth via X-Agent-Key header
  • api/endpoints/alerts.php — auto-generates alerts (CPU >85%, RAM >85%, disk >88%, offline agents, site down); dispatches restart commands to agents when their services fail
  • api/endpoints/facts_collector.php — runs every 3 min via cron (php8.3); collects agent metrics, KB facts, Proxmox/HA status, and all 7 site HTTP health checks. Site checks use external URLs (JARVIS VM is NOT the web host). $fresh() queries WHERE category=? (not fact_category).
  • api/endpoints/stats_cache.php — runs every 5 min via cron; weather/news/Proxmox cache refresh. Proxmox API at https://10.48.200.90:8006 (direct LAN).
  • api/endpoints/do_server.php — reads /proc for JARVIS VM stats; also includes DO server agent metrics (do_server key from jarvis-do agent via Tailscale).
  • api/endpoints/planner.php — tasks & appointments CRUD; routes: planner/tasks, planner/appointments, planner/today, planner/done
  • api/endpoints/ha.php — HA entity list reads from ha_entities table (real-time agent push); service calls go direct to HA_URL (http://10.48.200.97:8123)
  • api/lib/kb_engine.phpstoreFact() ON DUPLICATE KEY UPDATE always sets updated_at=NOW() explicitly; without this, unchanged values don't bump the timestamp and freshness checks break.

Voice system (index.html):

  • Continuous SpeechRecognition; mic stays open always (mute toggle button)
  • Phase 1 wake: say "wake up JARVIS" or "daddy's home" → activates voice mode once
  • Phase 2 command: say "JARVIS [command]" → executes; opens 17-second free-listen window (no prefix needed for follow-ups)
  • After 30 min of no commands → voice sleeps; full wake phrase required again
  • Mic paused during ElevenLabs TTS playback (isSpeaking guard) to prevent feedback loop
  • Auto-reload after 5 min idle is silent (no greeting speech)

Planner system (2026-05-31):

  • DB tables: tasks (id, title, notes, category, priority, status, due_date, due_time, completed_at) and appointments (id, title, description, category, start_at, end_at, location, all_day, reminder_min, alerted)
  • Voice commands: "add task [title]", "my tasks", "mark [x] done", "schedule [title] on [date]", "my calendar", "daily briefing"
  • Home page: small top-bar badge "N TASKS · N APPTS" when items due today (no panel added)
  • Admin CRUD at /admin under PLANNER section → TASKS and APPOINTMENTS tabs

Agent system: Agents phone home every 10s (heartbeat) / 30s (metrics) to http://10.48.200.211 (direct LAN — no Cloudflare). Config at /etc/jarvis-agent/config.json or /opt/jarvis-agent/config.json on each Linux agent. Agent installer (one-liner for any Linux host): curl -sk http://10.48.200.211/install-agent.sh | bash -s <hostname> <linux|homeassistant|proxmox> DO server agent (jarvis-do) uses Tailscale: jarvis_url: http://100.77.178.42

Agent file paths by host (for manual updates — push to correct path then restart service):

  • Most Linux hosts: /opt/jarvis-agent/jarvis-agent.py · service: systemctl restart jarvis-agent
  • WireGuard CT (10.48.200.19, Alpine): /opt/jarvis-agent/agent.py · service: rc-service jarvis-agent restart
  • public_html/agent/jarvis-agent.py is the self-update URL — must be kept in sync with agent/jarvis-agent.py (both are tracked in git; auto-deploy keeps them in sync after 2026-06-17)

Self-healing: /usr/local/bin/jarvis-watchdog.sh runs every 5 min (root cron on DO). Restarts lsws/mysql/redis on DO if down. Log: /usr/local/lsws/logs/watchdog.log on DO.

JARVIS DB: jarvis_db on JARVIS VM localhost (MariaDB). User: jarvis_user / J4rv1s_Pr0t0c0l_2026!. phpMyAdmin at /phpmyadmin on JARVIS VM (myron / Joker1974!!!). Core tables: agent_commands, agent_metrics, alerts, api_cache, appointments, conversations, ha_entities, kb_facts, kb_intents, kb_ollama_models, kb_preferences, known_commands, metrics_history, network_devices, registered_agents, tasks, users. Arc Reactor adds: arc_jobs, guardian_events, guardian_config, agent_screenshots.

kb_facts schema: (id, category, fact_key, fact_value, host, expires_at, updated_at) — column is category not fact_category.

Arc Reactor daemon: Python service at /opt/jarvis-arc/reactor.py on JARVIS VM, port 7474, managed by systemctl restart jarvis-arc. Deploy source: deploy/reactor.py in the jarvis repo. After pushing to GitHub, auto-deploy pulls to /var/www/jarvis/deploy/reactor.py — then manually cp /var/www/jarvis/deploy/reactor.py /opt/jarvis-arc/reactor.py && systemctl restart jarvis-arc. Log: /var/www/jarvis/logs/arc_reactor.log.

Arc Reactor AI routing:

Feature Provider Model
Guardian anomaly alerts Groq llama-3.3-70b-versatile
SITREP Groq llama-3.3-70b-versatile
Vision: text-only sysinfo snapshot Groq llama-3.3-70b-versatile
Vision: actual screenshot image Claude claude-opus-4-8-20251101
Email drafting, research, tool_loop Claude claude-sonnet-4-6

llm_call(messages, provider) cascades: groq → ollama on failure. Pass "groq" or "claude" as provider.

Groq API note: Use model name compound-beta-mini directly — NOT groq/compound-beta-mini (that's OpenAI router syntax and will 404 on api.groq.com).

Tom's Java Jive

PHP e-commerce site. DB: toms_tjj_db / toms_tjj_user / +60wlPc+55e@gFq4. Key quirks:

  • No slug column on products — URLs use ?id=product_id
  • All tables must be utf8mb4_unicode_ci — mixed collation causes MySQL error 1267 on JOINs
  • wallet_transactions.type and loyalty_transactions.type have strict enums (see memory for values)

MediaStack

Automated media server on PVE1 VM 113. All traffic routes through WireGuard VPN → DO, bypassing home ISP.

SSH: From PVE1: ssh -o StrictHostKeyChecking=no -i /root/.ssh/id_rsa root@10.48.200.35

Services:

Service Port Login
qBittorrent :8080 admin / Joker1974!!!
Sonarr :8989 API key: b43e04350a594846b4ee95261c29e9e0
Radarr :7878 API key: 53c4268360444feeae5f98c0cc24e0e3
Prowlarr :9696 API key: 9d0ce6c5660743b5bf1c7951efc62252

Media paths: downloads → /media/downloads/complete | movies → /media/movies | tv → /media/tv Jellyfin NFS mounts (VM 112, 10.48.200.33): /mnt/mediastack/movies and /mnt/mediastack/tv WireGuard: wg0 IP 10.200.0.4 → CT110 (10.48.200.19:51821) → DO. Kill-switch active; LAN always allowed. DNS: FortiGate blocks port 53 outbound. PVE1 runs dnsmasq on :53 → 100.100.100.100. MediaStack uses DNS=10.48.200.90. Indexer: IPTorrents via Prowlarr (cookie auth). Prowlarr syncs to Sonarr + Radarr automatically. GitHub: myronblair/mediastack (private) — config files, systemd units, README with full setup notes. JARVIS agent quirks: needs jarvis_url, registration_key (f846a9aaf7ce9a61742c63c87c4186052a71d2a580c65518), ssl_verify: false in config.

NovaCPX Panel

Custom web hosting control panel (PVE1 VM 120, 10.48.200.110). Root SSH: sshpass -p 'Joker1974!!!' ssh -o StrictHostKeyChecking=no root@10.48.200.110 (direct LAN — use this, Tailscale 100.86.51.18 requires re-auth periodically).

Public URLs (via NPM → FortiGate VIP port 443 → 10.48.200.201):

  • Admin: https://admin.novacpx.orbishosting.com (→ port 8882) · admin / Admin2026! or myron / Joker1974!!!
  • Reseller: https://reseller.novacpx.orbishosting.com (→ port 8881)
  • User: https://panel.novacpx.orbishosting.com or https://novacpx.orbishosting.com (→ port 8880)
  • Webmail: port 8883 (Roundcube) — no public NPM proxy yet
  • https://web.orbishosting.com → port 80 (placeholder for a new hosted website)

Ports: 8880 (user) · 8881 (reseller) · 8882 (admin) · 8883 (Roundcube webmail)

Paths: Panel web root /srv/novacpx/public/ · Git repo /opt/novacpx-src/ · DB /var/lib/novacpx/panel.db (SQLite) · Config /etc/novacpx/config.ini

Config notes: /etc/novacpx/config.ini must have server = nginx (not apache) — VhostManager checks this to write the correct vhost format.

⚠ After any restore from PBS backup: config.ini reverts to server = apache. Always run: sed -i "s/^server = apache/server = nginx/" /etc/novacpx/config.ini after a restore. Also: PHP-FPM will fail to start if orphaned pool configs exist from pre-restore accounts — run the cleanup: for f in /etc/php/8.3/fpm/pool.d/*.conf; do [[ "$f" == *"www.conf"* ]] && continue; u=$(basename "$f" .conf); id "$u" &>/dev/null || rm -f "$f"; done && systemctl start php8.3-fpm. The webacct hosting account and its nginx vhost must be recreated after restore (Linux user survives but DB record and vhost are lost).

GitHub: myronblair/novacpx (private). Auto-deploy active: push to main (stable) or beta → webhook → VM pulls. GitHub Actions auto-bumps VERSION: main→PATCH, beta→-beta.N suffix. Current version: 1.0.40.

Update channels: stable tracks origin/main, beta tracks origin/beta. Set in Admin → Settings → Update Channel.

Local clone: /tmp/novacpx/ on this machine. All edits go here first, then git push origin main. The deploy runner syncs panel//srv/novacpx/public/ and panel/lib//srv/novacpx/public/lib/. For immediate changes use SCP to root@10.48.200.110:/srv/novacpx/public/.

PHP-FPM: Per-account pools in /etc/php/8.3/fpm/pool.d/. If php8.3-fpm fails to start, check for orphaned pool configs referencing deleted Linux users — remove them and systemctl start php8.3-fpm.

JARVIS agent: Installed, online. Agent ID: novacpx_e3b07264.

SQLite quirk: Never use MySQL syntax (ON DUPLICATE KEY, NOW(), DATE_ADD, etc.). DB.php has translate() layer but endpoints must also use SQLite syntax directly.

Parker Slingshot Rentals

Admin portal at /admin/index.php uses HMAC-signed cookie auth (not PHP sessions — sessions were unreliable under LiteSpeed caching). Admin: admin / Parker2026!. DB: epic_parkersling / epic_parkersling / Joker1974!!!.

FusionPBX / FreeSWITCH

Production at 134.209.72.226. Web: https://fusion.orbishosting.com (admin / fY7XP5swgtpbzrYLhkeVYkA4744). SIP profiles served via Lua XML handler — config changes require deleting /var/cache/fusionpbx/FusionPBX.configuration.sofia.conf to force reload. Extension 1000 (Yealink T48S at 10.48.200.43) registered on production server via port 5080 with aggressive-nat-detection=true to bypass FortiGate SIP ALG.

SSH access: Direct via Tailscale (preferred):

sshpass -p 'Joker1974!@#' ssh -o StrictHostKeyChecking=no root@100.74.46.120

Fallback if Tailscale down — relay through DO:

sshpass -p 'Gonewalk1974!@#' ssh -o StrictHostKeyChecking=no root@165.22.1.228 \
  'sshpass -p "Joker1974!@#" ssh -o StrictHostKeyChecking=no root@134.209.72.226 "command"'

Backup: myronblair/fusionpbx-config — PostgreSQL dump (gzip, ~29MB) + FreeSWITCH configs + restore wizard. Weekly Sunday 5am. Launcher: /usr/local/bin/fusionpbx-backup.

Proxmox

PVE1 at 10.48.200.90, PVE2 at 10.48.200.91. Root login direct. Run commands inside VMs via:

qm guest exec <VMID> -- bash -c 'command'

Proxmox --nameserver must be space-separated: "8.8.8.8 1.1.1.1" (comma causes netplan bug).

Backup: myronblair/proxmox-config — shared cluster configs (VM .conf, storage, HA, SDN) + per-node (network, cron, systemd, scripts). Weekly Sunday 3am. Launcher: /usr/local/bin/proxmox-backup on both nodes.

PHP / OLS Runtime

CyberPanel uses lsphp85. Run PHP scripts directly with:

/usr/local/lsws/lsphp85/bin/lsphp /path/to/script.php

For PHP syntax checking use php8.3 -l file.php — lsphp segfaults on -l flag. When a PHP endpoint uses ob_start() + header.php pattern, add ob_end_clean() before any CSV/JSON response output.

Cloudflare Rocket Loader: JARVIS uses data-cfasync="false" on every <script> tag in index.html (including CDN scripts like face-api.js). One untagged script is enough to trigger Rocket Loader's bootstrap, which injects mainScript.js and causes SyntaxError: Identifier 'mainScriptFlag' has already been declared. Cache-Control: no-store, no-cache, must-revalidate, no-transform is set in index.php but tag every new script with data-cfasync="false" anyway.

Cloudflare auto-deploy cache problem: After pushing JS fixes, Cloudflare CDN serves stale cached files even on hard refresh. Bump the ?v=YYYYMMDD query param on script tags in index.html to force a cache miss. Current version param: ?v=20260617b.

JS file structure (as of 2026-06-17):

  • assets/js/jarvis-effects.js — canvas particle effects, sparklines
  • assets/js/jarvis-overlays.js — sleep overlay, network map canvas
  • assets/js/jarvis-app.js — globals, init, chat, voice, system/network/HA/alerts/weather panels
  • assets/js/panels/jarvis-arc.js — Arc Reactor, Intel Protocol, Comms Protocol, Guardian Mode
  • assets/js/panels/jarvis-agents.js — Missions, Directives, Memory, Clearance, Agents tab, Sites, Vision Protocol
  • assets/js/panels/jarvis-assistant.js — Chat History, Command Palette, Suggestions, Mobile switcher, Topology map

A SyntaxError in any panels/ file breaks only that group — other panels stay functional. escHtml() is defined in jarvis-arc.js (loads first) and is global to all subsequent files.

GitHub Repos

Repo Site DB Schema
myronblair/jarvis jarvis.orbishosting.com db/schema.sql (15 tables)
myronblair/tomsjavajive tomsjavajive.com db/schema.sql (45 tables)
myronblair/epictravelexpeditions epictravelexpeditions.com db/schema.sql (7 tables)
myronblair/parkerslingshot parkerslingshot.epictravelexpeditions.com (no schema file — DB managed directly)
myronblair/parkerslingshotrentals parkerslingshotrentals.com db/schema.sql (10 tables)
myronblair/orbishosting orbishosting.com
myronblair/orbis-hosting-portal orbis.orbishosting.com
myronblair/tomtomgames tomtomgames.com db/schema.sql (22 tables)
myronblair/infra server configs cron, systemd, agent configs
myronblair/mediastack MediaStack VM 113 config/, systemd units, wg0.conf, README
myronblair/do-server-config DO server backup scripts, systemd, WG, OLS vhosts, restore.sh
myronblair/proxmox-config PVE1+PVE2 backup shared cluster configs + per-node, restore.sh
myronblair/novacpx admin.novacpx.orbishosting.com db/schema.sql (SQLite, 19+ tables)
myronblair/web-dashboard web.orbishosting.com
myronblair/fusionpbx-config FusionPBX backup PostgreSQL dump (gzip) + FS configs, restore.sh

All repos are private. Each has config/vhost/ with OLS vhost config. The jarvis repo also has deploy/ (watchdog, deploy runner, systemd units) and agent/jarvis-agent.py.

Split DNS (added 2026-06-21)

PVE1 dnsmasq (/etc/dnsmasq.d/forwarder.conf) has split DNS entries so all NPM-proxied domains resolve to 10.48.200.201 (NPM internal IP) for LAN clients — bypasses FortiGate hairpin NAT for HTTPS:

address=/jarvis.orbishosting.com/10.48.200.201
address=/hoa.orbishosting.com/10.48.200.201
address=/novacpx.orbishosting.com/10.48.200.201
address=/admin.novacpx.orbishosting.com/10.48.200.201
address=/reseller.novacpx.orbishosting.com/10.48.200.201
address=/panel.novacpx.orbishosting.com/10.48.200.201
address=/web.orbishosting.com/10.48.200.201

For any LAN device to use this: set DNS to 10.48.200.90 in Wi-Fi settings (or update FortiGate DHCP to push 10.48.200.90 as DNS for all clients).

NovaCPX Panel (updated 2026-06-22)

Post-restore notes added — see deploy/novacpx-post-restore.sh which fixes config.ini, cleans pools, rebuilds webacct account. VM IDs changed after restore: MediaStack VM103, NPM VM105, Ollama VM106. Run script after any PBS restore.

NovaCPX v1.0.54 — security fixes (CORS, open redirect, CSS injection, exception leakage), subdomains (#36), parked domains (#37), account settings page (#38), better default index (#39), post-restore script (#50), collapsible nav (#48).

web.orbishosting.com — Blair HQ dashboard with server-backed notes (notes.php → /home/webacct/notes.json). Password: myronblair@outlook.com / Joker1974!!!