# ━━━━━ PLUGIN / UNRAID / TOOLS ━━━━━

Platform-specific tooling for the WebGUI and the Unraid installation itself. Two of these keep
the UI fast, two are operator tools for specific recovery and migration situations.

They live here rather than in the top-level `Tools/` because each one manipulates something
Unraid-specific — share `.cfg` files, the plugin's own PHP payload builders, or the
installation's storage location.

| File | Type | Runs |
|------|------|------|
| `api_cache_writer.sh` + `.php` | UI cache | **Every minute** via cron |
| `remote_arr_cache_writer.sh` | UI cache | **Every 2 hours** via cron |
| `recreate_shares.sh` | Recovery | Manual — after a rebuild or fresh install |
| `storage_migrate.sh` | Migration | Manual — switching storage mode |

---

## ━━━ THE CACHE WRITERS — WHY PAGES LOAD INSTANTLY ━━━

The monitor and arrs pages present a lot of state: per-core CPU, memory, network, GPUs,
containers, VMs, transcodes, arr library counts. Building that live on every page view means
dozens of API calls and `docker inspect` runs per refresh, from a WebGUI that is already the
first thing to slow down under load.

Instead, both are written to `/tmp/vv_cache` (tmpfs — RAM-speed reads, cleared on reboot) and
the pages serve from there.

### ⚡  `api_cache_writer.sh` — local payloads, every minute

A deliberate one-line shim: `php api_cache_writer.php`. All logic is in the PHP because the
payload builders (`vv_monitor_*`, `vv_arrs_*`) are the **same functions the live API endpoints
call**. A bash reimplementation would be a second version of the same payload, free to drift.

Nothing here is allowed to break the UI:

- A failed run simply leaves the cache unrefreshed — pages fall back to live calls. Slower,
  still correct.
- A missing or unparseable cache is treated as absent, never as empty data.
- `?live=1` bypasses the cache entirely.
- Runs unprivileged with no lock — a torn cache file is replaced within 60 seconds, and every
  reader already has the live fallback.

### 🌐  `remote_arr_cache_writer.sh` — partner payloads, every 2 hours

SSHes to each partner and calls `vv_arrs_local_node()` **on their** PHP stack, caching the
result locally as `arrs_remote_<hostid>.json`.

The partner builds its own payload rather than this host querying the partner's arr APIs
directly. That matters: the partner already has working local URLs and API keys for its own
arrs, so no cross-host credentials and no path mapping are involved. This host never holds
keys for a remote's arrs.

A partner whose Tailscale IP will not resolve is skipped, not fatal — the arrs page shows what
it has and falls back for the rest.

---

## ━━━ THE OPERATOR TOOLS ━━━

### 🗂️  `recreate_shares.sh` — after a rebuild

Recreates share **directories** on the correct disks by reading `/boot/config/shares/*.cfg`
and honouring each share's `shareInclude` disk list.

The `.cfg` files are Unraid's own record of what a share is and which disks it spans — they
are the source of truth, not a list Varaverk maintains. Typically needed on a host after a
full disk replacement or fresh install, where the config survived but the folders did not.

**Create only, never delete.** A directory that already exists is left alone. This runs at a
moment when the operator's picture of what should exist may be out of date, and removing
anything on that basis is how a recovery step becomes a data-loss step.

**Refuses to run without `/mnt/user` mounted.** Creating share directories against an
unmounted array writes them into the underlying root filesystem, where they then shadow the
real shares once the array does mount.

### 📦  `storage_migrate.sh` — moving the whole installation

Relocates Varaverk between the two storage modes:

| Mode | `SCRIPTS_DIR` | Trade-off |
|------|---------------|-----------|
| Internal | `/boot/config/plugins/varaverk` | Direct git pull/push. Available before the array mounts. |
| Flash | `/mnt/user/appdata/Varaverk` | Preserves USB flash lifetime. **Array must be started** for Varaverk to function at all. |

This is the most destructive script in the folder — `rsync --delete`, `cp -a`, `rm -rf` — and
it rewrites the pointers everything else derives from:

```
  varaverk.cfg   SCRIPTS_DIR                    ← the authoritative path
  master.conf    TARGET_DIR
  host*.conf     HOST*_STORAGE_MODE_INTERNAL
  varaverk.cron  regenerated so job paths follow the new SCRIPTS_DIR
```

`STATE_DIR`, `DATA_DIR`, `PERSISTENT_CONF_CACHE` and every orchestrator job path are built
from `SCRIPTS_DIR`. Changing storage mode moves all of them at once, which is why the cron is
**regenerated** rather than edited.

In flash mode, `git_pull_execute.sh` syncs `Plugin/` back to `/boot/` after each pull, so the
WebGUI keeps serving current PHP even though the scripts live in appdata.

---

## ━━━ SAFEGUARDS ━━━

**The cache writers are deliberately unprivileged and lockless.** Their output is regenerable
within a minute and every consumer has a live fallback. There is no privileged operation to
gate and no state worth locking. This is documented in their headers so it is not "corrected"
later.

**The operator tools are the opposite.** Both enforce root, take a lock, and support
`--dry-run`. `storage_migrate.sh` additionally exits cleanly if already in the requested mode,
and removes the old location only once the new one is confirmed in place.

**`recreate_shares.sh` will not act on an unmounted array** — see above; this is the guard
that prevents shadow directories.

---

## ━━━ RELATIONSHIP TO THE WEB UI ━━━

```
  pages/monitor.php ─┐
                     ├─► include/monitor.php  (vv_monitor_*)
  api/monitor.php ───┘            ▲
                                  │
  Tools/api_cache_writer.php ─────┘   same builders → cache can't disagree with live
        │
        └─► /tmp/vv_cache/monitor.json ──► served by api/monitor.php unless ?live=1
```

Adding a metric means adding it in `include/` once. The page, the live endpoint, and the cache
all pick it up together.
