'', 'primary' => '', 'role' => '', 'phase' => 0, 'networks' => [], 'networks_src' => '']; $vvMasterRaw = vv_read_conf_raw('master.conf'); if ($vvMasterRaw !== '' && $detectedHostname !== '') { preg_match_all('/^\s*(HOST\d+)\s*=\s*"([^"]*)"/m', $vvMasterRaw, $vvHm, PREG_SET_ORDER); $vvSlots = []; foreach ($vvHm as $m) { $val = trim($m[2]); if ($val !== '') $vvSlots[strtolower($m[1])] = $val; } foreach ($vvSlots as $slot => $name) { if (strcasecmp($name, $detectedHostname) === 0) { $vvDetected['slot'] = $slot; $vvDetected['role'] = ($slot === 'host1') ? 'primary' : 'partner'; break; } } // The primary is whatever HOST1 says, and it is only useful to a host that is not HOST1. if ($vvDetected['role'] === 'partner') $vvDetected['primary'] = $vvSlots['host1'] ?? ''; } // ── How far this host's own onboarding has got ─────────────────────────────────────────────── // Lets the inline join panel show the SSH step or the join button rather than both. Same flags // api/checklist.php reads, and the same two spellings, because the state file has been written // by both bash and PHP over its life. if ($vvDetected['slot'] !== '') { $vvSt = vv_setup_state_read(); $vvSlUp = strtoupper($vvDetected['slot']); $vvDetected['phase'] = (!empty($vvSt[$vvSlUp . '_PHASE2_DONE']) || !empty($vvSt[$vvDetected['slot'] . '_phase2_done'])) ? 2 : ((!empty($vvSt[$vvSlUp . '_PHASE1_DONE']) || !empty($vvSt[$vvDetected['slot'] . '_phase1_done'])) ? 1 : 0); } // ── Is Phase 2 running on the owner right now? ─────────────────────────────────────────────── // Derived here rather than only tracked in the browser, so it survives a reload. The client flag // alone was set when the operator pressed Join and lost on refresh — and refreshing is the first // thing anyone does when a page looks stuck, which put them straight back at the phase-1 panel // the in-progress state exists to replace. // // The evidence is the mirror's own job record: it finishes in about three seconds because all it // does is notify the owner, so "this job ended recently AND our phase is still below 2" is // precisely the window in which the owner is mid-Phase-2. Bounded at 20 minutes so an onboard // abandoned days ago does not render as in progress for ever. $vvPhase2Since = 0; if ($vvDetected['role'] === 'partner' && ($vvDetected['phase'] ?? 0) < 2) { $vvJob = '/var/log/varaverk/Partnership/partnership_onboard.json'; if (is_file($vvJob)) { $vvJ = json_decode((string)@file_get_contents($vvJob), true); if (is_array($vvJ)) { $vvEnd = (int)($vvJ['end'] ?? 0); $vvOk = ($vvJ['status'] ?? '') === 'ok'; if ($vvOk && $vvEnd > 0 && (time() - $vvEnd) < 1200) { $vvPhase2Since = $vvEnd * 1000; } } } } // ── Custom docker networks, adopted from the owner ─────────────────────────────────────────── // master.conf names the hosts; it does not name the networks, which live in each host's own // host*.conf. The owner's copy is available here anyway: onboard Phase 1 caches it into // VV_CONF_RAM_CACHE_DIR as soon as SSH works, before this node has finished setup. // // This is the value a fresh mirror cannot know and cannot be expected to type. host.conf.template // ships NETWORK_CONNECT_NETWORKS with its only entry commented out, so a mirror comes up with an // empty list while the owner deploys containers onto a network named in the owner's templates — // which is exactly how twelve containers ended up created against a network that did not exist. // // Adopting the owner's list also means docker_network_connect.sh will recreate the network here // after an Unraid update wipes it, which is the whole reason that script exists. if ($vvDetected['role'] === 'partner' && $vvDetected['slot'] !== '') { $vvOwnerConf = VV_CONF_RAM_CACHE_DIR . '/host1.conf'; if (is_readable($vvOwnerConf)) { $vvOwnerRaw = (string)@file_get_contents($vvOwnerConf); $vvNets = vv_parse_conf_list($vvOwnerRaw, 'HOST1_NETWORK_CONNECT_NETWORKS'); // br* is ipvlan/macvlan tied to the owner's own hardware — its parent interface does not // transfer, and adopting the name would attach this host's containers to the wrong thing. $vvNets = array_values(array_filter($vvNets, fn($n) => $n !== '' && !preg_match('/^(bridge|host|none|br\d)/i', $n))); if ($vvNets) { $vvDetected['networks'] = $vvNets; $vvDetected['networks_src'] = basename($vvOwnerConf); } } } ?>
HOST1_EMBY_API_KEY — Emby Dashboard → API Keys → + New KeyHOST1_JELLYFIN_API_KEY — Jellyfin Dashboard → Administration → API Keys (if using Jellyfin)HOST1_DISCORD_WEBHOOK — for notifications (optional)HOST1_DAILY_SYNC_SHARES — media paths to rsync nightly