Audit all .sh and .php files under /boot/config/plugins/varaverk for hardcoded paths that should be variables. Do NOT audit files under /boot/config/plugins/varaverk/Deployment/conf_templates/ or .git/.

The codebase uses these canonical variables (defined in load_config.sh and config.php):
- SCRIPTS_DIR — repo root (e.g. /boot/config/plugins/varaverk)
- CONF_DIR — $SCRIPTS_DIR/Configurations
- STATE_DIR — $SCRIPTS_DIR/State_Files
- DATA_DIR — $SCRIPTS_DIR/data
- LOG_DIR — /var/log/varaverk
- VV_CACHE_DIR — /tmp/vv_cache  (PHP constant)
- SCRIPTS_DIR (PHP constant) — same as bash
- CONF_DIR (PHP constant)
- TARGET_DIR — set in master.conf (same as SCRIPTS_DIR in practice)

Paths that are inherently platform-specific and acceptable as hardcoded (don't flag these):
- /usr/local/emhttp/... — Unraid webUI paths (already in Plugin/unraid/)
- /boot/config/plugins/dockerMan/... — Unraid Docker Manager
- /boot/config/plugins/folder.view3/... — Unraid plugin
- /etc/unraid-version — Unraid version file
- /var/log/... — system log paths (not varaverk-specific)
- /root/.ssh/... — SSH key paths
- /mnt/user/... — Unraid array mount (these ARE platform-specific in Plugin/unraid/ context)
- /tmp/user.scripts — Unraid User Scripts plugin path

Flag these patterns across ALL .sh and .php files:
1. /boot/config/plugins/varaverk — should use SCRIPTS_DIR / SCRIPTS_DIR constant
2. /mnt/user/appdata/Varaverk or /mnt/user/appdata-Fallback/... used as config/state paths (not media) — check if they should use DATA_DIR or STATE_DIR
3. /tmp/vv_cache — should use VV_CACHE_DIR
4. /var/log/varaverk — should use LOG_DIR
5. Any other Varaverk-specific paths not using an appropriate variable

For each finding report: file path (relative to /boot/config/plugins/varaverk/), line number, the hardcoded string, and which variable should replace it.

Group findings by file. Keep it concise — list findings only, no explanations needed per line. Note total count at end.


- update-config: Use this skill to configure the Claude Code harness via settings.json. Automated behaviors ("from now on when X", "each time X", "whenever X", "before/after X") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions ("allow X", "add permission", "move permission to"), env vars ("set X=Y"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: "allow npm commands", "add bq permission to global settings", "move permission to user settings", "set DEBUG=true", "when claude stops show X". For simple settings like theme/model, suggest the /config command.
- keybindings-help: Use when the user wants to customize keyboard shortcuts, rebind keys, add chord bindings, or modify ~/.claude/keybindings.json. Examples: "rebind ctrl+s", "add a chord shortcut", "change the submit key", "customize keybindings".
- verify: Verify that a code change actually does what it's supposed to by running the app and observing behavior. Use when asked to verify a PR, confirm a fix works, test a change manually, check that a feature works, or validate local changes before pushing.
- code-review: Review the current diff for correctness bugs and reuse/simplification/efficiency cleanups at the given effort level (low/medium: fewer, high-confidence findings; high→max: broader coverage, may include uncertain findings; ultra: deep multi-agent review in the cloud). Pass --comment to post findings as inline PR comments, or --fix to apply the findings to the working tree after the review.
- simplify: Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.
- fewer-permission-prompts: Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.
- loop: Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace. - When the user wants to set up a recurring task, poll for status, or run something repeatedly on an interval (e.g. "check the deploy every 5 minutes", "keep running /babysit-prs"). Do NOT invoke for one-off tasks.
- schedule: Create, update, list, or run scheduled remote agents (routines) that execute on a cron schedule. - When the user wants to schedule a recurring remote agent, set up automated tasks, create a cron job for Claude Code, or manage their scheduled agents/routines. Also use when the user wants a one-time scheduled run ("run this once at 3pm", "remind me to check X tomorrow").
- claude-api: Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.
TRIGGER — read BEFORE opening the target file; don't skip because it "looks like a one-liner" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens).
SKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).
- run: Launch and drive this project's app to see a change working. Use when asked to run, start, or screenshot the app, or to confirm a change works in the real app (not just tests). First looks for a project skill that already covers launching the app; otherwise falls back to built-in patterns per project type (CLI, server, TUI, Electron, browser-driven, library).
- init: Initialize a new CLAUDE.md file with codebase documentation
- review: Review a pull request
- security-review: Complete a security review of the pending changes on the current branch

update-config keybindings-help verify code-review simplify fewer-permission-prompts loop schedule claude-api run init review security-review


I'll audit all .sh and .php files under /boot/config/plugins/varaverk for hardcoded paths that should use canonical variables. Let me start by searching for the pattern of hardcoded paths.


Bash

find /boot/config/plugins/varaverk -type f \( -name "*.sh" -o -name "*.php" \) ! -path "*/Deployment/conf_templates/*" ! -path "*/.git/*" | head -50


Let me get all files and search for hardcoded paths:


find /boot/config/plugins/varaverk -type f \( -name "*.sh" -o -name "*.php" \) ! -path "*/Deployment/conf_templates/*" ! -path "*/.git/*" | wc -l


Now let me search for the specific hardcoded paths. I'll run multiple grep searches in parallel to find each pattern:


grep -rn "/boot/config/plugins/varaverk" /boot/config/plugins/varaverk --include="*.sh" --include="*.php" | grep -v "/Deployment/conf_templates/" | grep -v "\.git/" | head -100


/boot/config/plugins/varaverk/git_pull_execute.sh:258:    if [[ "$TARGET_DIR" != "/boot/config/plugins/varaverk" ]]; then
/boot/config/plugins/varaverk/git_pull_execute.sh:261:        if rsync -a --delete "$TARGET_DIR/Plugin/" "/boot/config/plugins/varaverk/Plugin/" 2>/dev/null; then
/boot/config/plugins/varaverk/Plugin/unraid/run_job.sh:47:    _VV_CFG="/boot/config/plugins/varaverk/varaverk.cfg"
/boot/config/plugins/varaverk/Plugin/unraid/run_job.sh:49:    SCHEDULE_FILE="${_SCRIPTS_DIR:-/boot/config/plugins/varaverk}/schedule.json"
/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh:296:        "grep -m1 '^SCRIPTS_DIR' /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d '\"'" \
/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh:298:    _REMOTE_SD="${_REMOTE_SD:-/boot/config/plugins/varaverk}"
/boot/config/plugins/varaverk/Plugin/unraid/api/storage.php:21:    $internalDir = '/boot/config/plugins/varaverk';
/boot/config/plugins/varaverk/Partnership/partnership_onboard.sh:499:            'grep SCRIPTS_DIR /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d "\"'"'"'" 2>/dev/null' 2>/dev/null | tr -d '[:space:]')
/boot/config/plugins/varaverk/Partnership/partnership_onboard.sh:500:        OWNER_SCRIPTS_DIR="${OWNER_SCRIPTS_DIR:-/boot/config/plugins/varaverk}"
/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh:123:    partner_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:43:#   Scripts:       /boot/config/plugins/varaverk/
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:44:#   Configuration: /boot/config/plugins/varaverk/Configurations/master.conf
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:45:#   Per-host:      /boot/config/plugins/varaverk/Configurations/host1.conf (or host2.conf)
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:46:#   Library:       /boot/config/plugins/varaverk/common.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:138:# bash /boot/config/plugins/varaverk/Orchestrators/array_started.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:158:# bash /boot/config/plugins/varaverk/Orchestrators/array_stopping.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:159:# bash /boot/config/plugins/varaverk/Orchestrators/array_stopping.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:160:# bash /boot/config/plugins/varaverk/Orchestrators/array_stopping.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:185:# bash /boot/config/plugins/varaverk/Orchestrators/watchdog_orchestrator.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:186:# bash /boot/config/plugins/varaverk/Orchestrators/watchdog_orchestrator.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:187:# bash /boot/config/plugins/varaverk/Orchestrators/watchdog_orchestrator.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:216:# bash /boot/config/plugins/varaverk/Orchestrators/transcode_management.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:241:# bash /boot/config/plugins/varaverk/Orchestrators/critical_sync_maintenance.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:268:# bash /boot/config/plugins/varaverk/Orchestrators/intermediate_sync_maintenance.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:295:# bash /boot/config/plugins/varaverk/Orchestrators/daily_sync_maintenance.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:310:# bash /boot/config/plugins/varaverk/Rsync/rsync.sh \
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:346:# bash /boot/config/plugins/varaverk/Orchestrators/weekly_sync_maintenance.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:365:# bash /boot/config/plugins/varaverk/Orchestrators/monthly_maintenance.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:366:# bash /boot/config/plugins/varaverk/Orchestrators/monthly_maintenance.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:367:# bash /boot/config/plugins/varaverk/Orchestrators/monthly_maintenance.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:368:# bash /boot/config/plugins/varaverk/Orchestrators/monthly_maintenance.sh --force
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:392:# bash /boot/config/plugins/varaverk/Orchestrators/sunday_morning_coffee_report.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:412:# bash /boot/config/plugins/varaverk/Monitors/weekly_health_digest.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:431:# bash /boot/config/plugins/varaverk/Monitors/system_tuning_monitor.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:482:# bash /boot/config/plugins/varaverk/Fallback/fallback.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:483:# bash /boot/config/plugins/varaverk/Fallback/fallback.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:484:# bash /boot/config/plugins/varaverk/Fallback/fallback.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:495:# bash /boot/config/plugins/varaverk/Fallback/fallback_test.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:496:# bash /boot/config/plugins/varaverk/Fallback/fallback_test.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:506:# bash /boot/config/plugins/varaverk/Tools/fallback_state_reset.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:507:# bash /boot/config/plugins/varaverk/Tools/fallback_state_reset.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:508:# bash /boot/config/plugins/varaverk/Tools/fallback_state_reset.sh --force
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:530:# bash /boot/config/plugins/varaverk/Rsync/rsync.sh \
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:532:# bash /boot/config/plugins/varaverk/Rsync/rsync.sh \
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:534:# bash /boot/config/plugins/varaverk/Rsync/rsync.sh \
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:536:# bash /boot/config/plugins/varaverk/Rsync/rsync.sh \
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:538:# bash /boot/config/plugins/varaverk/Rsync/rsync.sh \
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:540:# bash /boot/config/plugins/varaverk/Rsync/rsync.sh \
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:542:# bash /boot/config/plugins/varaverk/Rsync/rsync.sh /mnt/user/Movies
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:543:# bash /boot/config/plugins/varaverk/Rsync/rsync.sh /mnt/user/Tv_Shows
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:544:# bash /boot/config/plugins/varaverk/Rsync/rsync.sh /mnt/user/Music
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:545:# bash /boot/config/plugins/varaverk/Rsync/rsync.sh /mnt/user/Anime_Shows
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:546:# bash /boot/config/plugins/varaverk/Rsync/rsync.sh /mnt/user/Anime_Movies
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:554:# bash /boot/config/plugins/varaverk/System_Essentials/rsync_stop.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:555:# bash /boot/config/plugins/varaverk/System_Essentials/rsync_stop.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:556:# bash /boot/config/plugins/varaverk/System_Essentials/rsync_stop.sh --full-stop
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:585:# bash /boot/config/plugins/varaverk/Watchdogs/docker_watchdog.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:586:# bash /boot/config/plugins/varaverk/Watchdogs/docker_watchdog.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:601:# bash /boot/config/plugins/varaverk/Tools/watchdog_skip_list_manager.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:602:# bash /boot/config/plugins/varaverk/Tools/watchdog_skip_list_manager.sh --clear ContainerName
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:603:# bash /boot/config/plugins/varaverk/Tools/watchdog_skip_list_manager.sh --clear ContainerName --force
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:604:# bash /boot/config/plugins/varaverk/Tools/watchdog_skip_list_manager.sh --clear-all
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:605:# bash /boot/config/plugins/varaverk/Tools/watchdog_skip_list_manager.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:618:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_container_stop.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:619:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_container_stop.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:620:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_container_stop.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:630:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_daily_restart.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:631:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_daily_restart.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:632:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_daily_restart.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:641:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_weekly_restart.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:642:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_weekly_restart.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:643:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_weekly_restart.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:652:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_network_connect.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:653:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_network_connect.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:654:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_network_connect.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:663:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_update.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:664:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_update.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:665:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_update.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:675:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_update_remaining.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:676:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_update_remaining.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:677:# bash /boot/config/plugins/varaverk/Docker_Essentials/docker_update_remaining.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:696:# bash /boot/config/plugins/varaverk/Docker_Essentials/downloaders_reset.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:697:# bash /boot/config/plugins/varaverk/Docker_Essentials/downloaders_reset.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:698:# bash /boot/config/plugins/varaverk/Docker_Essentials/downloaders_reset.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:730:# bash /boot/config/plugins/varaverk/Watchdogs/stability_watchdog.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:731:# bash /boot/config/plugins/varaverk/Watchdogs/stability_watchdog.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:749:# bash /boot/config/plugins/varaverk/Watchdogs/System/storage_watchdog.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:750:# bash /boot/config/plugins/varaverk/Watchdogs/System/storage_watchdog.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:763:# bash /boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:764:# bash /boot/config/plugins/varaverk/Watchdogs/System/network_watchdog.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:780:# bash /boot/config/plugins/varaverk/Watchdogs/resource_watchdog.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:781:# bash /boot/config/plugins/varaverk/Watchdogs/resource_watchdog.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:782:# bash /boot/config/plugins/varaverk/Watchdogs/resource_watchdog.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:796:# bash /boot/config/plugins/varaverk/System_Essentials/inotify_tuning.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:797:# bash /boot/config/plugins/varaverk/System_Essentials/inotify_tuning.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:805:# bash /boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/php_fpm_max_children.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:806:# bash /boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/php_fpm_max_children.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:814:# bash /boot/config/plugins/varaverk/System_Essentials/docker_syslog_filter.sh --status
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:815:# bash /boot/config/plugins/varaverk/System_Essentials/docker_syslog_filter.sh
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:820:# bash /boot/config/plugins/varaverk/Plugin/unraid/Watchdogs/System/webgui_watchdog.sh --dry-run


Let me continue searching and also check for /tmp/vv_cache and other patterns:


grep -rn "/tmp/vv_cache" /boot/config/plugins/varaverk --include="*.sh" --include="*.php" | grep -v "/Deployment/conf_templates/" | grep -v "\.git/"


/boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.sh:8:# Builds the monitor and arrs API payloads and writes them to /tmp/vv_cache/
/boot/config/plugins/varaverk/Plugin/unraid/Tools/remote_arr_cache_writer.sh:9:# stack, and caches the result locally in /tmp/vv_cache/arrs_remote_hostN.json.
/boot/config/plugins/varaverk/Plugin/unraid/Tools/remote_arr_cache_writer.sh:41:mkdir -p /tmp/vv_cache
/boot/config/plugins/varaverk/Plugin/unraid/Tools/remote_arr_cache_writer.sh:67:    cache_file="/tmp/vv_cache/arrs_remote_${host_id}.json"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/remote_arr_cache_writer.sh:125:    MONITOR_CACHE="/tmp/vv_cache/monitor_remote_${host_id}.json"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.php:3:// Builds monitor + arrs payloads and writes them to /tmp/vv_cache/ so page
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:295:// ── File-based API cache (/tmp/vv_cache — tmpfs, cleared on reboot) ───────────
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:297:define('VV_CACHE_DIR', '/tmp/vv_cache');


Now let me search for /var/log/varaverk:


grep -rn "/var/log/varaverk" /boot/config/plugins/varaverk --include="*.sh" --include="*.php" | grep -v "/Deployment/conf_templates/" | grep -v "\.git/"


/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:260:        define('LOG_DIR',     '/var/log/varaverk');
/boot/config/plugins/varaverk/Plugin/unraid/run_job.sh:12:# Writes: /var/log/varaverk/<id>.json — status, timestamps, exit code, pid
/boot/config/plugins/varaverk/Plugin/unraid/run_job.sh:13:#         /var/log/varaverk/<id>.log  — appended per run, trimmed to LOG_MAX_LINES
/boot/config/plugins/varaverk/Plugin/unraid/run_job.sh:14:#         /var/log/varaverk/<id>.manual_ts — sentinel: epoch of last manual completion
/boot/config/plugins/varaverk/Plugin/unraid/run_job.sh:22:LOG_DIR="/var/log/varaverk"
/boot/config/plugins/varaverk/Plugin/unraid/api/recent.php:4:$logDir = '/var/log/varaverk';
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:12:define('LOG_DIR',     '/var/log/varaverk');
/boot/config/plugins/varaverk/Plugin/unraid/include/common.php:776:    $logFile = '/var/log/varaverk/Orchestrators/transcode_management.log';


Now let me check for other Varaverk-specific paths (/mnt/user/appdata patterns, Configurations, State_Files, data):


grep -rn "/mnt/user/appdata\|/mnt/user/appdata-Fallback" /boot/config/plugins/varaverk --include="*.sh" --include="*.php" | grep -v "/Deployment/conf_templates/" | grep -v "\.git/" | grep -i "varaverk"


/boot/config/plugins/varaverk/Plugin/build.sh:22:#   mounts. A symlink into /mnt/user/appdata can't be made that early (appdata
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:1145:                "/mnt/user/appdata-Fallback/Critical-Data" \
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:14:# Flash mode:    SCRIPTS_DIR = /mnt/user/appdata/Varaverk
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:34:#     Migrate to /mnt/user/appdata/Varaverk
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:52:FLASH_DIR="/mnt/user/appdata/Varaverk"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/conf_populate.sh:119:    [[ -z "$config_path" ]] && config_path="${DOCKER_APPDATA_BASE:-/mnt/user/appdata}/${container_name}"
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:531:#     /mnt/user/appdata-Fallback/Critical-Data
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:533:#     /mnt/user/appdata-Fallback/Important-Data
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:535:#     /mnt/user/appdata-Fallback/Arrs_Stack
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:541:#     /mnt/user/appdata-Fallback/Gmer4Lfe
/boot/config/plugins/varaverk/Plugin/unraid/api/storage.php:22:    $flashDir    = '/mnt/user/appdata/Varaverk';
/boot/config/plugins/varaverk/Plugin/unraid/include/auth.php:16:        'authelia_config'    => $v["{$host}_AUTHELIA_CONFIG"]          ?? '/mnt/user/appdata-Fallback/Critical-Data/Authelia/configuration.yml',
/boot/config/plugins/varaverk/Tools/claude_startup.sh:33:PERSIST_DIR="/mnt/user/appdata/claude-code"
/boot/config/plugins/varaverk/Media/arr_sync.sh:95:#   DOCKER_APPDATA_BASE      — base path for arr appdata dirs (default: /mnt/user/appdata)
/boot/config/plugins/varaverk/Media/arr_sync.sh:181:DOCKER_APPDATA_BASE="${DOCKER_APPDATA_BASE:-/mnt/user/appdata}"


Now let me check for Configurations, State_Files, and data directories:


grep -rn "Configurations\|State_Files\|/data" /boot/config/plugins/varaverk --include="*.sh" --include="*.php" | grep -v "/Deployment/conf_templates/" | grep -v "\.git/" | grep "/boot/config/plugins/varaverk" | head -150


/boot/config/plugins/varaverk/load_config.sh:28:# 1. Create Configurations/host3.conf following the same structure as HOST1/HOST2
/boot/config/plugins/varaverk/load_config.sh:46:# HOST1 only pulls Configurations/host1.conf — never HOST2's credentials.
/boot/config/plugins/varaverk/load_config.sh:47:# HOST2 only pulls Configurations/host2.conf — never HOST1's credentials.
/boot/config/plugins/varaverk/load_config.sh:49:# Both servers pull all non-credential conf files (Configurations/master.conf, common.sh, load_config.sh).
/boot/config/plugins/varaverk/load_config.sh:70:    if [[ ! -f "$LOAD_CONFIG_DIR/Configurations/master.conf" ]]; then
/boot/config/plugins/varaverk/load_config.sh:71:        echo "[FATAL] master.conf not found at $LOAD_CONFIG_DIR/Configurations/master.conf" >&2
/boot/config/plugins/varaverk/load_config.sh:75:    source "$LOAD_CONFIG_DIR/Configurations/master.conf"
/boot/config/plugins/varaverk/load_config.sh:93:    done < <(printf '%s\n' "$LOAD_CONFIG_DIR/Configurations"/host*.conf 2>/dev/null | sort)
/boot/config/plugins/varaverk/git_pull_execute.sh:284:CONF_DIR="$TARGET_DIR/Configurations"
/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh:302:        "$SCRIPTS_ROOT/Configurations/master.conf" \
/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh:303:        "root@${MIRROR_IP}:${_REMOTE_SD}/Configurations/master.conf" 2>/dev/null && \
/boot/config/plugins/varaverk/Partnership/ssh_setup.sh:88:HOST_CONF="$SCRIPTS_ROOT/Configurations/host${HOST_NUM}.conf"
/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh:40:CONF_FILE="$SCRIPT_DIR/../Configurations/${MY_ID,,}.conf"
/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh:123:    partner_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:44:#   Configuration: /boot/config/plugins/varaverk/Configurations/master.conf
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:45:#   Per-host:      /boot/config/plugins/varaverk/Configurations/host1.conf (or host2.conf)
/boot/config/plugins/varaverk/Plugin/unraid/Tools/conf_populate.sh:62:CONF_FILE="$SCRIPTS_ROOT/Configurations/${MY_ID,,}.conf"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:211:NEW_MASTER="$DST/Configurations/master.conf"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:215:        sed -i "s|^\(\s*DATA_DIR\s*=\s*\)\"[^\"]*\"|\1\"$DST/data\"|"                           "$NEW_MASTER"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:216:        sed -i "s|^\(\s*STATE_DIR\s*=\s*\)\"[^\"]*\"|\1\"$DST/State_Files\"|"                   "$NEW_MASTER"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:218:        echo "  DATA_DIR   → $DST/data ✅"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:219:        echo "  STATE_DIR  → $DST/State_Files ✅"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:232:NEW_CONF="$DST/Configurations/${MY_ID,,}.conf"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:257:        define('CONF_DIR',    SCRIPTS_DIR . '/Configurations');
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:258:        define('DATA_DIR',    SCRIPTS_DIR . '/data');
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:259:        define('STATE_DIR',   SCRIPTS_DIR . '/State_Files');
/boot/config/plugins/varaverk/Plugin/unraid/api/rsync_win_arrays.php:12:    $exclude = ['Plugin', '.git', 'Orchestrators', 'Custom', 'Configurations',
/boot/config/plugins/varaverk/Plugin/unraid/api/rsync_win_arrays.php:13:                'Deployment', 'State_Files', 'data', 'Old_Arch_Still_Works', 'Kernel'];
/boot/config/plugins/varaverk/Plugin/unraid/api/setup.php:53:    $remoteConf = rtrim($sm[1] ?? '/boot/config/plugins/varaverk', '/') . '/Configurations';
/boot/config/plugins/varaverk/Plugin/unraid/include/common.php:730:    $stateDir  = rtrim($v['STATE_DIR'] ?? '/boot/config/plugins/varaverk/State_Files', '/');
/boot/config/plugins/varaverk/Plugin/unraid/include/scheduler.php:336:    $exclude = ['.git', 'Orchestrators', 'Custom', 'Configurations'];
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:9:define('CONF_DIR',    SCRIPTS_DIR . '/Configurations');
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:10:define('DATA_DIR',    SCRIPTS_DIR . '/data');
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:11:define('STATE_DIR',   SCRIPTS_DIR . '/State_Files');
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:94:        // Configurations/ exists, and master.conf is already present.
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:101:            . ' && test -d \"${sd}/Configurations\"'
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:102:            . ' && test -f \"${sd}/Configurations/master.conf\"'
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:112:        $remoteConf = rtrim($probe, '/') . '/Configurations';
/boot/config/plugins/varaverk/System_Essentials/conf_sync.sh:53:MY_CONF="$SCRIPTS_ROOT/Configurations/${MY_ID,,}.conf"
/boot/config/plugins/varaverk/System_Essentials/conf_sync.sh:98:        remote_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"
/boot/config/plugins/varaverk/Deployment/deploy.sh:7:#   Deploys both Plugin and Configurations with full output.
/boot/config/plugins/varaverk/Deployment/deploy.sh:12:#   Configurations/ → conf_upgrade.sh (adds new keys, preserves prod values)
/boot/config/plugins/varaverk/Deployment/deploy.sh:31:        tmpl="$DEV_ROOT/Configurations/$conf"
/boot/config/plugins/varaverk/Deployment/deploy.sh:32:        target="$PROD_ROOT/Configurations/$conf"
/boot/config/plugins/varaverk/Deployment/deploy.sh:60:    echo "── Configurations ──"
/boot/config/plugins/varaverk/Deployment/deploy.sh:75:    if [[ "$FILE" == "$DEV_ROOT/Configurations/"* ]]; then
/boot/config/plugins/varaverk/Deployment/conf_upgrade.sh:56:# conf_upgrade.sh --template master.conf.template --target Configurations/master.conf --dry-run
/boot/config/plugins/varaverk/Deployment/conf_upgrade.sh:59:# conf_upgrade.sh --template master.conf.template --target Configurations/master.conf --backup
/boot/config/plugins/varaverk/Deployment/conf_upgrade.sh:62:# conf_upgrade.sh --template master.conf.template --target Configurations/master.conf


Let me get more specific findings. I'll look for direct hardcoded paths (not in comments):


grep -rn "/boot/config/plugins/varaverk" /boot/config/plugins/varaverk --include="*.sh" --include="*.php" | grep -v "/Deployment/conf_templates/" | grep -v "\.git/" | grep -v "^[^:]*:[^:]*:#" | wc -l


Let me get those non-comment matches more clearly:


grep -rn "/boot/config/plugins/varaverk" /boot/config/plugins/varaverk --include="*.sh" --include="*.php" | grep -v "/Deployment/conf_templates/" | grep -v "\.git/" | grep -v "^[^:]*:[^:]*:#"


/boot/config/plugins/varaverk/git_pull_execute.sh:258:    if [[ "$TARGET_DIR" != "/boot/config/plugins/varaverk" ]]; then
/boot/config/plugins/varaverk/git_pull_execute.sh:261:        if rsync -a --delete "$TARGET_DIR/Plugin/" "/boot/config/plugins/varaverk/Plugin/" 2>/dev/null; then
/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh:296:        "grep -m1 '^SCRIPTS_DIR' /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d '\"'" \
/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh:298:    _REMOTE_SD="${_REMOTE_SD:-/boot/config/plugins/varaverk}"
/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh:123:    partner_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:50:VV_CFG="/boot/config/plugins/varaverk/varaverk.cfg"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:51:INTERNAL_DIR="/boot/config/plugins/varaverk"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:254:        define('PLUGIN_CFG',  '/boot/config/plugins/varaverk/varaverk.cfg');
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:256:        define('SCRIPTS_DIR', \$_c['SCRIPTS_DIR'] ?? '/boot/config/plugins/varaverk');
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:281:    if rsync -a --delete "$DST/Plugin/" "/boot/config/plugins/varaverk/Plugin/" 2>/dev/null; then
/boot/config/plugins/varaverk/Partnership/partnership_onboard.sh:499:            'grep SCRIPTS_DIR /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d "\"'"'"'" 2>/dev/null' 2>/dev/null | tr -d '[:space:]')
/boot/config/plugins/varaverk/Partnership/partnership_onboard.sh:500:        OWNER_SCRIPTS_DIR="${OWNER_SCRIPTS_DIR:-/boot/config/plugins/varaverk}"
/boot/config/plugins/varaverk/Plugin/unraid/api/storage.php:21:    $internalDir = '/boot/config/plugins/varaverk';
/boot/config/plugins/varaverk/Plugin/unraid/api/settings.php:16:$cfgFile = '/boot/config/plugins/varaverk/varaverk.cfg';
/boot/config/plugins/varaverk/Plugin/unraid/run_job.sh:47:    _VV_CFG="/boot/config/plugins/varaverk/varaverk.cfg"
/boot/config/plugins/varaverk/Plugin/unraid/run_job.sh:49:    SCHEDULE_FILE="${_SCRIPTS_DIR:-/boot/config/plugins/varaverk}/schedule.json"
/boot/config/plugins/varaverk/Plugin/unraid/include/common.php:730:    $stateDir  = rtrim($v['STATE_DIR'] ?? '/boot/config/plugins/varaverk/State_Files', '/');
/boot/config/plugins/varaverk/System_Essentials/conf_sync.sh:98:        remote_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"
/boot/config/plugins/varaverk/Deployment/deploy.sh:17:PROD_ROOT="/boot/config/plugins/varaverk"
/boot/config/plugins/varaverk/Plugin/unraid/api/setup.php:51:    $remoteCfg = trim(shell_exec($sshBase . ' "grep SCRIPTS_DIR /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null"') ?: '');
/boot/config/plugins/varaverk/Plugin/unraid/api/setup.php:53:    $remoteConf = rtrim($sm[1] ?? '/boot/config/plugins/varaverk', '/') . '/Configurations';
/boot/config/plugins/varaverk/Plugin/unraid/api/system.php:26:@file_put_contents('/boot/config/plugins/varaverk/actions.log', $logLine, FILE_APPEND | LOCK_EX);
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:5:define('PLUGIN_CFG',  '/boot/config/plugins/varaverk/varaverk.cfg');
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:8:define('SCRIPTS_DIR', $_vv_cfg['SCRIPTS_DIR'] ?? '/boot/config/plugins/varaverk');
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:99:            $sshBase . ' "cfg=$(grep SCRIPTS_DIR /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null)'
/boot/config/plugins/varaverk/Plugin/unraid/include/scheduler.php:9:define('CRON_FILE',     '/boot/config/plugins/varaverk/varaverk.cron'); // must stay in /boot — update_cron scans there
/boot/config/plugins/varaverk/Plugin/unraid/include/docker.php:3:// Primary store: /boot/config/plugins/varaverk/docker_folders.json (Varaverk-owned, no external deps)


Now let me look at files with /tmp/vv_cache hardcodes (non-comment):


grep -rn "/tmp/vv_cache" /boot/config/plugins/varaverk --include="*.sh" --include="*.php" | grep -v "/Deployment/conf_templates/" | grep -v "\.git/" | grep -v "^[^:]*:[^:]*:#"


/boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.php:3:// Builds monitor + arrs payloads and writes them to /tmp/vv_cache/ so page
/boot/config/plugins/varaverk/Plugin/unraid/Tools/remote_arr_cache_writer.sh:41:mkdir -p /tmp/vv_cache
/boot/config/plugins/varaverk/Plugin/unraid/Tools/remote_arr_cache_writer.sh:67:    cache_file="/tmp/vv_cache/arrs_remote_${host_id}.json"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/remote_arr_cache_writer.sh:125:    MONITOR_CACHE="/tmp/vv_cache/monitor_remote_${host_id}.json"
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:295:// ── File-based API cache (/tmp/vv_cache — tmpfs, cleared on reboot) ───────────
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:297:define('VV_CACHE_DIR', '/tmp/vv_cache');


Now let me look at /var/log/varaverk hardcodes (non-comment):


grep -rn "/var/log/varaverk" /boot/config/plugins/varaverk --include="*.sh" --include="*.php" | grep -v "/Deployment/conf_templates/" | grep -v "\.git/" | grep -v "^[^:]*:[^:]*:#"


/boot/config/plugins/varaverk/Plugin/unraid/run_job.sh:22:LOG_DIR="/var/log/varaverk"
/boot/config/plugins/varaverk/Plugin/unraid/include/common.php:776:    $logFile = '/var/log/varaverk/Orchestrators/transcode_management.log';
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:12:define('LOG_DIR',     '/var/log/varaverk');
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:260:        define('LOG_DIR',     '/var/log/varaverk');
/boot/config/plugins/varaverk/Plugin/unraid/api/recent.php:4:$logDir = '/var/log/varaverk';


Now let me check for any paths using Configurations, State_Files, data, or .cron hardcoded:


grep -rn "\.cron\|varaverk\.cfg" /boot/config/plugins/varaverk --include="*.sh" --include="*.php" | grep -v "/Deployment/conf_templates/" | grep -v "\.git/" | grep -v "^[^:]*:[^:]*:#"


/boot/config/plugins/varaverk/Plugin/unraid/run_job.sh:47:    _VV_CFG="/boot/config/plugins/varaverk/varaverk.cfg"
/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh:296:        "grep -m1 '^SCRIPTS_DIR' /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d '\"'" \
/boot/config/plugins/varaverk/Partnership/partnership_onboard.sh:496:        # Read OWNER's SCRIPTS_DIR from their varaverk.cfg — don't assume same path as mirror
/boot/config/plugins/varaverk/Partnership/partnership_onboard.sh:499:            'grep SCRIPTS_DIR /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d "\"'"'"'" 2>/dev/null' 2>/dev/null | tr -d '[:space:]')
/boot/config/plugins/varaverk/Plugin/unraid/pages/settings.php:260:  if (!confirm(`Migrate Varaverk storage to ${label}?\n\nThis will:\n• Copy all scripts, conf, and git repo to the new location\n• Update varaverk.cfg and master.conf\n• Delete the old location\n\nThe page will need a reload after migration.`)) return;
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:50:VV_CFG="/boot/config/plugins/varaverk/varaverk.cfg"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:82:    echo "$ICON_GEAR  varaverk.cfg: $VV_CFG"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:195:echo "━━━ $ICON_GEAR Step 3: Update varaverk.cfg ━━━"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:254:        define('PLUGIN_CFG',  '/boot/config/plugins/varaverk/varaverk.cfg');
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:294:        # Migrating internal→flash: keep varaverk.cfg and Plugin/ in /boot/, remove everything else
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:295:        echo "  Removing scripts/conf/state from /boot/ (keeping Plugin/ and varaverk.cfg)..."
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:298:            ! -name 'varaverk.cfg' \
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:302:        echo "  /boot/ cleaned ✅ (Plugin/ and varaverk.cfg preserved)"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:311:        warn "DRY RUN — would remove scripts/conf/state from /boot/ (keeping Plugin/ and varaverk.cfg)"
/boot/config/plugins/varaverk/Plugin/unraid/api/settings.php:16:$cfgFile = '/boot/config/plugins/varaverk/varaverk.cfg';
/boot/config/plugins/varaverk/Plugin/unraid/include/scheduler.php:9:define('CRON_FILE',     '/boot/config/plugins/varaverk/varaverk.cron'); // must stay in /boot — update_cron scans there
/boot/config/plugins/varaverk/Plugin/unraid/include/scheduler.php:124:    // Write to the plugin cron file; update_cron merges all plugin *.cron files into /etc/cron.d/root.
/boot/config/plugins/varaverk/Plugin/unraid/api/setup.php:48:    // Get HOST1's SCRIPTS_DIR from their varaverk.cfg
/boot/config/plugins/varaverk/Plugin/unraid/api/setup.php:51:    $remoteCfg = trim(shell_exec($sshBase . ' "grep SCRIPTS_DIR /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null"') ?: '');
/boot/config/plugins/varaverk/Plugin/unraid/api/setup.php:107:    echo json_encode(['ok' => false, 'error' => 'master.conf not found — check SCRIPTS_DIR in varaverk.cfg']);
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:5:define('PLUGIN_CFG',  '/boot/config/plugins/varaverk/varaverk.cfg');
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:99:            $sshBase . ' "cfg=$(grep SCRIPTS_DIR /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null)'
/boot/config/plugins/varaverk/Plugin/unraid/pages/scheduler.php:2025:          + ''   + vvEscHtml(r.cron)  + ''
/boot/config/plugins/varaverk/Plugin/unraid/include/common.php:435:    $cronFile = '/boot/config/plugins/dynamix/parity-check.cron';


Let me check for /boot/config/plugins/varaverk/schedule.json and actions.log:


grep -rn "schedule\.json\|actions\.log" /boot/config/plugins/varaverk --include="*.sh" --include="*.php" | grep -v "/Deployment/conf_templates/" | grep -v "\.git/" | grep -v "^[^:]*:[^:]*:#"


/boot/config/plugins/varaverk/Plugin/unraid/run_job.sh:49:    SCHEDULE_FILE="${_SCRIPTS_DIR:-/boot/config/plugins/varaverk}/schedule.json"
/boot/config/plugins/varaverk/Plugin/unraid/api/script.php:51:    // Ensure schedule.json has an entry so the script appears in the job list
/boot/config/plugins/varaverk/Plugin/unraid/api/rsync_standalone.php:4:// Stored in schedule.json under "__rsync_{FLAG_NAME}".
/boot/config/plugins/varaverk/Plugin/unraid/api/savefolders.php:2:// Save custom-script folder assignments to schedule.json (__folders key).
/boot/config/plugins/varaverk/Plugin/unraid/api/system.php:26:@file_put_contents('/boot/config/plugins/varaverk/actions.log', $logLine, FILE_APPEND | LOCK_EX);
/boot/config/plugins/varaverk/Plugin/unraid/pages/scheduler.php:440:
Toggle — saves immediately to schedule.json

/boot/config/plugins/varaverk/Plugin/unraid/pages/scheduler.php:606:
Varaverk owns the cron layer. schedule.json stores every orchestrator's enabled state, cron expression, and log setting. When you save, the plugin rebuilds /etc/cron.d/varaverk — one entry per enabled orchestrator. That's all cron knows about.
/boot/config/plugins/varaverk/Plugin/unraid/pages/scheduler.php:1526:      // Persist the off state to schedule.json so cron rebuild reflects it
/boot/config/plugins/varaverk/Plugin/unraid/include/scheduler.php:2:// Scheduler — manages schedule.json and the Unraid plugin cron file.
/boot/config/plugins/varaverk/Plugin/unraid/include/scheduler.php:3:// schedule.json is per-host, never synced.
/boot/config/plugins/varaverk/Plugin/unraid/include/scheduler.php:8:define('SCHEDULE_FILE', SCRIPTS_DIR . '/schedule.json');
/boot/config/plugins/varaverk/Plugin/unraid/include/scheduler.php:300:// Load rsync standalone config (location + cron) for a flag name from schedule.json.
/boot/config/plugins/varaverk/Plugin/unraid/include/scheduler.php:364:// Load custom-script folder assignments from schedule.json (__folders key).
/boot/config/plugins/varaverk/Plugin/unraid/include/scheduler.php:373:// Well-known event orchs get their cron seeded from $eventDefaults when not yet in schedule.json.
/boot/config/plugins/varaverk/Plugin/unraid/include/scheduler.php:379:    // First-run seeds only — applied when schedule.json has no entry for these IDs yet.


Now let me read the key files to understand what should and shouldn't be flagged. Let me look at the actual instances more carefully:


1	<?php
2	// Config file parser and writer.
3	// Reads master.conf and the appropriate host*.conf based on running host.
4
5	define('PLUGIN_CFG',  '/boot/config/plugins/varaverk/varaverk.cfg');
6
7	$_vv_cfg = @parse_ini_file(PLUGIN_CFG) ?: [];
8	define('SCRIPTS_DIR', $_vv_cfg['SCRIPTS_DIR'] ?? '/boot/config/plugins/varaverk');
9	define('CONF_DIR',    SCRIPTS_DIR . '/Configurations');
10	define('DATA_DIR',    SCRIPTS_DIR . '/data');
11	define('STATE_DIR',   SCRIPTS_DIR . '/State_Files');
12	define('LOG_DIR',     '/var/log/varaverk');
13	unset($_vv_cfg);
14
15	define('VV_SETUP_STATE_FILE', STATE_DIR . '/varaverk_setup.db');
16
17	// Read the setup state file into a key=>value array.
18	function vv_setup_state_read(): array {
19	    $out = [];
20	    foreach (file(VV_SETUP_STATE_FILE) ?: [] as $line) {


Good. Now let me check what the actual issues are. Let me read a few key files to understand the context better:


1	#!/bin/bash
2	# Varaverk job runner — wraps script execution with JSON status tracking.
3	# Called by /etc/cron.d/varaverk for every scheduled job.
4	#
5	# Usage: bash run_job.sh <job_id> <script_path> [flags...]
6	#
7	# Flags consumed by run_job.sh (stripped before passing to script):
8	#   --manual   — marks a UI-triggered run; writes a sentinel on completion so
9	#                the next cron fire is suppressed if it falls within the job's
10	#                own cron interval (prevents double-firing after manual run).
11	#
12	# Writes: /var/log/varaverk/<id>.json — status, timestamps, exit code, pid
13	#         /var/log/varaverk/<id>.log  — appended per run, trimmed to LOG_MAX_LINES
14	#         /var/log/varaverk/<id>.manual_ts — sentinel: epoch of last manual completion
15	#
16	# Status values: running → ok (exit 0) | warn (exit 1) | error (exit 2+)
17
18	JOB_ID="$1"
19	SCRIPT="$2"
20	shift 2
21
22	LOG_DIR="/var/log/varaverk"
23	BASE="${JOB_ID%.sh}"
24	LOG_FILE="$LOG_DIR/$BASE.log"
25	STAT_FILE="$LOG_DIR/$BASE.json"
26	MANUAL_TS_FILE="$LOG_DIR/$BASE.manual_ts"
27	LOG_MAX_LINES=1000
28
29	# Strip --manual from script args — it's for run_job.sh only
30	MANUAL=false
31	SCRIPT_ARGS=()
32	for _arg in "$@"; do
33	    [[ "$_arg" == "--manual" ]] && MANUAL=true || SCRIPT_ARGS+=("$_arg")
34	done
35	unset _arg
36
37	mkdir -p "$(dirname "$LOG_FILE")" "$(dirname "$STAT_FILE")"
38
39	# Cron invocation: skip if a manual run completed recently within this job's own interval.
40	if [[ "$MANUAL" == false && -f "$MANUAL_TS_FILE" ]]; then
41	    LAST_MANUAL=$(cat "$MANUAL_TS_FILE" 2>/dev/null || echo 0)
42	    ELAPSED=$(( $(date +%s) - LAST_MANUAL ))
43
44	    # Only suppress for interval-based crons (*/N * * * *).
45	    # Static schedules like "30 2 * * 0" run at their appointed time and are never suppressed.
46	    INTERVAL=0
47	    _VV_CFG="/boot/config/plugins/varaverk/varaverk.cfg"
48	    _SCRIPTS_DIR=$(grep -m1 '^SCRIPTS_DIR' "$_VV_CFG" 2>/dev/null | cut -d= -f2 | tr -d '"'"'" | tr -d '[:space:]')
49	    SCHEDULE_FILE="${_SCRIPTS_DIR:-/boot/config/plugins/varaverk}/schedule.json"
50	    if [[ -f "$SCHEDULE_FILE" ]]; then


I see that line 22 hardcodes LOG_DIR as /var/log/varaverk. But line 47 hardcodes the config file path. Let me read the load_config.sh to see what variables should be available:


1	#!/bin/bash
2	# ==============================================================================================
3	# ================================= CONFIGURATION LOADER =======================================
4	# ==============================================================================================
5	# Single entry point for all configuration sourcing across the ecosystem.
6	# Every script sources this file instead of sourcing master.conf files directly.
7	#
8	# ── HOW IT WORKS ──────────────────────────────────────────────────────────────────────────────
9	# 1. Detects OS platform → PLATFORM=unraid|truenas|unknown; exports SCRIPTS_DIR
10	# 2. Sources master.conf (shared config — hostnames, thresholds, toggles, profiles, job lists)
11	# 3. Auto-discovers and sources all host*.conf files in the same directory
12	#    Each host conf extends the shared profile arrays and adds host-specific credentials
13	# 4. Sources common.sh (shared functions — detect_hosts, logging, notifications etc.)
14	# 5. Sources Plugin/<platform>/adapter.sh (platform_*() functions for OS-specific ops)
15	#
16	# ── WHY THIS EXISTS ───────────────────────────────────────────────────────────────────────────
17	# Without this loader every script had to explicitly source each conf file:
18	#   source master.conf
19	#   source host1.conf
20	#   source host2.conf
21	#   source common.sh
22	#
23	# Adding a new server meant updating every script.
24	# With this loader — add host3.conf to the git repo and every server
25	# auto-discovers it on next git pull. Zero script changes required. Ever.
26	#
27	# ── ADDING A NEW SERVER ───────────────────────────────────────────────────────────────────────
28	# 1. Create Configurations/host3.conf following the same structure as HOST1/HOST2
29	# 2. Commit and push to git repo
30	# 3. All servers pull it automatically — no other changes needed
31	#
32	# ── USAGE IN SCRIPTS ──────────────────────────────────────────────────────────────────────────
33	# Replace the three source lines at the top of every script with:
34	#
35	#   SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
36	#   source "$SCRIPT_DIR/../load_config.sh"
37	#
38	# Scripts in subdirectories (Rsync/, Docker_Essentials/ etc.) use ../ to reach root.
39	# Scripts in root directory use ./ instead:
40	#
41	#   SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
42	#   source "$SCRIPT_DIR/load_config.sh"
43	#
44	# ── SPARSE CHECKOUT NOTE ──────────────────────────────────────────────────────────────────────
45	# Sparse checkout controls which host*.conf files each server receives.
46	# HOST1 only pulls Configurations/host1.conf — never HOST2's credentials.
47	# HOST2 only pulls Configurations/host2.conf — never HOST1's credentials.
48	# This loader sources whatever conf files ARE present — sparse checkout handles the rest.
49	# Both servers pull all non-credential conf files (Configurations/master.conf, common.sh, load_config.sh).
50	#
51	# ==============================================================================================
52
53	# ━━━ Locate config root ━━━
54	# load_config.sh always lives in the repo root.
55	# Scripts call it from subdirectories using ../ — resolve to the actual root.
56	    LOAD_CONFIG_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
57
58	# ━━━ Platform detection ━━━
59	# PLATFORM drives the adapter layer — Plugin/<platform>/adapter.sh
60	# Each platform adapter provides the same function API; scripts stay OS-agnostic.
61	    if   [[ -f /etc/unraid-version ]]; then  PLATFORM="unraid"
62	    elif [[ -f /etc/truenas ]];        then  PLATFORM="truenas"
63	    else                                     PLATFORM="unknown"
64	    fi
65	    export PLATFORM SCRIPTS_DIR="$LOAD_CONFIG_DIR"
66
67	# ━━━ Source shared config ━━━
68	# master.conf must be sourced first — it declares the shared PROFILE_* arrays
69	# that Host confs extend. Sourcing host confs before master.conf would fail.
70	    if [[ ! -f "$LOAD_CONFIG_DIR/Configurations/master.conf" ]]; then
71	        echo "[FATAL] master.conf not found at $LOAD_CONFIG_DIR/Configurations/master.conf" >&2
72	        echo "[FATAL] Check TARGET_DIR and git pull status" >&2
73	        exit 1
74	    fi
75	    source "$LOAD_CONFIG_DIR/Configurations/master.conf"
76
77	# ━━━ Auto-discover and source all host*.conf files ━━━
78	# Sorted for consistent load order — HOST1 before HOST2 before HOST3 etc.
79	# Each host conf extends the shared PROFILE_* arrays and adds host-specific vars.
80	# Missing files are silently skipped — sparse checkout intentionally withholds some.
81	# At least one host conf must be present or the ecosystem has no identity to work with.
82	    _host_confs_loaded=0
83	    declare -A _disk_conf_basenames=()
84
85	    # Use a sorted array glob — avoids word-splitting on paths with spaces
86	    while IFS= read -r _conf; do
87	        [[ -f "$_conf" ]] || continue
88	        source "$_conf"
89	        (( _host_confs_loaded++ ))
90	        _disk_conf_basenames["$(basename "$_conf")"]=1
91	        [[ "${ENABLE_LOGGING:-false}" == "true" ]] && \
92	            echo "[LOG] Loaded host config: $(basename "$_conf")" >&2
93	    done < <(printf '%s\n' "$LOAD_CONFIG_DIR/Configurations"/host*.conf 2>/dev/null | sort)
94
95	    if [[ "$_host_confs_loaded" -eq 0 ]]; then
96	        echo "[FATAL] No host*.conf files found in $LOAD_CONFIG_DIR" >&2
97	        echo "[FATAL] At least one host conf required — check git pull and sparse checkout" >&2
98	        exit 1
99	    fi
100


Perfect. Now I understand the structure. Let me continue analyzing to look at actual hardcoded paths that are problematic. Let me check a few more key files:


1	<?php
2	require_once __DIR__ . '/config.php';
3	require_once __DIR__ . '/unraid_api.php';
4
5	// Common helpers shared across all Varaverk pages.
6
7	function vv_system_info(): array {
8	    // ── Shared local reads (always needed regardless of API) ──────────────────
9	    $ident = @parse_ini_file('/boot/config/ident.cfg') ?: [];
10	    $var   = [];
11	    foreach (@file('/var/local/emhttp/var.ini') ?: [] as $line) {
12	        if (preg_match('/^(\w+)="([^"]*)"/', $line, $m)) $var[$m[1]] = $m[2];
13	    }
14	    $version = trim(@file_get_contents('/etc/unraid-version') ?: '');
15
16	    // ── API path ──────────────────────────────────────────────────────────────
17	    $api = vv_api_data();
18	    if ($api) {
19	        $os  = $api['info']['os']  ?? [];
20	        $cpu = $api['info']['cpu'] ?? [];
21
22	        // uptime is a String in this schema — try numeric (seconds) first, else display as-is
23	        $uptimeRaw = $os['uptime'] ?? '';
24	        if (is_numeric($uptimeRaw)) {
25	            $uptimeSec = (int)$uptimeRaw;
26	            $uptime    = vv_format_uptime($uptimeSec);
27	        } else {
28	            $uptimeSec = 0;
29	            $uptime    = $uptimeRaw ?: '—';
30	        }
31
32	        $load = sys_getloadavg();
33	        return [
34	            'name'        => $os['hostname']   ?? ($ident['NAME'] ?? gethostname()),
35	            'comment'     => $ident['COMMENT'] ?? '',
36	            'timezone'    => $ident['timeZone'] ?? 'UTC',
37	            'cpu_model'   => $cpu['brand']      ?? ($ident['SYS_MODEL'] ?? ''),
38	            'cpu_threads' => (int)($cpu['threads'] ?? 0),
39	            'cpu_cores'   => (int)($cpu['cores']   ?? 0),
40	            'reg_type'    => 'Unraid OS ' . ($var['regTy'] ?? ''),
41	            'reg_to'      => $var['regTo']      ?? '',
42	            'uptime'      => $uptime,
43	            'uptime_sec'  => $uptimeSec,
44	            'array_state' => strtoupper($api['array']['state'] ?? $var['mdState'] ?? 'UNKNOWN'),
45	            'version'     => trim($os['release'] ?? '') ?: $version,
46	            'load_avg'    => $load ? [round($load[0], 2), round($load[1], 2), round($load[2], 2)] : null,
47	        ];
48	    }
49
50	    // ── Local fallback ────────────────────────────────────────────────────────
51	    vv_api_record_fallback('system_info');
52	    $cpuModel = '';
53	    foreach (@file('/proc/cpuinfo') ?: [] as $line) {
54	        if (preg_match('/^model name\s*:\s*(.+)/', $line, $m)) { $cpuModel = trim($m[1]); break; }
55	    }
56	    $uptimeSec = (int)explode(' ', @file_get_contents('/proc/uptime') ?: '0')[0];
57	    $uptime    = vv_format_uptime($uptimeSec);
58
59	    $load = sys_getloadavg();
60	    return [
61	        'name'        => $ident['NAME']      ?? gethostname(),
62	        'comment'     => $ident['COMMENT']   ?? '',
63	        'timezone'    => $ident['timeZone']  ?? 'UTC',
64	        'cpu_model'   => $ident['SYS_MODEL'] ?? $cpuModel,
65	        'cpu_threads' => 0,
66	        'cpu_cores'   => 0,
67	        'reg_type'    => 'Unraid OS ' . ($var['regTy'] ?? ''),
68	        'reg_to'      => $var['regTo']       ?? '',
69	        'uptime'      => $uptime,
70	        'uptime_sec'  => $uptimeSec,
71	        'array_state' => $var['mdState']     ?? 'UNKNOWN',
72	        'version'     => $version,
73	        'load_avg'    => $load ? [round($load[0], 2), round($load[1], 2), round($load[2], 2)] : null,
74	    ];
75	}
76
77	function vv_docker_containers(): array {
78	    $out = shell_exec('docker ps --format \'{"name":"{{.Names}}","status":"{{.Status}}","image":"{{.Image}}"}\' 2>/dev/null');
79	    $containers = [];
80	    foreach (explode("\n", trim($out ?? '')) as $line) {
81	        if (!$line) continue;
82	        $c = json_decode($line, true);
83	        if ($c) $containers[] = $c;
84	    }
85	    return $containers;
86	}
87
88	function vv_docker_stopped(): array {
89	    $out = shell_exec('docker ps -a --filter "status=exited" --filter "status=created" --format \'{"name":"{{.Names}}","status":"{{.Status}}"}\' 2>/dev/null');
90	    $containers = [];
91	    foreach (explode("\n", trim($out ?? '')) as $line) {
92	        if (!$line) continue;
93	        $c = json_decode($line, true);
94	        if ($c) $containers[] = $c;
95	    }
96	    return $containers;
97	}
98
99	function vv_gpu_stats(): array {
100	    $out = shell_exec('nvidia-smi --query-gpu=name,memory.used,memory.total,utilization.gpu,temperature.gpu,power.draw,utilization.encoder,utilization.decoder --format=csv,noheader,nounits 2>/dev/null');
101	    if (!$out) return ['available' => false];
102
103	    $parts = array_map('trim', explode(',', $out));
104	    $power = is_numeric($parts[5] ?? '') ? round((float)$parts[5], 1) : null;
105	    return [
106	        'available'    => true,
107	        'name'         => $parts[0] ?? '',
108	        'memory_used'  => (int)($parts[1] ?? 0),
109	        'memory_total' => (int)($parts[2] ?? 0),
110	        'utilization'  => (int)($parts[3] ?? 0),
111	        'temperature'  => (int)($parts[4] ?? 0),
112	        'power_w'      => $power,
113	        'enc_pct'      => (int)($parts[6] ?? 0),
114	        'dec_pct'      => (int)($parts[7] ?? 0),
115	    ];
116	}
117
118	function vv_gpu_processes(): array {
119	    $out = shell_exec('nvidia-smi --query-compute-apps=pid,used_gpu_memory,name --format=csv,noheader,nounits 2>/dev/null');
120	    $procs = [];
121	    foreach (explode("\n", trim($out ?? '')) as $line) {
122	        if (!$line) continue;
123	        $parts = array_map('trim', explode(',', $line));
124	        $procs[] = [
125	            'pid'        => $parts[0] ?? '',
126	            'memory_mb'  => $parts[1] ?? '',
127	            'name'       => $parts[2] ?? '',
128	        ];
129	    }
130	    return $procs;
131	}
132
133	function vv_system_resources(): array {
134	    $mem = [];
135	    foreach (file('/proc/meminfo') ?: [] as $line) {
136	        if (preg_match('/^(MemTotal|MemAvailable):\s+(\d+)/', $line, $m))
137	            $mem[$m[1]] = (int)$m[2];
138	    }
139	    return [
140	        'ram_total_mb' => (int)(($mem['MemTotal']     ?? 0) / 1024),
141	        'ram_free_mb'  => (int)(($mem['MemAvailable'] ?? 0) / 1024),
142	        'cache'        => vv_df('/mnt/cache'),
143	    ];
144	}
145
146	function vv_cpu_per_core(): array {
147	    // Parse /proc/stat — [user, nice, system, idle, iowait, irq, softirq]
148	    $raw = [];
149	    foreach (file('/proc/stat') ?: [] as $line) {
150	        if (!preg_match('/^(cpu\d*)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/', $line, $m)) continue;
151	        $raw[$m[1]] = [(int)$m[2],(int)$m[3],(int)$m[4],(int)$m[5],(int)$m[6],(int)$m[7],(int)$m[8]];
152	    }
153
154	    $stateFile = '/tmp/vv_cpu_stat.json';
155	    $prev = file_exists($stateFile) ? (json_decode(file_get_contents($stateFile), true) ?: []) : [];
156	    // Atomic write — concurrent fast/slow polls read a consistent snapshot
157	    $tmp = $stateFile . '.tmp';
158	    file_put_contents($tmp, json_encode($raw));
159	    rename($tmp, $stateFile);
160
161	    $usage = function(array $c, ?array $p): int {
162	        if (!$p) return 0;
163	        $dt = array_sum($c) - array_sum($p);
164	        $di = ($c[3] + $c[4]) - ($p[3] + $p[4]);
165	        return $dt > 0 ? max(0, min(100, (int)round((1 - $di / $dt) * 100))) : 0;
166	    };
167
168	    $overall = $usage($raw['cpu'] ?? [], $prev['cpu'] ?? null);
169	    $cores   = [];
170	    foreach ($raw as $cpu => $c) {
171	        if ($cpu === 'cpu') continue;
172	        $num     = (int)substr($cpu, 3);
173	        $freqKhz = (int)@file_get_contents("/sys/devices/system/cpu/$cpu/cpufreq/scaling_cur_freq");
174	        $maxKhz  = (int)@file_get_contents("/sys/devices/system/cpu/$cpu/cpufreq/cpuinfo_max_freq");
175	        $minKhz  = (int)@file_get_contents("/sys/devices/system/cpu/$cpu/cpufreq/cpuinfo_min_freq");
176	        $cores[] = [
177	            'core'      => $num,
178	            'usage_pct' => $usage($c, $prev[$cpu] ?? null),
179	            'freq_mhz'  => $freqKhz > 0 ? (int)round($freqKhz / 1000) : 0,
180	            'max_mhz'   => $maxKhz  > 0 ? (int)round($maxKhz  / 1000) : 0,
181	            'min_mhz'   => $minKhz  > 0 ? (int)round($minKhz  / 1000) : 0,
182	        ];
183	    }
184	    usort($cores, fn($a, $b) => $a['core'] - $b['core']);
185	    return ['overall' => $overall, 'cores' => $cores];
186	}
187
188	function vv_memory_breakdown(): array {
189	    $mem = [];
190	    foreach (file('/proc/meminfo') ?: [] as $line) {
191	        if (preg_match('/^(\w+):\s+(\d+)/', $line, $m)) $mem[$m[1]] = (int)$m[2];
192	    }
193	    $totalKb = $mem['MemTotal'] ?? 0;
194
195	    // ZFS ARC
196	    $arcKb = 0;
197	    foreach (@file('/proc/spl/kstat/zfs/arcstats') ?: [] as $line) {
198	        if (preg_match('/^size\s+\d+\s+(\d+)/', $line, $m)) { $arcKb = (int)($m[1] / 1024); break; }
199	    }
200
201	    // Docker — sum docker stats used memory per container (matches Unraid dashboard)
202	    $dockerKb = 0;
203	    $dsOut = shell_exec("docker stats --no-stream --format '{{.MemUsage}}' 2>/dev/null") ?: '';
204	    foreach (explode("\n", trim($dsOut)) as $line) {
205	        if (!preg_match('/^([0-9.]+)(GiB|MiB|KiB|B)\s*\//', trim($line), $m)) continue;
206	        $val = (float)$m[1];
207	        $dockerKb += match($m[2]) {
208	            'GiB'   => (int)($val * 1048576),
209	            'MiB'   => (int)($val * 1024),
210	            'KiB'   => (int)$val,
211	            default => (int)($val / 1024),
212	        };
213	    }
214
215	    // VM (QEMU/KVM RSS)
216	    $vmKb = 0;
217	    foreach (preg_split('/\s+/', trim(shell_exec('ps -C qemu-system-x86_64 -o rss= 2>/dev/null') ?: '')) as $rss) {
218	        if (is_numeric($rss) && $rss > 0) $vmKb += (int)$rss;
219	    }
220
221	    $freeKb   = max(0, $mem['MemAvailable'] ?? 0);
222	    $systemKb = max(0, $totalKb - $freeKb - $arcKb - $dockerKb - $vmKb);
223
224	    // Top processes by RSS — group same-named procs, take top 5
225	    $grouped = [];
226	    $psOut = shell_exec("ps -eo comm,rss --sort=-rss 2>/dev/null | tail -n +2 | head -40") ?: '';
227	    foreach (explode("\n", trim($psOut)) as $line) {
228	        $parts = preg_split('/\s+/', trim($line), 2);
229	        if (count($parts) === 2 && is_numeric($parts[1]) && (int)$parts[1] > 0)
230	            $grouped[$parts[0]] = ($grouped[$parts[0]] ?? 0) + (int)$parts[1];
231	    }
232	    arsort($grouped);
233	    $topProcs = [];
234	    foreach (array_slice($grouped, 0, 3, true) as $name => $kb)
235	        $topProcs[] = ['name' => $name, 'kb' => $kb];
236
237	    // Swap — from API metrics when available, else /proc/meminfo
238	    $swapTotalKb = 0; $swapUsedKb = 0;
239	    $apiMem = vv_api_data()['metrics']['memory'] ?? [];
240	    if (!empty($apiMem['swapTotal'])) {
241	        $swapTotalKb = (int)(((float)$apiMem['swapTotal']) / 1024);
242	        $swapUsedKb  = (int)(((float)$apiMem['swapUsed'])  / 1024);
243	    } else {
244	        $swapTotalKb = $mem['SwapTotal'] ?? 0;
245	        $swapUsedKb  = ($mem['SwapTotal'] ?? 0) - ($mem['SwapFree'] ?? 0);
246	    }
247
248	    return [
249	        'total_kb'      => $totalKb,
250	        'system_kb'     => $systemKb,
251	        'vm_kb'         => $vmKb,
252	        'zfs_kb'        => $arcKb,
253	        'docker_kb'     => $dockerKb,
254	        'free_kb'       => $freeKb,
255	        'swap_total_kb' => $swapTotalKb,
256	        'swap_used_kb'  => $swapUsedKb,
257	        'top_procs'     => $topProcs,
258	    ];
259	}
260
261	function vv_df(string $path): array {
262	    $out = shell_exec("df -BM --output=size,used,avail '$path' 2>/dev/null | tail -1");
263	    if (!$out) return ['available' => false, 'path' => $path];
264	    $parts = preg_split('/\s+/', trim($out));
265	    return [
266	        'available' => true,
267	        'path'      => $path,
268	        'size_mb'   => (int)$parts[0],
269	        'used_mb'   => (int)$parts[1],
270	        'free_mb'   => (int)$parts[2],
271	    ];
272	}
273
274	function vv_network_stats(): array {
275	    $iface = trim(shell_exec("ip route show default 2>/dev/null | awk 'NR==1{print \$5}'") ?: '');
276	    if (!$iface) {
277	        $best = ''; $bestBytes = 0;
278	        foreach (file('/proc/net/dev') ?: [] as $line) {
279	            if (!preg_match('/^\s*(\w+):\s+(\d+)/', $line, $m) || $m[1] === 'lo') continue;
280	            if ((int)$m[2] > $bestBytes) { $bestBytes = (int)$m[2]; $best = $m[1]; }
281	        }
282	        $iface = $best;
283	    }
284	    if (!$iface) return ['available' => false];
285
286	    $rxBytes = $txBytes = 0;
287	    foreach (file('/proc/net/dev') ?: [] as $line) {
288	        if (!preg_match('/^\s*' . preg_quote($iface, '/') . ':\s+(.+)$/', $line, $m)) continue;
289	        $parts   = preg_split('/\s+/', trim($m[1]));
290	        $rxBytes = (int)($parts[0] ?? 0);
291	        $txBytes = (int)($parts[8] ?? 0);
292	        break;
293	    }
294
295	    $stateFile = '/tmp/vv_net_stat.json';
296	    $now  = ['rx' => $rxBytes, 'tx' => $txBytes, 'ts' => microtime(true)];
297	    $prev = file_exists($stateFile) ? (json_decode(file_get_contents($stateFile), true) ?: []) : [];
298	    $tmp = $stateFile . '.tmp';
299	    file_put_contents($tmp, json_encode($now));
300	    rename($tmp, $stateFile);
301
302	    $rxRate = $txRate = 0;
303	    if (!empty($prev['ts']) && ($dt = $now['ts'] - $prev['ts']) > 0.1) {
304	        $rxRate = max(0, (int)(($rxBytes - ($prev['rx'] ?? $rxBytes)) / $dt));
305	        $txRate = max(0, (int)(($txBytes - ($prev['tx'] ?? $txBytes)) / $dt));
306	    }
307
308	    $speedMbps = (int)@file_get_contents("/sys/class/net/$iface/speed");
309
310	    // Local IP — use primary iface
311	    $localIp = trim(shell_exec(
312	        "ip -4 addr show " . escapeshellarg($iface) . " 2>/dev/null | awk '/inet /{print \$2}' | cut -d/ -f1 | head -1"
313	    ) ?: '');
314
315	    // External IP — curl ifconfig.me, cached 5 min so we don't hammer it
316	    $extIp   = '';
317	    $extData = vv_cache_read('ext_ip', 300);
318	    if ($extData) {
319	        $extIp = $extData['ip'] ?? '';
320	    } else {
321	        $fetched = trim(shell_exec('curl -sf --max-time 4 https://ifconfig.me 2>/dev/null') ?: '');
322	        if (preg_match('/^\d+\.\d+\.\d+\.\d+$/', $fetched)) {
323	            $extIp = $fetched;
324	            vv_cache_write('ext_ip', ['ip' => $extIp]);
325	        }
326	    }
327
328	    // Tailscale IP — use `tailscale ip` CLI (interface name varies: tailscale0, tailscale1, etc.)
329	    $tsIp = trim(shell_exec('tailscale ip -4 2>/dev/null | head -1') ?: '');
330
331	    return [
332	        'available'  => true,
333	        'iface'      => $iface,
334	        'speed_mbps' => $speedMbps > 0 ? $speedMbps : null,
335	        'rx_bps'     => $rxRate,
336	        'tx_bps'     => $txRate,
337	        'local_ip'   => $localIp,
338	        'ext_ip'     => $extIp,
339	        'ts_ip'      => $tsIp,
340	    ];
341	}
342
343	function vv_disk_entry(array $d, string $key, string $role = 'data'): ?array {
344	    $name      = $d['name'] ?? $key;
345	    $isParity  = $role === 'parity';
346	    $mounted   = ($d['fsStatus'] ?? '') === 'Mounted';
347	    // Parity has no filesystem — use raw size only
348	    $size_kb   = (int)($isParity ? ($d['size'] ?? 0) : ($mounted ? ($d['fsSize'] ?? 0) : ($d['size'] ?? 0)));
349	    $used_kb   = (int)($isParity ? 0             : ($mounted ? ($d['fsUsed'] ?? 0) : 0));
350	    if ($size_kb <= 0) return null;
351	    $tempRaw   = trim($d['temp'] ?? '');
352	    return [
353	        'name'      => $name,
354	        'device'    => $d['device'] ?? $key,
355	        'role'      => $role,
356	        'size_gb'   => round($size_kb / 1048576, 1),
357	        'used_gb'   => round($used_kb / 1048576, 1),
358	        'pct'       => (!$isParity && $size_kb > 0) ? round($used_kb / $size_kb * 100, 1) : null,
359	        'temp'      => is_numeric($tempRaw) ? (int)$tempRaw : null,
360	        'transport' => $d['transport'] ?? 'ata',
361	        'mounted'   => $mounted,
362	        'status'    => $d['status'] ?? '',
363	    ];
364	}
365
366	function vv_ups_stats(): array {
367	    $raw = shell_exec('apcaccess 2>/dev/null') ?: '';
368	    if (!$raw) return ['available' => false];
369
370	    $fields = [];
371	    foreach (explode("\n", $raw) as $line) {
372	        if (preg_match('/^(\w+)\s*:\s*(.+)$/', trim($line), $m)) {
373	            $fields[trim($m[1])] = trim($m[2]);
374	        }
375	    }
376	    if (empty($fields)) return ['available' => false];
377
378	    $parse_num = fn(string $k) => isset($fields[$k]) ? (float)$fields[$k] : null;
379
380	    $loadPct  = $parse_num('LOADPCT');
381	    $nomPower = $parse_num('NOMPOWER');
382	    $watts    = ($loadPct !== null && $nomPower !== null) ? round($loadPct / 100 * $nomPower) : null;
383
384	    return [
385	        'available'  => true,
386	        'model'      => $fields['MODEL']   ?? '',
387	        'status'     => trim(explode(' ', $fields['STATUS'] ?? 'UNKNOWN')[0]),
388	        'line_v'     => $parse_num('LINEV'),
389	        'output_v'   => $parse_num('OUTPUTV'),
390	        'load_pct'   => $loadPct,
391	        'nom_power'  => $nomPower,
392	        'watts'      => $watts,
393	        'bcharge'    => $parse_num('BCHARGE'),
394	        'timeleft'   => $parse_num('TIMELEFT'),
395	        'num_xfers'  => (int)($fields['NUMXFERS'] ?? 0),
396	        'on_batt_s'  => $parse_num('CUMONBATT'),
397	        'selftest'   => $fields['SELFTEST'] ?? '',
398	    ];
399	}
400
401	function vv_parity_status(): array {
402	    $var = [];
403	    foreach (@file('/var/local/emhttp/var.ini') ?: [] as $line) {
404	        if (preg_match('/^(\w+)="([^"]*)"/', $line, $m)) $var[$m[1]] = $m[2];
405	    }
406
407	    $isValid    = ($var['mdNumInvalid'] ?? '0') === '0';
408	    $exitCode   = (int)($var['sbSyncExit'] ?? 0);
409	    $errors     = (int)($var['sbSyncErrs'] ?? 0);
410	    $inProgress = ($var['mdResync'] ?? '0') !== '0';
411	    $resyncPos  = (int)($var['mdResyncPos'] ?? 0);
412	    $resyncSize = (int)($var['mdResyncSize'] ?? 1);
413	    $resyncPct  = $resyncSize > 0 ? round($resyncPos / $resyncSize * 100, 1) : 0;
414
415	    // Last check from log
416	    $lastDate = null; $lastDuration = 0; $lastSpeed = 0; $lastErrors = 0; $lastExit = 0;
417	    $logFile  = '/boot/config/parity-checks.log';
418	    if (file_exists($logFile)) {
419	        $lines = file($logFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
420	        if ($lines) {
421	            $p = explode('|', trim(end($lines)));
422	            $lastDate     = trim($p[0] ?? '');
423	            $lastDuration = (int)($p[1] ?? 0);
424	            $lastSpeed    = (int)($p[2] ?? 0);
425	            $lastExit     = (int)($p[3] ?? 0);
426	            $lastErrors   = (int)($p[4] ?? 0);
427	        }
428	    }
429
430	    // Parse last date string to timestamp
431	    $lastTs = $lastDate ? strtotime($lastDate) : null;
432
433	    // Next scheduled check from cron
434	    $nextTs   = null;
435	    $cronFile = '/boot/config/plugins/dynamix/parity-check.cron';
436	    foreach (@file($cronFile) ?: [] as $line) {
437	        $line = trim($line);
438	        if ($line === '' || $line[0] === '#') continue;
439	        if (!str_contains($line, 'mdcmd')) continue;
440	        $p = preg_split('/\s+/', $line);
441	        // cron: min hour dom month dow command...
442	        if (count($p) >= 5 && is_numeric($p[0]) && is_numeric($p[1]) && is_numeric($p[2])) {
443	            $next = new DateTime('now');
444	            $next->setTime((int)$p[1], (int)$p[0], 0);
445	            $next->setDate((int)$next->format('Y'), (int)$next->format('n'), (int)$p[2]);
446	            if ($next->getTimestamp() <= time()) $next->modify('+1 month');
447	            $nextTs = $next->getTimestamp();
448	        }
449	        break;
450	    }
451
452	    $exitMap = ['0' => 'Completed', '-4' => 'Aborted', '-5' => 'Cancelled'];
453	    return [
454	        'valid'         => $isValid,
455	        'in_progress'   => $inProgress,
456	        'resync_pct'    => $resyncPct,
457	        'exit_code'     => $exitCode,
458	        'exit_label'    => $exitMap[(string)$lastExit] ?? 'Unknown',
459	        'errors'        => $lastErrors,
460	        'last_date'     => $lastDate,
461	        'last_ts'       => $lastTs,
462	        'last_duration' => $lastDuration,
463	        'last_speed_mb' => $lastSpeed > 0 ? round($lastSpeed / 1048576, 1) : null,
464	        'next_ts'       => $nextTs,
465	    ];
466	}
467
468	function vv_storage_pools(): array {
469	    // ── API path ──────────────────────────────────────────────────────────────
470	    $api = vv_api_data();
471	    if ($api && isset($api['array']['caches'])) {
472	        $out = [];
473	        foreach ($api['array']['caches'] as $d) {
474	            $entry = vv_api_disk_entry($d, 'data');
475	            if ($entry) $out[] = $entry;
476	        }
477	        if (!empty($out)) {
478	            usort($out, fn($a, $b) => strcmp($a['name'], $b['name']));
479	            return $out;
480	        }
481	    }
482
483	    // ── Local fallback ────────────────────────────────────────────────────────
484	    vv_api_record_fallback('storage_pools');
485	    $ini = @parse_ini_file('/var/local/emhttp/disks.ini', true) ?: [];
486	    $out = [];
487	    foreach ($ini as $key => $d) {
488	        if (($d['type'] ?? '') !== 'Cache') continue;
489	        if (($d['fsStatus'] ?? '') !== 'Mounted') continue;
490	        $entry = vv_disk_entry($d, $key);
491	        if ($entry) $out[] = $entry;
492	    }
493	    usort($out, fn($a, $b) => strcmp($a['name'], $b['name']));
494	    return $out;
495	}
496
497	function vv_array_disks(): array {
498	    // ── API path ──────────────────────────────────────────────────────────────
499	    $api = vv_api_data();
500	    if ($api && (isset($api['array']['parities']) || isset($api['array']['disks']))) {
501	        $parity = [];
502	        $data   = [];
503	        foreach ($api['array']['parities'] ?? [] as $d) {
504	            $entry = vv_api_disk_entry($d, 'parity');
505	            if ($entry) $parity[] = $entry;
506	        }
507	        foreach ($api['array']['disks'] ?? [] as $d) {
508	            $entry = vv_api_disk_entry($d, 'data');
509	            if ($entry) $data[] = $entry;
510	        }
511	        if (!empty($parity) || !empty($data)) {
512	            usort($parity, fn($a, $b) => strnatcmp($a['name'], $b['name']));
513	            usort($data,   fn($a, $b) => strnatcmp($a['name'], $b['name']));
514	            return array_merge($parity, $data);
515	        }
516	    }
517
518	    // ── Local fallback ────────────────────────────────────────────────────────
519	    vv_api_record_fallback('array_disks');
520	    $ini     = @parse_ini_file('/var/local/emhttp/disks.ini', true) ?: [];
521	    $parity  = [];
522	    $data    = [];
523	    foreach ($ini as $key => $d) {
524	        $type = $d['type'] ?? '';
525	        if ($type === 'Parity') {
526	            $entry = vv_disk_entry($d, $key, 'parity');
527	            if ($entry) $parity[] = $entry;
528	        } elseif ($type === 'Data') {
529	            $entry = vv_disk_entry($d, $key, 'data');
530	            if ($entry) $data[] = $entry;
531	        }
532	    }
533	    usort($parity, fn($a, $b) => strnatcmp($a['name'], $b['name']));
534	    usort($data,   fn($a, $b) => strnatcmp($a['name'], $b['name']));
535	    return array_merge($parity, $data);
536	}
537
538	function vv_disk_io_rates(): array {
539	    $snapFile = '/tmp/vv_diskio_snap.json';
540	    $now      = microtime(true);
541
542	    // Read current whole-disk stats from /proc/diskstats
543	    $current = [];
544	    foreach (@file('/proc/diskstats', FILE_IGNORE_NEW_LINES) ?: [] as $line) {
545	        $p = preg_split('/\s+/', trim($line));
546	        if (count($p) < 14) continue;
547	        $dev = $p[2];
548	        // Keep only whole disks: sda/sdb, nvme0n1, md*, not sda1/nvme0n1p1
549	        if (!preg_match('/^(sd[a-z]+|nvme\d+n\d+|md\d+)$/', $dev)) continue;
550	        $current[$dev] = [(int)$p[5], (int)$p[9]]; // [sectors_read, sectors_written]
551	    }
552
553	    // Load previous snapshot
554	    $snap     = @json_decode(@file_get_contents($snapFile) ?: '', true) ?: [];
555	    $prevTime = (float)($snap['t'] ?? $now);
556	    $prev     = $snap['d'] ?? [];
557
558	    // Save current snapshot
559	    @file_put_contents($snapFile, json_encode(['t' => $now, 'd' => $current], JSON_UNESCAPED_UNICODE));
560
561	    $dt  = max(0.5, $now - $prevTime);
562	    $out = [];
563	    foreach ($current as $dev => [$rs, $ws]) {
564	        $entry = [
565	            'tr' => round($rs * 512 / 1073741824, 2), // cumulative GB read
566	            'tw' => round($ws * 512 / 1073741824, 2), // cumulative GB written
567	        ];
568	        if (isset($prev[$dev])) {
569	            [$prs, $pws] = $prev[$dev];
570	            $r = max(0.0, ($rs - $prs) * 512 / $dt / 1048576);
571	            $w = max(0.0, ($ws - $pws) * 512 / $dt / 1048576);
572	            if ($r > 0.01) $entry['r'] = round($r, 1);
573	            if ($w > 0.01) $entry['w'] = round($w, 1);
574	        }
575	        $out[$dev] = $entry;
576	    }
577	    return $out;
578	}
579
580	function vv_disk_thresholds(): array {
581	    $cfg  = @file_get_contents('/boot/config/plugins/dynamix/dynamix.cfg') ?: '';
582	    $get  = function(string $key) use ($cfg): ?int {
583	        return preg_match('/^\s*' . preg_quote($key, '/') . '\s*=\s*"?(\d+)"?/m', $cfg, $m)
584	            ? (int)$m[1] : null;
585	    };
586	    return [
587	        'util_warn' => $get('warning')  ?? 70,
588	        'util_crit' => $get('critical') ?? 90,
589	        'hdd_warn'  => $get('hot')      ?? 45,
590	        'hdd_crit'  => $get('max')      ?? 55,
591	        'ssd_warn'  => $get('hotssd')   ?? 60,
592	        'ssd_crit'  => $get('maxssd')   ?? 70,
593	    ];
594	}
595
596	// Fetch a lightweight snapshot from each remote host that has an API key configured.
597	// Results are cached in /tmp for 30 seconds so rapid monitor polls don't hammer remote hosts.
598	function vv_remote_hosts_stats(): array {
599	    // Read ALL conf files — remote host keys live in their own host*.conf, not the current host's.
600	    $vars = vv_conf_vars();
601	    foreach (glob(CONF_DIR . '/host*.conf') ?: [] as $f) {
602	        $raw = file_get_contents($f) ?: '';
603	        preg_match_all('/^\s*([A-Z0-9_]+)\s*=\s*["\']?([^"\'#\n]*?)["\']?\s*(?:#.*)?$/m', $raw, $m);
604	        foreach ($m[1] as $i => $key) {
605	            if (!isset($vars[$key])) $vars[$key] = trim($m[2][$i]);
606	        }
607	    }
608	    $myHost  = vv_detect_host();
609	    $hostIds = array_filter(array_keys($vars), fn($k) => preg_match('/^HOST\d+$/', $k) && ($vars[$k] ?? '') !== '');
610	    sort($hostIds);
611
612	    $results = [];
613	    foreach ($hostIds as $id) {
614	        if (strtolower($id) === strtolower($myHost)) continue;
615	        // Background cache written by remote_arr_cache_writer.sh every 2h — use it if present.
616	        $bgCache = VV_CACHE_DIR . '/monitor_remote_' . strtolower($id) . '.json';
617	        if (file_exists($bgCache)) {
618	            $cached = json_decode(file_get_contents($bgCache), true);
619	            if ($cached) {
620	                $cached['cache_age'] = time() - (int)filemtime($bgCache);
621	                $results[$id] = $cached;
622	                continue;
623	            }
624	        }
625
626	        // No background cache yet — fall back to live call (uses 30s inline cache).
627	        $key = $vars[strtoupper($id) . '_UNRAID_API_KEY'] ?? '';
628	        if (!$key) {
629	            $results[$id] = ['available' => false, 'no_api_key' => true,
630	                             'host_id' => $id, 'hostname' => $vars[$id]];
631	            continue;
632	        }
633
634	        $cacheFile = "/tmp/vv_remote_{$id}.json";
635	        if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < 30) {
636	            $cached = json_decode(file_get_contents($cacheFile), true);
637	            if ($cached) { $results[$id] = $cached; continue; }
638	        }
639
640	        $gql = '{
641	  info { os { hostname uptime release } cpu { brand threads cores } }
642	  metrics { cpu { percentTotal } memory { percentTotal total used available } }
643	  array {
644	    state
645	    disks    { fsSize fsUsed temp }
646	    caches   { fsSize fsUsed temp }
647	    parities { temp }
648	  }
649	  vms { domains { name } }
650	}';
651	        $data = vv_unraid_api_query(strtolower($id), $gql, 4, $key);
652
653	        if (!$data) {
654	            $entry = ['available' => false, 'host_id' => $id, 'hostname' => $vars[$id]];
655	            file_put_contents($cacheFile, json_encode($entry));
656	            $results[$id] = $entry;
657	            continue;
658	        }
659
660	        $os   = $data['info']['os']  ?? [];
661	        $cpu  = $data['info']['cpu'] ?? [];
662	        $mMem = $data['metrics']['memory'] ?? [];
663
664	        $memPct = round((float)($mMem['percentTotal'] ?? 0));
665	        if ($memPct === 0) {
666	            $totalBytes = (float)($mMem['total']     ?? 0);
667	            $availBytes = (float)($mMem['available'] ?? 0);
668	            $memPct = $totalBytes > 0 ? (int)round(($totalBytes - $availBytes) / $totalBytes * 100) : 0;
669	        }
670	        $memTotalGb = isset($mMem['total']) ? _vv_api_bytes_to_gb((float)$mMem['total']) : 0;
671
672	        $uptimeRaw = $os['uptime'] ?? '';
673	        if (is_numeric($uptimeRaw)) {
674	            $uptimeSec = (int)$uptimeRaw;
675	            $days  = intdiv($uptimeSec, 86400);
676	            $hours = intdiv($uptimeSec % 86400, 3600);
677	            $mins  = intdiv($uptimeSec % 3600, 60);
678	            $uptime = ($days ? "{$days}d " : '') . ($hours ? "{$hours}h " : '') . "{$mins}m";
679	        } else {
680	            $uptimeSec = 0;
681	            $uptime = $uptimeRaw ?: '—';
682	        }
683
684	        $nodeMetrics = vv_api_node_metrics($data);
685	        $entry = array_merge([
686	            'available'    => true,
687	            'host_id'      => $id,
688	            'hostname'     => $os['hostname']  ?? $vars[$id],
689	            'version'      => $os['release']   ?? '',
690	            'uptime'       => $uptime,
691	            'uptime_sec'   => $uptimeSec,
692	            'cpu_load'     => $nodeMetrics['cpu_pct']      ?? 0,
693	            'cpu_threads'  => (int)($cpu['threads']        ?? 0),
694	            'mem_total_gb' => $memTotalGb,
695	            'mem_used_pct' => $memPct,
696	            'array_state'  => $data['array']['state']      ?? 'UNKNOWN',
697	        ], $nodeMetrics);
698	        file_put_contents($cacheFile, json_encode($entry));
699	        $results[$id] = $entry;
700	    }
701	    return $results;
702	}
703
704	function vv_log_tail(string $path, int $lines): string {
705	    $fp = @fopen($path, 'r');
706	    if (!$fp) return '';
707	    fseek($fp, 0, SEEK_END);
708	    $size = ftell($fp);
709	    if ($size <= 0) { fclose($fp); return ''; }
710	    $chunk = min($size, 4096);
711	    fseek($fp, -$chunk, SEEK_END);
712	    $data = fread($fp, $chunk);
713	    fclose($fp);
714	    $all = explode("\n", $data ?: '');
715	    return implode("\n", array_slice($all, -$lines));
716	}
717
718	function vv_parse_bash_array(string $raw, string $varName): array {
719	    if (!preg_match('/^\s*' . preg_quote($varName, '/') . '\s*=\s*\(([^)]*)\)/ms', $raw, $m)) return [];
720	    $items = [];
721	    foreach (explode("\n", $m[1]) as $line) {
722	        $line = trim(preg_replace('/#.*$/', '', $line), " \t\"'");
723	        if ($line !== '') $items[] = $line;
724	    }
725	    return $items;
726	}
727
728	function vv_transcode_sessions(): array {
729	    $v         = vv_conf_vars();
730	    $stateDir  = rtrim($v['STATE_DIR'] ?? '/boot/config/plugins/varaverk/State_Files', '/');
731	    $stateFile = "$stateDir/transcode_state.db";
732	    if (!file_exists($stateFile)) return ['available' => false];
733
734	    $raw = [];
735	    foreach (file($stateFile) ?: [] as $line) {
736	        [$k, $v] = array_pad(explode('=', trim($line), 2), 2, '');
737	        $raw[trim($k)] = trim($v);
738	    }
739
740	    $target       = $raw['current_target'] ?? '';
741	    $lastFlip     = (int)($raw['last_flip_time'] ?? 0);
742	    $flipCount    = (int)($raw['flip_count_hour'] ?? 0);
743	    $isRamdisk    = str_contains($target, 'ramdisk');
744
745	    // Count active session dirs in both known locations
746	    $ramdiskPath = '/mnt/ramdisk_transcodes/transcoding-temp';
747	    $ramSessions = count(glob("$ramdiskPath/*/", GLOB_ONLYDIR) ?: []);
748
749	    // SSD path: first transcoding-temp mount that is not a RAM filesystem (tmpfs/ramfs)
750	    $ssdPath     = '';
751	    $ssdSessions = 0;
752	    foreach (glob('/mnt/*/transcoding-temp/', GLOB_ONLYDIR) ?: [] as $p) {
753	        $parts = explode('/', rtrim($p, '/'));
754	        array_pop($parts);
755	        $mount  = implode('/', $parts) ?: '/';
756	        $fsType = trim(shell_exec('findmnt -n -o FSTYPE ' . escapeshellarg($mount) . ' 2>/dev/null') ?: '');
757	        if ($fsType === 'tmpfs' || $fsType === 'ramfs') continue;
758	        $ssdPath = $p;
759	        break;
760	    }
761	    $ssd = ['available' => false];
762	    if ($ssdPath) {
763	        $ssdSessions = count(glob($ssdPath . '/*/', GLOB_ONLYDIR) ?: []);
764	        $parts = explode('/', rtrim($ssdPath, '/'));
765	        array_pop($parts);
766	        $ssdMount = implode('/', $parts) ?: '/';
767	        $ssd = vv_df($ssdMount);
768	    }
769
770	    // Ramdisk disk usage
771	    $rd = vv_df('/mnt/ramdisk_transcodes');
772
773	    // Last cleanup values from transcode management log
774	    $lastRdFreed  = null;
775	    $lastSsdFreed = null;
776	    $logFile = '/var/log/varaverk/Orchestrators/transcode_management.log';
777	    if (file_exists($logFile)) {
778	        $lines = file($logFile, FILE_IGNORE_NEW_LINES) ?: [];
779	        foreach (array_reverse($lines) as $line) {
780	            if ($lastRdFreed === null && preg_match('/Ramdisk freed:\s*(\S+)/u', $line, $m))
781	                $lastRdFreed = $m[1];
782	            if ($lastSsdFreed === null && preg_match('/SSD freed:\s*(\S+)/u', $line, $m))
783	                $lastSsdFreed = $m[1];
784	            if ($lastRdFreed !== null && $lastSsdFreed !== null) break;
785	        }
786	    }
787
788	    return [
789	        'available'       => true,
790	        'current_target'  => $target,


I can see in line 730 there's a fallback to '/boot/config/plugins/varaverk/State_Files' that should be using STATE_DIR. Let me continue the search more systematically for all findings. Let me check /tmp/vv_cpu_stat and /tmp/vv_net_stat and /tmp/vv_remote and /tmp/vv_diskio:


grep -rn "/tmp/vv_" /boot/config/plugins/varaverk --include="*.sh" --include="*.php" | grep -v "/Deployment/conf_templates/" | grep -v "\.git/" | grep -v "^[^:]*:[^:]*:#"


/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh:124:    tmp=$(mktemp /tmp/vv_kp_XXXXXX.sh)
/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh:125:    remote="/tmp/vv_kp_${RANDOM}.sh"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.php:3:// Builds monitor + arrs payloads and writes them to /tmp/vv_cache/ so page
/boot/config/plugins/varaverk/Partnership/partnership_onboard.sh:507:            "nohup bash '${OWNER_SCRIPTS_DIR}/Partnership/partnership_onboard.sh' --phase2-only > /tmp/vv_phase2_onboard.log 2>&1 & echo triggered" \
/boot/config/plugins/varaverk/Partnership/partnership_onboard.sh:510:            log "Watch progress on $OWNER: tail -f /tmp/vv_phase2_onboard.log"
/boot/config/plugins/varaverk/Plugin/unraid/api/api_test.php:23:$debugFile = '/tmp/vv_api_debug.json';
/boot/config/plugins/varaverk/Plugin/unraid/Tools/remote_arr_cache_writer.sh:41:mkdir -p /tmp/vv_cache
/boot/config/plugins/varaverk/Plugin/unraid/Tools/remote_arr_cache_writer.sh:67:    cache_file="/tmp/vv_cache/arrs_remote_${host_id}.json"
/boot/config/plugins/varaverk/Plugin/unraid/Tools/remote_arr_cache_writer.sh:125:    MONITOR_CACHE="/tmp/vv_cache/monitor_remote_${host_id}.json"
/boot/config/plugins/varaverk/Plugin/unraid/api/snapshot.php:6:// CPU% — shares /tmp/vv_cpu_stat.json with vv_cpu_per_core() so both read the same baseline
/boot/config/plugins/varaverk/Plugin/unraid/api/manual_sync.php:139:    foreach (glob('/tmp/vv_ms_*.pid') ?: [] as $pf) {
/boot/config/plugins/varaverk/Plugin/unraid/api/manual_sync.php:165:    $logFile = '/tmp/vv_ms_' . $token . '.log';
/boot/config/plugins/varaverk/Plugin/unraid/api/manual_sync.php:166:    $pidFile = '/tmp/vv_ms_' . $token . '.pid';
/boot/config/plugins/varaverk/Plugin/unraid/api/manual_sync.php:197:    $pidFile = '/tmp/vv_ms_' . $token . '.pid';
/boot/config/plugins/varaverk/Plugin/unraid/api/manual_sync.php:198:    $logFile = '/tmp/vv_ms_' . $token . '.log';
/boot/config/plugins/varaverk/Plugin/unraid/api/manual_sync.php:218:    $logFile = '/tmp/vv_ms_' . $token . '.log';
/boot/config/plugins/varaverk/Plugin/unraid/api/manual_sync.php:219:    $pidFile = '/tmp/vv_ms_' . $token . '.pid';
/boot/config/plugins/varaverk/Plugin/unraid/include/auth.php:244:    $tmp = '/tmp/vv_auth_rd_' . getmypid() . '.py';
/boot/config/plugins/varaverk/Plugin/unraid/include/auth.php:277:    $tmp = '/tmp/vv_auth_wr_' . getmypid() . '.py';
/boot/config/plugins/varaverk/Plugin/unraid/include/common.php:154:    $stateFile = '/tmp/vv_cpu_stat.json';
/boot/config/plugins/varaverk/Plugin/unraid/include/common.php:295:    $stateFile = '/tmp/vv_net_stat.json';
/boot/config/plugins/varaverk/Plugin/unraid/include/common.php:539:    $snapFile = '/tmp/vv_diskio_snap.json';
/boot/config/plugins/varaverk/Plugin/unraid/include/common.php:634:        $cacheFile = "/tmp/vv_remote_{$id}.json";
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:216:// Debug log written to /tmp/vv_api_debug.json on failure.
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:266:        @file_put_contents('/tmp/vv_api_debug.json', json_encode([
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:281:        @file_put_contents('/tmp/vv_api_debug.json', json_encode([
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:295:// ── File-based API cache (/tmp/vv_cache — tmpfs, cleared on reboot) ───────────
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:297:define('VV_CACHE_DIR', '/tmp/vv_cache');
/boot/config/plugins/varaverk/Deployment/deploy.sh:19:LOG="/tmp/vv_deploy.log"
/boot/config/plugins/varaverk/Plugin/unraid/api/create_api_key.php:22:file_put_contents('/tmp/vv_apikey_debug.json', json_encode($dbg, JSON_PRETTY_PRINT));
/boot/config/plugins/varaverk/Plugin/unraid/api/create_api_key.php:25:    echo json_encode(['ok' => false, 'error' => 'unraid-api returned no output — check /tmp/vv_apikey_debug.json']);


Now let me look for references to config files without using variables. Let me create a comprehensive search for any patterns that suggest a hardcoded path should be variable. Let me check some specific files more carefully:


150	        if (!preg_match('/^(cpu\d*)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/', $line, $m)) continue;
151	        $raw[$m[1]] = [(int)$m[2],(int)$m[3],(int)$m[4],(int)$m[5],(int)$m[6],(int)$m[7],(int)$m[8]];
152	    }
153
154	    $stateFile = '/tmp/vv_cpu_stat.json';
155	    $prev = file_exists($stateFile) ? (json_decode(file_get_contents($stateFile), true) ?: []) : [];
156	    // Atomic write — concurrent fast/slow polls read a consistent snapshot
157	    $tmp = $stateFile . '.tmp';
158	    file_put_contents($tmp, json_encode($raw));
159	    rename($tmp, $stateFile);
160
161	    $usage = function(array $c, ?array $p): int {
162	        if (!$p) return 0;
163	        $dt = array_sum($c) - array_sum($p);
164	        $di = ($c[3] + $c[4]) - ($p[3] + $p[4]);
165	        return $dt > 0 ? max(0, min(100, (int)round((1 - $di / $dt) * 100))) : 0;
166	    };
167
168	    $overall = $usage($raw['cpu'] ?? [], $prev['cpu'] ?? null);
169	    $cores   = [];