Commit Graph
89 Commits
Author SHA1 Message Date
Gmer4Lfe d9f917ecef Give conf writes a backup, a lock and a read-back before they count
These files are gitignored, so a bad write had nothing to go back to; the escaping order
also stored any value containing a quote truncated but still parseable, which only a
read-back can catch.
2026-08-09 19:07:22 -04:00
Gmer4Lfe 3a45c18555 Document today's restructures, and give data/ a README that ships with it 2026-08-09 00:01:05 -04:00
Gmer4Lfe 38cd3edf99 Scope the migration's busy check to the installation being migrated 2026-08-08 23:53:13 -04:00
Gmer4Lfe e406bd445d Move the data directories too, not only the files inside them 2026-08-08 23:43:57 -04:00
Gmer4Lfe f1603349cc Put everything Varaverk persists under one root, state included 2026-08-08 23:26:46 -04:00
Gmer4Lfe d5c36db531 Put every tmpfs path in one place both layers can read 2026-08-08 22:57:29 -04:00
Gmer4Lfe 1e324636e0 Keep conversations, so a useful answer outlives the tab it was asked in 2026-08-08 22:00:58 -04:00
Gmer4Lfe e0cf83c944 Let one host template upgrade any host's conf
The pull substituted only HOSTN_, so bare HOSTN in comments kept tripping
conf_upgrade's own guard and no host conf had upgraded since the guard landed.
2026-08-08 13:08:24 -04:00
Gmer4Lfe 83508cfb1b Pull partner token ledgers into a RAM cache so fleet totals are fleet-wide 2026-08-04 18:14:38 -04:00
Gmer4Lfe 969a85f303 Record token usage per turn and show daily, weekly and all-time totals by host 2026-08-04 18:04:46 -04:00
Gmer4Lfe 0cf208b80a Remove the Open-WebUI retrieval bridge — Varaverk is the only Ollama client now 2026-08-04 17:11:46 -04:00
Gmer4Lfe 9c8eb8a2bc Refresh the AI index after a pull that changed tracked files
AI_INDEX_ON_PULL was designed, defaulted false, and never read by anything —
so nothing has ever rebuilt the index automatically. A pull is the only thing
that changes tracked files on a server, which makes it the only moment the
index can go stale, and a timer would do nothing 23 times a day while still
drifting from the pull that matters.

Staleness is invisible in the answers: the index keeps returning the old text
with confident, correct-looking citations. Gated on pull success, on
AI_INDEX_ON_PULL and on AI_ENABLED, and never fatal — a git pull must not fail
because an embedding call timed out.
2026-08-03 18:26:13 -04:00
Gmer4Lfe d0b3588f6c Give the assistant a standing memory file
A small operator-written file handed to the model at the start of every
conversation — who you are, how this install is set up, what has already been
decided. Injected ahead of the retrieved passages and marked operator-authored
so it outranks anything they contradict, and never cited as a source.

Deliberately not indexed and deliberately under DATA_DIR: it changes
constantly, vector similarity is the wrong way to retrieve things you were
told to remember, and gitignoring it keeps personal notes out of a pushed
repository. The character cap is a context budget — this text costs its share
of 16k on every single turn.
2026-08-03 17:11:27 -04:00
Gmer4Lfe 8896e05e35 Add a retrieval-only HTTP bridge so Open-WebUI can reach the index
Open-WebUI runs in its own container with no WebGUI session, and nginx applies
auth_request to everything it serves, so it cannot call the plugin API. Same
shape as the arr webhook listener: node outside nginx, shared secret, one
read-only verb. Serves chunks rather than answers because the caller already
has a model loaded.
2026-08-02 17:09:51 -04:00
Gmer4Lfe 2d07c7d80e Refuse a conf_upgrade that would keep nothing
Merging the raw host.conf.template against a live host conf matched HOSTN_
against HOST1_, classified all 140 real keys as deprecated and would have
removed every credential on the host. Also points the Ollama model default
at a tag that still exists.
2026-08-02 12:52:49 -04:00
Gmer4Lfe 6a959fb5e4 Add AI entry points, conf schema, and folder docs
ai_index.sh and ai_query.sh follow the usual conventions — fail-closed gate,
root check, lock, dry-run, status — with Node doing only the vector maths and
SQLite blobs, the same split api_cache_writer.sh uses for PHP.

AI_* and HOST*_OLLAMA_* land in both confs and both templates in this pass.
Everything ships off: AI_ENABLED false, every AI_ASSIST_* false, conf writes
disabled with an empty whitelist. Nothing in the ecosystem consults it.
2026-08-02 01:21:24 -04:00
Gmer4Lfe e2e76560a2 Fix Authelia config path and stale schedule comments in conf templates
The Authelia config default pointed at /mnt/user/appdata/Authelia, which does
not exist — the auth stack lives in the Critical-Data share so it gets synced.
Because conf_populate only fills empty fields, that wrong non-empty default
blocked auto-detection permanently and left the auth page's rules panel dead.

Also corrects three schedule comments that no longer matched varaverk.cron and
two references to scripts that have since been renamed or split.
2026-08-02 00:13:53 -04:00
Gmer4Lfe ab169a6f48 Document the Deployment folder
It was the only folder without docs, and it holds the only versioned record of what
configuration exists — the confs themselves are gitignored.
2026-08-01 23:12:18 -04:00
Gmer4Lfe 132a657f00 Refuse to auto-detect a container when the prefix is ambiguous
On this host "authelia" matches both Authelia and Authelia-Secondary, and taking the first
match writes the wrong instance into the conf every other script then trusts.
2026-08-01 22:44:23 -04:00
Gmer4Lfe 7669cd75b8 Install upgraded confs by atomic rename
A copy truncates the live conf and writes into it, so anything sourcing load_config.sh
during that window reads a partial file with empty path variables.
2026-08-01 22:44:23 -04:00
Gmer4Lfe 19a9f6410d Cache fanart.tv misses so the nightly art run stops re-querying artwork upstream has never had 2026-07-28 18:51:06 -04:00
Gmer4Lfe 74ea1a5218 Run the download orphan cleaner daily — a weekly batch of newly-eligible orphans can exceed the delete cap, which aborts the whole pass and rolls the backlog into an even larger next run 2026-07-27 17:24:40 -04:00
Gmer4Lfe 19036621dc Remove dangling emby-fallback example from host critical-sync template 2026-07-26 10:57:09 -04:00
Gmer4Lfe d96ddc8649 Condense conf redundancy: drop dead emby-fallback profile, no-op PROFILE_RETRY_COUNT, and default-valued profile rows; compose MEDIA patterns and Radarr protected patterns by reference 2026-07-26 10:51:31 -04:00
Gmer4Lfe 895347f20f Add weekly download orphan cleaner — nothing covered the SAB Completed folders and 755G of orphans accumulated since 2022 2026-07-26 09:55:17 -04:00
Gmer4Lfe 9425d16c19 Skip redundant restart for containers docker_update.sh already rebuilt
docker_update.sh rebuilds (stop+recreate) any container whose image
changed, in every mode — but for daily/weekly that was always followed
by the restart script's own unconditional pass, stopping and starting
the same container twice back to back. docker_update.sh now records
which containers it rebuilt this run to a file; docker_daily_restart.sh
and docker_weekly_restart.sh read it and skip those specifically,
still restarting everything else as before. A file older than
DOCKER_UPDATE_REBUILT_STALE_HOURS (default 12) is discarded rather
than trusted, so a missed or failed update run can't suppress a
restart indefinitely.
2026-07-19 16:09:55 -04:00
Gmer4Lfe 54919db13f Sync Sonarr/Radarr PROTECTED_PATTERNS template with live config
Template was missing theme.mkv/theme.mp4 and all trailer patterns,
including trailer-*.* for Trailarr's hyphenated TV trailer naming
(trailer-2.mkv) — a fresh install would hit the same false-positive
orphan bug just fixed live on prod.
2026-07-19 10:22:12 -04:00
Gmer4Lfe 2a09529e75 Document expanded FFPROBE_PATH_MAP coverage pattern in host.conf.template 2026-07-18 23:28:09 -04:00
Gmer4Lfe efa2f2edf8 Schedule arr_corruption_scan.sh --remediate weekly in master.conf.template 2026-07-18 22:54:22 -04:00
Gmer4Lfe 004bde2c62 Add strike system to arr_corruption_scan.sh — require repeat corrupt detections before remediating 2026-07-18 22:50:19 -04:00
Gmer4Lfe ab35fcd84d Add arr_corruption_scan.sh — sequential ffprobe-based corruption scan for Sonarr
Healarr does the same job but crashes on a Go concurrency bug (unsynchronized
map access) whenever multiple corruption events land close together — confirmed
via its own crash log, not fixable from our side. Processing one file at a time
here sidesteps the whole bug class instead of trying to work around it.

Delete + explicit EpisodeSearch rather than relying on Sonarr's own background
missing-search cycle, since that skips unmonitored episodes and this shouldn't.
2026-07-18 16:48:32 -04:00
Gmer4Lfe d5d1403ebf Raise Radarr cleanup safety threshold to 30GB
15GB was tripping on legitimate quality-upgrade orphan batches.
2026-07-18 12:05:21 -04:00
Gmer4Lfe 7bf47be106 Poll MoveMovie command to completion instead of trusting DB fields
Mirrors the Sonarr fix — same MoveMovieService one-at-a-time drain architecture,
never confirmed live on the Radarr side but the DB-instant/move-async split
is identical, so the same batch-verification race applies.
2026-07-17 23:00:08 -04:00
Gmer4Lfe 4f0cd1083a Poll MoveSeries command to completion instead of trusting DB fields
Confirmed live: DB fields flip instantly on the moveFiles=true PUT but the
physical move drains async, one command at a time — a batch could report
every series moved while most were still sitting at the old path.
2026-07-17 22:54:44 -04:00
Gmer4Lfe 1e1129687f Move arr_cache_prefill to intermediate tier, add classification scans to daily
arr_cache_prefill moved from critical (30min) to intermediate (4h) —
nothing in the critical tier reads this cache, and every actual
consumer already tolerates staleness well past 4h. Classification
scans run in daily, after the cleanup scripts (not before) — cleanup
frees disk space from orphans/junk first, so the searches --move
triggers for undownloaded relocated entries have headroom for the
resulting grabs.
2026-07-17 13:13:30 -04:00
Gmer4Lfe 79059c20fa Add --move mode to sonarr_classification_scan.sh, matching Radarr
Acts on forward misplacements and reverse-kids-leak (adult content in
the kids root — not a judgment call, same reasoning as the Radarr
side), moving to the new SONARR_GENERAL_ROOT config var. Reverse-anime
-leak stays report-only — deliberate style placements like Castlevania
are common and valid there. episodeFileCount is Sonarr's hasFile
equivalent: series with 0 files get relocated + an immediate
SeriesSearch instead of a file move. One series at a time, verified
after each — matches the race condition found doing this by hand for
the Fate/Zero and Fate/Stay Night moves earlier in the session.
2026-07-17 13:02:22 -04:00
Gmer4Lfe 9c05692a24 Include reverse-kids-leak in --move — it's not actually a judgment call
Unlike reverse-anime-leak (deliberate style placements like Castlevania
are common and valid there), reverse-kids-leak's signal is specifically
"adult certification with zero Family/Animation genre" — confirmed live
the 4 titles it catches (Addams Family, Saving Mr. Banks, Dark Shadows,
The DUFF) are all genuinely non-kids content. Moves them to the new
RADARR_GENERAL_ROOT config var (container-path literal for the general
Movies root, needed since RADARR_MOVIES_ROOT is a filesystem path).
2026-07-17 12:20:23 -04:00
Gmer4Lfe 9dbcdc0bd7 Add anime/kids content misclassification detection for Radarr and Sonarr
Overseerr lets users request content into the wrong root folder; these
new report-only scans classify every tracked movie/series (anime, kids,
regular) from metadata alone and flag mismatches against the actual
root folder, in both directions. Rules were validated against real
library data before being adopted — see the header comments in each
script and the master.conf notes above the curated lists.
2026-07-17 11:49:23 -04:00
Gmer4Lfe 5866097d6a Make all arr library-list fetches cache-first with live fallback
Every script that fetches the full Lidarr/Sonarr/Radarr tracked-library
list now goes through arr_get_tracked_data() instead of hitting the API
directly -- cache-first when fresh, live fetch as fallback when stale,
waits out an active rescan before either. Per-item file data (trackFile/
episodefile/moviefile) stays live-only everywhere, since that's the
actual disk-truth these scripts' decisions depend on and was never part
of what's cached.

Also adds arr_cache_prefill.sh to CRITICAL_MAINTENANCE_SCRIPTS (30min
tier) with a short 1min wait ceiling, so the cache stays consistently
fresh instead of only refreshing whenever some other script happens to
write through. A full cache refresh for all three arrs measured at ~12s
total live -- nothing like the multi-hour cost of an actual rescan.
2026-07-16 23:27:23 -04:00
Gmer4Lfe fb13958881 Add weekly full-library rescan job for Lidarr/Sonarr/Radarr
Organic scans only touch files actually involved in an import — an
artist/series/movie that already has files sitting untouched on disk
never gets its tracked-file stats refreshed on its own. Confirmed
2026-07-16: Lidarr reported ~23% of its true trackFileCount with no
scan running, for artists whose files were verified present and
readable the whole time. Downstream scripts trust these stats as
source of truth for the share, so drift needs to be caught before
someone notices a suspiciously low number.
2026-07-16 15:57:12 -04:00
Gmer4Lfe c7ecf99c3f Generalize tracked-data cache from Lidarr-only to all three arrs
Shared cache/rescan-duration logic in common.sh now takes an arr_type
param instead of being Lidarr-specific, so Sonarr and Radarr cleanup
scripts get the same cache-first fetch + rescan-aware retry Lidarr had.
Avoids redundant full-library API calls across scripts run back to back,
and stops false failures when a fetch lands mid-rescan.
2026-07-16 15:34:56 -04:00
Gmer4Lfe 62bb166f9a Lower RSYNC_MAX_RUNTIME_HOURS from 23 to 19
Daily maintenance jobs alone now take ~4h with rsync disabled. 19h cap
leaves enough room in the 24h cycle for maintenance to still run
same-day before the next 1am fire, even if a share hits the cap.
2026-07-16 14:06:22 -04:00
Gmer4Lfe 59cee06f45 Add Lidarr tracked-data cache + duplicate artist cleanup
Shared cache (lidarr_get_tracked_data() in common.sh) so scripts stop
hitting Lidarr's live API for tracked counts every run, and stop
treating a mid-rescan dip as a genuine problem — a whole-library
RescanFolders legitimately makes trackFileCount read far below normal
while it re-verifies every file (confirmed 2026-07-16: 22% of normal
mid-scan). Cache reads fresh-if-recent, waits out an active rescan
(calibrated to that command's own historical duration, tracked per
command name since RescanFolders and DownloadedAlbumsScan take wildly
different amounts of time), then falls back to a stale cache rather
than hard-failing after a few strikes.

lidarr_cleanup.sh: no longer stacks a fresh DownloadedAlbumsScan on
top of one already running, and the tracked-count floor check now
waits out a genuine rescan instead of aborting on every overlap.

lidarr_duplicate_artist_cleanup.sh (new): finds case-insensitive
duplicate artist entries — same display name, different MusicBrainz
ID, added when a search/list-sync matches the wrong same-named artist.
Deletes the empty phantom side and blocks it from Import List
Exclusions, leaves genuinely-different-real-artists alone (checked by
album title overlap, deduped per-artist first so a legitimate reissue
under an artist's own catalog doesn't false-flag as cross-artist
overlap), and only notifies for the rare case where both sides have
real, overlapping content.

lidarr_cache_prefill.sh (new): warms the cache at array start so nothing
reads it cold after boot.

lidarr_missing_art.sh, lidarr_release_fixer.sh: write-through the cache
as a side effect of fetches they already needed for their own purposes.
2026-07-16 14:02:35 -04:00
Gmer4Lfe 8bdf7eeb9c Add smart-import decision for importBlocked items before blocklisting
Most importBlocked downloads are junk/duplicates and still fall straight
through to blocklist+research unchanged. But some are releases arr already
correctly parsed (episode/movie, quality, language all known) that just
trip the title-vs-grab-history safety net — those get imported directly
if the target has no file yet, or the candidate is a same-language
resolution upgrade over what's already there. Sonarr/Radarr only; Lidarr's
manual-import matching doesn't reliably resolve album/track context.

Gated by ARR_SMART_IMPORT_ENABLED (default true) and
ARR_SMART_IMPORT_PREFERRED_LANGUAGE (default English).
2026-07-15 17:30:18 -04:00
Gmer4Lfe 958391e326 Add Emby/Jellyfin deep API health checks to docker_watchdog.sh
Both had a basic HTTP check (Emby) or no coverage at all (Jellyfin), but
neither would have caught today's real incident: Jellyfin's SQLite
database locked up hard (repeated 'database table is locked' errors,
30s+ query timeouts) while its own /System/Info endpoint kept responding
200 the whole time — a basic HTTP check on that endpoint would never have
tripped. /Users forces an actual DB round-trip and was confirmed live to
hang during the exact incident.

Generalized the API check's success condition to also accept array-shaped
responses (/Users returns an array; the existing check only recognized
object fields like .ServerName/.Id/.Version, which would error when
applied to an array) — benefits any future array-returning endpoint, not
just this one. Also corrected the host.conf.template's API_CHECKS format
comment, which described a 3-field format the code never actually used.
2026-07-11 18:23:00 -04:00
Gmer4Lfe 1574db3eab Add circuit breaker to arrs_failed_stalled_recovery.sh
Some items (e.g. an album missing 1-2 tracks where no available release
matches the existing edition/track count) can never resolve via blind
retry. Without a limit, the same media ID gets blocklisted and re-searched
every 4 hours forever — confirmed live on ~19 Lidarr albums cycling
identically across five consecutive runs today, each one downloading a
fresh release, failing import for the same structural reason, and
starting over.

Tracks consecutive failures per (arr_type, media_id) in a persisted state
file. After ARR_RECOVERY_MAX_ATTEMPTS (default 3) failures, the item is
still blocklisted and removed from the queue, but auto re-search stops —
notified once when it crosses the threshold, then left for manual review
instead of retried forever.
2026-07-11 17:39:37 -04:00
Gmer4Lfe ba07634a88 Replace system free-RAM warning with ARC headroom check
The memory warning used plain system-wide 'free' RAM (via free -g), a
carryover from before this script was ZFS-specific. ZFS ARC deliberately
consumes most otherwise-unused RAM, so 'free' being low is normal and not
a meaningful signal — it fired a false alarm on 2026-07-05 (free 5.8Gi,
but available a healthy 45Gi).

Replaced ZFS_REPORT_FREE_WARN_GB with ZFS_REPORT_ARC_FREE_WARN_GB: warns
when ARC headroom (ARC_MAX - ARC_CURRENT) drops below threshold, which is
what actually indicates ARC is running out of room to grow. Available RAM
check is unchanged — it's a legitimate system-pressure signal on its own.
2026-07-11 17:27:59 -04:00
Gmer4Lfe 85c3aef1b0 Fix rsync.sh global lock bug and add a max-runtime cap
acquire_lock (no args) ran before profile inference, so every rsync.sh
invocation — regardless of share — fought over one generic, unparameterized
lock. The per-profile acquire_rsync_lock() further down (with
RSYNC_MAX_CONCURRENT) never got a chance to matter: a single slow transfer
(e.g. Movies during the HOST2 rebuild) monopolized the lock and starved
every other profile, including Critical-Data's 30-minute sync, for days.

Removed the generic acquire_lock call; acquire_rsync_lock "$PROFILE_NAME"
already provides correct per-profile locking on its own.

Also added RSYNC_MAX_RUNTIME_HOURS (default 23): any single transfer
attempt exceeding it is terminated via timeout, logged as paused rather
than failed, and resumes from where it left off next scheduled run
(safe because --partial is already in DEFAULT_RSYNC_OPTS). Bounds the
worst case for one huge/stuck share instead of letting it hold its lock
indefinitely.
2026-07-11 17:10:37 -04:00
Gmer4Lfe 0fb901e275 Enable lidarr_release_fixer/lidarr_cleanup/sonarr_cleanup in daily maintenance template
Already enabled in the live config; template was out of date.
2026-07-11 17:03:38 -04:00
Gmer4Lfe f7fa75fdfb Fix dead/incorrect vars in Plugin/ found during full codebase audit
- WEBGUI_PHP_WAIT was referenced by webgui_watchdog.sh but never defined
  in master.conf, always silently falling back to a hardcoded default
- arrs.php/confform.php still pointed at Media/ for arr cleanup/discovery
  scripts moved to Arrs_Stack/ in b4bc926 — broke the Arrs page's stats
  and the per-script settings editor for those scripts
- docker_folders.php read directly from the optional folder.view3 plugin's
  file instead of Varaverk's own docker_folders.json (the primary store
  since the Docker tab got its own config) — left the Monitor page's
  Docker Folders widget empty on any host without folder.view3 installed
- vv_wd_remote_data() read remote watchdog state files from hardcoded
  /tmp or /boot/config paths instead of the remote's actual STATE_DIR
  (which resolves dynamically and can differ under flash mode) — remote
  node's Watchdog panel was always empty; same wrong path also used for
  two local reads (system_watchdog_oom.db, watchdog_appdata_growth.db)
- rsync.php referenced a {HOST}_MONTHLY_SYNC_SHARES conf var that never
  existed (monthly_maintenance.sh has no rsync section) — nulled out to
  match the existing pattern used for the fallback window
- vv_arr_node_names() did a pointless identity array_map
- vv_dk_webui() had its own duplicate local-IP resolution instead of
  using vv_local_ip(), despite config.php's comment claiming that exact
  duplication was already consolidated
2026-07-04 23:00:26 -04:00