Files
infra/CLAUDE.md
T

19 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!!!
FusionPBX 100.74.46.120 134.209.72.226 Joker1974!@#
JARVIS VM (new) 100.77.178.42 10.48.200.211 Joker1974!!!
NPM VM 100.110.239.71 10.48.200.200 Joker1974!!!
Ollama VM 100.96.100.113 10.48.200.95 Joker1974!!!
HomeBridge 100.124.182.18 10.48.200.18
WireGuard CT 100.122.55.10 10.48.200.19

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.200 root / Joker1974!!! (via Tailscale 100.110.239.71) Nginx Proxy Manager — PVE1 VM 200
Ollama VM 10.48.200.95 root / Joker1974!!! (via Tailscale 100.96.100.113) Local LLM — PVE1 VM 210, 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 113)
NovaCPX 10.48.200.110 root / Joker1974!!! (direct SSH works) Custom hosting control panel (PVE1 VM 120)

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 /home/jarvis.orbishosting.com/ myronblair/jarvis
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.

Webhook handler: https://jarvis.orbishosting.com/webhook.php (HMAC secret: 4c8805f0285214ff0a0602b5880270b935f36a896946c7f1) Deploy queue: /tmp/jarvis-deploy-queue.txt | Runner: /usr/local/bin/jarvis-deploy.sh (cron every min) Deploy log: /home/jarvis.orbishosting.com/logs/deploy.log

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

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 https://jarvis.orbishosting.com. All files on DO at /home/jarvis.orbishosting.com/.

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 VM deleted — no longer in chain.
  • 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; collects agent metrics, KB facts, Proxmox/HA status, and all 7 site HTTP health checks. Site checks use http://127.0.0.1 with Host: header (avoids Cloudflare CDN timeouts). $fresh() queries WHERE category=? (not fact_category).
  • api/endpoints/stats_cache.php — runs every 5 min via cron; weather/news/Proxmox cache refresh
  • 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://orbisne.fortiddns.com: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 https://165.22.1.228 with Host: jarvis.orbishosting.com header (bypasses Cloudflare). Config at /opt/jarvis-agent/config.json on each Linux agent. Agent installer (one-liner for any Linux host): curl -sk https://jarvis.orbishosting.com/install-agent.sh | bash -s <hostname> <linux|homeassistant|proxmox>

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). Restarts lsws/mysql/redis if down, restarts offline Proxmox VM agents via qm guest exec, inserts alerts to DB, rotates logs. Watchdog log: /home/jarvis.orbishosting.com/logs/watchdog.log

JARVIS DB: jarvis_db on localhost. User: jarvis_user / J4rv1s_Pr0t0c0l_2026!. phpMyAdmin at /phpmyadmin (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, port 7474, managed by systemctl restart jarvis-arc. Deploy source: deploy/reactor.py in the jarvis repo. After pushing to GitHub, the auto-deploy pulls to /home/jarvis.orbishosting.com/deploy/reactor.py — then manually cp deploy/reactor.py /opt/jarvis-arc/reactor.py && systemctl restart jarvis-arc. Log: /home/jarvis.orbishosting.com/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 at https://10.48.200.110 (PVE1 VM 120). Admin: admin / Admin2026!. Root SSH: sshpass -p 'Joker1974!!!' ssh -o StrictHostKeyChecking=no root@10.48.200.110 (direct, no hop needed — PVE1 SSH currently broken).

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

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.27.

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.

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 10.48.200.110:8882 db/schema.sql (SQLite, 19+ tables)
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.