No hardcoded paths. The `/etc/cron.d/root` already shows the correct `Plugin/unraid/Tools/` path. The error was from the stale cron entry that was still firing in the minute between the rename and when `update_cron` propagated the rebuilt `varaverk.cron`. It won't recur — next time the `* * * * *` tick fires it'll use the correct path.


ok lets also update the user_script_plug-in.sh to reflect the new paths


Bash

Find all stale paths in user_script_plug-in.sh


805:# bash /boot/config/plugins/varaverk/System_Essentials/php_fpm_max_children.sh --status
806:# bash /boot/config/plugins/varaverk/System_Essentials/php_fpm_max_children.sh
820:# bash /boot/config/plugins/varaverk/Watchdogs/System/webgui_watchdog.sh --dry-run
821:# bash /boot/config/plugins/varaverk/Watchdogs/System/webgui_watchdog.sh --status
834:# bash /boot/config/plugins/varaverk/System_Essentials/mover_stop.sh --status
835:# bash /boot/config/plugins/varaverk/System_Essentials/mover_stop.sh --dry-run
836:# bash /boot/config/plugins/varaverk/System_Essentials/mover_stop.sh
854:# bash /boot/config/plugins/varaverk/System_Essentials/user_scripts_stop.sh --status
855:# bash /boot/config/plugins/varaverk/System_Essentials/user_scripts_stop.sh --dry-run
856:# bash /boot/config/plugins/varaverk/System_Essentials/user_scripts_stop.sh
1404:# bash /boot/config/plugins/varaverk/Tools/recreate_shares.sh --dry-run
1405:# bash /boot/config/plugins/varaverk/Tools/recreate_shares.sh --status
1406:# bash /boot/config/plugins/varaverk/Tools/recreate_shares.sh


grep -n "unraid_api_key_renew" /boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh

Find unraid_api_key_renew references


(Bash completed with no output)


sed -i \
  -e 's|/boot/config/plugins/varaverk/System_Essentials/php_fpm_max_children.sh|/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/php_fpm_max_children.sh|g' \
  -e 's|/boot/config/plugins/varaverk/System_Essentials/mover_stop.sh|/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/mover_stop.sh|g' \
  -e 's|/boot/config/plugins/varaverk/System_Essentials/user_scripts_stop.sh|/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/user_scripts_stop.sh|g' \
  -e 's|/boot/config/plugins/varaverk/Watchdogs/System/webgui_watchdog.sh|/boot/config/plugins/varaverk/Plugin/unraid/Watchdogs/System/webgui_watchdog.sh|g' \
  -e 's|/boot/config/plugins/varaverk/Tools/recreate_shares.sh|/boot/config/plugins/varaverk/Plugin/unraid/Tools/recreate_shares.sh|g' \
  /boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh

echo "Done — verifying:"
grep -n "Plugin/unraid/System_Essentials\|Plugin/unraid/Watchdogs\|Plugin/unraid/Tools/recreate" \
  /boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh

Update all stale paths in user_script_plug-in.sh


Done — verifying:
805:# bash /boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/php_fpm_max_children.sh --status
806:# bash /boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/php_fpm_max_children.sh
820:# bash /boot/config/plugins/varaverk/Plugin/unraid/Watchdogs/System/webgui_watchdog.sh --dry-run
821:# bash /boot/config/plugins/varaverk/Plugin/unraid/Watchdogs/System/webgui_watchdog.sh --status
834:# bash /boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/mover_stop.sh --status
835:# bash /boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/mover_stop.sh --dry-run
836:# bash /boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/mover_stop.sh
854:# bash /boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/user_scripts_stop.sh --status
855:# bash /boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/user_scripts_stop.sh --dry-run
856:# bash /boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/user_scripts_stop.sh
1404:# bash /boot/config/plugins/varaverk/Plugin/unraid/Tools/recreate_shares.sh --dry-run
1405:# bash /boot/config/plugins/varaverk/Plugin/unraid/Tools/recreate_shares.sh --status
1406:# bash /boot/config/plugins/varaverk/Plugin/unraid/Tools/recreate_shares.sh


All 13 occurrences updated across the 5 scripts. The template now points to the correct `Plugin/unraid/` locations.


now i think only onboarding .sh has os specific, to the point it would be easier to just make a new script per distro? check that and whole partner, offboard, se if its worth breaking them down or if on and off boarding need moved


Let me read through all the Partnership scripts:


wc -l /boot/config/plugins/varaverk/Partnership/*.sh
echo "---"
ls /boot/config/plugins/varaverk/Partnership/

Get sizes and list of all Partnership scripts


   469 /boot/config/plugins/varaverk/Partnership/gitea_ssh_setup.sh
   152 /boot/config/plugins/varaverk/Partnership/onboard_cancel.sh
  1772 /boot/config/plugins/varaverk/Partnership/partnership_manager.sh
   712 /boot/config/plugins/varaverk/Partnership/partnership_offboard.sh
   879 /boot/config/plugins/varaverk/Partnership/partnership_onboard.sh
   343 /boot/config/plugins/varaverk/Partnership/partnership_transfer.sh
   381 /boot/config/plugins/varaverk/Partnership/ssh_setup.sh
  4708 total
---
Manual-Partnership.md
README-Partnership.md
gitea_ssh_setup.sh*
onboard_cancel.sh*
partnership_manager.sh*
partnership_offboard.sh*
partnership_onboard.sh*
partnership_transfer.sh*
ssh_setup.sh*


grep -n "unraid\|mover\|unRAID\|emhttp\|/boot/config\|update_cron\|user.scripts\|/mnt/user\|unraid_api\|php_fpm\|webgui\|Unraid\|array.start\|array.stop\|disk" \
  /boot/config/plugins/varaverk/Partnership/partnership_onboard.sh | head -50

Find Unraid-specific code in partnership_onboard.sh


44:#   The same XMLs that Unraid's Docker Manager uses are what get SCPed — the mirror's
140:TEMPLATES_DIR="/boot/config/plugins/dockerMan/templates-user"
207:    local state_file="${VARAVERK_SETUP_FILE:-${STATE_DIR:-/boot/config}/varaverk_setup.db}"
215:        php -r "require_once '/usr/local/emhttp/plugins/varaverk/include/config.php'; vv_push_setup_state();" 2>/dev/null || true
228:# ── HELPER: deploy a container from a local Unraid template XML to a remote host ─────────────
254:    # SCP the XML so Unraid Docker Manager recognises and can manage the container
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:]')
500:        OWNER_SCRIPTS_DIR="${OWNER_SCRIPTS_DIR:-/boot/config/plugins/varaverk}"
595:                    local _setup_f="${VARAVERK_SETUP_FILE:-${STATE_DIR:-/boot/config}/varaverk_setup.db}"
652:require_once '/usr/local/emhttp/plugins/varaverk/include/config.php';
703:        "test -d /usr/local/emhttp/plugins/folder.view3 && echo yes" 2>/dev/null)
823:require_once '/usr/local/emhttp/plugins/varaverk/include/config.php';


now i think only onboarding .sh has os specific, to the point it would be easier to just make a new script per distro? check that and whole partner, offboard, se if its worth breaking them down or if…


Find Unraid-specific code in partnership_offboard.sh


74:TEMPLATES_DIR="/boot/config/plugins/dockerMan/templates-user"
120:LOCAL_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${LOCAL_SERVER_NAME}.db"
121:REMOTE_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${REMOTE_SERVER_NAME}.db"
122:OWNER_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${OWNER}.db"
123:MIRROR_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${MIRROR}.db"
124:OFFLINE_COUNTER="${STATE_DIR:-/boot/config}/partnership_offline_days.db"
219:# local templates-user/ copies (SCPed there during onboard) to get container names and
584:                "fv3='/boot/config/plugins/folder.view3/docker.json'


grep -n "unraid\|mover\|unRAID\|emhttp\|/boot/config\|update_cron\|user.scripts\|/mnt/user\|unraid_api\|php_fpm\|webgui\|Unraid\|dockerMan\|templates-user\|folder.view\|array.start" \
  /boot/config/plugins/varaverk/Partnership/partnership_manager.sh | head -40

Find Unraid-specific code in partnership_manager.sh


77:#   --onboard verifies both servers are on compatible unRAID versions.
96:#   /boot/config/partnership_HOST1.db  — HOST1 writes, HOST2 reads via SSH
97:#   /boot/config/partnership_HOST2.db  — HOST2 writes, HOST1 reads via SSH
98:#   /boot/config/partnership_blocklist.db  — hostname|timestamp|reason, persists until cleared
100:# On /boot/config — survives reboots, available before array starts, minimal flash wear.
192:BLOCKLIST_FILE="${PARTNERSHIP_BLOCKLIST_FILE:-${STATE_DIR:-/boot/config}/partnership_blocklist.db}"
239:    "/usr/local/emhttp/plugins/dynamix/scripts/notify" \
241:    "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
270:LOCAL_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${LOCAL_SERVER_NAME}.db"
271:REMOTE_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${REMOTE_SERVER_NAME}.db"
272:OWNER_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${OWNER}.db"
273:MIRROR_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${MIRROR}.db"
274:OFFLINE_COUNTER="${STATE_DIR:-/boot/config}/partnership_offline_days.db"
386:        "grep -rl '<WebUI>' /boot/config/plugins/dockerMan/templates-user/ 2>/dev/null | \
417:            /boot/config/plugins/dockerMan/templates-user/ 2>/dev/null | \
577:FOLDERVIEW3_DIR="/usr/local/emhttp/plugins/folder.view3"
578:FOLDERVIEW3_JSON="/boot/config/plugins/folder.view3/docker.json"
580:# Derive short partner name: strip unraid- prefix (case-insensitive) if present
584:    [[ "$short" == unraid-* ]] && short="${short:7}"
775:# Returns just the short name portion: "unRAID-Gmer4Lfe" → "Gmer4Lfe"
779:    [[ "$short" == unraid-* ]] && short="${short:7}"
1113:    mountpoint -q /mnt/user 2>/dev/null || { error "Local array not healthy"; return 1; }
1121:        "mountpoint -q /mnt/user && timeout 10 docker ps" >/dev/null 2>&1 || {
1145:                "/mnt/user/appdata-Fallback/Critical-Data" \
1148:                "/mnt/user/Media_Server/Emby" \
1185:        error "Example: partnership_manager.sh --unblock unRAID-Jayred365"
1443:require_once '/usr/local/emhttp/plugins/varaverk/include/config.php';
1466:        local_state_file="${VARAVERK_SETUP_FILE:-${STATE_DIR:-/boot/config}/varaverk_setup.db}"
1475:                php -r "require_once '/usr/local/emhttp/plugins/varaverk/include/config.php'; vv_push_setup_state();" 2>/dev/null || true
1513:    # Version parity — both servers must agree on unRAID version
1514:    check_unraid_version_parity || exit 1


grep -n "check_unraid_version\|unraid_version\|/usr/local/sbin\|/etc/unraid\|unraid-version\|os.version\|OS_VERSION" \
  /boot/config/plugins/varaverk/Partnership/partnership_manager.sh | head -20
echo "---"
grep -n "unraid\|mover\|unRAID\|emhttp\|/boot/config\|dockerMan\|Unraid\|array.start" \
  /boot/config/plugins/varaverk/Partnership/ssh_setup.sh \
  /boot/config/plugins/varaverk/Partnership/gitea_ssh_setup.sh \
  /boot/config/plugins/varaverk/Partnership/onboard_cancel.sh \
  /boot/config/plugins/varaverk/Partnership/partnership_transfer.sh 2>/dev/null | head -30

Check version checks and remaining Partnership scripts for Unraid specifics


1514:    check_unraid_version_parity || exit 1
---
/boot/config/plugins/varaverk/Partnership/onboard_cancel.sh:57:STATE_FILE="${VARAVERK_SETUP_FILE:-${STATE_DIR:-/boot/config}/varaverk_setup.db}"
/boot/config/plugins/varaverk/Partnership/onboard_cancel.sh:91:                 sed -i \"/^${MIRROR_ID}_PHASE\|^${MIRROR_ID}_KEY_READY/d\" /boot/config/varaverk_setup.db 2>/dev/null
/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh:130:    "/usr/local/emhttp/plugins/dynamix/scripts/notify" \
/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh:132:    "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh:148:LOCAL_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${LOCAL_SERVER_NAME}.db"
/boot/config/plugins/varaverk/Partnership/partnership_transfer.sh:149:REMOTE_STATE_FILE="${STATE_DIR:-/boot/config}/partnership_${REMOTE_SERVER_NAME}.db"
/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/Partnership/ssh_setup.sh:6:# Key named after this server: hostname lowercased, unraid- prefix stripped.
/boot/config/plugins/varaverk/Partnership/ssh_setup.sh:7:#   unRAID-Gmer4Lfe → gmer4lfe_rsync_automation
/boot/config/plugins/varaverk/Partnership/ssh_setup.sh:8:#   unRAID-Jayred365 → jayred365_rsync_automation
/boot/config/plugins/varaverk/Partnership/ssh_setup.sh:72:    "/usr/local/emhttp/plugins/dynamix/scripts/notify" \
/boot/config/plugins/varaverk/Partnership/ssh_setup.sh:74:    "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
/boot/config/plugins/varaverk/Partnership/ssh_setup.sh:79:# Strip unraid- prefix (case-insensitive) if present → gmer4lfe_rsync_automation
/boot/config/plugins/varaverk/Partnership/ssh_setup.sh:81:[[ "${SHORT_NAME}" == unraid-* ]] && SHORT_NAME="${SHORT_NAME:7}"
/boot/config/plugins/varaverk/Partnership/gitea_ssh_setup.sh:34:#        Title: "unraid-{host_id}" (e.g. unraid-host1)
/boot/config/plugins/varaverk/Partnership/gitea_ssh_setup.sh:79:#   GITEA_SSH_KEY           Path for the SSH keypair (default: /root/.ssh/unraid_gitea)
/boot/config/plugins/varaverk/Partnership/gitea_ssh_setup.sh:139:    "/usr/local/emhttp/plugins/dynamix/scripts/notify" \
/boot/config/plugins/varaverk/Partnership/gitea_ssh_setup.sh:141:    "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
/boot/config/plugins/varaverk/Partnership/gitea_ssh_setup.sh:197:    error "  3. Generate New Token → name: unraid-setup → scope: write:user"
/boot/config/plugins/varaverk/Partnership/gitea_ssh_setup.sh:272:KEY_TITLE="unraid-$(echo "$MY_ID" | tr '[:upper:]' '[:lower:]')"


Let me look deeper at the most OS-specific sections:


grep -n "check_unraid_version\|TEMPLATES_DIR\|dockerMan\|folder.view3\|deploy_container\|xml\|XML\|template" \
  /boot/config/plugins/varaverk/Partnership/partnership_onboard.sh | head -40

Find depth of container deployment code in onboard


25:#   Step 4: Deploy auth stack    — push XMLs, pull images, create + start on mirror
28:#   Step 6: Deploy arr stack     — push arr XMLs, pull images, create + start on mirror
42:# XML templates are the single source of truth for deployed containers
43:#   The owner's templates-user/ XMLs define every container deployed on the mirror.
44:#   The same XMLs that Unraid's Docker Manager uses are what get SCPed — the mirror's
80:#       XML filenames (from this server's templates-user/) to push and deploy on the
93:#       XML filenames to push and deploy on the mirror as its arr stack.
140:TEMPLATES_DIR="/boot/config/plugins/dockerMan/templates-user"
228:# ── HELPER: deploy a container from a local Unraid template XML to a remote host ─────────────
230:# Parses Port / Path / Variable Config entries from the XML, SCPs the template and a
234:deploy_container_from_xml() {
235:    local xml_file="$1" remote_ip="$2" ssh_key="$3"
236:    local xml_name
237:    xml_name=$(basename "$xml_file")
241:    name=$(       awk 'match($0,/<Name>([^<]+)<\/Name>/,        a){print a[1];exit}' "$xml_file")
242:    repo=$(       awk 'match($0,/<Repository>([^<]+)<\/Repository>/,a){print a[1];exit}' "$xml_file")
243:    network=$(    awk 'match($0,/<Network>([^<]+)<\/Network>/,   a){print a[1];exit}' "$xml_file")
244:    extra=$(      awk 'match($0,/<ExtraParams>([^<]*)<\/ExtraParams>/,a){print a[1];exit}' "$xml_file")
245:    privileged=$( awk 'match($0,/<Privileged>([^<]+)<\/Privileged>/,a){print a[1];exit}' "$xml_file")
248:        warn "  Cannot parse Name/Repository from $xml_name — skipping"
254:    # SCP the XML so Unraid Docker Manager recognises and can manage the container
257:            "$xml_file" "root@${remote_ip}:${TEMPLATES_DIR}/${xml_name}" 2>/dev/null || {
258:            warn "  SCP failed for $xml_name — skipping $name"
262:        warn "  DRY RUN — would SCP $xml_name → $MIRROR:${TEMPLATES_DIR}/"
293:        }' "$xml_file"
304:        }' "$xml_file"
313:        }' "$xml_file"
394:# deploy_container_from_xml() already stops/removes containers with the same name as what's
431:# ── HELPER: deploy a stack of XMLs to the mirror, health-checking db deps between batches ────
440:deploy_xml_stack() {
441:    local -n xml_array_ref="$1"
445:    for xml_name in "${xml_array_ref[@]}"; do
446:        local xml_file="${TEMPLATES_DIR}/${xml_name}"
447:        if [[ ! -f "$xml_file" ]]; then
448:            warn "$xml_name not found in $TEMPLATES_DIR — skipping"
455:        cname=$(awk 'match($0,/<Name>([^<]+)<\/Name>/,a){print a[1];exit}' "$xml_file")
457:        if deploy_container_from_xml "$xml_file" "$MIRROR_IP" "$MIRROR_SSH_KEY"; then
703:        "test -d /usr/local/emhttp/plugins/folder.view3 && echo yes" 2>/dev/null)
744:    deploy_xml_stack PARTNERSHIP_AUTH_STACK
771:    deploy_xml_stack PARTNERSHIP_ARR_STACK


grep -n "check_unraid_version\|function check_unraid\|unraid_version\|os_version\|OS_VERSION" \
  /boot/config/plugins/varaverk/Partnership/partnership_manager.sh | head -20

Find the unraid version parity check implementation


1514:    check_unraid_version_parity || exit 1


grep -n "function check_unraid_version\|check_unraid_version" \
  /boot/config/plugins/varaverk/common.sh \
  /boot/config/plugins/varaverk/load_config.sh \
  /boot/config/plugins/varaverk/Partnership/*.sh 2>/dev/null

Find where check_unraid_version_parity is defined


/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:1514:    check_unraid_version_parity || exit 1
/boot/config/plugins/varaverk/common.sh:83:#            check_unraid_version_parity() — refuses remote ops on version mismatch
/boot/config/plugins/varaverk/common.sh:1745:# Usage: check_unraid_version_parity || exit 1
/boot/config/plugins/varaverk/common.sh:1747:check_unraid_version_parity() {


1745	# Usage: check_unraid_version_parity || exit 1
1746
1747	check_unraid_version_parity() {
1748	    local local_version remote_version
1749
1750	    # Read local version
1751	    if [[ ! -f /etc/unraid-version ]]; then
1752	        warn "Cannot read local /etc/unraid-version — skipping version parity check"
1753	        return 0
1754	    fi
1755	    local_version=$(grep -oP '(?<=version=")[^"]+' /etc/unraid-version 2>/dev/null)
1756	    if [[ -z "$local_version" ]]; then
1757	        warn "Cannot parse local unRAID version — skipping parity check"
1758	        return 0
1759	    fi
1760
1761	    # Read remote version via SSH
1762	    remote_version=$(ssh -i "$SSH_KEY" -o ConnectTimeout=10 root@"$REMOTE_SERVER" \
1763	        "grep -oP '(?<=version=\")[^\"]+' /etc/unraid-version 2>/dev/null" 2>/dev/null)
1764	    if [[ -z "$remote_version" ]]; then
1765	        warn "Cannot read remote unRAID version from $REMOTE_SERVER_NAME — skipping parity check"
1766	        return 0
1767	    fi
1768
1769	    log "Version parity: local=$local_version remote=$remote_version"
1770
1771	    if [[ "$local_version" == "$remote_version" ]]; then
1772	        log "unRAID versions match: $local_version ✅"
1773	        return 0
1774	    fi

# Usage: check_unraid_version_parity || exit 1

check_unraid_version_parity() {
    local local_version remote_version

    # Read local version
    if [[ ! -f /etc/unraid-version ]]; then
        warn "Cannot read local /etc/unraid-version — skipping version parity check"
        return 0
    fi
    local_version=$(grep -oP '(?<=version=")[^"]+' /etc/unraid-version 2>/dev/null)
    if [[ -z "$local_version" ]]; then
        warn "Cannot parse local unRAID version — skipping parity check"
        return 0
    fi

    # Read remote version via SSH
    remote_version=$(ssh -i "$SSH_KEY" -o ConnectTimeout=10 root@"$REMOTE_SERVER" \
        "grep -oP '(?<=version=\")[^\"]+' /etc/unraid-version 2>/dev/null" 2>/dev/null)
    if [[ -z "$remote_version" ]]; then
        warn "Cannot read remote unRAID version from $REMOTE_SERVER_NAME — skipping parity check"
        return 0
    fi

    log "Version parity: local=$local_version remote=$remote_version"

    if [[ "$local_version" == "$remote_version" ]]; then
        log "unRAID versions match: $local_version ✅"
        return 0
    fi


grep -n "TEMPLATES_DIR\|dockerMan\|folder.view3\|deploy_xml\|deploy_container\|xml_file\|xml_name\|XML" \
  /boot/config/plugins/varaverk/Partnership/partnership_offboard.sh | head -30

Check depth of XML/docker template code in offboard


46:#       Auth container XMLs to push during onboard — used on offboard to identify what
51:#       Arr container XMLs — same cleanup logic as auth stack.
74:TEMPLATES_DIR="/boot/config/plugins/dockerMan/templates-user"
155:# container names from local XML templates. SSHes to remote to stop, remove, and delete
161:    local -a xml_names=()
162:    [[ ${#PARTNERSHIP_AUTH_STACK[@]} -gt 0 ]] && xml_names+=("${PARTNERSHIP_AUTH_STACK[@]}")
163:    [[ ${#PARTNERSHIP_ARR_STACK[@]}  -gt 0 ]] && xml_names+=("${PARTNERSHIP_ARR_STACK[@]}")
165:    if [[ ${#xml_names[@]} -eq 0 ]]; then
171:    for xml_name in "${xml_names[@]}"; do
172:        [[ -z "$xml_name" ]] && continue
173:        local xml_file="${TEMPLATES_DIR}/${xml_name}"
174:        if [[ ! -f "$xml_file" ]]; then
175:            warn "  $xml_name not found in local $TEMPLATES_DIR — skipping"
180:        cname=$(awk 'match($0,/<Name>([^<]+)<\/Name>/,a){print a[1];exit}' "$xml_file")
224:    local -a xml_names=()
238:        xml_names=("${auth_arr[@]}" "${arr_arr[@]}")
241:    if [[ ${#xml_names[@]} -eq 0 ]]; then
247:    for xml_name in "${xml_names[@]}"; do
248:        [[ -z "$xml_name" ]] && continue
249:        local xml_file="${TEMPLATES_DIR}/${xml_name}"
250:        if [[ ! -f "$xml_file" ]]; then
251:            warn "  $xml_name not found locally — skipping"
256:        cname=$(awk 'match($0,/<Name>([^<]+)<\/Name>/,a){print a[1];exit}' "$xml_file")
584:                "fv3='/boot/config/plugins/folder.view3/docker.json'


grep -n "partnership_onboard\|partnership_offboard\|source.*onboard\|bash.*onboard" \
  /boot/config/plugins/varaverk/Partnership/partnership_manager.sh | head -10
echo "---"
# Check what calls onboard/offboard at the top level
grep -rn "partnership_onboard\|partnership_offboard" \
  /boot/config/plugins/varaverk --include="*.sh" --include="*.php" --include="*.conf" \
  | grep -v "^Binary\|Partnership/partnership_onboard\|Partnership/partnership_offboard"

Check if manager calls onboard/offboard or if they're standalone


9:# monitoring, offboard, and ownership transfer. Called by partnership_onboard.sh
278:# with partnership_offboard.sh which sources this file and registers the same trap.
1175:# partnership_offboard.sh sources this file with PARTNERSHIP_LIB_MODE=1 to get helper
1382:            warn "Owner finalising offboard — spawning partnership_offboard.sh in background"
1383:            nohup bash "$SCRIPT_DIR/partnership_offboard.sh" \
1385:                >> /var/log/partnership_offboard.log 2>&1 &
1386:            warn "Offboard PID $! running — tail /var/log/partnership_offboard.log to follow"
1389:            nohup bash "$SCRIPT_DIR/partnership_offboard.sh" \
1391:                >> /var/log/partnership_offboard.log 2>&1 &
1392:            warn "Offboard PID $! running — tail /var/log/partnership_offboard.log to follow"
---
/boot/config/plugins/varaverk/Plugin/unraid/pages/partnership.php:201:  if (!confirm('Run full partnership_onboard.sh?\n\nRun on the MIRROR first, then on the OWNER.\n\nUse Phase 1 + Phase 2 buttons for step-by-step control.')) return;
/boot/config/plugins/varaverk/Plugin/unraid/pages/partnership.php:260:  if (!confirm('Run partnership_offboard.sh?\n\nThis will end the partnership, reconfigure WebUIs, and revoke SSH access.\n\nContinue?')) return;
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:9:# monitoring, offboard, and ownership transfer. Called by partnership_onboard.sh
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:278:# with partnership_offboard.sh which sources this file and registers the same trap.
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:1175:# partnership_offboard.sh sources this file with PARTNERSHIP_LIB_MODE=1 to get helper
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:1382:            warn "Owner finalising offboard — spawning partnership_offboard.sh in background"
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:1383:            nohup bash "$SCRIPT_DIR/partnership_offboard.sh" \
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:1385:                >> /var/log/partnership_offboard.log 2>&1 &
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:1386:            warn "Offboard PID $! running — tail /var/log/partnership_offboard.log to follow"
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:1389:            nohup bash "$SCRIPT_DIR/partnership_offboard.sh" \
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:1391:                >> /var/log/partnership_offboard.log 2>&1 &
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:1392:            warn "Offboard PID $! running — tail /var/log/partnership_offboard.log to follow"
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:1416:    # partnership_onboard.sh --phase1-only so HOST1 can complete its own side
/boot/config/plugins/varaverk/Partnership/partnership_manager.sh:1618:    exec bash "$SCRIPT_DIR/partnership_offboard.sh" "${FILTERED_ARGS[@]}" --reason="$REASON"
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:79:#           Initial_run/ section: ssh_setup.sh + partnership_onboard.sh.
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:101:#           Partnership/: gitea_ssh_setup.sh, partnership_offboard.sh, partnership_transfer.sh.
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:1249:# partnership_offboard.sh — cleanly end a partnership (standalone version of --offboard)
/boot/config/plugins/varaverk/Plugin/unraid/user_script_plug-in.sh:1305:# partnership_onboard.sh — full partnership setup orchestrator (run once to join a partner server)


Now I have the full picture. Here's my honest assessment:

**The coupling matters:**
- `partnership_manager.sh` spawns offboard via `nohup bash "$SCRIPT_DIR/partnership_offboard.sh"` and `exec bash "$SCRIPT_DIR/partnership_offboard.sh"` — both using `$SCRIPT_DIR`
- `partnership_offboard.sh` sources `partnership_manager.sh` with `PARTNERSHIP_LIB_MODE=1` to get its helpers
- They're a tightly coupled pair — moving one without the other breaks both

**Where the actual Unraid-specific code lives:**

| Script | OS-specific | What |
|---|---|---|
| `partnership_onboard.sh` | ~200 lines | `deploy_container_from_xml()` + `deploy_xml_stack()` — parses Unraid Docker Manager XML templates, SCPs them, builds docker run commands from XML fields |
| `partnership_offboard.sh` | ~100 lines | Container teardown using those same XML templates |
| `partnership_manager.sh` | Scattered | `check_unraid_version_parity`, `folder.view3` JSON, `dockerMan` path refs, Unraid notify — but all gracefully degrade |
| `ssh_setup.sh`, `gitea_ssh_setup.sh`, `transfer.sh`, `onboard_cancel.sh` | ~5 lines each | Unraid notify (gracefully degraded), `unraid-` hostname prefix strip |

**My recommendation: don't move anything yet.**

The right split point when a second platform arrives is a container deployment hook:
```
Partnership/partnership_onboard.sh       ← keeps orchestration (generic)
Plugin/unraid/Partnership/deploy_containers.sh   ← 200 lines of XML logic moved here
Plugin/proxmox/Partnership/deploy_containers.sh  ← docker-compose equivalent
```

Moving `onboard.sh` and `offboard.sh` to `Plugin/unraid/` now would require `partnership_manager.sh` to also be platform-aware (it calls offboard directly via `$SCRIPT_DIR`), which defeats the purpose of keeping manager generic. You'd end up moving the whole `Partnership/` directory, and then there's no generic foundation left for a future platform to build on.

The XML container deployment in `onboard.sh` is the only part that's fundamentally non-portable. Everything else degrades gracefully or is trivially abstracted. Worth doing the hook extraction when Proxmox support is actively being added — not before.


cache writer is either not working.... monitor page is not hitting cache first again


cat /boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.sh
echo "==="
ls -la /tmp/vv_cache/ 2>/dev/null || echo "cache dir missing"
echo "==="
# Check recent log for cache writer
cat /var/log/varaverk/Plugin/unraid/Tools/api_cache_writer.sh.log 2>/dev/null | tail -20 || echo "no log"

Check cache writer script, cache dir contents, and recent log


#!/bin/bash
# ==============================================================================================
# ================================= API Cache Writer ===========================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# Builds the monitor and arrs API payloads and writes them to /tmp/vv_cache/
# so page loads can serve from the file instantly instead of making live HTTP
# calls on every request.
#
# Runs every minute via the Varaverk scheduler. /tmp is tmpfs — files are
# RAM-speed reads and auto-cleared on reboot.
#
# ==============================================================================================

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
php "$SCRIPT_DIR/api_cache_writer.php"
===
total 44
drwxr-xr-x  2 root root   140 Jun  5 20:36 ./
drwxrwxrwt 18 root root   960 Jun  5 20:39 ../
-rw-r--r--  1 root root  5132 Jun  5 20:04 arrs.json
-rw-r--r--  1 root root    57 Jun  5 20:35 board_partner.json
-rw-r--r--  1 root root    22 Jun  5 20:36 ext_ip.json
-rw-r--r--  1 root root 24092 Jun  5 20:04 monitor.json
-rw-r--r--  1 root root    38 Jun  5 20:36 snap_media.json
===


Read full api_cache_writer.php


<?php
// API cache writer — runs every minute via Varaverk scheduler.
// Builds monitor + arrs payloads and writes them to /tmp/vv_cache/ so page
// loads can serve instantly from the file instead of making live HTTP calls.
//
// Called by api_cache_writer.sh (bash wrapper required by the scheduler).

$_base = dirname(__DIR__) . '/Plugin/unraid';
require_once $_base . '/include/monitor.php';
require_once $_base . '/include/vms.php';
require_once $_base . '/include/docker_folders.php';
require_once $_base . '/include/arrs.php';

$t = microtime(true);

// ── Monitor payload ───────────────────────────────────────────────────────────
// Call vv_api_data() once — result is static-cached for the rest of this process.
vv_api_data();

$monitor = [
    'system'          => vv_system_info(),
    'fallback'        => vv_fallback_state(),
    'fallback_active' => vv_fallback_active(),
    'partner'         => vv_partner_state(),
    'resources'       => vv_system_resources(),
    'cpu'             => vv_cpu_per_core(),
    'mem'             => vv_memory_breakdown(),
    'net'             => vv_network_stats(),
    'gpu'             => vv_gpu_stats(),
    'gpu_procs'       => vv_gpu_processes(),
    'containers'      => vv_docker_containers(),
    'stopped'         => vv_docker_stopped(),
    'transcode'       => vv_transcode_sessions(),
    'ups'             => vv_ups_stats(),
    'parity'          => vv_parity_status(),
    'storage'         => vv_storage_pools(),
    'array_disks'     => vv_array_disks(),
    'disk_io'         => vv_disk_io_rates(),
    'watchdog'        => vv_watchdog_summary(),
    'scripts'         => vv_scripts_status(),
    'rsync'           => vv_rsync_status(),
    'thresholds'      => vv_disk_thresholds(),
    'vms'             => vv_get_vms(),
    'docker_folders'  => vv_get_docker_folders(),
    'remote_hosts'    => vv_remote_hosts_stats(),
    '_api_status'     => vv_api_get_status(),
    'ts'              => time(),
];
vv_cache_write('monitor', $monitor);

// ── Arrs payload ──────────────────────────────────────────────────────────────
$arrs = vv_arrs_all();
vv_cache_write('arrs', $arrs);

$elapsed = round((microtime(true) - $t) * 1000);
echo "Cache written in {$elapsed}ms — monitor + arrs\n";


ls -la /usr/local/emhttp/plugins/varaverk/
echo "---"
# Test running the cache writer directly to see the error
php /boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.php 2>&1 | head -20

Check plugin symlink and run cache writer to see actual error


total 121
drwxrwxrwx 12 root root    17 Jun  5 20:32 ./
drwxrwxrwx  5 root root    10 Jun  5 19:02 ../
drwxrwxrwx  2 root root     6 Jun  5 20:22 System_Essentials/
drwxrwxrwx  2 root root     8 Jun  5 20:22 Tools/
-rw-r--r--  1 root root  2229 Jun  5 18:43 Varaverk.page
-rw-r--r--  1 root root  1304 Jun  4 01:00 VaraverkSettings.page
drwxrwxrwx  3 root root     3 Jun  5 20:22 Watchdogs/
-rwxrwxrwx  1 root root 16000 Jun  4 18:14 adapter.sh*
drwxrwxrwx  2 root root    47 Jun  5 20:23 api/
drwxrwxrwx  2 root root     3 Jun  4 21:53 css/
drwxrwxrwx  4 root root     4 May 28 22:24 event/
drwxrwxrwx  2 root root     3 May 28 22:24 icons/
drwxrwxrwx  2 root root    18 Jun  5 20:29 include/
drwxrwxrwx  2 root root     3 May 28 22:24 js/
drwxrwxrwx  2 root root    13 Jun  5 20:13 pages/
-rwxr-xr-x  1 root root  3913 Jun  4 01:00 run_job.sh*
-rwxrwxrwx  1 root root 97300 Jun  5 20:32 user_script_plug-in.sh*
---


ls -la /usr/local/emhttp/plugins/varaverk/Tools/
echo "---"
php /usr/local/emhttp/plugins/varaverk/Tools/api_cache_writer.php 2>&1 | head -10
echo "---"
# Check what __DIR__ resolves to in emhttp vs boot
php -r "echo __DIR__ . '\n';" 2>/dev/null

Check emhttp Tools dir and test running from there


total 56
drwxrwxrwx  2 root root     8 Jun  5 20:22 ./
drwxrwxrwx 12 root root    17 Jun  5 20:32 ../
-rw-rw-rw-  1 root root  2510 Jun  3 16:14 api_cache_writer.php
-rwxr-xr-x  1 root root  1052 Jun  4 01:00 api_cache_writer.sh*
-rwxrwxrwx  1 root root 11499 Jun  5 20:04 conf_populate.sh*
-rwxrwxrwx  1 root root 11850 Jun  5 20:22 recreate_shares.sh*
-rwxr-xr-x  1 root root  5409 Jun  5 20:04 remote_arr_cache_writer.sh*
-rwxr-xr-x  1 root root 15688 Jun  5 20:04 storage_migrate.sh*
---
---
/boot/config/plugins/varaverk\n


head -15 /boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.php
echo "---"
# Run directly from boot path to see actual PHP error
php /boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.php 2>&1 | head -20

Check boot path PHP file and run it to see errors


<?php
// API cache writer — runs every minute via Varaverk scheduler.
// Builds monitor + arrs payloads and writes them to /tmp/vv_cache/ so page
// loads can serve instantly from the file instead of making live HTTP calls.
//
// Called by api_cache_writer.sh (bash wrapper required by the scheduler).

$_base = dirname(__DIR__) . '/Plugin/unraid';
require_once $_base . '/include/monitor.php';
require_once $_base . '/include/vms.php';
require_once $_base . '/include/docker_folders.php';
require_once $_base . '/include/arrs.php';

$t = microtime(true);

---


ls -la /boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.php
echo "---"
ls -la /boot/config/plugins/varaverk/Plugin/
echo "---"
# Check with error_reporting on
php -r "
error_reporting(E_ALL);
ini_set('display_errors', 1);
chdir('/boot/config/plugins/varaverk/Plugin/unraid/Tools');
include '/boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.php';
" 2>&1 | head -20

Check for symlinks and test PHP with full error reporting


-rw-rw-rw- 1 root root 2510 Jun  3 16:14 /boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.php
---
total 77
drwxrwxrwx  5 root     root     10 Jun  5 19:02 ./
drwxrwxrwx 23 root     root     37 Jun  5 20:23 ../
drwxrwxrwx  2 gmer4lfe users     3 May 28 22:57 Icons/
-rw-r--r--  1 root     root   4926 Jun  4 18:07 Manual-Plugin.md
-rw-rw-rw-  1 root     root   7193 Jun  4 18:14 README-Plugin.md
-rwxr-xr-x  1 root     root   5625 Jun  4 01:00 build.sh*
-rwxr-xr-x  1 root     root   4882 Jun  4 01:00 dev_install.sh*
drwxrwxrwx  2 root     root      3 May 31 13:33 dist/
drwxrwxrwx 12 root     root     17 Jun  5 20:32 unraid/
-rw-r--r--  1 root     root  12023 Jun  5 19:02 varaverk.plg
---

Warning: require_once(/boot/config/plugins/varaverk/Plugin/unraid/Plugin/unraid/include/monitor.php): Failed to open stream: No such file or directory in /boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.php on line 9

Fatal error: Uncaught Error: Failed opening required '/boot/config/plugins/varaverk/Plugin/unraid/Plugin/unraid/include/monitor.php' (include_path='.:/usr/local/emhttp') in /boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.php:9
Stack trace:
#0 Command line code(5): include()
#1 {main}
  thrown in /boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.php on line 9


Found it — `$_base = dirname(__DIR__) . '/Plugin/unraid'` resolves to `.../Plugin/unraid/Plugin/unraid` — wrong path. Also needs config.php bootstrapped first. Let me also check why the monitor page stopped reading cache:


grep -n "vv_cache_read\|cache_read\|vv_cache\|monitor\.json\|VV_CACHE_DIR" \
  /boot/config/plugins/varaverk/Plugin/unraid/api/monitor.php | head -20

Check monitor API cache read logic


6:    $_vv_cached = vv_cache_read('monitor', 300);
7:    if ($_vv_cached !== null) { echo json_encode($_vv_cached); exit; }
8:    unset($_vv_cached);


grep -n "function vv_cache_read\|function vv_cache_write\|VV_CACHE_DIR\|vv_cache" \
  /boot/config/plugins/varaverk/Plugin/unraid/include/config.php | head -20

Find vv_cache_read definition and VV_CACHE_DIR


295:// ── File-based API cache (/tmp/vv_cache — tmpfs, cleared on reboot) ───────────
297:define('VV_CACHE_DIR', '/tmp/vv_cache');
300:function vv_cache_read(string $key, int $maxAge = 90): ?array {
301:    $f = VV_CACHE_DIR . '/' . $key . '.json';
308:function vv_cache_write(string $key, array $data): void {
309:    if (!is_dir(VV_CACHE_DIR)) @mkdir(VV_CACHE_DIR, 0755, true);
310:    $f   = VV_CACHE_DIR . '/' . $key . '.json';


head -5 /boot/config/plugins/varaverk/Plugin/unraid/include/monitor.php
head -5 /boot/config/plugins/varaverk/Plugin/unraid/include/arrs.php

Check if monitor/arrs include config.php themselves


<?php
require_once __DIR__ . '/common.php';

// Monitor-page-specific helpers — partner state, fallback state, watchdog summary, scripts status.

<?php
// Arr (Sonarr / Radarr / Lidarr) data helpers

require_once __DIR__ . '/config.php';


Check if common.php includes config.php


<?php
require_once __DIR__ . '/config.php';
require_once __DIR__ . '/unraid_api.php';


Good — config.php loads transitively through monitor.php→common.php. The only fix needed is the `$_base` path. From `Plugin/unraid/Tools/`, `dirname(__DIR__)` already IS `Plugin/unraid/` — just remove the stale `'/Plugin/unraid'` suffix:


1	<?php
2	// API cache writer — runs every minute via Varaverk scheduler.
3	// Builds monitor + arrs payloads and writes them to /tmp/vv_cache/ so page
4	// loads can serve instantly from the file instead of making live HTTP calls.
5	//
6	// Called by api_cache_writer.sh (bash wrapper required by the scheduler).
7
8	$_base = dirname(__DIR__) . '/Plugin/unraid';
9	require_once $_base . '/include/monitor.php';
10	require_once $_base . '/include/vms.php';
11	require_once $_base . '/include/docker_folders.php';
12	require_once $_base . '/include/arrs.php';
13
14	$t = microtime(true);
15
16	// ── Monitor payload ───────────────────────────────────────────────────────────
17	// Call vv_api_data() once — result is static-cached for the rest of this process.
18	vv_api_data();
19
20	$monitor = [
21	    'system'          => vv_system_info(),
22	    'fallback'        => vv_fallback_state(),
23	    'fallback_active' => vv_fallback_active(),
24	    'partner'         => vv_partner_state(),
25	    'resources'       => vv_system_resources(),
26	    'cpu'             => vv_cpu_per_core(),
27	    'mem'             => vv_memory_breakdown(),
28	    'net'             => vv_network_stats(),
29	    'gpu'             => vv_gpu_stats(),
30	    'gpu_procs'       => vv_gpu_processes(),
31	    'containers'      => vv_docker_containers(),
32	    'stopped'         => vv_docker_stopped(),
33	    'transcode'       => vv_transcode_sessions(),
34	    'ups'             => vv_ups_stats(),
35	    'parity'          => vv_parity_status(),
36	    'storage'         => vv_storage_pools(),
37	    'array_disks'     => vv_array_disks(),
38	    'disk_io'         => vv_disk_io_rates(),
39	    'watchdog'        => vv_watchdog_summary(),
40	    'scripts'         => vv_scripts_status(),
41	    'rsync'           => vv_rsync_status(),
42	    'thresholds'      => vv_disk_thresholds(),
43	    'vms'             => vv_get_vms(),
44	    'docker_folders'  => vv_get_docker_folders(),
45	    'remote_hosts'    => vv_remote_hosts_stats(),
46	    '_api_status'     => vv_api_get_status(),
47	    'ts'              => time(),
48	];
49	vv_cache_write('monitor', $monitor);
50
51	// ── Arrs payload ──────────────────────────────────────────────────────────────
52	$arrs = vv_arrs_all();
53	vv_cache_write('arrs', $arrs);
54
55	$elapsed = round((microtime(true) - $t) * 1000);
56	echo "Cache written in {$elapsed}ms — monitor + arrs\n";
57


Edit

$_base = dirname(__DIR__) . '/Plugin/unraid';


The file /boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.php has been updated successfully. (file state is current in your context — no need to Read it back)

<?php
// API cache writer — runs every minute via Varaverk scheduler.
// Builds monitor + arrs payloads and writes them to /tmp/vv_cache/ so page
// loads can serve instantly from the file instead of making live HTTP calls.
//
// Called by api_cache_writer.sh (bash wrapper required by the scheduler).

$_base = dirname(__DIR__) . '/Plugin/unraid';
require_once $_base . '/include/monitor.php';
require_once $_base . '/include/vms.php';
require_once $_base . '/include/docker_folders.php';
require_once $_base . '/include/arrs.php';

$t = microtime(true);

// ── Monitor payload ───────────────────────────────────────────────────────────
// Call vv_api_data() once — result is static-cached for the rest of this process.
vv_api_data();

$monitor = [
    'system'          => vv_system_info(),
    'fallback'        => vv_fallback_state(),
    'fallback_active' => vv_fallback_active(),
    'partner'         => vv_partner_state(),
    'resources'       => vv_system_resources(),
    'cpu'             => vv_cpu_per_core(),
    'mem'             => vv_memory_breakdown(),
    'net'             => vv_network_stats(),
    'gpu'             => vv_gpu_stats(),
    'gpu_procs'       => vv_gpu_processes(),
    'containers'      => vv_docker_containers(),
    'stopped'         => vv_docker_stopped(),
    'transcode'       => vv_transcode_sessions(),
    'ups'             => vv_ups_stats(),
    'parity'          => vv_parity_status(),
    'storage'         => vv_storage_pools(),
    'array_disks'     => vv_array_disks(),
    'disk_io'         => vv_disk_io_rates(),
    'watchdog'        => vv_watchdog_summary(),
    'scripts'         => vv_scripts_status(),
    'rsync'           => vv_rsync_status(),
    'thresholds'      => vv_disk_thresholds(),
    'vms'             => vv_get_vms(),
    'docker_folders'  => vv_get_docker_folders(),
    'remote_hosts'    => vv_remote_hosts_stats(),
    '_api_status'     => vv_api_get_status(),
    'ts'              => time(),
];
vv_cache_write('monitor', $monitor);

// ── Arrs payload ──────────────────────────────────────────────────────────────
$arrs = vv_arrs_all();
vv_cache_write('arrs', $arrs);

$elapsed = round((microtime(true) - $t) * 1000);
echo "Cache written in {$elapsed}ms — monitor + arrs\n";

-$_base = dirname(__DIR__) . '/Plugin/unraid';


Bash

Test the fixed cache writer


tail: error writing 'standard output': Broken pipe
Cache written in 12183ms — monitor + arrs


ls -la /tmp/vv_cache/monitor.json /tmp/vv_cache/arrs.json

Confirm cache files were just written


-rw-rw-rw- 1 root root  5132 Jun  5 20:41 /tmp/vv_cache/arrs.json
-rw-rw-rw- 1 root root 24003 Jun  5 20:41 /tmp/vv_cache/monitor.json


Both files updated. The `broken pipe` warning is harmless — it's from a `tail` inside one of the library functions writing to a closed pipe, not from the cache writer itself.

Also need to sync the fix to the emhttp copy since that's what the web server serves:


cp /boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.php \
   /usr/local/emhttp/plugins/varaverk/Tools/api_cache_writer.php
echo "synced"

Sync fixed PHP to emhttp serving path


cp: '/boot/config/plugins/varaverk/Plugin/unraid/Tools/api_cache_writer.php' and '/usr/local/emhttp/plugins/varaverk/Tools/api_cache_writer.php' are the same file
synced


They're the same file (hardlinked). Cache writer is fixed — `dirname(__DIR__)` from `Plugin/unraid/Tools/` correctly resolves to `Plugin/unraid/`, the `/Plugin/unraid` suffix was a leftover from when the file lived somewhere else. The cron fires every minute so the monitor page will hit fresh cache on the next load.


ok check all paths in all scripts.. should be no hard codes example /mnt/appdata/varaverk/, when it can be avoided


Agent

Hardcoded path audit across Varaverk scripts

Prompt:
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.


Now let me compile all the findings. Based on my analysis, here are the hardcoded paths that should be using variables:

## AUDIT FINDINGS

**Total hardcoded paths that should use variables: 58**

### File: Plugin/unraid/run_job.sh
- Line 22: `LOG_DIR="/var/log/varaverk"` → should use LOG_DIR constant
- Line 47: `_VV_CFG="/boot/config/plugins/varaverk/varaverk.cfg"` → should use PLUGIN_CFG or derive from SCRIPTS_DIR

### File: Plugin/unraid/api/storage.php
- Line 21: `$internalDir = '/boot/config/plugins/varaverk'` → should use SCRIPTS_DIR constant

### File: Plugin/unraid/api/settings.php
- Line 16: `$cfgFile = '/boot/config/plugins/varaverk/varaverk.cfg'` → should use PLUGIN_CFG constant

### File: Plugin/unraid/api/recent.php
- Line 4: `$logDir = '/var/log/varaverk'` → should use LOG_DIR constant

### File: Plugin/unraid/include/config.php
- Line 5: `define('PLUGIN_CFG', '/boot/config/plugins/varaverk/varaverk.cfg')` → Note: This is the definition, hardcoded fallback
- Line 8: `define('SCRIPTS_DIR', $_vv_cfg['SCRIPTS_DIR'] ?? '/boot/config/plugins/varaverk')` → acceptable (fallback)
- Line 12: `define('LOG_DIR', '/var/log/varaverk')` → acceptable (canonical definition)
- Line 99: `$sshBase . ' "cfg=$(grep SCRIPTS_DIR /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null)` → should use PLUGIN_CFG value

### File: Plugin/unraid/include/common.php
- Line 154: `$stateFile = '/tmp/vv_cpu_stat.json'` → should use VV_CACHE_DIR (or similar VV_* constant)
- Line 295: `$stateFile = '/tmp/vv_net_stat.json'` → should use VV_CACHE_DIR (or similar VV_* constant)
- Line 539: `$snapFile = '/tmp/vv_diskio_snap.json'` → should use VV_CACHE_DIR (or similar VV_* constant)
- Line 634: `$cacheFile = "/tmp/vv_remote_{$id}.json"` → should use VV_CACHE_DIR constant
- Line 730: `$stateDir = rtrim($v['STATE_DIR'] ?? '/boot/config/plugins/varaverk/State_Files', '/')` → fallback acceptable but should be STATE_DIR

### File: Plugin/unraid/include/scheduler.php
- Line 9: `define('CRON_FILE', '/boot/config/plugins/varaverk/varaverk.cron')` → hardcoded in /boot/ (platform-specific, acceptable for Plugin context)

### File: git_pull_execute.sh
- Line 258: `if [[ "$TARGET_DIR" != "/boot/config/plugins/varaverk" ]]` → should use SCRIPTS_DIR
- Line 261: `if rsync -a --delete "$TARGET_DIR/Plugin/" "/boot/config/plugins/varaverk/Plugin/"` → should use SCRIPTS_DIR

### File: Plugin/unraid/System_Essentials/unraid_api_key_renew.sh
- Line 123: `partner_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"` → should use CONF_DIR

### File: Plugin/unraid/Tools/storage_migrate.sh
- Line 50: `VV_CFG="/boot/config/plugins/varaverk/varaverk.cfg"` → should use PLUGIN_CFG
- Line 51: `INTERNAL_DIR="/boot/config/plugins/varaverk"` → should use SCRIPTS_DIR
- Line 254: `define('PLUGIN_CFG', '/boot/config/plugins/varaverk/varaverk.cfg')` → PHP fallback, acceptable
- Line 256: `define('SCRIPTS_DIR', $_c['SCRIPTS_DIR'] ?? '/boot/config/plugins/varaverk')` → fallback acceptable
- Line 260: `define('LOG_DIR', '/var/log/varaverk')` → definition, acceptable
- Line 281: `if rsync -a --delete "$DST/Plugin/" "/boot/config/plugins/varaverk/Plugin/"` → should use SCRIPTS_DIR

### File: Partnership/partnership_transfer.sh
- Line 296: `"grep -m1 '^SCRIPTS_DIR' /boot/config/plugins/varaverk/varaverk.cfg` → should use PLUGIN_CFG path
- Line 298: `_REMOTE_SD="${_REMOTE_SD:-/boot/config/plugins/varaverk}"` → fallback acceptable but should use SCRIPTS_DIR

### File: Partnership/partnership_onboard.sh
- Line 499: `'grep SCRIPTS_DIR /boot/config/plugins/varaverk/varaverk.cfg` → should use PLUGIN_CFG path
- Line 500: `OWNER_SCRIPTS_DIR="${OWNER_SCRIPTS_DIR:-/boot/config/plugins/varaverk}"` → fallback acceptable

### File: System_Essentials/conf_sync.sh
- Line 98: `remote_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"` → should use CONF_DIR

### File: Plugin/unraid/api/system.php
- Line 26: `@file_put_contents('/boot/config/plugins/varaverk/actions.log'` → should use SCRIPTS_DIR

### File: Plugin/unraid/api/setup.php
- Line 51: `$sshBase . ' "grep SCRIPTS_DIR /boot/config/plugins/varaverk/varaverk.cfg` → should use PLUGIN_CFG path
- Line 53: `$remoteConf = rtrim($sm[1] ?? '/boot/config/plugins/varaverk', '/')` → fallback acceptable

### File: Plugin/unraid/Tools/remote_arr_cache_writer.sh
- Line 41: `mkdir -p /tmp/vv_cache` → should use VV_CACHE_DIR
- Line 67: `cache_file="/tmp/vv_cache/arrs_remote_${host_id}.json"` → should use VV_CACHE_DIR
- Line 125: `MONITOR_CACHE="/tmp/vv_cache/monitor_remote_${host_id}.json"` → should use VV_CACHE_DIR

### File: Plugin/unraid/api/manual_sync.php
- Line 139: `foreach (glob('/tmp/vv_ms_*.pid')` → custom temp files, acceptable if working directory is managed
- Line 165: `$logFile = '/tmp/vv_ms_' . $token . '.log'` → custom temp files, acceptable
- Line 166: `$pidFile = '/tmp/vv_ms_' . $token . '.pid'` → custom temp files, acceptable
- Line 197-199, 218-219: More /tmp/vv_ms_* → custom temp files, acceptable

### File: Plugin/unraid/api/api_test.php
- Line 23: `$debugFile = '/tmp/vv_api_debug.json'` → should use VV_CACHE_DIR

### File: Plugin/unraid/include/config.php
- Line 266: `@file_put_contents('/tmp/vv_api_debug.json'` → should use VV_CACHE_DIR
- Line 281: `@file_put_contents('/tmp/vv_api_debug.json'` → should use VV_CACHE_DIR

### File: Plugin/unraid/include/auth.php
- Line 244: `$tmp = '/tmp/vv_auth_rd_' . getmypid() . '.py'` → custom temp files, acceptable
- Line 277: `$tmp = '/tmp/vv_auth_wr_' . getmypid() . '.py'` → custom temp files, acceptable

### File: Deployment/deploy.sh
- Line 17: `PROD_ROOT="/boot/config/plugins/varaverk"` → should use SCRIPTS_DIR detection
- Line 19: `LOG="/tmp/vv_deploy.log"` → should use VV_CACHE_DIR or similar

### File: Plugin/unraid/System_Essentials/unraid_api_key_renew.sh
- Line 124-125: `/tmp/vv_kp_*` → temporary task files, acceptable if isolated to that script

### File: Partnership/partnership_onboard.sh
- Line 507: `/tmp/vv_phase2_onboard.log` → temporary task files, acceptable

### File: Plugin/unraid/api/create_api_key.php
- Line 22: `/tmp/vv_apikey_debug.json` → debug file, should use VV_CACHE_DIR
- Line 25: Comment mentioning `/tmp/vv_apikey_debug.json` (acceptable in comment)

**Summary of actual violations (excluding acceptable fallbacks, definitions, and platform-specific Unraid paths):**

Hardcoded paths in executable code that should use variables: **28 instances** across 15 files requiring fixes.

Prompt:
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.


Good audit. Let me read the specific lines before fixing:


Bash

grep -n "/boot/config/plugins/varaverk\|/tmp/vv_cache\|/var/log/varaverk" \
  /boot/config/plugins/varaverk/Plugin/unraid/api/storage.php \
  /boot/config/plugins/varaverk/Plugin/unraid/api/settings.php \
  /boot/config/plugins/varaverk/Plugin/unraid/api/recent.php \
  /boot/config/plugins/varaverk/Plugin/unraid/api/system.php \
  /boot/config/plugins/varaverk/Plugin/unraid/api/setup.php \
  /boot/config/plugins/varaverk/Plugin/unraid/api/api_test.php

Check hardcoded paths in all API files


/boot/config/plugins/varaverk/Plugin/unraid/api/recent.php:4:$logDir = '/var/log/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/api/system.php:26:@file_put_contents('/boot/config/plugins/varaverk/actions.log', $logLine, FILE_APPEND | LOCK_EX);
/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';


grep -n "/tmp/vv_cache\|/var/log/varaverk\|/boot/config/plugins/varaverk" \
  /boot/config/plugins/varaverk/Plugin/unraid/include/common.php \
  /boot/config/plugins/varaverk/Plugin/unraid/include/config.php \
  /boot/config/plugins/varaverk/Plugin/unraid/api/api_test.php | grep -v "^.*:#"

Check hardcoded paths in include files


/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/common.php:776:    $logFile = '/var/log/varaverk/Orchestrators/transcode_management.log';
/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:12:define('LOG_DIR',     '/var/log/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/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');


grep -n "/tmp/vv_cpu_stat\|/tmp/vv_net_stat\|/tmp/vv_diskio_snap\|/tmp/vv_remote_\|/tmp/vv_api_debug" \
  /boot/config/plugins/varaverk/Plugin/unraid/include/common.php \
  /boot/config/plugins/varaverk/Plugin/unraid/include/config.php \
  /boot/config/plugins/varaverk/Plugin/unraid/api/api_test.php

Check /tmp hardcoded cache file paths


/boot/config/plugins/varaverk/Plugin/unraid/api/api_test.php:23:$debugFile = '/tmp/vv_api_debug.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/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";


grep -n "/tmp/vv_cache\|/boot/config/plugins/varaverk\|/var/log/varaverk" \
  /boot/config/plugins/varaverk/Plugin/unraid/Tools/remote_arr_cache_writer.sh \
  /boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh \
  /boot/config/plugins/varaverk/System_Essentials/conf_sync.sh \
  /boot/config/plugins/varaverk/git_pull_execute.sh \
  /boot/config/plugins/varaverk/Partnership/partnership_transfer.sh \
  /boot/config/plugins/varaverk/Partnership/partnership_onboard.sh

Check hardcoded paths in bash scripts


/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/System_Essentials/conf_sync.sh:98:        remote_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"
/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/System_Essentials/unraid_api_key_renew.sh:123:    partner_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"
/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/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}"


grep -n "/boot/config/plugins/varaverk\|/var/log/varaverk\|/tmp/vv_cache" \
  /boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh \
  /boot/config/plugins/varaverk/Plugin/unraid/run_job.sh

Check storage_migrate.sh and run_job.sh for hardcoded paths


/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/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/Tools/storage_migrate.sh:10:# Internal mode: SCRIPTS_DIR = /boot/config/plugins/varaverk
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:31:#     Migrate to /boot/config/plugins/varaverk
/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:260:        define('LOG_DIR',     '/var/log/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


Good audit. I'll skip acceptable bootstraps (run_job.sh's hardcoded cfg path before SCRIPTS_DIR is known, remote SSH commands reading a remote machine's cfg, storage_migrate.sh's INTERNAL_DIR definition which is intentional). Everything else gets fixed. Let me do it file by file:


1	<?php
2	header('Content-Type: application/json');
3
4	$logDir = '/var/log/varaverk';
5	$runs   = [];
6
7	try {
8	    $ri = new RecursiveIteratorIterator(

<?php
header('Content-Type: application/json');

$logDir = '/var/log/varaverk';
$runs   = [];

try {
    $ri = new RecursiveIteratorIterator(


14	}
15
16	$cfgFile = '/boot/config/plugins/varaverk/varaverk.cfg';
17	$cfgDir  = dirname($cfgFile);
18	if (!is_dir($cfgDir)) mkdir($cfgDir, 0755, true);

}

$cfgFile = '/boot/config/plugins/varaverk/varaverk.cfg';
$cfgDir  = dirname($cfgFile);
if (!is_dir($cfgDir)) mkdir($cfgDir, 0755, true);


24
25	$logLine = date('Y-m-d H:i:s') . " action={$action} ip=" . ($_SERVER['REMOTE_ADDR'] ?? 'unknown') . "\n";
26	@file_put_contents('/boot/config/plugins/varaverk/actions.log', $logLine, FILE_APPEND | LOCK_EX);
27
28	exec($cmd . ' > /dev/null 2>&1 &');

$logLine = date('Y-m-d H:i:s') . " action={$action} ip=" . ($_SERVER['REMOTE_ADDR'] ?? 'unknown') . "\n";
@file_put_contents('/boot/config/plugins/varaverk/actions.log', $logLine, FILE_APPEND | LOCK_EX);

exec($cmd . ' > /dev/null 2>&1 &');


20	];
21
22	// Show last debug log if present
23	$debugFile = '/tmp/vv_api_debug.json';
24	if (file_exists($debugFile)) {
25	    $result['debug_log'] = json_decode(file_get_contents($debugFile), true);

];

// Show last debug log if present
$debugFile = '/tmp/vv_api_debug.json';
if (file_exists($debugFile)) {
    $result['debug_log'] = json_decode(file_get_contents($debugFile), true);


260	        $resp     = @file_get_contents($url, false, $ctx);
261	        $httpCode = $resp !== false ? 200 : 0;
262	        $curlErr  = '';
263	    }
264
265	    if ($resp === false || $resp === '' || ($httpCode !== 0 && $httpCode !== 200)) {
266	        @file_put_contents('/tmp/vv_api_debug.json', json_encode([
267	            'ts'        => time(),
268	            'host'      => $hostId,
269	            'url'       => $url,
270	            'http_code' => $httpCode,
271	            'curl_err'  => $curlErr,
272	            'response'  => substr((string)$resp, 0, 800),
273	        ], JSON_PRETTY_PRINT));
274	        return null;
275	    }
276
277	    $decoded = json_decode((string)$resp, true);
278
279	    // If the API returned GraphQL errors, log them for diagnosis.
280	    if (!empty($decoded['errors'])) {
281	        @file_put_contents('/tmp/vv_api_debug.json', json_encode([
282	            'ts'        => time(),
283	            'host'      => $hostId,
284	            'url'       => $url,
285	            'http_code' => $httpCode,
286	            'errors'    => $decoded['errors'],
287	            'data'      => $decoded['data'] ?? null,
288	        ], JSON_PRETTY_PRINT));
289	    }

        $resp     = @file_get_contents($url, false, $ctx);
        $httpCode = $resp !== false ? 200 : 0;
        $curlErr  = '';
    }

    if ($resp === false || $resp === '' || ($httpCode !== 0 && $httpCode !== 200)) {
        @file_put_contents('/tmp/vv_api_debug.json', json_encode([
            'ts'        => time(),
            'host'      => $hostId,
            'url'       => $url,
            'http_code' => $httpCode,
            'curl_err'  => $curlErr,
            'response'  => substr((string)$resp, 0, 800),
        ], JSON_PRETTY_PRINT));
        return null;
    }

    $decoded = json_decode((string)$resp, true);

    // If the API returned GraphQL errors, log them for diagnosis.
    if (!empty($decoded['errors'])) {
        @file_put_contents('/tmp/vv_api_debug.json', json_encode([
            'ts'        => time(),
            'host'      => $hostId,
            'url'       => $url,
            'http_code' => $httpCode,
            'errors'    => $decoded['errors'],
            'data'      => $decoded['data'] ?? null,
        ], JSON_PRETTY_PRINT));
    }


290
291	    // data key present (even if null means query ran but returned nothing useful).
292	    return array_key_exists('data', $decoded ?? []) ? $decoded['data'] : null;
293	}
294
295	// ── File-based API cache (/tmp/vv_cache — tmpfs, cleared on reboot) ───────────
296
297	define('VV_CACHE_DIR', '/tmp/vv_cache');
298
299	// Read a cached payload. Returns null if missing or older than $maxAge seconds.
300	function vv_cache_read(string $key, int $maxAge = 90): ?array {
301	    $f = VV_CACHE_DIR . '/' . $key . '.json';
302	    if (!file_exists($f) || (time() - filemtime($f)) > $maxAge) return null;
303	    $raw = file_get_contents($f);
304	    return $raw ? (json_decode($raw, true) ?: null) : null;

    // data key present (even if null means query ran but returned nothing useful).
    return array_key_exists('data', $decoded ?? []) ? $decoded['data'] : null;
}

// ── File-based API cache (/tmp/vv_cache — tmpfs, cleared on reboot) ───────────

define('VV_CACHE_DIR', '/tmp/vv_cache');

// Read a cached payload. Returns null if missing or older than $maxAge seconds.
function vv_cache_read(string $key, int $maxAge = 90): ?array {
    $f = VV_CACHE_DIR . '/' . $key . '.json';
    if (!file_exists($f) || (time() - filemtime($f)) > $maxAge) return null;
    $raw = file_get_contents($f);
    return $raw ? (json_decode($raw, true) ?: null) : null;


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';

        if (!preg_match('/^(cpu\d*)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/', $line, $m)) continue;
        $raw[$m[1]] = [(int)$m[2],(int)$m[3],(int)$m[4],(int)$m[5],(int)$m[6],(int)$m[7],(int)$m[8]];
    }

    $stateFile = '/tmp/vv_cpu_stat.json';
    $prev = file_exists($stateFile) ? (json_decode(file_get_contents($stateFile), true) ?: []) : [];
    // Atomic write — concurrent fast/slow polls read a consistent snapshot
    $tmp = $stateFile . '.tmp';


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';

        $txBytes = (int)($parts[8] ?? 0);
        break;
    }

    $stateFile = '/tmp/vv_net_stat.json';
    $now  = ['rx' => $rxBytes, 'tx' => $txBytes, 'ts' => microtime(true)];
    $prev = file_exists($stateFile) ? (json_decode(file_get_contents($stateFile), true) ?: []) : [];
    $tmp = $stateFile . '.tmp';


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

    return array_merge($parity, $data);
}

function vv_disk_io_rates(): array {
    $snapFile = '/tmp/vv_diskio_snap.json';
    $now      = microtime(true);

    // Read current whole-disk stats from /proc/diskstats


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; }

                             'host_id' => $id, 'hostname' => $vars[$id]];
            continue;
        }

        $cacheFile = "/tmp/vv_remote_{$id}.json";
        if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < 30) {
            $cached = json_decode(file_get_contents($cacheFile), true);
            if ($cached) { $results[$id] = $cached; continue; }


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

}

function vv_transcode_sessions(): array {
    $v         = vv_conf_vars();
    $stateDir  = rtrim($v['STATE_DIR'] ?? '/boot/config/plugins/varaverk/State_Files', '/');
    $stateFile = "$stateDir/transcode_state.db";
    if (!file_exists($stateFile)) return ['available' => false];


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) {

    // Last cleanup values from transcode management log
    $lastRdFreed  = null;
    $lastSsdFreed = null;
    $logFile = '/var/log/varaverk/Orchestrators/transcode_management.log';
    if (file_exists($logFile)) {
        $lines = file($logFile, FILE_IGNORE_NEW_LINES) ?: [];
        foreach (array_reverse($lines) as $line) {


Now let me check if VV_CACHE_DIR exists in bash context and where settings.php/recent.php include config.php:


grep -n "VV_CACHE_DIR\|vv_cache" /boot/config/plugins/varaverk/common.sh | head -5
echo "---"
head -5 /boot/config/plugins/varaverk/Plugin/unraid/api/recent.php
head -5 /boot/config/plugins/varaverk/Plugin/unraid/api/settings.php
head -5 /boot/config/plugins/varaverk/Plugin/unraid/api/system.php
head -5 /boot/config/plugins/varaverk/Plugin/unraid/api/api_test.php

Check VV_CACHE_DIR in bash and config.php inclusion in API files


---
<?php
header('Content-Type: application/json');

$logDir = '/var/log/varaverk';
$runs   = [];
<?php
header('Content-Type: application/json');

$scriptsDir = trim($_POST['scripts_dir'] ?? '');

<?php
header('Content-Type: application/json');
require_once dirname(__DIR__) . '/include/config.php';

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
<?php
// Diagnostic endpoint — tests the Unraid GraphQL API and returns raw results.
// Hit from browser: /plugins/varaverk/api/api_test.php
header('Content-Type: application/json');
require_once dirname(__DIR__) . '/include/config.php';


grep -n "require_once\|include.*config" \
  /boot/config/plugins/varaverk/Plugin/unraid/api/recent.php \
  /boot/config/plugins/varaverk/Plugin/unraid/api/settings.php | head -10
echo "---"
grep -n "^VV_CACHE_DIR\|export VV_CACHE_DIR\|VV_CACHE_DIR=" /boot/config/plugins/varaverk/load_config.sh /boot/config/plugins/varaverk/common.sh 2>/dev/null | head -5

Check if recent/settings include config.php, check bash VV_CACHE_DIR


1	<?php
2	header('Content-Type: application/json');
3
4	$scriptsDir = trim($_POST['scripts_dir'] ?? '');
5
6	if (!$scriptsDir) {
7	    echo json_encode(['ok' => false, 'error' => 'scripts_dir is required']);
8	    exit;
9	}
10
11	if (!is_dir($scriptsDir)) {
12	    echo json_encode(['ok' => false, 'error' => 'Directory does not exist']);
13	    exit;
14	}
15
16	$cfgFile = '/boot/config/plugins/varaverk/varaverk.cfg';
17	$cfgDir  = dirname($cfgFile);
18	if (!is_dir($cfgDir)) mkdir($cfgDir, 0755, true);
19
20	$content = 'SCRIPTS_DIR="' . addslashes($scriptsDir) . '"' . "\n";
21	$ok = file_put_contents($cfgFile, $content) !== false;
22
23	echo json_encode(['ok' => $ok, 'error' => $ok ? null : 'Failed to write cfg file']);
24

<?php
header('Content-Type: application/json');

$scriptsDir = trim($_POST['scripts_dir'] ?? '');

if (!$scriptsDir) {
    echo json_encode(['ok' => false, 'error' => 'scripts_dir is required']);
    exit;
}

if (!is_dir($scriptsDir)) {
    echo json_encode(['ok' => false, 'error' => 'Directory does not exist']);
    exit;
}

$cfgFile = '/boot/config/plugins/varaverk/varaverk.cfg';
$cfgDir  = dirname($cfgFile);
if (!is_dir($cfgDir)) mkdir($cfgDir, 0755, true);

$content = 'SCRIPTS_DIR="' . addslashes($scriptsDir) . '"' . "\n";
$ok = file_put_contents($cfgFile, $content) !== false;

echo json_encode(['ok' => $ok, 'error' => $ok ? null : 'Failed to write cfg file']);


1	<?php
2	header('Content-Type: application/json');
3
4	$logDir = '/var/log/varaverk';
5	$runs   = [];
6
7	try {
8	    $ri = new RecursiveIteratorIterator(
9	        new RecursiveDirectoryIterator($logDir, RecursiveDirectoryIterator::SKIP_DOTS)
10	    );
11	    foreach ($ri as $file) {
12	        if ($file->getExtension() !== 'json') continue;
13	        $d = @json_decode(@file_get_contents($file->getPathname()), true);
14	        if (!is_array($d) || empty($d['start']) || empty($d['status'])) continue;
15	        if ($d['status'] === 'running') continue;

<?php
header('Content-Type: application/json');

$logDir = '/var/log/varaverk';
$runs   = [];

try {
    $ri = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($logDir, RecursiveDirectoryIterator::SKIP_DOTS)
    );
    foreach ($ri as $file) {
        if ($file->getExtension() !== 'json') continue;
        $d = @json_decode(@file_get_contents($file->getPathname()), true);
        if (!is_array($d) || empty($d['start']) || empty($d['status'])) continue;
        if ($d['status'] === 'running') continue;


250	    echo "━━━ $ICON_GEAR Permissions ━━━"
251	    log "Setting executable permissions on all .sh files..."
252	    find "$TARGET_DIR" -type f -name "*.sh" -exec chmod +x {} \;
253	    echo "  Permissions set on .sh files"
254
255	    # ── Flash mode: sync Plugin/ to /boot/ so the webUI picks up updates ─────
256	    # In flash mode SCRIPTS_DIR is in appdata — Plugin/ lives in the repo there
257	    # but Unraid serves PHP from /boot/. Sync after every pull to keep them in step.
258	    if [[ "$TARGET_DIR" != "/boot/config/plugins/varaverk" ]]; then
259	        echo ""
260	        echo "━━━ $ICON_SYNC Flash mode: sync Plugin/ → /boot/ ━━━"
261	        if rsync -a --delete "$TARGET_DIR/Plugin/" "/boot/config/plugins/varaverk/Plugin/" 2>/dev/null; then
262	            echo "  Plugin/ synced to /boot/ ✅"
263	        else
264	            warn "Plugin/ sync to /boot/ failed — webUI may be stale until next pull"
265	        fi
266	    fi
267	fi
268
269	END=$(date +%s)

    echo "━━━ $ICON_GEAR Permissions ━━━"
    log "Setting executable permissions on all .sh files..."
    find "$TARGET_DIR" -type f -name "*.sh" -exec chmod +x {} \;
    echo "  Permissions set on .sh files"

    # ── Flash mode: sync Plugin/ to /boot/ so the webUI picks up updates ─────
    # In flash mode SCRIPTS_DIR is in appdata — Plugin/ lives in the repo there
    # but Unraid serves PHP from /boot/. Sync after every pull to keep them in step.
    if [[ "$TARGET_DIR" != "/boot/config/plugins/varaverk" ]]; then
        echo ""
        echo "━━━ $ICON_SYNC Flash mode: sync Plugin/ → /boot/ ━━━"
        if rsync -a --delete "$TARGET_DIR/Plugin/" "/boot/config/plugins/varaverk/Plugin/" 2>/dev/null; then
            echo "  Plugin/ synced to /boot/ ✅"
        else
            warn "Plugin/ sync to /boot/ failed — webUI may be stale until next pull"
        fi
    fi
fi

END=$(date +%s)


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) {

<?php
// Config file parser and writer.
// Reads master.conf and the appropriate host*.conf based on running host.

define('PLUGIN_CFG',  '/boot/config/plugins/varaverk/varaverk.cfg');

$_vv_cfg = @parse_ini_file(PLUGIN_CFG) ?: [];
define('SCRIPTS_DIR', $_vv_cfg['SCRIPTS_DIR'] ?? '/boot/config/plugins/varaverk');
define('CONF_DIR',    SCRIPTS_DIR . '/Configurations');
define('DATA_DIR',    SCRIPTS_DIR . '/data');
define('STATE_DIR',   SCRIPTS_DIR . '/State_Files');
define('LOG_DIR',     '/var/log/varaverk');
unset($_vv_cfg);

define('VV_SETUP_STATE_FILE', STATE_DIR . '/varaverk_setup.db');

// Read the setup state file into a key=>value array.
function vv_setup_state_read(): array {
    $out = [];
    foreach (file(VV_SETUP_STATE_FILE) ?: [] as $line) {


90	    if [[ -z "$partner_ip" ]]; then
91	        warn "$partner_host — cannot resolve Tailscale IP, skipping"
92	        (( FAILED++ ))
93	        continue
94	    fi
95
96	    # ── Pull: grab partner's conf from their disk → our local cache ──────────
97	    if [[ "$PUSH_ONLY" == false ]]; then
98	        remote_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"
99	        if [[ "$DRY_RUN" == true ]]; then
100	            warn "DRY RUN — would pull $partner_host:$remote_conf → $CACHE_DIR/${partner_slot}.conf"
101	        elif timeout "$SSH_TIMEOUT" scp -i "$SSH_KEY" \
102	            -o ConnectTimeout="$SSH_TIMEOUT" -o BatchMode=yes -o StrictHostKeyChecking=no \
103	            "root@${partner_ip}:${remote_conf}" \
104	            "$CACHE_DIR/${partner_slot}.conf" 2>/dev/null; then
105	            log "Pulled ${partner_slot}.conf from $partner_host ✅"
106	            (( PULLED++ ))
107	        else
108	            warn "Could not pull ${partner_slot}.conf from $partner_host"
109	            (( FAILED++ ))

    if [[ -z "$partner_ip" ]]; then
        warn "$partner_host — cannot resolve Tailscale IP, skipping"
        (( FAILED++ ))
        continue
    fi

    # ── Pull: grab partner's conf from their disk → our local cache ──────────
    if [[ "$PUSH_ONLY" == false ]]; then
        remote_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"
        if [[ "$DRY_RUN" == true ]]; then
            warn "DRY RUN — would pull $partner_host:$remote_conf → $CACHE_DIR/${partner_slot}.conf"
        elif timeout "$SSH_TIMEOUT" scp -i "$SSH_KEY" \
            -o ConnectTimeout="$SSH_TIMEOUT" -o BatchMode=yes -o StrictHostKeyChecking=no \
            "root@${partner_ip}:${remote_conf}" \
            "$CACHE_DIR/${partner_slot}.conf" 2>/dev/null; then
            log "Pulled ${partner_slot}.conf from $partner_host ✅"
            (( PULLED++ ))
        else
            warn "Could not pull ${partner_slot}.conf from $partner_host"
            (( FAILED++ ))


118	    partner_slot="${host_var,,}"   # e.g. host2
119	    partner_ip=$(resolve_tailscale_ip "$partner_host" 2>/dev/null || true)
120	    [[ -z "$partner_ip" ]] && { log "Cannot resolve IP for $partner_host — skipping"; continue; }
121
122	    # Target is the partner's OWN conf on their machine
123	    partner_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"
124	    tmp=$(mktemp /tmp/vv_kp_XXXXXX.sh)
125	    remote="/tmp/vv_kp_${RANDOM}.sh"
126	    chmod 700 "$tmp"
127
128	    # Key stays in the temp file — never appears in SSH command args
129	    cat > "$tmp" <<PUSHSCRIPT
130	#!/bin/sh
131	target='${partner_conf}'
132	if grep -q "\b${VAR_NAME}\b" "\$target" 2>/dev/null; then

    partner_slot="${host_var,,}"   # e.g. host2
    partner_ip=$(resolve_tailscale_ip "$partner_host" 2>/dev/null || true)
    [[ -z "$partner_ip" ]] && { log "Cannot resolve IP for $partner_host — skipping"; continue; }

    # Target is the partner's OWN conf on their machine
    partner_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"
    tmp=$(mktemp /tmp/vv_kp_XXXXXX.sh)
    remote="/tmp/vv_kp_${RANDOM}.sh"
    chmod 700 "$tmp"

    # Key stays in the temp file — never appears in SSH command args
    cat > "$tmp" <<PUSHSCRIPT
#!/bin/sh
target='${partner_conf}'
if grep -q "\b${VAR_NAME}\b" "\$target" 2>/dev/null; then


Good. Now I have the full picture. Fixes to make:
- PHP: recent.php, settings.php, system.php, api_test.php, config.php (move VV_CACHE_DIR earlier, fix debug path), common.php (6 paths)
- Bash: add VV_CACHE_DIR to common.sh, fix remote_arr_cache_writer.sh, storage_migrate.sh:281, git_pull_execute.sh
- Skip conf_sync.sh:98 and unraid_api_key_renew.sh:123 — those are remote machine paths accessed via SCP, not local paths

Start with PHP files in parallel:


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.

unset($_vv_cfg);

define('VV_SETUP_STATE_FILE', STATE_DIR . '/varaverk_setup.db');

// Read the setup state file into a key=>value array.


Edit

define('VV_SETUP_STATE_FILE', STATE_DIR . '/varaverk_setup.db');

define('VV_SETUP_STATE_FILE', STATE_DIR . '/varaverk_setup.db');
define('VV_CACHE_DIR',        '/tmp/vv_cache');


The file /boot/config/plugins/varaverk/Plugin/unraid/include/config.php has been updated successfully. (file state is current in your context — no need to Read it back)

define('VV_SETUP_STATE_FILE', STATE_DIR . '/varaverk_setup.db');
define('VV_CACHE_DIR',        '/tmp/vv_cache');

+define('VV_CACHE_DIR',        '/tmp/vv_cache');

// Read the setup state file into a key=>value array.


// ── File-based API cache (/tmp/vv_cache — tmpfs, cleared on reboot) ───────────

define('VV_CACHE_DIR', '/tmp/vv_cache');

// Read a cached payload.

// ── File-based API cache (/tmp/vv_cache — tmpfs, cleared on reboot) ───────────

// Read a cached payload.


// ── File-based API cache (/tmp/vv_cache — tmpfs, cleared on reboot) ───────────

define('VV_CACHE_DIR', '/tmp/vv_cache');

// Read a cached payload.

// ── File-based API cache (/tmp/vv_cache — tmpfs, cleared on reboot) ───────────

// Read a cached payload.

// Read a cached payload. Returns null if missing or older than $maxAge seconds.

function vv_cache_read(string $key, int $maxAge = 90): ?array {

$f = VV_CACHE_DIR . '/' . $key . '.json';


The file /boot/config/plugins/varaverk/Plugin/unraid/include/config.php has been updated. All occurrences were successfully replaced. (file state is current in your context — no need to Read it back)

-        @file_put_contents('/tmp/vv_api_debug.json', json_encode([

+        @file_put_contents(VV_CACHE_DIR . '/vv_api_debug.json', json_encode([

// If the API returned GraphQL errors, log them for diagnosis.


Now fix common.php — 6 paths:


$stateFile = VV_CACHE_DIR . '/vv_cpu_stat.json';


The file /boot/config/plugins/varaverk/Plugin/unraid/include/common.php has been updated successfully. (file state is current in your context — no need to Read it back)

-    $stateFile = '/tmp/vv_cpu_stat.json';

+    $stateFile = VV_CACHE_DIR . '/vv_cpu_stat.json';

$prev = file_exists($stateFile) ? (json_decode(file_get_contents($stateFile), true) ?: []) : [];

// Atomic write — concurrent fast/slow polls read a consistent snapshot


$stateFile = VV_CACHE_DIR . '/vv_net_stat.json';


-    $stateFile = '/tmp/vv_net_stat.json';

+    $stateFile = VV_CACHE_DIR . '/vv_net_stat.json';

$now  = ['rx' => $rxBytes, 'tx' => $txBytes, 'ts' => microtime(true)];


$snapFile = VV_CACHE_DIR . '/vv_diskio_snap.json';


-    $snapFile = '/tmp/vv_diskio_snap.json';

+    $snapFile = VV_CACHE_DIR . '/vv_diskio_snap.json';

// Read current whole-disk stats from /proc/diskstats


$cacheFile = VV_CACHE_DIR . "/vv_remote_{$id}.json";


-        $cacheFile = "/tmp/vv_remote_{$id}.json";

+        $cacheFile = VV_CACHE_DIR . "/vv_remote_{$id}.json";

if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < 30) {

$cached = json_decode(file_get_contents($cacheFile), true);

if ($cached) { $results[$id] = $cached; continue; }


$stateDir  = rtrim($v['STATE_DIR'] ?? '/boot/config/plugins/varaverk/State_Files', '/');

$stateDir  = rtrim($v['STATE_DIR'] ?? STATE_DIR, '/');


function vv_transcode_sessions(): array {

-    $stateDir  = rtrim($v['STATE_DIR'] ?? '/boot/config/plugins/varaverk/State_Files', '/');

+    $stateDir  = rtrim($v['STATE_DIR'] ?? STATE_DIR, '/');

if (!file_exists($stateFile)) return ['available' => false];


$logFile = LOG_DIR . '/Orchestrators/transcode_management.log';


// Last cleanup values from transcode management log

-    $logFile = '/var/log/varaverk/Orchestrators/transcode_management.log';

+    $logFile = LOG_DIR . '/Orchestrators/transcode_management.log';

$lines = file($logFile, FILE_IGNORE_NEW_LINES) ?: [];

foreach (array_reverse($lines) as $line) {


Now fix the three API files:


<?php
header('Content-Type: application/json');

$logDir = '/var/log/varaverk';

<?php
header('Content-Type: application/json');
require_once dirname(__DIR__) . '/include/config.php';

$logDir = LOG_DIR;


The file /boot/config/plugins/varaverk/Plugin/unraid/api/recent.php has been updated successfully. (file state is current in your context — no need to Read it back)

<?php
header('Content-Type: application/json');
require_once dirname(__DIR__) . '/include/config.php';

$logDir = LOG_DIR;

<?php
header('Content-Type: application/json');

$logDir = '/var/log/varaverk';
$runs   = [];

try {
    $ri = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($logDir, RecursiveDirectoryIterator::SKIP_DOTS)
    );
    foreach ($ri as $file) {
        if ($file->getExtension() !== 'json') continue;
        $d = @json_decode(@file_get_contents($file->getPathname()), true);
        if (!is_array($d) || empty($d['start']) || empty($d['status'])) continue;
        if ($d['status'] === 'running') continue;
        $id = (string)($d['id'] ?? '');
        $runs[] = [
            'id'     => $id,
            'label'  => basename(str_replace('.sh', '', $id)),
            'status' => $d['status'],
            'start'  => (int)$d['start'],
            'dur'    => isset($d['end']) ? max(0, (int)$d['end'] - (int)$d['start']) : 0,
        ];
    }
} catch (Exception $e) {}

usort($runs, fn($a, $b) => $b['start'] - $a['start']);
echo json_encode(['ok' => true, 'runs' => array_slice($runs, 0, 24)]);

+require_once dirname(__DIR__) . '/include/config.php';


The file /boot/config/plugins/varaverk/Plugin/unraid/api/settings.php has been updated successfully. (file state is current in your context — no need to Read it back)

<?php
header('Content-Type: application/json');

$scriptsDir = trim($_POST['scripts_dir'] ?? '');

if (!$scriptsDir) {
    echo json_encode(['ok' => false, 'error' => 'scripts_dir is required']);
    exit;
}

if (!is_dir($scriptsDir)) {
    echo json_encode(['ok' => false, 'error' => 'Directory does not exist']);
    exit;
}

$cfgFile = '/boot/config/plugins/varaverk/varaverk.cfg';
$cfgDir  = dirname($cfgFile);
if (!is_dir($cfgDir)) mkdir($cfgDir, 0755, true);

$content = 'SCRIPTS_DIR="' . addslashes($scriptsDir) . '"' . "\n";
$ok = file_put_contents($cfgFile, $content) !== false;

echo json_encode(['ok' => $ok, 'error' => $ok ? null : 'Failed to write cfg file']);


@file_put_contents('/boot/config/plugins/varaverk/actions.log', $logLine, FILE_APPEND | LOCK_EX);

@file_put_contents(SCRIPTS_DIR . '/actions.log', $logLine, FILE_APPEND | LOCK_EX);


The file /boot/config/plugins/varaverk/Plugin/unraid/api/system.php has been updated successfully. (file state is current in your context — no need to Read it back)

<?php
header('Content-Type: application/json');
require_once dirname(__DIR__) . '/include/config.php';

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    echo json_encode(['ok' => false, 'error' => 'POST only']);
    exit;
}

$body  = json_decode(file_get_contents('php://input'), true) ?? [];
$action = $body['action'] ?? '';

$allowed = ['stop', 'shutdown', 'restart'];
if (!in_array($action, $allowed, true)) {
    echo json_encode(['ok' => false, 'error' => 'invalid action']);
    exit;
}

$cmd = match($action) {
    'stop'     => '/usr/local/sbin/mdcmd stop',
    'shutdown' => '/sbin/shutdown -h now',
    'restart'  => '/sbin/shutdown -r now',
};

$logLine = date('Y-m-d H:i:s') . " action={$action} ip=" . ($_SERVER['REMOTE_ADDR'] ?? 'unknown') . "\n";
@file_put_contents('/boot/config/plugins/varaverk/actions.log', $logLine, FILE_APPEND | LOCK_EX);

exec($cmd . ' > /dev/null 2>&1 &');
echo json_encode(['ok' => true]);

-@file_put_contents('/boot/config/plugins/varaverk/actions.log', $logLine, FILE_APPEND | LOCK_EX);

+@file_put_contents(SCRIPTS_DIR . '/actions.log', $logLine, FILE_APPEND | LOCK_EX);


$debugFile = VV_CACHE_DIR . '/vv_api_debug.json';


The file /boot/config/plugins/varaverk/Plugin/unraid/api/api_test.php has been updated successfully. (file state is current in your context — no need to Read it back)

<?php
// Diagnostic endpoint — tests the Unraid GraphQL API and returns raw results.
// Hit from browser: /plugins/varaverk/api/api_test.php
header('Content-Type: application/json');
require_once dirname(__DIR__) . '/include/config.php';

$hostId = vv_detect_host();
$vars   = vv_conf_vars();
$key    = $vars[strtoupper($hostId) . '_UNRAID_API_KEY'] ?? '';

$result = [
    'host'        => $hostId,
    'key_present' => $key !== '',
    'key_prefix'  => $key ? substr($key, 0, 8) . '...' : null,
    'curl_available' => function_exists('curl_init'),
    'allow_url_fopen'=> (bool)ini_get('allow_url_fopen'),
    'debug_log'   => null,
    'probe'       => null,
    'probe_raw'   => null,
];

// Show last debug log if present
$debugFile = '/tmp/vv_api_debug.json';
if (file_exists($debugFile)) {
    $result['debug_log'] = json_decode(file_get_contents($debugFile), true);
}

// Run a minimal probe query
if ($key) {
    $url  = 'http://localhost/graphql';
    $body = json_encode(['query' => '{ info { os { hostname } } }']);

    if (function_exists('curl_init')) {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_POST           => true,
            CURLOPT_HTTPHEADER     => ['Content-Type: application/json', "x-api-key: {$key}"],
            CURLOPT_POSTFIELDS     => $body,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => 5,
            CURLOPT_CONNECTTIMEOUT => 3,
        ]);
        $raw      = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlErr  = curl_error($ch);
        curl_close($ch);
    } else {
        $ctx = stream_context_create(['http' => [
            'method'        => 'POST',
            'header'        => "Content-Type: application/json\r\nx-api-key: {$key}",
            'content'       => $body,
            'timeout'       => 5,
            'ignore_errors' => true,
        ]]);
        $raw      = @file_get_contents($url, false, $ctx);
        $httpCode = $raw !== false ? 200 : 0;
        $curlErr  = '';
    }

    $result['probe'] = [
        'url'       => $url,
        'http_code' => $httpCode,
        'curl_err'  => $curlErr ?: null,
        'decoded'   => json_decode((string)$raw, true),
    ];
    $result['probe_raw'] = substr((string)$raw, 0, 1000);

// ── Schema introspection — discover actual field names ────────────────────────
if ($key) {
    // ArrayParity and ArrayCache were separate named types in 7.2.5 — removed in 7.3 (now same as ArrayDisk)
    $types = ['InfoOs','InfoCpu','InfoMemory','ArrayDisk','VmDomain'];
    $introspectGql = '{ ' . implode(' ', array_map(fn($t) =>
        "{$t}: __type(name: \"{$t}\") { fields { name type { name kind ofType { name kind } } } }",
        $types
    )) . ' }';

    $body2 = json_encode(['query' => $introspectGql]);
    $ch2 = curl_init('http://localhost/graphql');
    curl_setopt_array($ch2, [
        CURLOPT_POST           => true,
        CURLOPT_HTTPHEADER     => ['Content-Type: application/json', "x-api-key: {$key}"],
        CURLOPT_POSTFIELDS     => $body2,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 8,
    ]);
    $raw2 = curl_exec($ch2);
    curl_close($ch2);
    $result['schema'] = json_decode((string)$raw2, true)['data'] ?? null;

    // Pool drive names — what does the API actually return for cache/pool drives?
    $ch_pools = curl_init('http://localhost/graphql');
    curl_setopt_array($ch_pools, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => ['Content-Type: application/json', "x-api-key: {$key}"],
        CURLOPT_POSTFIELDS => json_encode(['query' => '{ array { caches { name device type status fsType } } }']),
        CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 8,
    ]);
    $result['pool_drives'] = json_decode((string)curl_exec($ch_pools), true)['data'] ?? null;
    curl_close($ch_pools);

    // Round 2: introspect CpuUtilization and MemoryUtilization field names
    $types2 = ['CpuUtilization','MemoryUtilization','TemperatureMetrics'];
    $gql2 = '{ ' . implode(' ', array_map(fn($t) =>
        "{$t}: __type(name: \"{$t}\") { kind fields { name type { name kind ofType { name kind } } } }",
        $types2
    )) . ' }';
    $ch3 = curl_init('http://localhost/graphql');
    curl_setopt_array($ch3, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => ['Content-Type: application/json', "x-api-key: {$key}"],
        CURLOPT_POSTFIELDS => json_encode(['query' => $gql2]),
        CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 8,
    ]);
    $result['schema2'] = json_decode((string)curl_exec($ch3), true)['data'] ?? null;
    curl_close($ch3);
}
}

echo json_encode($result, JSON_PRETTY_PRINT);

+$debugFile = VV_CACHE_DIR . '/vv_api_debug.json';


settings.php needs config.php included first (PLUGIN_CFG is now referenced before it was loaded):


1	<?php
2	header('Content-Type: application/json');
3
4	$scriptsDir = trim($_POST['scripts_dir'] ?? '');
5
6	if (!$scriptsDir) {
7	    echo json_encode(['ok' => false, 'error' => 'scripts_dir is required']);
8	    exit;

<?php
header('Content-Type: application/json');

$scriptsDir = trim($_POST['scripts_dir'] ?? '');

if (!$scriptsDir) {
    echo json_encode(['ok' => false, 'error' => 'scripts_dir is required']);
    exit;


<?php
header('Content-Type: application/json');

$scriptsDir = trim($_POST['scripts_dir'] ?? '');

<?php
header('Content-Type: application/json');
require_once dirname(__DIR__) . '/include/config.php';

$scriptsDir = trim($_POST['scripts_dir'] ?? '');