Edit

function vv_auto_create_api_key(string $hostId, string $confFile): array {
    $varName = strtoupper($hostId) . '_UNRAID_API_KEY';
    $hostname = trim((string)shell_exec("hostname -s 2>/dev/null | sed 's/^[Uu][Nn][Rr][Aa][Ii][Dd]-//'")) ?: 'Varaverk';
    $keyName  = 'Varaverk ' . $hostname;
    $output   = shell_exec('timeout 10 /usr/local/sbin/unraid-api apikey --name ' . escapeshellarg($keyName) . ' --create --overwrite --description "Varaverk plugin" --roles ADMIN --json </dev/null 2>&1');
    if (!$output) {
        return ['ok' => false, 'error' => 'unraid-api returned no output'];
    }
    $data = json_decode(trim($output), true);
    $key  = $data['key'] ?? null;
    if (!$key) {
        return ['ok' => false, 'error' => 'No key in response'];
    }
    $raw = vv_read_conf_raw($confFile);
    if ($raw === '') {
        return ['ok' => false, 'error' => 'Cannot read ' . $confFile];
    }
    if (!str_contains($raw, $varName)) {
        foreach ([strtoupper($hostId) . '_OWNER_EMAIL', strtoupper($hostId) . '_SSH_KEY'] as $anchor) {
            if (str_contains($raw, $anchor)) {
                $raw = preg_replace('/^(\s*' . preg_quote($anchor, '/') . '\s*=.*$)/m',
                    '$1' . "\n    " . $varName . '=""', $raw, 1);
                break;
            }
        }
    }
    $raw = preg_replace('/^(\s*' . preg_quote($varName, '/') . '\s*=\s*)"[^"]*"/m',
        '${1}"' . $key . '"', $raw);
    vv_write_conf_raw($confFile, $raw);
    return ['ok' => true, 'key_preview' => substr($key, 0, 8) . '...' . substr($key, -4)];
}

function vv_auto_create_api_key(string $hostId, string $confFile): array {
    $script = SCRIPTS_DIR . '/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh';
    if (!file_exists($script)) {
        return ['ok' => false, 'error' => 'unraid_api_key_renew.sh not found'];
    }
    exec('bash ' . escapeshellarg($script) . ' 2>&1', $out, $rc);
    if ($rc !== 0) {
        $msg = implode(' ', array_filter(array_map('trim', $out)));
        return ['ok' => false, 'error' => $msg ?: 'Script failed'];
    }
    $varName = strtoupper($hostId) . '_UNRAID_API_KEY';
    $raw     = vv_read_conf_raw($confFile);
    preg_match('/^\s*' . preg_quote($varName, '/') . '\s*=\s*"([^"]+)"/m', $raw, $m);
    $key = $m[1] ?? '';
    return ['ok' => true, 'key_preview' => $key ? substr($key, 0, 8) . '...' . substr($key, -4) : 'registered'];
}


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)

function vv_auto_create_api_key(string $hostId, string $confFile): array {
    $varName = strtoupper($hostId) . '_UNRAID_API_KEY';
    $hostname = trim((string)shell_exec("hostname -s 2>/dev/null | sed 's/^[Uu][Nn][Rr][Aa][Ii][Dd]-//'")) ?: 'Varaverk';
    $keyName  = 'Varaverk ' . $hostname;
    $output   = shell_exec('timeout 10 /usr/local/sbin/unraid-api apikey --name ' . escapeshellarg($keyName) . ' --create --overwrite --description "Varaverk plugin" --roles ADMIN --json </dev/null 2>&1');
    if (!$output) {
        return ['ok' => false, 'error' => 'unraid-api returned no output'];
    }
    $data = json_decode(trim($output), true);
    $key  = $data['key'] ?? null;
    if (!$key) {
        return ['ok' => false, 'error' => 'No key in response'];
    }
    $raw = vv_read_conf_raw($confFile);
    if ($raw === '') {
        return ['ok' => false, 'error' => 'Cannot read ' . $confFile];
    }
    if (!str_contains($raw, $varName)) {
        foreach ([strtoupper($hostId) . '_OWNER_EMAIL', strtoupper($hostId) . '_SSH_KEY'] as $anchor) {
            if (str_contains($raw, $anchor)) {
                $raw = preg_replace('/^(\s*' . preg_quote($anchor, '/') . '\s*=.*$)/m',
                    '$1' . "\n    " . $varName . '=""', $raw, 1);
                break;
            }
        }
    }
    $raw = preg_replace('/^(\s*' . preg_quote($varName, '/') . '\s*=\s*)"[^"]*"/m',
        '${1}"' . $key . '"', $raw);
    vv_write_conf_raw($confFile, $raw);
    return ['ok' => true, 'key_preview' => substr($key, 0, 8) . '...' . substr($key, -4)];
}

function vv_auto_create_api_key(string $hostId, string $confFile): array {
    $script = SCRIPTS_DIR . '/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh';
    if (!file_exists($script)) {
        return ['ok' => false, 'error' => 'unraid_api_key_renew.sh not found'];
    }
    exec('bash ' . escapeshellarg($script) . ' 2>&1', $out, $rc);
    if ($rc !== 0) {
        $msg = implode(' ', array_filter(array_map('trim', $out)));
        return ['ok' => false, 'error' => $msg ?: 'Script failed'];
    }
    $varName = strtoupper($hostId) . '_UNRAID_API_KEY';
    $raw     = vv_read_conf_raw($confFile);
    preg_match('/^\s*' . preg_quote($varName, '/') . '\s*=\s*"([^"]+)"/m', $raw, $m);
    $key = $m[1] ?? '';
    return ['ok' => true, 'key_preview' => $key ? substr($key, 0, 8) . '...' . substr($key, -4) : 'registered'];
}

// Create (or overwrite) the Varaverk Unraid API key and write it into host conf.

// Returns ['ok'=>true,'key_preview'=>'...'] or ['ok'=>false,'error'=>'...'].

-    $varName = strtoupper($hostId) . '_UNRAID_API_KEY';

-    $hostname = trim((string)shell_exec("hostname -s 2>/dev/null | sed 's/^[Uu][Nn][Rr][Aa][Ii][Dd]-//'")) ?: 'Varaverk';

-    $keyName  = 'Varaverk ' . $hostname;

-    $output   = shell_exec('timeout 10 /usr/local/sbin/unraid-api apikey --name ' . escapeshellarg($keyName) . ' --create --overwrite --description "Varaverk plugin" --roles ADMIN --json </dev/null 2>&1');

-        return ['ok' => false, 'error' => 'unraid-api returned no output'];

+    $script = SCRIPTS_DIR . '/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh';

+        return ['ok' => false, 'error' => 'unraid_api_key_renew.sh not found'];

-    $data = json_decode(trim($output), true);

-        return ['ok' => false, 'error' => 'No key in response'];

+    exec('bash ' . escapeshellarg($script) . ' 2>&1', $out, $rc);

+        $msg = implode(' ', array_filter(array_map('trim', $out)));

+        return ['ok' => false, 'error' => $msg ?: 'Script failed'];

-    $raw = vv_read_conf_raw($confFile);

-        return ['ok' => false, 'error' => 'Cannot read ' . $confFile];

-    if (!str_contains($raw, $varName)) {

-        foreach ([strtoupper($hostId) . '_OWNER_EMAIL', strtoupper($hostId) . '_SSH_KEY'] as $anchor) {

-            if (str_contains($raw, $anchor)) {

-    vv_write_conf_raw($confFile, $raw);

-    return ['ok' => true, 'key_preview' => substr($key, 0, 8) . '...' . substr($key, -4)];

+    $varName = strtoupper($hostId) . '_UNRAID_API_KEY';

+    $raw     = vv_read_conf_raw($confFile);

+    return ['ok' => true, 'key_preview' => $key ? substr($key, 0, 8) . '...' . substr($key, -4) : 'registered'];

// Local LAN IP via routing table — static-cached per request.


Bash

git add Plugin/unraid/include/config.php && git commit -m "API key button delegates to unraid_api_key_renew.sh — checks registry before creating"

Commit config.php change


[main a535319] API key button delegates to unraid_api_key_renew.sh — checks registry before creating
 1 file changed, 12 insertions(+), 25 deletions(-)


Push to remote


remote: . Processing 1 references
remote: Processed 1 references in total
To ssh://192.168.50.2:221/FailedProxy/Varaverk.git
   bd73e87..a535319  main -> main


Done. Now "Create API Key" button and the setup wizard both run `unraid_api_key_renew.sh` — same code path as the cron. If the key is already valid in the registry, it syncs it to conf and returns without generating a new one. Only creates fresh if the registry has lost it.


Two fixes this session: conf_sync failures during HOST2 rebuild are silenced, and the API key button now runs the same script as the cron. No pending tasks.


unRAID-Gmer4Lfe
HOST1
USOWNER
✓ Local setup complete · waiting for partner
⚡ No API key — Monitor stats unavailable


Let me find where that "No API key — Monitor stats unavailable" message comes from:


grep -rn "No API key\|Monitor stats unavailable\|USOWNER\|waiting for partner" /boot/config/plugins/varaverk/Plugin/unraid/ 2>/dev/null

Find the source of the No API key message


/boot/config/plugins/varaverk/Plugin/unraid/pages/partnership.php:813:        ? `<div style="font-size:10px;color:#4caf50;margin-bottom:6px;padding:2px 6px;background:#0a1a0a;border:1px solid #2a5a2a;border-radius:3px;">✓ Local setup complete · waiting for partner</div>`
/boot/config/plugins/varaverk/Plugin/unraid/pages/partnership.php:831:             <span>⚡ No API key — Monitor stats unavailable</span>


unRAID-Gmer4Lfe HOST1 USOWNER ✓ Local setup complete · waiting for partner ⚡ No API key — Monitor stats unavailable


800	  const fbCol  = fbColors[fb] || '#444';
801	  const fbLbl  = fbLabels[fb] || fb;
802
803	  // Partnership DB
804	  const pt      = node.partnership || {};
805	  const ptState = (pt.state || '').toUpperCase();
806	  const [ptCol, ptLbl] = ptStates[ptState] || ['#444', ptState || '—'];
807	  const ptUpdated = pt.updated ? _relTime(parseInt(pt.updated)) : null;
808
809	  // Onboard phase badge (remote nodes) or local-done badge (self)
810	  const phase = node.onboard_phase;
811	  const phaseHtml = node.is_me
812	    ? (node.local_done
813	        ? `<div style="font-size:10px;color:#4caf50;margin-bottom:6px;padding:2px 6px;background:#0a1a0a;border:1px solid #2a5a2a;border-radius:3px;">✓ Local setup complete · waiting for partner</div>`
814	        : '')
815	    : (phase === 2 ? '' :
816	       phase === 1 ? `<div style="font-size:10px;color:#ff9800;margin-bottom:6px;padding:2px 6px;background:#1a1200;border:1px solid #3a2800;border-radius:3px;">⏳ SSH ready · awaiting onboard</div>` :
817	       node.key_ready ? `<div style="font-size:10px;color:#ff9800;margin-bottom:6px;padding:2px 6px;background:#1a1200;border:1px solid #3a2800;border-radius:3px;">🔑 Key generated · install on HOST2 then Push Conf</div>` :
818	                        `<div style="font-size:10px;color:#555;margin-bottom:6px;padding:2px 6px;background:#111;border:1px solid #222;border-radius:3px;">○ Not yet provisioned</div>`);
819
820	  const apiKeyHtml = node.is_me
821	    ? (node.api_key_set
822	        ? `

824	             🔑 API key: ${node.api_key_preview}
825	             <button onclick="vvApiKey(this)"
826	               style="font-size:9px;padding:1px 6px;background:#0d0d0d;color:#444;border:1px solid #222;
827	                      border-radius:2px;cursor:pointer;margin-left:6px;white-space:nowrap;">↻ renew</button>
828
`
829	        : `

831	             ⚡ No API key — Monitor stats unavailable
832	             <button onclick="vvApiKey(this)"
833	               style="font-size:9px;padding:1px 8px;background:#1a3a1a;color:#6fcf97;border:1px solid #2e6b2e;
834	                      border-radius:2px;cursor:pointer;margin-left:6px;white-space:nowrap;">+ Create</button>
835
`)
836	    : '';
837
838	  // ── Metrics strip ────────────────────────────────────────────────────────────
839	  const m = node.metrics || {};
840	  function _ptCol(v, warn, crit) {
841	    return v >= crit ? '#f44336' : v >= warn ? '#ff9800' : '#4caf50';
842	  }
843	  const metricPairs = [];
844	  if (m.cpu_pct     != null) metricPairs.push(['CPU',   `<span style="color:${_ptCol(m.cpu_pct,70,90)};">${m.cpu_pct}%</span>`]);
845	  if (m.ram_used_gb != null) {
846	    const ramPct = m.ram_total_gb ? Math.round(m.ram_used_gb / m.ram_total_gb * 100) : 0;
847	    metricPairs.push(['RAM', `<span style="color:${_ptCol(ramPct,75,90)};">${m.ram_used_gb}<span style="color:#444;font-size:9px;">/${m.ram_total_gb}GB</span></span>`]);
848	  }
849	  if (m.load_avg    != null) metricPairs.push(['Load',  `<span style="color:${m.load_avg>8?'#f44336':m.load_avg>4?'#ff9800':'#4caf50'};">${m.load_avg}</span>`]);
850	  if (m.containers  != null) metricPairs.push(['Conts', `<span style="color:#bbb;">${m.containers}</span>`]);
851	  if (m.array_used_tb != null) metricPairs.push(['Array', `<span style="color:#bbb;">${m.array_used_tb}<span style="color:#444;font-size:9px;">/${m.array_total_tb}TB</span></span>`]);
852	  if (m.max_disk_temp != null) metricPairs.push(['Temp', `<span style="color:${_ptCol(m.max_disk_temp,55,70)};">${m.max_disk_temp}°C</span>`]);
853	  if (m.vm_count      != null && m.vm_count > 0) metricPairs.push(['VMs', `<span style="color:#bbb;">${m.vm_count}</span>`]);
854
855	  let metricsHtml = '';
856	  if (metricPairs.length) {
857	    let cells = '';
858	    for (const [lbl, val] of metricPairs) {
859	      cells += `<span style="color:#555;">${lbl}</span>${val}`;
860	    }
861	    if (metricPairs.length % 2 !== 0) cells += '';
862	    metricsHtml = `<div style="display:grid;grid-template-columns:auto 1fr auto 1fr;gap:2px 8px;font-size:10px;margin-bottom:6px;">${cells}</div>`;
863	  }
864
865	  let body = '';
866
867	  body += phaseHtml;
868	  body += apiKeyHtml;
869	  body += metricsHtml;
870
871	  // Network
872	  body += `

873
874	    ${dotTip}`;
875	  if (node.ts_ip) body += `<span style="font-size:10px;color:#444;margin-left:4px;">${node.ts_ip}</span>`;
876	  body += `</div>`;
877
878	  // System
879	  body += _row('unRAID', ver);

const fbCol  = fbColors[fb] || '#444';
  const fbLbl  = fbLabels[fb] || fb;

  // Partnership DB
  const pt      = node.partnership || {};
  const ptState = (pt.state || '').toUpperCase();
  const [ptCol, ptLbl] = ptStates[ptState] || ['#444', ptState || '—'];
  const ptUpdated = pt.updated ? _relTime(parseInt(pt.updated)) : null;

  // Onboard phase badge (remote nodes) or local-done badge (self)
  const phase = node.onboard_phase;
  const phaseHtml = node.is_me
    ? (node.local_done
        ? `<div style="font-size:10px;color:#4caf50;margin-bottom:6px;padding:2px 6px;background:#0a1a0a;border:1px solid #2a5a2a;border-radius:3px;">✓ Local setup complete · waiting for partner</div>`
        : '')
    : (phase === 2 ? '' :
       phase === 1 ? `<div style="font-size:10px;color:#ff9800;margin-bottom:6px;padding:2px 6px;background:#1a1200;border:1px solid #3a2800;border-radius:3px;">⏳ SSH ready · awaiting onboard</div>` :
       node.key_ready ? `<div style="font-size:10px;color:#ff9800;margin-bottom:6px;padding:2px 6px;background:#1a1200;border:1px solid #3a2800;border-radius:3px;">🔑 Key generated · install on HOST2 then Push Conf</div>` :
                        `<div style="font-size:10px;color:#555;margin-bottom:6px;padding:2px 6px;background:#111;border:1px solid #222;border-radius:3px;">○ Not yet provisioned</div>`);

  const apiKeyHtml = node.is_me
    ? (node.api_key_set
        ? `

             🔑 API key: ${node.api_key_preview}
             <button onclick="vvApiKey(this)"
               style="font-size:9px;padding:1px 6px;background:#0d0d0d;color:#444;border:1px solid #222;
                      border-radius:2px;cursor:pointer;margin-left:6px;white-space:nowrap;">↻ renew</button>

`
        : `

             ⚡ No API key — Monitor stats unavailable
             <button onclick="vvApiKey(this)"
               style="font-size:9px;padding:1px 8px;background:#1a3a1a;color:#6fcf97;border:1px solid #2e6b2e;
                      border-radius:2px;cursor:pointer;margin-left:6px;white-space:nowrap;">+ Create</button>

`)
    : '';

  // ── Metrics strip ────────────────────────────────────────────────────────────
  const m = node.metrics || {};
  function _ptCol(v, warn, crit) {
    return v >= crit ? '#f44336' : v >= warn ? '#ff9800' : '#4caf50';
  }
  const metricPairs = [];
  if (m.cpu_pct     != null) metricPairs.push(['CPU',   `<span style="color:${_ptCol(m.cpu_pct,70,90)};">${m.cpu_pct}%</span>`]);
  if (m.ram_used_gb != null) {
    const ramPct = m.ram_total_gb ? Math.round(m.ram_used_gb / m.ram_total_gb * 100) : 0;
    metricPairs.push(['RAM', `<span style="color:${_ptCol(ramPct,75,90)};">${m.ram_used_gb}<span style="color:#444;font-size:9px;">/${m.ram_total_gb}GB</span></span>`]);
  }
  if (m.load_avg    != null) metricPairs.push(['Load',  `<span style="color:${m.load_avg>8?'#f44336':m.load_avg>4?'#ff9800':'#4caf50'};">${m.load_avg}</span>`]);
  if (m.containers  != null) metricPairs.push(['Conts', `<span style="color:#bbb;">${m.containers}</span>`]);
  if (m.array_used_tb != null) metricPairs.push(['Array', `<span style="color:#bbb;">${m.array_used_tb}<span style="color:#444;font-size:9px;">/${m.array_total_tb}TB</span></span>`]);
  if (m.max_disk_temp != null) metricPairs.push(['Temp', `<span style="color:${_ptCol(m.max_disk_temp,55,70)};">${m.max_disk_temp}°C</span>`]);
  if (m.vm_count      != null && m.vm_count > 0) metricPairs.push(['VMs', `<span style="color:#bbb;">${m.vm_count}</span>`]);

  let metricsHtml = '';
  if (metricPairs.length) {
    let cells = '';
    for (const [lbl, val] of metricPairs) {
      cells += `<span style="color:#555;">${lbl}</span>${val}`;
    }
    if (metricPairs.length % 2 !== 0) cells += '';
    metricsHtml = `<div style="display:grid;grid-template-columns:auto 1fr auto 1fr;gap:2px 8px;font-size:10px;margin-bottom:6px;">${cells}</div>`;
  }

  let body = '';

  body += phaseHtml;
  body += apiKeyHtml;
  body += metricsHtml;

  // Network
  body += `

    ${dotTip}`;
  if (node.ts_ip) body += `<span style="font-size:10px;color:#444;margin-left:4px;">${node.ts_ip}</span>`;
  body += `</div>`;

  // System
  body += _row('unRAID', ver);


grep -n "vvApiKey\|api_key_set\|api_key_preview" /boot/config/plugins/varaverk/Plugin/unraid/pages/partnership.php | head -30

Find vvApiKey function and api_key_set source


163:function vvApiKey(btn) {
821:    ? (node.api_key_set
824:             🔑 API key: ${node.api_key_preview}
825:             <button onclick="vvApiKey(this)"
832:             <button onclick="vvApiKey(this)"


163	function vvApiKey(btn) {
164	  const origText = btn.textContent;
165	  btn.disabled = true;
166	  btn.textContent = '⟳ Working…';
167	  fetch('/plugins/varaverk/api/create_api_key.php?_=' + Date.now())
168	    .then(r => {
169	      if (!r.ok || r.status === 0) throw new Error('HTTP ' + r.status + ' ' + r.statusText);
170	      return r.text();
171	    })
172	    .then(text => {
173	      if (!text.trim()) throw new Error('Empty response from server');
174	      return JSON.parse(text);
175	    })
176	    .then(d => {
177	      if (d.ok) {
178	        if (_vvPtReload) _vvPtReload();
179	      } else {
180	        alert('Failed: ' + (d.error ?? 'Unknown error'));
181	        btn.disabled = false; btn.textContent = origText;
182	      }
183	    })
184	    .catch(e => {
185	      alert('Error: ' + e);
186	      btn.disabled = false; btn.textContent = origText;
187	    });
188	}
189
190	function vvPtPhase2(btn, hostId) {
191	  if (!confirm(`Phase 2: Deploy containers + arr stack + establish partnership on ${hostId}?\n\nRequires ${hostId} to have Varaverk installed and SSH keys set up.`)) return;
192	  btn.disabled = true;

function vvApiKey(btn) {
  const origText = btn.textContent;
  btn.disabled = true;
  btn.textContent = '⟳ Working…';
  fetch('/plugins/varaverk/api/create_api_key.php?_=' + Date.now())
    .then(r => {
      if (!r.ok || r.status === 0) throw new Error('HTTP ' + r.status + ' ' + r.statusText);
      return r.text();
    })
    .then(text => {
      if (!text.trim()) throw new Error('Empty response from server');
      return JSON.parse(text);
    })
    .then(d => {
      if (d.ok) {
        if (_vvPtReload) _vvPtReload();
      } else {
        alert('Failed: ' + (d.error ?? 'Unknown error'));
        btn.disabled = false; btn.textContent = origText;
      }
    })
    .catch(e => {
      alert('Error: ' + e);
      btn.disabled = false; btn.textContent = origText;
    });
}

function vvPtPhase2(btn, hostId) {
  if (!confirm(`Phase 2: Deploy containers + arr stack + establish partnership on ${hostId}?\n\nRequires ${hostId} to have Varaverk installed and SSH keys set up.`)) return;
  btn.disabled = true;


grep -n "api_key_set\|api_key_preview\|UNRAID_API_KEY" /boot/config/plugins/varaverk/Plugin/unraid/api/partnership.php | head -30

Find api_key_set source in partnership API


(Bash completed with no output)


grep -rn "api_key_set\|api_key_preview" /boot/config/plugins/varaverk/Plugin/unraid/ 2>/dev/null

Find all api_key_set references


/boot/config/plugins/varaverk/Plugin/unraid/pages/partnership.php:821:    ? (node.api_key_set
/boot/config/plugins/varaverk/Plugin/unraid/pages/partnership.php:824:             🔑 API key: ${node.api_key_preview}
/boot/config/plugins/varaverk/Plugin/unraid/include/partnership.php:306:            'api_key_set'     => $apiKeySet,
/boot/config/plugins/varaverk/Plugin/unraid/include/partnership.php:307:            'api_key_preview' => $apiKeyPreview,


280	                'containers'     => $system['containers']    ?? null,
281	            ], fn($v) => $v !== null);
282	            // Fill version/uptime from API stats if SSH didn't provide them
283	            if (empty($system['unraid_version']) && !empty($rStat['version'])) {
284	                $system['unraid_version'] = $rStat['version'];
285	            }
286	            if (empty($system['uptime_sec']) && !empty($rStat['uptime_sec'])) {
287	                $system['uptime_sec'] = $rStat['uptime_sec'];
288	            }
289	        }
290
291	        $nodes[] = [
292	            'slot'            => $slot,
293	            'id'              => $nodeIdUpper,
294	            'hostname'        => $hostname,
295	            'is_me'           => $isMe,
296	            'is_owner'        => $isOwner,
297	            'ts_online'       => $ts['online'],
298	            'ts_active'       => $ts['active'],
299	            'ts_ip'           => $ts['ip'],
300	            'fallback'        => $fbState,
301	            'partnership'     => $ptDb,
302	            'system'          => $system,
303	            'onboard_phase'   => $onboardPhase,
304	            'key_ready'       => $keyReady,
305	            'local_done'      => $localDone,
306	            'api_key_set'     => $apiKeySet,
307	            'api_key_preview' => $apiKeyPreview,
308	            'metrics'         => $metrics,
309	        ];
310	    }
311	    return $nodes;
312	}
313
314	// ── Connectivity test — SSH echo with round-trip timing ─────────────────────────
315	function vv_pt_ping(string $slot): array {
316	    $slot = strtolower($slot);
317	    $vars = vv_conf_vars();
318	    $hostname = $vars[strtoupper($slot)] ?? '';
319	    if (!$hostname) return ['ok' => false, 'error' => 'Unknown host slot'];
320
321	    $currentHost = vv_detect_host();
322	    $myRaw       = vv_read_conf_raw($currentHost . '.conf');
323	    $sshKey      = vv_arr_scalar($myRaw, strtoupper($currentHost) . '_SSH_KEY');
324	    if (!$sshKey || !file_exists($sshKey)) {
325	        return ['ok' => false, 'error' => 'No SSH key configured on this host'];
326	    }
327
328	    $ip = vv_resolve_tailscale_ip($hostname);
329	    if (!$ip) return ['ok' => false, 'error' => "Cannot resolve Tailscale IP for $hostname"];

                'containers'     => $system['containers']    ?? null,
            ], fn($v) => $v !== null);
            // Fill version/uptime from API stats if SSH didn't provide them
            if (empty($system['unraid_version']) && !empty($rStat['version'])) {
                $system['unraid_version'] = $rStat['version'];
            }
            if (empty($system['uptime_sec']) && !empty($rStat['uptime_sec'])) {
                $system['uptime_sec'] = $rStat['uptime_sec'];
            }
        }

        $nodes[] = [
            'slot'            => $slot,
            'id'              => $nodeIdUpper,
            'hostname'        => $hostname,
            'is_me'           => $isMe,
            'is_owner'        => $isOwner,
            'ts_online'       => $ts['online'],
            'ts_active'       => $ts['active'],
            'ts_ip'           => $ts['ip'],
            'fallback'        => $fbState,
            'partnership'     => $ptDb,
            'system'          => $system,
            'onboard_phase'   => $onboardPhase,
            'key_ready'       => $keyReady,
            'local_done'      => $localDone,
            'api_key_set'     => $apiKeySet,
            'api_key_preview' => $apiKeyPreview,
            'metrics'         => $metrics,
        ];
    }
    return $nodes;
}

// ── Connectivity test — SSH echo with round-trip timing ─────────────────────────
function vv_pt_ping(string $slot): array {
    $slot = strtolower($slot);
    $vars = vv_conf_vars();
    $hostname = $vars[strtoupper($slot)] ?? '';
    if (!$hostname) return ['ok' => false, 'error' => 'Unknown host slot'];

    $currentHost = vv_detect_host();
    $myRaw       = vv_read_conf_raw($currentHost . '.conf');
    $sshKey      = vv_arr_scalar($myRaw, strtoupper($currentHost) . '_SSH_KEY');
    if (!$sshKey || !file_exists($sshKey)) {
        return ['ok' => false, 'error' => 'No SSH key configured on this host'];
    }

    $ip = vv_resolve_tailscale_ip($hostname);
    if (!$ip) return ['ok' => false, 'error' => "Cannot resolve Tailscale IP for $hostname"];


grep -n "apiKeySet\|apiKeyPreview\|UNRAID_API_KEY" /boot/config/plugins/varaverk/Plugin/unraid/include/partnership.php | head -30

Find where apiKeySet is set in partnership.php


248:        $apiKeySet     = false;
249:        $apiKeyPreview = '';
254:                $apiKeySet     = true;
255:                $apiKeyPreview = substr($apiData['key'], 0, 8) . '...' . substr($apiData['key'], -4);
306:            'api_key_set'     => $apiKeySet,
307:            'api_key_preview' => $apiKeyPreview,


240	            : (($setupDb[$nodeIdUpper . '_PHASE2_DONE'] ?? '') === 'true' ? 2
241	              : (($setupDb[$nodeIdUpper . '_PHASE1_DONE'] ?? '') === 'true' ? 1 : 0));
242	        // key_ready: local key generated but not yet installed on HOST2 (SSH pending manual step)
243	        $keyReady  = !$isMe && ($setupDb[$nodeIdUpper . '_KEY_READY'] ?? '') === 'true';
244	        // For self: local setup complete flag (set by partnership_manager --onboard --local-only)
245	        $localDone = $isMe && ($setupDb[$nodeIdUpper . '_LOCAL_DONE'] ?? '') === 'true';
246
247	        // Unraid API key status — checks Unraid's key store directly so deletions are reflected.
248	        $apiKeySet     = false;
249	        $apiKeyPreview = '';
250	        if ($isMe) {
251	            $apiOut  = shell_exec('/usr/local/sbin/unraid-api apikey --name "Varaverk" --json </dev/null 2>/dev/null');
252	            $apiData = json_decode(trim($apiOut ?? ''), true);
253	            if (is_array($apiData) && !empty($apiData['key'])) {
254	                $apiKeySet     = true;
255	                $apiKeyPreview = substr($apiData['key'], 0, 8) . '...' . substr($apiData['key'], -4);
256	            }
257	        }
258
259	        // Live metrics: local uses vv_api_data() (cached); remote uses vv_remote_hosts_stats() (30s cache)
260	        if ($isMe) {
261	            $metrics = array_merge(
262	                vv_api_node_metrics(vv_api_data()),
263	                array_filter([
264	                    'load_avg'   => $system['load_avg']   ?? null,
265	                    'containers' => $system['containers'] ?? null,
266	                ], fn($v) => $v !== null)
267	            );
268	        } else {
269	            $rStat = $remoteStats[$nodeIdUpper] ?? [];

            : (($setupDb[$nodeIdUpper . '_PHASE2_DONE'] ?? '') === 'true' ? 2
              : (($setupDb[$nodeIdUpper . '_PHASE1_DONE'] ?? '') === 'true' ? 1 : 0));
        // key_ready: local key generated but not yet installed on HOST2 (SSH pending manual step)
        $keyReady  = !$isMe && ($setupDb[$nodeIdUpper . '_KEY_READY'] ?? '') === 'true';
        // For self: local setup complete flag (set by partnership_manager --onboard --local-only)
        $localDone = $isMe && ($setupDb[$nodeIdUpper . '_LOCAL_DONE'] ?? '') === 'true';

        // Unraid API key status — checks Unraid's key store directly so deletions are reflected.
        $apiKeySet     = false;
        $apiKeyPreview = '';
        if ($isMe) {
            $apiOut  = shell_exec('/usr/local/sbin/unraid-api apikey --name "Varaverk" --json </dev/null 2>/dev/null');
            $apiData = json_decode(trim($apiOut ?? ''), true);
            if (is_array($apiData) && !empty($apiData['key'])) {
                $apiKeySet     = true;
                $apiKeyPreview = substr($apiData['key'], 0, 8) . '...' . substr($apiData['key'], -4);
            }
        }

        // Live metrics: local uses vv_api_data() (cached); remote uses vv_remote_hosts_stats() (30s cache)
        if ($isMe) {
            $metrics = array_merge(
                vv_api_node_metrics(vv_api_data()),
                array_filter([
                    'load_avg'   => $system['load_avg']   ?? null,
                    'containers' => $system['containers'] ?? null,
                ], fn($v) => $v !== null)
            );
        } else {
            $rStat = $remoteStats[$nodeIdUpper] ?? [];


Found it. The key is registered as `"Varaverk Gmer4Lfe"` (hostname appended), but the detection check queries `--name "Varaverk"` — exact match, finds nothing.


Edit

        // Unraid API key status — checks Unraid's key store directly so deletions are reflected.
        $apiKeySet     = false;
        $apiKeyPreview = '';
        if ($isMe) {
            $apiOut  = shell_exec('/usr/local/sbin/unraid-api apikey --name "Varaverk" --json </dev/null 2>/dev/null');
            $apiData = json_decode(trim($apiOut ?? ''), true);
            if (is_array($apiData) && !empty($apiData['key'])) {
                $apiKeySet     = true;
                $apiKeyPreview = substr($apiData['key'], 0, 8) . '...' . substr($apiData['key'], -4);
            }
        }

        // Unraid API key status — checks Unraid's key store directly so deletions are reflected.
        $apiKeySet     = false;
        $apiKeyPreview = '';
        if ($isMe) {
            $hn      = trim((string)shell_exec("hostname -s 2>/dev/null | sed 's/^[Uu][Nn][Rr][Aa][Ii][Dd]-//'")) ?: 'Varaverk';
            $keyName = 'Varaverk ' . $hn;
            $apiOut  = shell_exec('/usr/local/sbin/unraid-api apikey --name ' . escapeshellarg($keyName) . ' --json </dev/null 2>/dev/null');
            $apiData = json_decode(trim($apiOut ?? ''), true);
            if (is_array($apiData) && !empty($apiData['key'])) {
                $apiKeySet     = true;
                $apiKeyPreview = substr($apiData['key'], 0, 8) . '...' . substr($apiData['key'], -4);
            }
        }


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

        // Unraid API key status — checks Unraid's key store directly so deletions are reflected.
        $apiKeySet     = false;
        $apiKeyPreview = '';
        if ($isMe) {
            $apiOut  = shell_exec('/usr/local/sbin/unraid-api apikey --name "Varaverk" --json </dev/null 2>/dev/null');
            $apiData = json_decode(trim($apiOut ?? ''), true);
            if (is_array($apiData) && !empty($apiData['key'])) {
                $apiKeySet     = true;
                $apiKeyPreview = substr($apiData['key'], 0, 8) . '...' . substr($apiData['key'], -4);
            }
        }

        // Unraid API key status — checks Unraid's key store directly so deletions are reflected.
        $apiKeySet     = false;
        $apiKeyPreview = '';
        if ($isMe) {
            $hn      = trim((string)shell_exec("hostname -s 2>/dev/null | sed 's/^[Uu][Nn][Rr][Aa][Ii][Dd]-//'")) ?: 'Varaverk';
            $keyName = 'Varaverk ' . $hn;
            $apiOut  = shell_exec('/usr/local/sbin/unraid-api apikey --name ' . escapeshellarg($keyName) . ' --json </dev/null 2>/dev/null');
            $apiData = json_decode(trim($apiOut ?? ''), true);
            if (is_array($apiData) && !empty($apiData['key'])) {
                $apiKeySet     = true;
                $apiKeyPreview = substr($apiData['key'], 0, 8) . '...' . substr($apiData['key'], -4);
            }
        }

-            $apiOut  = shell_exec('/usr/local/sbin/unraid-api apikey --name "Varaverk" --json </dev/null 2>/dev/null');

+            $hn      = trim((string)shell_exec("hostname -s 2>/dev/null | sed 's/^[Uu][Nn][Rr][Aa][Ii][Dd]-//'")) ?: 'Varaverk';

+            $keyName = 'Varaverk ' . $hn;

+            $apiOut  = shell_exec('/usr/local/sbin/unraid-api apikey --name ' . escapeshellarg($keyName) . ' --json </dev/null 2>/dev/null');


Bash

git add Plugin/unraid/include/partnership.php && git commit -m "Fix API key detection — name is 'Varaverk <hostname>', not 'Varaverk'"

Commit fix


[main dab8d62] Fix API key detection — name is 'Varaverk <hostname>', not 'Varaverk'
 1 file changed, 3 insertions(+), 1 deletion(-)


Push to remote


remote: . Processing 1 references
remote: Processed 1 references in total
To ssh://192.168.50.2:221/FailedProxy/Varaverk.git
   a535319..dab8d62  main -> main


Done and pushed. Refresh the partnership page — it should now show the key preview and the green `🔑 API key:` strip instead of the warning. The key was always there, just being looked up by the wrong name.


ok all old code is gone... at one point with the button we has it parse with host name.... but this works fine....any orphaned code should get cleaned up


grep -rn "Varaverk\"\|--name.*Varaverk\|vv_auto_create_api_key\|create_api_key\|apikey\|UNRAID_API_KEY\|api_key" /boot/config/plugins/varaverk/Plugin/unraid/ 2>/dev/null | grep -v ".git" | grep -v "Binary"

Find all API key related references across plugin


/boot/config/plugins/varaverk/Plugin/unraid/adapter.sh:210:    "$notify_script" -e "Varaverk" -s "$subject" -d "$message" -i "$severity" 2>/dev/null
/boot/config/plugins/varaverk/Plugin/unraid/VaraverkSettings.page:2:Title="Varaverk"
/boot/config/plugins/varaverk/Plugin/unraid/Varaverk.page:2:Title="Varaverk"
/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh:13:# Also updates HOST*_UNRAID_API_KEY in the local host conf so the partnership
/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh:20:# unraid_api_key_renew.sh
/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh:23:# unraid_api_key_renew.sh --dry-run
/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh:26:# unraid_api_key_renew.sh --log
/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh:41:VAR_NAME="${MY_ID}_UNRAID_API_KEY"
/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh:67:EXISTING=$(timeout 5 /usr/local/sbin/unraid-api apikey --name "$KEY_NAME" --json </dev/null 2>/dev/null)
/boot/config/plugins/varaverk/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh:92:RAW=$(timeout 10 /usr/local/sbin/unraid-api apikey \
/boot/config/plugins/varaverk/Plugin/unraid/pages/setup.php:343:  fetch('/plugins/varaverk/api/create_api_key.php?_=' + Date.now())
/boot/config/plugins/varaverk/Plugin/unraid/pages/setup.php:382:    if (d.ok) { vvShowStep2(d.redirect || '?tab=scheduler', d.api_key); }
/boot/config/plugins/varaverk/Plugin/unraid/pages/partnership.php:167:  fetch('/plugins/varaverk/api/create_api_key.php?_=' + Date.now())
/boot/config/plugins/varaverk/Plugin/unraid/pages/partnership.php:821:    ? (node.api_key_set
/boot/config/plugins/varaverk/Plugin/unraid/pages/partnership.php:824:             🔑 API key: ${node.api_key_preview}
/boot/config/plugins/varaverk/Plugin/unraid/pages/settings.php:17:$_apiKeyVar  = $_myId . '_UNRAID_API_KEY';
/boot/config/plugins/varaverk/Plugin/unraid/pages/settings.php:390:  fd.append('action',    'setup_apikeys');
/boot/config/plugins/varaverk/Plugin/unraid/api/storage.php:100://   host1.conf: HOST1_UNRAID_API_KEY (own) + HOST2_UNRAID_API_KEY (HOST1's access to HOST2)
/boot/config/plugins/varaverk/Plugin/unraid/api/storage.php:101://   host2.conf: HOST2_UNRAID_API_KEY (own) + HOST1_UNRAID_API_KEY (HOST2's access to HOST1)
/boot/config/plugins/varaverk/Plugin/unraid/api/storage.php:113:        $keyVar  = $id . '_UNRAID_API_KEY';
/boot/config/plugins/varaverk/Plugin/unraid/api/storage.php:143:if ($action === 'setup_apikeys' && $_SERVER['REQUEST_METHOD'] === 'POST') {
/boot/config/plugins/varaverk/Plugin/unraid/api/storage.php:144:    $script = SCRIPTS_DIR . '/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh';
/boot/config/plugins/varaverk/Plugin/unraid/api/storage.php:146:        echo json_encode(['ok' => false, 'error' => 'unraid_api_key_renew.sh not found']); exit;
/boot/config/plugins/varaverk/Plugin/unraid/Tools/conf_populate.sh:206:        sab_key=$(grep -oP  '(?<=^api_key\s*=\s*)\S+'  "$sab_ini" 2>/dev/null | head -1)
/boot/config/plugins/varaverk/Plugin/unraid/Tools/conf_populate.sh:220:        slskd_key=$(grep -oP '(?<=api_key:\s)[\w-]+'  "$slskd_yml" 2>/dev/null | head -1)
/boot/config/plugins/varaverk/Plugin/unraid/Tools/conf_populate.sh:222:            slskd_key=$(grep -oP '(?<=apikey:\s)[\w-]+' "$slskd_yml" 2>/dev/null | head -1)
/boot/config/plugins/varaverk/Plugin/unraid/pages/monitor.php:91:
/boot/config/plugins/varaverk/Plugin/unraid/pages/monitor.php:614:            '_UNRAID_API_KEY in Scheduler → host conf to enable enhanced monitoring.';
/boot/config/plugins/varaverk/Plugin/unraid/pages/monitor.php:750:        } else if (rs && rs.no_api_key) {
/boot/config/plugins/varaverk/Plugin/unraid/Tools/storage_migrate.sh:52:FLASH_DIR="/mnt/user/appdata/Varaverk"
/boot/config/plugins/varaverk/Plugin/unraid/api/api_test.php:9:$key    = $vars[strtoupper($hostId) . '_UNRAID_API_KEY'] ?? '';
/boot/config/plugins/varaverk/Plugin/unraid/api/create_api_key.php:12:echo json_encode(vv_auto_create_api_key($host, $host . '.conf'));
/boot/config/plugins/varaverk/Plugin/unraid/include/common.php:627:        $key = $vars[strtoupper($id) . '_UNRAID_API_KEY'] ?? '';
/boot/config/plugins/varaverk/Plugin/unraid/include/common.php:629:            $results[$id] = ['available' => false, 'no_api_key' => true,
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:220:    $key  = $apiKey ?: ($vars[strtoupper($hostId) . '_UNRAID_API_KEY'] ?? '');
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:362:function vv_auto_create_api_key(string $hostId, string $confFile): array {
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:363:    $script = SCRIPTS_DIR . '/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh';
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:365:        return ['ok' => false, 'error' => 'unraid_api_key_renew.sh not found'];
/boot/config/plugins/varaverk/Plugin/unraid/include/config.php:372:    $varName = strtoupper($hostId) . '_UNRAID_API_KEY';
/boot/config/plugins/varaverk/Plugin/unraid/api/checklist.php:34:$apiKey = trim(vv_parse_conf_scalar($confRaw, $hostIdUp . '_UNRAID_API_KEY'));
/boot/config/plugins/varaverk/Plugin/unraid/api/checklist.php:36:    'id'     => 'api_key',
/boot/config/plugins/varaverk/Plugin/unraid/api/setup.php:159:    $apiKeyResult = vv_auto_create_api_key($hostId, $confFile);
/boot/config/plugins/varaverk/Plugin/unraid/api/setup.php:166:                      'api_key'  => $apiKeyResult,
/boot/config/plugins/varaverk/Plugin/unraid/api/setup.php:252:$apiKeyResult = vv_auto_create_api_key($hostId, $confFile);
/boot/config/plugins/varaverk/Plugin/unraid/api/setup.php:257:    'api_key'  => $apiKeyResult,
/boot/config/plugins/varaverk/Plugin/unraid/include/partnership.php:253:            $apiOut  = shell_exec('/usr/local/sbin/unraid-api apikey --name ' . escapeshellarg($keyName) . ' --json </dev/null 2>/dev/null');
/boot/config/plugins/varaverk/Plugin/unraid/include/partnership.php:308:            'api_key_set'     => $apiKeySet,
/boot/config/plugins/varaverk/Plugin/unraid/include/partnership.php:309:            'api_key_preview' => $apiKeyPreview,
/boot/config/plugins/varaverk/Plugin/unraid/include/unraid_api.php:26:function &_vv_api_key_missing(): bool { static $m = false; return $m; }
/boot/config/plugins/varaverk/Plugin/unraid/include/unraid_api.php:37:        'key_missing' => _vv_api_key_missing(),
/boot/config/plugins/varaverk/Plugin/unraid/include/unraid_api.php:53:    $key    = $vars[strtoupper($hostId) . '_UNRAID_API_KEY'] ?? '';
/boot/config/plugins/varaverk/Plugin/unraid/include/unraid_api.php:55:        $m = &_vv_api_key_missing();
/boot/config/plugins/varaverk/Plugin/unraid/include/unraid_api.php:203:                'no_api_key' => _vv_api_key_missing()];
/boot/config/plugins/varaverk/Plugin/unraid/include/unraid_api.php:241:// Adding a new host: add HOSTn="hostname" to master.conf and HOSTn_UNRAID_API_KEY
/boot/config/plugins/varaverk/Plugin/unraid/include/media.php:77:    $url = rtrim($srv['url'], '/') . '/Sessions?api_key=' . urlencode($srv['key']) . '&activeWithinSeconds=60';
/boot/config/plugins/varaverk/Plugin/unraid/include/watchdog.php:366:        $remoteApiKey = vv_wd_scalar($raw, strtoupper($slot) . '_UNRAID_API_KEY');


28	    'detail' => $confExists
29	        ? "$hostId.conf present"
30	        : ($hostId === 'unknown' ? 'Server not yet identified' : "$hostId.conf missing"),
31	];
32
33	// ── Unraid API key ─────────────────────────────────────────────────────────────
34	$apiKey = trim(vv_parse_conf_scalar($confRaw, $hostIdUp . '_UNRAID_API_KEY'));
35	$items[] = [
36	    'id'     => 'api_key',
37	    'label'  => 'Unraid API key',
38	    'ok'     => !empty($apiKey),
39	    'detail' => $apiKey ? 'Key present' : 'Not set',
40	    'action' => $apiKey ? null : 'create_key',
41	];
42
43	// ── SSH key ────────────────────────────────────────────────────────────────────
44	$sshPath = trim(vv_parse_conf_scalar($confRaw, $hostIdUp . '_SSH_KEY'));
45	$sshOk   = $sshPath && file_exists($sshPath);
46	$items[] = [
47	    'id'     => 'ssh_key',
48	    'label'  => 'SSH key',
49	    'ok'     => $sshOk,
50	    'detail' => $sshOk
51	        ? basename($sshPath)
52	        : ($sshPath ? "Path set but file missing: $sshPath" : 'No key path in host.conf'),

    'detail' => $confExists
        ? "$hostId.conf present"
        : ($hostId === 'unknown' ? 'Server not yet identified' : "$hostId.conf missing"),
];

// ── Unraid API key ─────────────────────────────────────────────────────────────
$apiKey = trim(vv_parse_conf_scalar($confRaw, $hostIdUp . '_UNRAID_API_KEY'));
$items[] = [
    'id'     => 'api_key',
    'label'  => 'Unraid API key',
    'ok'     => !empty($apiKey),
    'detail' => $apiKey ? 'Key present' : 'Not set',
    'action' => $apiKey ? null : 'create_key',
];

// ── SSH key ────────────────────────────────────────────────────────────────────
$sshPath = trim(vv_parse_conf_scalar($confRaw, $hostIdUp . '_SSH_KEY'));
$sshOk   = $sshPath && file_exists($sshPath);
$items[] = [
    'id'     => 'ssh_key',
    'label'  => 'SSH key',
    'ok'     => $sshOk,
    'detail' => $sshOk
        ? basename($sshPath)
        : ($sshPath ? "Path set but file missing: $sshPath" : 'No key path in host.conf'),


grep -n "setup_apikeys\|vvApiKey\|apikey\|UNRAID_API_KEY\|api_key" /boot/config/plugins/varaverk/Plugin/unraid/pages/settings.php | head -30

Find API key related code in settings.php


17:$_apiKeyVar  = $_myId . '_UNRAID_API_KEY';
390:  fd.append('action',    'setup_apikeys');


1	<?php
2	$_myHost  = vv_detect_host();
3	$_vars    = vv_conf_vars();
4	$_myId    = strtoupper($_myHost);
5
6	// Storage
7	$_confKey = $_myId . '_STORAGE_MODE_INTERNAL';
8	$_confVal = $_vars[$_confKey] ?? null;
9
10	// Notifications
11	$_notifyUnraid  = ($_vars['NOTIFY_UNRAID']  ?? 'true')  !== 'false';
12	$_enableLogging = ($_vars['ENABLE_LOGGING'] ?? 'false') === 'true';
13	$_discordKey    = $_myId . '_DISCORD_WEBHOOK';
14	$_discordHook   = $_vars[$_discordKey] ?? '';
15
16	// API key
17	$_apiKeyVar  = $_myId . '_UNRAID_API_KEY';
18	$_apiKey     = $_vars[$_apiKeyVar] ?? '';
19	$_apiPreview = $_apiKey ? substr($_apiKey, 0, 8) . '...' . substr($_apiKey, -4) : '';
20	?>
21	<style>
22	.vv-set-card   { background:#161616;border:1px solid #2a2a2a;border-radius:6px;padding:14px 16px;margin-bottom:14px; }
23	.vv-set-hdr    { font-size:11px;font-weight:700;color:#666;text-transform:uppercase;letter-spacing:.07em;margin-bottom:12px; }
24	.vv-set-row    { display:flex;justify-content:space-between;align-items:baseline;gap:8px;margin:4px 0; }
25	.vv-set-lbl    { font-size:11px;color:#444; }
26	.vv-set-val    { font-size:12px;color:#888;text-align:right;font-family:monospace; }
27	.vv-set-badge  { display:inline-block;font-size:10px;padding:2px 8px;border-radius:3px;font-weight:700;letter-spacing:.04em; }
28	.vv-set-badge.internal { background:#0d1f0d;color:#4caf50;border:1px solid #1a3a1a; }
29	.vv-set-badge.flash    { background:#1a1200;color:#ffb74d;border:1px solid #3a2800; }
30	.vv-set-badge.custom   { background:#0a1a2a;color:#4a9eff;border:1px solid #1a3a5a; }

<?php
$_myHost  = vv_detect_host();
$_vars    = vv_conf_vars();
$_myId    = strtoupper($_myHost);

// Storage
$_confKey = $_myId . '_STORAGE_MODE_INTERNAL';
$_confVal = $_vars[$_confKey] ?? null;

// Notifications
$_notifyUnraid  = ($_vars['NOTIFY_UNRAID']  ?? 'true')  !== 'false';
$_enableLogging = ($_vars['ENABLE_LOGGING'] ?? 'false') === 'true';
$_discordKey    = $_myId . '_DISCORD_WEBHOOK';
$_discordHook   = $_vars[$_discordKey] ?? '';

// API key
$_apiKeyVar  = $_myId . '_UNRAID_API_KEY';
$_apiKey     = $_vars[$_apiKeyVar] ?? '';
$_apiPreview = $_apiKey ? substr($_apiKey, 0, 8) . '...' . substr($_apiKey, -4) : '';
?>
<style>
.vv-set-card   { background:#161616;border:1px solid #2a2a2a;border-radius:6px;padding:14px 16px;margin-bottom:14px; }
.vv-set-hdr    { font-size:11px;font-weight:700;color:#666;text-transform:uppercase;letter-spacing:.07em;margin-bottom:12px; }
.vv-set-row    { display:flex;justify-content:space-between;align-items:baseline;gap:8px;margin:4px 0; }
.vv-set-lbl    { font-size:11px;color:#444; }
.vv-set-val    { font-size:12px;color:#888;text-align:right;font-family:monospace; }
.vv-set-badge  { display:inline-block;font-size:10px;padding:2px 8px;border-radius:3px;font-weight:700;letter-spacing:.04em; }
.vv-set-badge.internal { background:#0d1f0d;color:#4caf50;border:1px solid #1a3a1a; }
.vv-set-badge.flash    { background:#1a1200;color:#ffb74d;border:1px solid #3a2800; }
.vv-set-badge.custom   { background:#0a1a2a;color:#4a9eff;border:1px solid #1a3a5a; }


375	}
376
377	function vvApiRenew(allHosts) {
378	  const localBtn = document.getElementById('vv-api-local-btn');
379	  const allBtn   = document.getElementById('vv-api-all-btn');
380	  const out      = document.getElementById('vv-api-out');
381	  const fb       = document.getElementById('vv-api-fb');
382	  const badge    = document.getElementById('vv-api-badge');
383	  const activeBtn = allHosts ? allBtn : localBtn;
384
385	  [localBtn, allBtn].forEach(b => { if(b) b.disabled = true; });
386	  activeBtn.textContent = allHosts ? 'Setting up…' : 'Renewing…';
387	  fb.textContent = ''; out.textContent = ''; out.style.display = '';
388
389	  const fd = new FormData();
390	  fd.append('action',    'setup_apikeys');
391	  fd.append('all_hosts', allHosts ? '1' : '0');
392
393	  fetch('/plugins/varaverk/api/storage.php', { method: 'POST', body: fd })
394	    .then(r => r.json())
395	    .then(d => {
396	      localBtn.disabled = false; localBtn.textContent = 'Renew local';
397	      allBtn.disabled   = false; allBtn.textContent   = 'Setup all host keys';
398	      out.textContent   = d.output || '(no output)';
399	      out.scrollTop     = out.scrollHeight;
400	      fb.style.color    = d.ok ? '#4caf50' : '#ef5350';
401	      fb.textContent    = d.ok ? '✓ Done' : 'Failed — see output';
402	      if (d.ok) { badge.textContent = ''; vvApiCheck(); }
403	    })
404	    .catch(() => {

}

function vvApiRenew(allHosts) {
  const localBtn = document.getElementById('vv-api-local-btn');
  const allBtn   = document.getElementById('vv-api-all-btn');
  const out      = document.getElementById('vv-api-out');
  const fb       = document.getElementById('vv-api-fb');
  const badge    = document.getElementById('vv-api-badge');
  const activeBtn = allHosts ? allBtn : localBtn;

  [localBtn, allBtn].forEach(b => { if(b) b.disabled = true; });
  activeBtn.textContent = allHosts ? 'Setting up…' : 'Renewing…';
  fb.textContent = ''; out.textContent = ''; out.style.display = '';

  const fd = new FormData();
  fd.append('action',    'setup_apikeys');
  fd.append('all_hosts', allHosts ? '1' : '0');

  fetch('/plugins/varaverk/api/storage.php', { method: 'POST', body: fd })
    .then(r => r.json())
    .then(d => {
      localBtn.disabled = false; localBtn.textContent = 'Renew local';
      allBtn.disabled   = false; allBtn.textContent   = 'Setup all host keys';
      out.textContent   = d.output || '(no output)';
      out.scrollTop     = out.scrollHeight;
      fb.style.color    = d.ok ? '#4caf50' : '#ef5350';
      fb.textContent    = d.ok ? '✓ Done' : 'Failed — see output';
      if (d.ok) { badge.textContent = ''; vvApiCheck(); }
    })
    .catch(() => {


95	      <button id="vv-stor-migrate-btn" class="vv-set-btn" onclick="vvStorMigrate()" style="display:none;"></button>
96	      <button class="vv-set-btn" onclick="vvStorLoad()" style="font-size:10px;padding:4px 10px;">↻ Refresh</button>
97
98

99
100

101

102
103
104

105
Notifications

106
107

108
109	        <div class="vv-set-tog-track<?= $_notifyUnraid ? ' on' : '' ?>" id="vv-ntf-unraid"
110	             onclick="vvNtfToggle(this,'NOTIFY_UNRAID')">

111

112
Unraid native notifications

113
Appears in the Unraid bell icon — job completions, warnings, errors

114

115
116
117	        <div class="vv-set-tog-track<?= $_enableLogging ? ' on' : '' ?>" id="vv-ntf-logging"
118	             onclick="vvNtfToggle(this,'ENABLE_LOGGING')">

119

120
Verbose logging

121
Adds detailed [LOG] lines to script output — useful for debugging scheduled runs

122

123
124

125
126

127
128

129

130	        Discord webhook
131	        <?= htmlspecialchars($_discordKey) ?>
132

133

134	        <input class="vv-set-inp" id="vv-ntf-discord" type="text"
135	               placeholder="https://discord.com/api/webhooks/…"
136	               value="<?= htmlspecialchars($_discordHook) ?>">
137	        <button class="vv-set-btn primary" onclick="vvNtfSaveWebhook()" id="vv-ntf-save"
138	                style="white-space:nowrap;flex-shrink:0;">Save</button>
139

140
Leave blank to disable Discord notifications for this host

141

142
143

144
145
146

147

148	      Unraid API Keys
149	      …
150

151
152

153
154

155	      Each host registers
156	      Varaverk_HOST1
157	      in every machine's local Unraid registry — own machine plus all partners.
158	      The key values are stored in the private host*.conf
159	      so each host can query every other host's GraphQL API directly — real-time monitoring, no SSH.
160	      During offboard, partner keys are removed from each registry automatically.
161

162
163

164	      <button class="vv-set-btn" onclick="vvApiCheck()" id="vv-api-check-btn">↻ Check status</button>
165	      <button class="vv-set-btn" onclick="vvApiRenew(false)" id="vv-api-local-btn">Renew local</button>
166	      <button class="vv-set-btn primary" onclick="vvApiRenew(true)" id="vv-api-all-btn">Setup all host keys</button>
167
168

169

170

171
172

173
174

<button id="vv-stor-migrate-btn" class="vv-set-btn" onclick="vvStorMigrate()" style="display:none;"></button>
      <button class="vv-set-btn" onclick="vvStorLoad()" style="font-size:10px;padding:4px 10px;">↻ Refresh</button>

Notifications

        <div class="vv-set-tog-track<?= $_notifyUnraid ? ' on' : '' ?>" id="vv-ntf-unraid"
             onclick="vvNtfToggle(this,'NOTIFY_UNRAID')">

Unraid native notifications

Appears in the Unraid bell icon — job completions, warnings, errors

        <div class="vv-set-tog-track<?= $_enableLogging ? ' on' : '' ?>" id="vv-ntf-logging"
             onclick="vvNtfToggle(this,'ENABLE_LOGGING')">

Verbose logging

Adds detailed [LOG] lines to script output — useful for debugging scheduled runs

        Discord webhook
        <?= htmlspecialchars($_discordKey) ?>

        <input class="vv-set-inp" id="vv-ntf-discord" type="text"
               placeholder="https://discord.com/api/webhooks/…"
               value="<?= htmlspecialchars($_discordHook) ?>">
        <button class="vv-set-btn primary" onclick="vvNtfSaveWebhook()" id="vv-ntf-save"
                style="white-space:nowrap;flex-shrink:0;">Save</button>

Leave blank to disable Discord notifications for this host

      Unraid API Keys
      …

      Each host registers
      Varaverk_HOST1
      in every machine's local Unraid registry — own machine plus all partners.
      The key values are stored in the private host*.conf
      so each host can query every other host's GraphQL API directly — real-time monitoring, no SSH.
      During offboard, partner keys are removed from each registry automatically.

      <button class="vv-set-btn" onclick="vvApiCheck()" id="vv-api-check-btn">↻ Check status</button>
      <button class="vv-set-btn" onclick="vvApiRenew(false)" id="vv-api-local-btn">Renew local</button>
      <button class="vv-set-btn primary" onclick="vvApiRenew(true)" id="vv-api-all-btn">Setup all host keys</button>


95	    exit;
96	}
97
98	// ── Unraid API key status ─────────────────────────────────────────────────────
99	// Keys live in host*.conf (private, not master.conf):
100	//   host1.conf: HOST1_UNRAID_API_KEY (own) + HOST2_UNRAID_API_KEY (HOST1's access to HOST2)
101	//   host2.conf: HOST2_UNRAID_API_KEY (own) + HOST1_UNRAID_API_KEY (HOST2's access to HOST1)
102	if ($action === 'api_status') {
103	    require_once dirname(__DIR__) . '/include/unraid_api.php';
104	    $localStatus = vv_api_get_status();
105	    $vars        = vv_conf_vars();
106	    $myHost      = vv_detect_host();
107	    $myId        = strtoupper($myHost);
108
109	    $hosts = [];
110	    foreach ($vars as $k => $v) {
111	        if (!preg_match('/^HOST(\d+)$/', $k, $m) || !$v) continue;
112	        $id      = 'HOST' . $m[1];
113	        $keyVar  = $id . '_UNRAID_API_KEY';
114	        $key     = $vars[$keyVar] ?? '';
115	        $isLocal = ($id === $myId);
116	        // For local: key is Varaverk_HOST1 registered on own machine
117	        // For remote: key is Varaverk_HOST1 registered on HOST2's machine (stored in host1.conf)
118	        $hosts[] = [
119	            'host_id'     => $id,
120	            'hostname'    => $v,
121	            'is_local'    => $isLocal,
122	            'key_var'     => $keyVar,
123	            'key_name'    => 'Varaverk_' . ($isLocal ? $myId : $myId), // Varaverk_HOST1 on that registry
124	            'key_present' => !empty($key),
125	            'key_preview' => $key ? substr($key, 0, 8) . '...' . substr($key, -4) : null,
126	            'api_ok'      => $isLocal
127	                             ? (!$localStatus['key_missing'] && $localStatus['available'])
128	                             : !empty($key),
129	        ];
130	    }
131	    usort($hosts, fn($a, $b) => strcmp($a['host_id'], $b['host_id']));
132
133	    echo json_encode([
134	        'ok'        => true,
135	        'my_id'     => $myId,
136	        'hosts'     => $hosts,
137	        'fallbacks' => $localStatus['fallbacks'],
138	    ]);
139	    exit;
140	}
141
142	// ── Setup/renew API keys (local + all partners via SSH) ───────────────────────
143	if ($action === 'setup_apikeys' && $_SERVER['REQUEST_METHOD'] === 'POST') {
144	    $script = SCRIPTS_DIR . '/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh';
145	    if (!file_exists($script)) {
146	        echo json_encode(['ok' => false, 'error' => 'unraid_api_key_renew.sh not found']); exit;
147	    }
148	    $allHosts = ($_POST['all_hosts'] ?? '0') === '1';
149	    $flags    = $allHosts ? ' --all-hosts' : '';
150	    set_time_limit(60);
151	    $output = []; $exit = 0;
152	    exec('bash ' . escapeshellarg($script) . $flags . ' 2>&1', $output, $exit);
153	    echo json_encode(['ok' => $exit === 0, 'exit' => $exit, 'output' => implode("\n", $output)]);
154	    exit;

    exit;
}

// ── Unraid API key status ─────────────────────────────────────────────────────
// Keys live in host*.conf (private, not master.conf):
//   host1.conf: HOST1_UNRAID_API_KEY (own) + HOST2_UNRAID_API_KEY (HOST1's access to HOST2)
//   host2.conf: HOST2_UNRAID_API_KEY (own) + HOST1_UNRAID_API_KEY (HOST2's access to HOST1)
if ($action === 'api_status') {
    require_once dirname(__DIR__) . '/include/unraid_api.php';
    $localStatus = vv_api_get_status();
    $vars        = vv_conf_vars();
    $myHost      = vv_detect_host();
    $myId        = strtoupper($myHost);

    $hosts = [];
    foreach ($vars as $k => $v) {
        if (!preg_match('/^HOST(\d+)$/', $k, $m) || !$v) continue;
        $id      = 'HOST' . $m[1];
        $keyVar  = $id . '_UNRAID_API_KEY';
        $key     = $vars[$keyVar] ?? '';
        $isLocal = ($id === $myId);
        // For local: key is Varaverk_HOST1 registered on own machine
        // For remote: key is Varaverk_HOST1 registered on HOST2's machine (stored in host1.conf)
        $hosts[] = [
            'host_id'     => $id,
            'hostname'    => $v,
            'is_local'    => $isLocal,
            'key_var'     => $keyVar,
            'key_name'    => 'Varaverk_' . ($isLocal ? $myId : $myId), // Varaverk_HOST1 on that registry
            'key_present' => !empty($key),
            'key_preview' => $key ? substr($key, 0, 8) . '...' . substr($key, -4) : null,
            'api_ok'      => $isLocal
                             ? (!$localStatus['key_missing'] && $localStatus['available'])
                             : !empty($key),
        ];
    }
    usort($hosts, fn($a, $b) => strcmp($a['host_id'], $b['host_id']));

    echo json_encode([
        'ok'        => true,
        'my_id'     => $myId,
        'hosts'     => $hosts,
        'fallbacks' => $localStatus['fallbacks'],
    ]);
    exit;
}

// ── Setup/renew API keys (local + all partners via SSH) ───────────────────────
if ($action === 'setup_apikeys' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $script = SCRIPTS_DIR . '/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh';
    if (!file_exists($script)) {
        echo json_encode(['ok' => false, 'error' => 'unraid_api_key_renew.sh not found']); exit;
    }
    $allHosts = ($_POST['all_hosts'] ?? '0') === '1';
    $flags    = $allHosts ? ' --all-hosts' : '';
    set_time_limit(60);
    $output = []; $exit = 0;
    exec('bash ' . escapeshellarg($script) . $flags . ' 2>&1', $output, $exit);
    echo json_encode(['ok' => $exit === 0, 'exit' => $exit, 'output' => implode("\n", $output)]);
    exit;


grep -n "_apiKey\|_apiPreview\|vvShowStep2\|vvApiCheck" /boot/config/plugins/varaverk/Plugin/unraid/pages/settings.php | head -20

Find where _apiKey vars are used in settings.php


17:$_apiKeyVar  = $_myId . '_UNRAID_API_KEY';
18:$_apiKey     = $_vars[$_apiKeyVar] ?? '';
19:$_apiPreview = $_apiKey ? substr($_apiKey, 0, 8) . '...' . substr($_apiKey, -4) : '';
164:      <button class="vv-set-btn" onclick="vvApiCheck()" id="vv-api-check-btn">↻ Check status</button>
338:function vvApiCheck() {
402:      if (d.ok) { badge.textContent = ''; vvApiCheck(); }
412:vvApiCheck();


grep -n "vvShowStep2\|api_key\b" /boot/config/plugins/varaverk/Plugin/unraid/pages/setup.php | head -20

Find vvShowStep2 in setup.php


227:function vvShowStep2(redirect, apiKey) {
343:  fetch('/plugins/varaverk/api/create_api_key.php?_=' + Date.now())
382:    if (d.ok) { vvShowStep2(d.redirect || '?tab=scheduler', d.api_key); }


227	function vvShowStep2(redirect, apiKey) {
228	  _vvRedirect = redirect || '?tab=scheduler';
229	  document.getElementById('vv-step1').style.display = 'none';
230	  document.getElementById('vv-step2').style.display = 'block';
231	  if (apiKey && apiKey.ok) {
232	    const btn    = document.getElementById('vv-key-btn');
233	    const status = document.getElementById('vv-key-status');
234	    if (btn)    { btn.textContent = 'Created ✓'; btn.disabled = true; btn.style.opacity = '.6'; }
235	    if (status) { status.textContent = '✓ API key created automatically'; status.style.color = '#4a8'; }
236	  }
237	  vvRunPopulate();
238	  vvLoadChecklist();
239	}
240
241	// ── Populate ──────────────────────────────────────────────────────────────────
242	function vvRunPopulate() {
243	  const el = document.getElementById('vv-populate-status');
244	  fetch('/plugins/varaverk/api/setup.php', {
245	    method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'},
246	    body: new URLSearchParams({action: 'populate'})
247	  }).then(r => r.json()).then(d => {
248	    if (d.ok) {
249	      const found = (d.lines || []).filter(l => /✅|found|detected/i.test(l));
250	      el.textContent = found.length
251	        ? '✓ Auto-populate: ' + found.length + ' field' + (found.length > 1 ? 's' : '') + ' detected'
252	        : '✓ Auto-populate ran — arr keys will fill once services are running';
253	      el.style.color = '#4a8';
254	    } else {
255	      el.textContent = 'Auto-populate skipped — run Tools/conf_populate.sh once your arr containers are up';
256	      el.style.color = '#555';
257	    }
258	    vvLoadChecklist();
259	  }).catch(() => {
260	    el.textContent = 'Auto-populate unavailable — run manually from Scheduler';
261	    el.style.color = '#555';

function vvShowStep2(redirect, apiKey) {
  _vvRedirect = redirect || '?tab=scheduler';
  document.getElementById('vv-step1').style.display = 'none';
  document.getElementById('vv-step2').style.display = 'block';
  if (apiKey && apiKey.ok) {
    const btn    = document.getElementById('vv-key-btn');
    const status = document.getElementById('vv-key-status');
    if (btn)    { btn.textContent = 'Created ✓'; btn.disabled = true; btn.style.opacity = '.6'; }
    if (status) { status.textContent = '✓ API key created automatically'; status.style.color = '#4a8'; }
  }
  vvRunPopulate();
  vvLoadChecklist();
}

// ── Populate ──────────────────────────────────────────────────────────────────
function vvRunPopulate() {
  const el = document.getElementById('vv-populate-status');
  fetch('/plugins/varaverk/api/setup.php', {
    method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    body: new URLSearchParams({action: 'populate'})
  }).then(r => r.json()).then(d => {
    if (d.ok) {
      const found = (d.lines || []).filter(l => /✅|found|detected/i.test(l));
      el.textContent = found.length
        ? '✓ Auto-populate: ' + found.length + ' field' + (found.length > 1 ? 's' : '') + ' detected'
        : '✓ Auto-populate ran — arr keys will fill once services are running';
      el.style.color = '#4a8';
    } else {
      el.textContent = 'Auto-populate skipped — run Tools/conf_populate.sh once your arr containers are up';
      el.style.color = '#555';
    }
    vvLoadChecklist();
  }).catch(() => {
    el.textContent = 'Auto-populate unavailable — run manually from Scheduler';
    el.style.color = '#555';


335	}
336
337	// ── Unraid API Keys ───────────────────────────────────────────────────────────
338	function vvApiCheck() {
339	  const btn   = document.getElementById('vv-api-check-btn');
340	  const badge = document.getElementById('vv-api-badge');
341	  const hosts = document.getElementById('vv-api-hosts');
342	  btn.disabled = true; btn.textContent = 'Checking…';
343	  fetch('/plugins/varaverk/api/storage.php?action=api_status&_=' + Date.now())
344	    .then(r => r.json())
345	    .then(d => {
346	      btn.disabled = false; btn.textContent = '↻ Check status';
347	      if (!d.ok) return;
348	      const allOk = d.hosts.every(h => h.api_ok);
349	      const anyMissing = d.hosts.some(h => !h.key_present);
350	      badge.textContent = allOk ? 'all ok' : anyMissing ? 'keys missing' : 'partial';
351	      badge.className   = 'vv-set-badge ' + (allOk ? 'internal' : 'flash');
352
353	      if (hosts) hosts.innerHTML = d.hosts.map(h => {
354	        const tag    = h.is_local
355	          ? `<span style="color:#2a3a2a;font-size:9px;margin-left:4px;">own</span>`
356	          : `<span style="color:#2a2a3a;font-size:9px;margin-left:4px;">${d.my_id}'s key on ${h.host_id}</span>`;
357	        const keyBit = h.key_present
358	          ? `<code style="font-size:9px;color:#4a6a4a;">${h.key_preview}</code>`
359	          : `<span style="color:#8b2a2a;font-size:10px;">not set — click Setup</span>`;
360	        const dot      = h.api_ok ? '#4caf50' : (h.key_present ? '#ffb74d' : '#444');
361	        // Key name in that machine's registry: always Varaverk_<MY_ID>
362	        const keyName  = `Varaverk_${d.my_id}`;
363	        const registry = h.is_local ? h.host_id : h.host_id;
364	        return `

365
366	          ${h.host_id}
367	          ${keyName}
368	          on ${registry}
369	          ${tag}
370	          ${keyBit}
371
`;
372	      }).join('');
373	    })
374	    .catch(() => { btn.disabled = false; btn.textContent = '↻ Check status'; });
375	}
376
377	function vvApiRenew(allHosts) {
378	  const localBtn = document.getElementById('vv-api-local-btn');
379	  const allBtn   = document.getElementById('vv-api-all-btn');
380	  const out      = document.getElementById('vv-api-out');
381	  const fb       = document.getElementById('vv-api-fb');
382	  const badge    = document.getElementById('vv-api-badge');
383	  const activeBtn = allHosts ? allBtn : localBtn;
384
385	  [localBtn, allBtn].forEach(b => { if(b) b.disabled = true; });
386	  activeBtn.textContent = allHosts ? 'Setting up…' : 'Renewing…';
387	  fb.textContent = ''; out.textContent = ''; out.style.display = '';
388
389	  const fd = new FormData();
390	  fd.append('action',    'setup_apikeys');
391	  fd.append('all_hosts', allHosts ? '1' : '0');
392
393	  fetch('/plugins/varaverk/api/storage.php', { method: 'POST', body: fd })
394	    .then(r => r.json())
395	    .then(d => {
396	      localBtn.disabled = false; localBtn.textContent = 'Renew local';
397	      allBtn.disabled   = false; allBtn.textContent   = 'Setup all host keys';
398	      out.textContent   = d.output || '(no output)';
399	      out.scrollTop     = out.scrollHeight;
400	      fb.style.color    = d.ok ? '#4caf50' : '#ef5350';
401	      fb.textContent    = d.ok ? '✓ Done' : 'Failed — see output';
402	      if (d.ok) { badge.textContent = ''; vvApiCheck(); }
403	    })
404	    .catch(() => {
405	      localBtn.disabled = false; localBtn.textContent = 'Renew local';
406	      allBtn.disabled   = false; allBtn.textContent   = 'Setup all host keys';
407	      fb.style.color = '#ef5350'; fb.textContent = 'Request failed';
408	    });
409	}
410
411	// Auto-check on load
412	vvApiCheck();
413	</script>
414

}

// ── Unraid API Keys ───────────────────────────────────────────────────────────
function vvApiCheck() {
  const btn   = document.getElementById('vv-api-check-btn');
  const badge = document.getElementById('vv-api-badge');
  const hosts = document.getElementById('vv-api-hosts');
  btn.disabled = true; btn.textContent = 'Checking…';
  fetch('/plugins/varaverk/api/storage.php?action=api_status&_=' + Date.now())
    .then(r => r.json())
    .then(d => {
      btn.disabled = false; btn.textContent = '↻ Check status';
      if (!d.ok) return;
      const allOk = d.hosts.every(h => h.api_ok);
      const anyMissing = d.hosts.some(h => !h.key_present);
      badge.textContent = allOk ? 'all ok' : anyMissing ? 'keys missing' : 'partial';
      badge.className   = 'vv-set-badge ' + (allOk ? 'internal' : 'flash');

      if (hosts) hosts.innerHTML = d.hosts.map(h => {
        const tag    = h.is_local
          ? `<span style="color:#2a3a2a;font-size:9px;margin-left:4px;">own</span>`
          : `<span style="color:#2a2a3a;font-size:9px;margin-left:4px;">${d.my_id}'s key on ${h.host_id}</span>`;
        const keyBit = h.key_present
          ? `<code style="font-size:9px;color:#4a6a4a;">${h.key_preview}</code>`
          : `<span style="color:#8b2a2a;font-size:10px;">not set — click Setup</span>`;
        const dot      = h.api_ok ? '#4caf50' : (h.key_present ? '#ffb74d' : '#444');
        // Key name in that machine's registry: always Varaverk_<MY_ID>
        const keyName  = `Varaverk_${d.my_id}`;
        const registry = h.is_local ? h.host_id : h.host_id;
        return `

          ${h.host_id}
          ${keyName}
          on ${registry}
          ${tag}
          ${keyBit}

`;
      }).join('');
    })
    .catch(() => { btn.disabled = false; btn.textContent = '↻ Check status'; });
}

function vvApiRenew(allHosts) {
  const localBtn = document.getElementById('vv-api-local-btn');
  const allBtn   = document.getElementById('vv-api-all-btn');
  const out      = document.getElementById('vv-api-out');
  const fb       = document.getElementById('vv-api-fb');
  const badge    = document.getElementById('vv-api-badge');
  const activeBtn = allHosts ? allBtn : localBtn;

  [localBtn, allBtn].forEach(b => { if(b) b.disabled = true; });
  activeBtn.textContent = allHosts ? 'Setting up…' : 'Renewing…';
  fb.textContent = ''; out.textContent = ''; out.style.display = '';

  const fd = new FormData();
  fd.append('action',    'setup_apikeys');
  fd.append('all_hosts', allHosts ? '1' : '0');

  fetch('/plugins/varaverk/api/storage.php', { method: 'POST', body: fd })
    .then(r => r.json())
    .then(d => {
      localBtn.disabled = false; localBtn.textContent = 'Renew local';
      allBtn.disabled   = false; allBtn.textContent   = 'Setup all host keys';
      out.textContent   = d.output || '(no output)';
      out.scrollTop     = out.scrollHeight;
      fb.style.color    = d.ok ? '#4caf50' : '#ef5350';
      fb.textContent    = d.ok ? '✓ Done' : 'Failed — see output';
      if (d.ok) { badge.textContent = ''; vvApiCheck(); }
    })
    .catch(() => {
      localBtn.disabled = false; localBtn.textContent = 'Renew local';
      allBtn.disabled   = false; allBtn.textContent   = 'Setup all host keys';
      fb.style.color = '#ef5350'; fb.textContent = 'Request failed';
    });
}

// Auto-check on load
vvApiCheck();
</script>


Three categories of orphaned code across these files:

1. `settings.php` — PHP vars `$_apiKey*` declared but never used in rendering
2. `storage.php` `api_status` — dead ternary `$isLocal ? $myId : $myId`, wrong key name format `Varaverk_HOST1`
3. `storage.php` + `settings.php` — `--all-hosts` flag that doesn't exist in the shell script; "Setup all host keys" and "Renew local" both do identical things since the script always pushes to partners
4. `settings.php` JS — `keyName = \`Varaverk_${d.my_id}\`` shows `Varaverk_HOST1` when the real registry name is `Varaverk Gmer4Lfe`; plus dead ternary `h.is_local ? h.host_id : h.host_id`

Let me read both files in full to make precise edits:


1	<?php
2	$_myHost  = vv_detect_host();
3	$_vars    = vv_conf_vars();
4	$_myId    = strtoupper($_myHost);
5
6	// Storage
7	$_confKey = $_myId . '_STORAGE_MODE_INTERNAL';
8	$_confVal = $_vars[$_confKey] ?? null;
9
10	// Notifications
11	$_notifyUnraid  = ($_vars['NOTIFY_UNRAID']  ?? 'true')  !== 'false';
12	$_enableLogging = ($_vars['ENABLE_LOGGING'] ?? 'false') === 'true';
13	$_discordKey    = $_myId . '_DISCORD_WEBHOOK';
14	$_discordHook   = $_vars[$_discordKey] ?? '';
15
16	// API key
17	$_apiKeyVar  = $_myId . '_UNRAID_API_KEY';
18	$_apiKey     = $_vars[$_apiKeyVar] ?? '';
19	$_apiPreview = $_apiKey ? substr($_apiKey, 0, 8) . '...' . substr($_apiKey, -4) : '';
20	?>
21
62
63

64
65
66

67

68	      Storage Location
69	      …
70

71
72
Loading…

73
74

75
76

77	      Varaverk stores all scripts, configuration, state files, and the git repository in
78	      SCRIPTS_DIR.
79

80	      Internal NVMe/SSD — direct access, zero write-wear concern, git pull/push from boot volume.

81	      USB Flash — preserves flash lifetime. Requires array to be started. git pull syncs
82	      Plugin/
83	      back to /boot/
84	      after each pull so the webUI always stays current.
85

86
87

88	      Array must be started for Varaverk to function in flash mode. The plugin tab is always accessible.
89

90

91	      Array is not started — cannot migrate to flash mode right now. Start the array first.
92

93
94

95	      <button id="vv-stor-migrate-btn" class="vv-set-btn" onclick="vvStorMigrate()" style="display:none;"></button>
96	      <button class="vv-set-btn" onclick="vvStorLoad()" style="font-size:10px;padding:4px 10px;">↻ Refresh</button>
97
98

99
100

101

102
103
104

105
Notifications

106
107

108
109	        <div class="vv-set-tog-track<?= $_notifyUnraid ? ' on' : '' ?>" id="vv-ntf-unraid"
110	             onclick="vvNtfToggle(this,'NOTIFY_UNRAID')">

111

112
Unraid native notifications

113
Appears in the Unraid bell icon — job completions, warnings, errors

114

115
116
117	        <div class="vv-set-tog-track<?= $_enableLogging ? ' on' : '' ?>" id="vv-ntf-logging"
118	             onclick="vvNtfToggle(this,'ENABLE_LOGGING')">

119

120
Verbose logging

121
Adds detailed [LOG] lines to script output — useful for debugging scheduled runs

122

123
124

125
126

127
128

129

130	        Discord webhook
131	        <?= htmlspecialchars($_discordKey) ?>
132

133

134	        <input class="vv-set-inp" id="vv-ntf-discord" type="text"
135	               placeholder="https://discord.com/api/webhooks/…"
136	               value="<?= htmlspecialchars($_discordHook) ?>">
137	        <button class="vv-set-btn primary" onclick="vvNtfSaveWebhook()" id="vv-ntf-save"
138	                style="white-space:nowrap;flex-shrink:0;">Save</button>
139

140
Leave blank to disable Discord notifications for this host

141

142
143

144
145
146

147

148	      Unraid API Keys
149	      …
150

151
152

153
154

155	      Each host registers
156	      Varaverk_HOST1
157	      in every machine's local Unraid registry — own machine plus all partners.
158	      The key values are stored in the private host*.conf
159	      so each host can query every other host's GraphQL API directly — real-time monitoring, no SSH.
160	      During offboard, partner keys are removed from each registry automatically.
161

162
163

164	      <button class="vv-set-btn" onclick="vvApiCheck()" id="vv-api-check-btn">↻ Check status</button>
165	      <button class="vv-set-btn" onclick="vvApiRenew(false)" id="vv-api-local-btn">Renew local</button>
166	      <button class="vv-set-btn primary" onclick="vvApiRenew(true)" id="vv-api-all-btn">Setup all host keys</button>
167
168

169

170

171
172

173
174
414

<?php
$_myHost  = vv_detect_host();
$_vars    = vv_conf_vars();
$_myId    = strtoupper($_myHost);

// Storage
$_confKey = $_myId . '_STORAGE_MODE_INTERNAL';
$_confVal = $_vars[$_confKey] ?? null;

// Notifications
$_notifyUnraid  = ($_vars['NOTIFY_UNRAID']  ?? 'true')  !== 'false';
$_enableLogging = ($_vars['ENABLE_LOGGING'] ?? 'false') === 'true';
$_discordKey    = $_myId . '_DISCORD_WEBHOOK';
$_discordHook   = $_vars[$_discordKey] ?? '';

// API key
$_apiKeyVar  = $_myId . '_UNRAID_API_KEY';
$_apiKey     = $_vars[$_apiKeyVar] ?? '';
$_apiPreview = $_apiKey ? substr($_apiKey, 0, 8) . '...' . substr($_apiKey, -4) : '';
?>

      Storage Location
      …

Loading…

      Varaverk stores all scripts, configuration, state files, and the git repository in
      SCRIPTS_DIR.

      Internal NVMe/SSD — direct access, zero write-wear concern, git pull/push from boot volume.

      USB Flash — preserves flash lifetime. Requires array to be started. git pull syncs
      Plugin/
      back to /boot/
      after each pull so the webUI always stays current.

      Array must be started for Varaverk to function in flash mode. The plugin tab is always accessible.

      Array is not started — cannot migrate to flash mode right now. Start the array first.

      <button id="vv-stor-migrate-btn" class="vv-set-btn" onclick="vvStorMigrate()" style="display:none;"></button>
      <button class="vv-set-btn" onclick="vvStorLoad()" style="font-size:10px;padding:4px 10px;">↻ Refresh</button>

Notifications

        <div class="vv-set-tog-track<?= $_notifyUnraid ? ' on' : '' ?>" id="vv-ntf-unraid"
             onclick="vvNtfToggle(this,'NOTIFY_UNRAID')">

Unraid native notifications

Appears in the Unraid bell icon — job completions, warnings, errors

        <div class="vv-set-tog-track<?= $_enableLogging ? ' on' : '' ?>" id="vv-ntf-logging"
             onclick="vvNtfToggle(this,'ENABLE_LOGGING')">

Verbose logging

Adds detailed [LOG] lines to script output — useful for debugging scheduled runs

        Discord webhook
        <?= htmlspecialchars($_discordKey) ?>

        <input class="vv-set-inp" id="vv-ntf-discord" type="text"
               placeholder="https://discord.com/api/webhooks/…"
               value="<?= htmlspecialchars($_discordHook) ?>">
        <button class="vv-set-btn primary" onclick="vvNtfSaveWebhook()" id="vv-ntf-save"
                style="white-space:nowrap;flex-shrink:0;">Save</button>

Leave blank to disable Discord notifications for this host

      Unraid API Keys
      …

      Each host registers
      Varaverk_HOST1
      in every machine's local Unraid registry — own machine plus all partners.
      The key values are stored in the private host*.conf
      so each host can query every other host's GraphQL API directly — real-time monitoring, no SSH.
      During offboard, partner keys are removed from each registry automatically.

      <button class="vv-set-btn" onclick="vvApiCheck()" id="vv-api-check-btn">↻ Check status</button>
      <button class="vv-set-btn" onclick="vvApiRenew(false)" id="vv-api-local-btn">Renew local</button>
      <button class="vv-set-btn primary" onclick="vvApiRenew(true)" id="vv-api-all-btn">Setup all host keys</button>


95	    exit;
96	}
97
98	// ── Unraid API key status ─────────────────────────────────────────────────────
99	// Keys live in host*.conf (private, not master.conf):
100	//   host1.conf: HOST1_UNRAID_API_KEY (own) + HOST2_UNRAID_API_KEY (HOST1's access to HOST2)
101	//   host2.conf: HOST2_UNRAID_API_KEY (own) + HOST1_UNRAID_API_KEY (HOST2's access to HOST1)
102	if ($action === 'api_status') {
103	    require_once dirname(__DIR__) . '/include/unraid_api.php';
104	    $localStatus = vv_api_get_status();
105	    $vars        = vv_conf_vars();
106	    $myHost      = vv_detect_host();
107	    $myId        = strtoupper($myHost);
108
109	    $hosts = [];
110	    foreach ($vars as $k => $v) {
111	        if (!preg_match('/^HOST(\d+)$/', $k, $m) || !$v) continue;
112	        $id      = 'HOST' . $m[1];
113	        $keyVar  = $id . '_UNRAID_API_KEY';
114	        $key     = $vars[$keyVar] ?? '';
115	        $isLocal = ($id === $myId);
116	        // For local: key is Varaverk_HOST1 registered on own machine
117	        // For remote: key is Varaverk_HOST1 registered on HOST2's machine (stored in host1.conf)
118	        $hosts[] = [
119	            'host_id'     => $id,
120	            'hostname'    => $v,
121	            'is_local'    => $isLocal,
122	            'key_var'     => $keyVar,
123	            'key_name'    => 'Varaverk_' . ($isLocal ? $myId : $myId), // Varaverk_HOST1 on that registry
124	            'key_present' => !empty($key),
125	            'key_preview' => $key ? substr($key, 0, 8) . '...' . substr($key, -4) : null,
126	            'api_ok'      => $isLocal
127	                             ? (!$localStatus['key_missing'] && $localStatus['available'])
128	                             : !empty($key),
129	        ];
130	    }
131	    usort($hosts, fn($a, $b) => strcmp($a['host_id'], $b['host_id']));
132
133	    echo json_encode([
134	        'ok'        => true,
135	        'my_id'     => $myId,
136	        'hosts'     => $hosts,
137	        'fallbacks' => $localStatus['fallbacks'],
138	    ]);
139	    exit;
140	}
141
142	// ── Setup/renew API keys (local + all partners via SSH) ───────────────────────
143	if ($action === 'setup_apikeys' && $_SERVER['REQUEST_METHOD'] === 'POST') {
144	    $script = SCRIPTS_DIR . '/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh';
145	    if (!file_exists($script)) {
146	        echo json_encode(['ok' => false, 'error' => 'unraid_api_key_renew.sh not found']); exit;
147	    }
148	    $allHosts = ($_POST['all_hosts'] ?? '0') === '1';
149	    $flags    = $allHosts ? ' --all-hosts' : '';
150	    set_time_limit(60);
151	    $output = []; $exit = 0;
152	    exec('bash ' . escapeshellarg($script) . $flags . ' 2>&1', $output, $exit);
153	    echo json_encode(['ok' => $exit === 0, 'exit' => $exit, 'output' => implode("\n", $output)]);
154	    exit;
155	}
156
157	echo json_encode(['ok' => false, 'error' => 'Unknown action']);
158

    exit;
}

// ── Unraid API key status ─────────────────────────────────────────────────────
// Keys live in host*.conf (private, not master.conf):
//   host1.conf: HOST1_UNRAID_API_KEY (own) + HOST2_UNRAID_API_KEY (HOST1's access to HOST2)
//   host2.conf: HOST2_UNRAID_API_KEY (own) + HOST1_UNRAID_API_KEY (HOST2's access to HOST1)
if ($action === 'api_status') {
    require_once dirname(__DIR__) . '/include/unraid_api.php';
    $localStatus = vv_api_get_status();
    $vars        = vv_conf_vars();
    $myHost      = vv_detect_host();
    $myId        = strtoupper($myHost);

    $hosts = [];
    foreach ($vars as $k => $v) {
        if (!preg_match('/^HOST(\d+)$/', $k, $m) || !$v) continue;
        $id      = 'HOST' . $m[1];
        $keyVar  = $id . '_UNRAID_API_KEY';
        $key     = $vars[$keyVar] ?? '';
        $isLocal = ($id === $myId);
        // For local: key is Varaverk_HOST1 registered on own machine
        // For remote: key is Varaverk_HOST1 registered on HOST2's machine (stored in host1.conf)
        $hosts[] = [
            'host_id'     => $id,
            'hostname'    => $v,
            'is_local'    => $isLocal,
            'key_var'     => $keyVar,
            'key_name'    => 'Varaverk_' . ($isLocal ? $myId : $myId), // Varaverk_HOST1 on that registry
            'key_present' => !empty($key),
            'key_preview' => $key ? substr($key, 0, 8) . '...' . substr($key, -4) : null,
            'api_ok'      => $isLocal
                             ? (!$localStatus['key_missing'] && $localStatus['available'])
                             : !empty($key),
        ];
    }
    usort($hosts, fn($a, $b) => strcmp($a['host_id'], $b['host_id']));

    echo json_encode([
        'ok'        => true,
        'my_id'     => $myId,
        'hosts'     => $hosts,
        'fallbacks' => $localStatus['fallbacks'],
    ]);
    exit;
}

// ── Setup/renew API keys (local + all partners via SSH) ───────────────────────
if ($action === 'setup_apikeys' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $script = SCRIPTS_DIR . '/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh';
    if (!file_exists($script)) {
        echo json_encode(['ok' => false, 'error' => 'unraid_api_key_renew.sh not found']); exit;
    }
    $allHosts = ($_POST['all_hosts'] ?? '0') === '1';
    $flags    = $allHosts ? ' --all-hosts' : '';
    set_time_limit(60);
    $output = []; $exit = 0;
    exec('bash ' . escapeshellarg($script) . $flags . ' 2>&1', $output, $exit);
    echo json_encode(['ok' => $exit === 0, 'exit' => $exit, 'output' => implode("\n", $output)]);
    exit;
}

echo json_encode(['ok' => false, 'error' => 'Unknown action']);


Now I have everything. Four focused changes:

**`storage.php`:** remove dead `key_name` per-host, fix stale comment, add correct `key_name` at top level; drop `$allHosts`/`$flags` dead code.

**`settings.php`:** drop unused PHP vars; fix description text; collapse two identical buttons into one; fix JS `keyName` to use `d.key_name`; remove dead ternary `h.is_local ? h.host_id : h.host_id`.


Edit

// ── Unraid API key status ─────────────────────────────────────────────────────
// Keys live in host*.conf (private, not master.conf):
//   host1.conf: HOST1_UNRAID_API_KEY (own) + HOST2_UNRAID_API_KEY (HOST1's access to HOST2)
//   host2.conf: HOST2_UNRAID_API_KEY (own) + HOST1_UNRAID_API_KEY (HOST2's access to HOST1)
if ($action === 'api_status') {
    require_once dirname(__DIR__) . '/include/unraid_api.php';
    $localStatus = vv_api_get_status();
    $vars        = vv_conf_vars();
    $myHost      = vv_detect_host();
    $myId        = strtoupper($myHost);

    $hosts = [];
    foreach ($vars as $k => $v) {
        if (!preg_match('/^HOST(\d+)$/', $k, $m) || !$v) continue;
        $id      = 'HOST' . $m[1];
        $keyVar  = $id . '_UNRAID_API_KEY';
        $key     = $vars[$keyVar] ?? '';
        $isLocal = ($id === $myId);
        // For local: key is Varaverk_HOST1 registered on own machine
        // For remote: key is Varaverk_HOST1 registered on HOST2's machine (stored in host1.conf)
        $hosts[] = [
            'host_id'     => $id,
            'hostname'    => $v,
            'is_local'    => $isLocal,
            'key_var'     => $keyVar,
            'key_name'    => 'Varaverk_' . ($isLocal ? $myId : $myId), // Varaverk_HOST1 on that registry
            'key_present' => !empty($key),
            'key_preview' => $key ? substr($key, 0, 8) . '...' . substr($key, -4) : null,
            'api_ok'      => $isLocal
                             ? (!$localStatus['key_missing'] && $localStatus['available'])
                             : !empty($key),
        ];
    }
    usort($hosts, fn($a, $b) => strcmp($a['host_id'], $b['host_id']));

    echo json_encode([
        'ok'        => true,
        'my_id'     => $myId,
        'hosts'     => $hosts,
        'fallbacks' => $localStatus['fallbacks'],
    ]);
    exit;
}

// ── Setup/renew API keys (local + all partners via SSH) ───────────────────────
if ($action === 'setup_apikeys' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $script = SCRIPTS_DIR . '/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh';
    if (!file_exists($script)) {
        echo json_encode(['ok' => false, 'error' => 'unraid_api_key_renew.sh not found']); exit;
    }
    $allHosts = ($_POST['all_hosts'] ?? '0') === '1';
    $flags    = $allHosts ? ' --all-hosts' : '';
    set_time_limit(60);
    $output = []; $exit = 0;
    exec('bash ' . escapeshellarg($script) . $flags . ' 2>&1', $output, $exit);
    echo json_encode(['ok' => $exit === 0, 'exit' => $exit, 'output' => implode("\n", $output)]);
    exit;
}

// ── Unraid API key status ─────────────────────────────────────────────────────
if ($action === 'api_status') {
    require_once dirname(__DIR__) . '/include/unraid_api.php';
    $localStatus = vv_api_get_status();
    $vars        = vv_conf_vars();
    $myHost      = vv_detect_host();
    $myId        = strtoupper($myHost);
    $hn          = trim((string)shell_exec("hostname -s 2>/dev/null | sed 's/^[Uu][Nn][Rr][Aa][Ii][Dd]-//'")) ?: 'Varaverk';
    $localKeyName = 'Varaverk ' . $hn;

    $hosts = [];
    foreach ($vars as $k => $v) {
        if (!preg_match('/^HOST(\d+)$/', $k, $m) || !$v) continue;
        $id      = 'HOST' . $m[1];
        $keyVar  = $id . '_UNRAID_API_KEY';
        $key     = $vars[$keyVar] ?? '';
        $isLocal = ($id === $myId);
        $hosts[] = [
            'host_id'     => $id,
            'hostname'    => $v,
            'is_local'    => $isLocal,
            'key_var'     => $keyVar,
            'key_present' => !empty($key),
            'key_preview' => $key ? substr($key, 0, 8) . '...' . substr($key, -4) : null,
            'api_ok'      => $isLocal
                             ? (!$localStatus['key_missing'] && $localStatus['available'])
                             : !empty($key),
        ];
    }
    usort($hosts, fn($a, $b) => strcmp($a['host_id'], $b['host_id']));

    echo json_encode([
        'ok'        => true,
        'my_id'     => $myId,
        'key_name'  => $localKeyName,
        'hosts'     => $hosts,
        'fallbacks' => $localStatus['fallbacks'],
    ]);
    exit;
}

// ── Setup/renew API keys (local + all partners via SSH) ───────────────────────
if ($action === 'setup_apikeys' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $script = SCRIPTS_DIR . '/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh';
    if (!file_exists($script)) {
        echo json_encode(['ok' => false, 'error' => 'unraid_api_key_renew.sh not found']); exit;
    }
    set_time_limit(60);
    $output = []; $exit = 0;
    exec('bash ' . escapeshellarg($script) . ' 2>&1', $output, $exit);
    echo json_encode(['ok' => $exit === 0, 'exit' => $exit, 'output' => implode("\n", $output)]);
    exit;
}


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

// ── Unraid API key status ─────────────────────────────────────────────────────
if ($action === 'api_status') {
    require_once dirname(__DIR__) . '/include/unraid_api.php';
    $localStatus = vv_api_get_status();
    $vars        = vv_conf_vars();
    $myHost      = vv_detect_host();
    $myId        = strtoupper($myHost);
    $hn          = trim((string)shell_exec("hostname -s 2>/dev/null | sed 's/^[Uu][Nn][Rr][Aa][Ii][Dd]-//'")) ?: 'Varaverk';
    $localKeyName = 'Varaverk ' . $hn;

    $hosts = [];
    foreach ($vars as $k => $v) {
        if (!preg_match('/^HOST(\d+)$/', $k, $m) || !$v) continue;
        $id      = 'HOST' . $m[1];
        $keyVar  = $id . '_UNRAID_API_KEY';
        $key     = $vars[$keyVar] ?? '';
        $isLocal = ($id === $myId);
        $hosts[] = [
            'host_id'     => $id,
            'hostname'    => $v,
            'is_local'    => $isLocal,
            'key_var'     => $keyVar,
            'key_present' => !empty($key),
            'key_preview' => $key ? substr($key, 0, 8) . '...' . substr($key, -4) : null,
            'api_ok'      => $isLocal
                             ? (!$localStatus['key_missing'] && $localStatus['available'])
                             : !empty($key),
        ];
    }
    usort($hosts, fn($a, $b) => strcmp($a['host_id'], $b['host_id']));

    echo json_encode([
        'ok'        => true,
        'my_id'     => $myId,
        'key_name'  => $localKeyName,
        'hosts'     => $hosts,
        'fallbacks' => $localStatus['fallbacks'],
    ]);
    exit;
}

// ── Setup/renew API keys (local + all partners via SSH) ───────────────────────
if ($action === 'setup_apikeys' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $script = SCRIPTS_DIR . '/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh';
    if (!file_exists($script)) {
        echo json_encode(['ok' => false, 'error' => 'unraid_api_key_renew.sh not found']); exit;
    }
    set_time_limit(60);
    $output = []; $exit = 0;
    exec('bash ' . escapeshellarg($script) . ' 2>&1', $output, $exit);
    echo json_encode(['ok' => $exit === 0, 'exit' => $exit, 'output' => implode("\n", $output)]);
    exit;
}

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

$action = $_GET['action'] ?? $_POST['action'] ?? '';

// ── Boot device detection ─────────────────────────────────────────────────────
function vv_storage_detect_transport(): string {
    $part = trim(shell_exec("findmnt -n -o SOURCE /boot 2>/dev/null") ?: '');
    if (!$part) return 'unknown';
    $disk = trim(shell_exec("lsblk -no pkname " . escapeshellarg($part) . " 2>/dev/null") ?: '');
    if (!$disk) return 'unknown';
    return strtolower(trim(shell_exec("lsblk -dno TRAN /dev/" . escapeshellarg($disk) . " 2>/dev/null") ?: 'unknown'));
}

// ── Current mode status ───────────────────────────────────────────────────────
if ($action === 'status') {
    $transport   = vv_storage_detect_transport();
    $detected    = ($transport === 'usb') ? 'flash' : 'internal';
    $currentDir  = SCRIPTS_DIR;
    $internalDir = '/boot/config/plugins/varaverk';
    $flashDir    = '/mnt/user/appdata/Varaverk';
    $currentMode = ($currentDir === $internalDir) ? 'internal'
                 : ($currentDir === $flashDir     ? 'flash' : 'custom');

    $myHost  = vv_detect_host();
    $vars    = vv_conf_vars();
    $confKey = strtoupper($myHost) . '_STORAGE_MODE_INTERNAL';
    $confVal = $vars[$confKey] ?? null;

    // Boot device name for display
    $bootPart = trim(shell_exec("findmnt -n -o SOURCE /boot 2>/dev/null") ?: '');
    $bootDisk = $bootPart ? trim(shell_exec("lsblk -no pkname " . escapeshellarg($bootPart) . " 2>/dev/null") ?: '') : '';

    echo json_encode([
        'ok'           => true,
        'current_mode' => $currentMode,
        'current_dir'  => $currentDir,
        'internal_dir' => $internalDir,
        'flash_dir'    => $flashDir,
        'transport'    => $transport,
        'detected'     => $detected,
        'boot_disk'    => $bootDisk ? '/dev/' . $bootDisk : 'unknown',
        'conf_key'     => $confKey,
        'conf_val'     => $confVal,
        'array_started'=> is_dir('/mnt/user') && count(scandir('/mnt/user')) > 2,
    ]);
    exit;
}

// ── Run migration ─────────────────────────────────────────────────────────────
if ($action === 'migrate' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $to = trim($_POST['to'] ?? '');
    if (!in_array($to, ['internal', 'flash'], true)) {
        echo json_encode(['ok' => false, 'error' => 'Invalid target: must be internal or flash']);
        exit;
    }

    $script = dirname(__DIR__) . '/Tools/storage_migrate.sh';
    if (!file_exists($script)) {
        echo json_encode(['ok' => false, 'error' => 'storage_migrate.sh not found']);
        exit;
    }

    set_time_limit(300);
    $output = [];
    $exit   = 0;
    exec('bash ' . escapeshellarg($script) . ' --to=' . escapeshellarg($to) . ' 2>&1', $output, $exit);

    echo json_encode([
        'ok'     => $exit === 0,
        'exit'   => $exit,
        'output' => implode("\n", $output),
    ]);
    exit;
}

// ── Auto-detect and write to conf ─────────────────────────────────────────────
if ($action === 'detect' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $transport = vv_storage_detect_transport();
    $detected  = ($transport === 'usb') ? 'false' : 'true';
    $myHost    = vv_detect_host();
    $confKey   = strtoupper($myHost) . '_STORAGE_MODE_INTERNAL';
    $confFile  = $myHost . '.conf';

    $results = vv_conf_write_changes([[
        'file'  => $confFile,
        'key'   => $confKey,
        'value' => $detected,
        'type'  => 'scalar',
    ]]);

    $ok = !in_array(false, $results, true);
    echo json_encode(['ok' => $ok, 'detected' => $detected, 'transport' => $transport]);
    exit;
}

// ── Unraid API key status ─────────────────────────────────────────────────────
// Keys live in host*.conf (private, not master.conf):
//   host1.conf: HOST1_UNRAID_API_KEY (own) + HOST2_UNRAID_API_KEY (HOST1's access to HOST2)
//   host2.conf: HOST2_UNRAID_API_KEY (own) + HOST1_UNRAID_API_KEY (HOST2's access to HOST1)
if ($action === 'api_status') {
    require_once dirname(__DIR__) . '/include/unraid_api.php';
    $localStatus = vv_api_get_status();
    $vars        = vv_conf_vars();
    $myHost      = vv_detect_host();
    $myId        = strtoupper($myHost);

    $hosts = [];
    foreach ($vars as $k => $v) {
        if (!preg_match('/^HOST(\d+)$/', $k, $m) || !$v) continue;
        $id      = 'HOST' . $m[1];
        $keyVar  = $id . '_UNRAID_API_KEY';
        $key     = $vars[$keyVar] ?? '';
        $isLocal = ($id === $myId);
        // For local: key is Varaverk_HOST1 registered on own machine
        // For remote: key is Varaverk_HOST1 registered on HOST2's machine (stored in host1.conf)
        $hosts[] = [
            'host_id'     => $id,
            'hostname'    => $v,
            'is_local'    => $isLocal,
            'key_var'     => $keyVar,
            'key_name'    => 'Varaverk_' . ($isLocal ? $myId : $myId), // Varaverk_HOST1 on that registry
            'key_present' => !empty($key),
            'key_preview' => $key ? substr($key, 0, 8) . '...' . substr($key, -4) : null,
            'api_ok'      => $isLocal
                             ? (!$localStatus['key_missing'] && $localStatus['available'])
                             : !empty($key),
        ];
    }
    usort($hosts, fn($a, $b) => strcmp($a['host_id'], $b['host_id']));

    echo json_encode([
        'ok'        => true,
        'my_id'     => $myId,
        'hosts'     => $hosts,
        'fallbacks' => $localStatus['fallbacks'],
    ]);
    exit;
}

// ── Setup/renew API keys (local + all partners via SSH) ───────────────────────
if ($action === 'setup_apikeys' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $script = SCRIPTS_DIR . '/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh';
    if (!file_exists($script)) {
        echo json_encode(['ok' => false, 'error' => 'unraid_api_key_renew.sh not found']); exit;
    }
    $allHosts = ($_POST['all_hosts'] ?? '0') === '1';
    $flags    = $allHosts ? ' --all-hosts' : '';
    set_time_limit(60);
    $output = []; $exit = 0;
    exec('bash ' . escapeshellarg($script) . $flags . ' 2>&1', $output, $exit);
    echo json_encode(['ok' => $exit === 0, 'exit' => $exit, 'output' => implode("\n", $output)]);
    exit;
}

echo json_encode(['ok' => false, 'error' => 'Unknown action']);

-// Keys live in host*.conf (private, not master.conf):

-//   host1.conf: HOST1_UNRAID_API_KEY (own) + HOST2_UNRAID_API_KEY (HOST1's access to HOST2)

-//   host2.conf: HOST2_UNRAID_API_KEY (own) + HOST1_UNRAID_API_KEY (HOST2's access to HOST1)

+    $hn          = trim((string)shell_exec("hostname -s 2>/dev/null | sed 's/^[Uu][Nn][Rr][Aa][Ii][Dd]-//'")) ?: 'Varaverk';

-        // For local: key is Varaverk_HOST1 registered on own machine

-        // For remote: key is Varaverk_HOST1 registered on HOST2's machine (stored in host1.conf)

-            'key_name'    => 'Varaverk_' . ($isLocal ? $myId : $myId), // Varaverk_HOST1 on that registry

-    $flags    = $allHosts ? ' --all-hosts' : '';

-    exec('bash ' . escapeshellarg($script) . $flags . ' 2>&1', $output, $exit);

+    exec('bash ' . escapeshellarg($script) . ' 2>&1', $output, $exit);


Now settings.php — PHP section, HTML, and JS all in one pass:


// API key
$_apiKeyVar  = $_myId . '_UNRAID_API_KEY';
$_apiKey     = $_vars[$_apiKeyVar] ?? '';
$_apiPreview = $_apiKey ? substr($_apiKey, 0, 8) . '...' . substr($_apiKey, -4) : '';
?>


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

// API key
$_apiKeyVar  = $_myId . '_UNRAID_API_KEY';
$_apiKey     = $_vars[$_apiKeyVar] ?? '';
$_apiPreview = $_apiKey ? substr($_apiKey, 0, 8) . '...' . substr($_apiKey, -4) : '';
?>

$_discordKey    = $_myId . '_DISCORD_WEBHOOK';

$_discordHook   = $_vars[$_discordKey] ?? '';

-$_apiKeyVar  = $_myId . '_UNRAID_API_KEY';

-$_apiKey     = $_vars[$_apiKeyVar] ?? '';

-$_apiPreview = $_apiKey ? substr($_apiKey, 0, 8) . '...' . substr($_apiKey, -4) : '';

.vv-set-card   { background:#161616;border:1px solid #2a2a2a;border-radius:6px;padding:14px 16px;margin-bottom:14px; }