The failures this subsystem actually has are configuration drift, so each
check names the setting to change rather than reporting that retrieval
failed. Notably it catches a conf model tag that is no longer installed, and
an index built by a different embedder than the one configured — vectors
from two models are not comparable, and that failure returns confident
nonsense rather than erroring. Diagnostic questions also get recent log
warnings, attached only then because they cost budget the passages need.
Token and poll rather than SSE, so the api layer keeps one response
convention and reuses the pattern manual_sync already proved. History is
capped at three turns because the model is only fully offloaded at 16384
context and unbounded history would cross that silently. The tab exists
only while AI_ENABLED is true, rejected server-side and not merely hidden.
Pasted into Open-WebUI rather than installed by a script: tools live in its own
database, and writing there means guessing at the schema and access control of
a live app. The secret is a Valve so this file stays committable.
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.
Both filters were parsed and honoured under `search` but never passed through
under `ask`, so --section=CONFIGURATION silently retrieved from the whole index
and returned a plausible answer built from the wrong chunks.
The index already stored chunk origin and search.js already filtered on it;
only the wrapper refused the flag. Intent routing boosts PURPOSE for "what
is X", which buried README.md and made the system answer that it had no
definition of itself.
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.
Unraid already enforces CSRF on every POST via auto_prepend, but its
injector is jQuery-only — the plugin's native fetch() calls carried no
token and were being terminated before the endpoint ran, silently,
because csrf_terminate exits with an empty body that r.json() swallows.
Writing down what each endpoint actually guarantees made the places it
didn't obvious — shell arguments reaching a crontab or a bash -c
unescaped, master.conf written without tmp+rename, and conf edits that
could be saved without ever being parsed.
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.
Chunker splits on the header sections the audit standardised, then sub-splits
named-paragraph safeguards — without that a specific question about one of
rsync.sh's fourteen safeguards scored below unrelated chunks, because the other
thirteen dominated the vector. Index is SQLite with raw float32 blobs and is
incremental on mtime; a no-op re-index takes 66ms.
The stale defaults were found by asking the system a question and checking its
answer: it correctly reported what mover_stop.sh's header claimed, and the
header was wrong.
Documents each tab's purpose, what it renders, and which endpoints it polls.
Pages that start with markup get the header in a <?php ?> block so it never
reaches the browser.
Also corrects the layer diagram in README-unraid.md: eight of eleven pages hold
no PHP logic and poll api/ for everything — only auth, monitor and scheduler
require an include/ file directly.
The 2026-07-19 prune covered the skip list and intentional stops but not the
per-container strike counts, which have the same failure: set_strikes 0 only
fires when a container is seen running again, so an uninstalled one keeps its
count forever and every consumer reads the host as unhealthy. Reserved daemon_*
keys are excluded.
All 16 include/ files now carry PURPOSE / DESIGN PRINCIPLES / OPERATIONAL
SAFEGUARDS / EXPORTS / CONFIGURATION, keeping the first three section names
identical to the bash headers so retrieval can route across both languages.
monitor.php read six watchdog state files from /tmp while the watchdogs write
to STATE_DIR, so every strike set came back empty and the summary reported
healthy unconditionally. docs.php gained path containment before it is wired
to a page.
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.
The header audit and per-folder docs pass produced a corpus with properties
worth capturing before they are forgotten: deterministic chunk boundaries,
section type as a retrieval filter, and an index that cannot leak credentials
because the conf files were never tracked.
The repo front page read like generic documentation rather than a record of
why any of this exists. Adds the origin story, the current honest state, real
failure history, and fixes drift in platform_*() naming, the fallback tier
variable pattern, and the Plugin/unraid script locations.
It is the front page and it read like a spec sheet. Adds the incidents the safeguards came
from, an honest note about what is actually switched on, and the layers built since.
The folder that translates the OS into Varaverk's vocabulary had no docs of its own, so the
adapter contract and the three-layer web UI were only discoverable by reading the code.
The fallback docs used an older COVERS naming in the wrong conf file, and rsync's merge-run
mode — the only path that carries --delete — was undocumented entirely.
The docs had drifted from the scripts — a script that no longer exists, three wrong variable
names, a reversed run order, and seven scheduled scripts that were never documented at all.
Finishes the pass: every script now documents its safeguards, and the deliberate absences
in the sourced libraries are recorded so they are not "corrected" later.
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.
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.
Artist and title strings are full of regex metacharacters, so a new candidate could match
an unrelated history entry and be silently skipped as already seen.
The estimates in the original note were wrong in both directions, and Unraid's Apply does
not reliably recreate a container, which is worth knowing before the next tuning pass.
Captures the reasoning behind a not-yet-built feature so the constraints survive the
session, chiefly that AI stays enhancement-only and never load-bearing.
MY_ID is not exported and is unset until detect_hosts runs, so the DDNS and NPM checks
were silently resolving empty config and skipping themselves every cycle.
No such rc.d service exists on Unraid (emhttpd isn't managed via a
standalone rc.emhttp script) and nothing in the codebase calls
platform_restart_service/platform_stop_service with 'emhttp' — confirmed
during a post-7.3.2-upgrade audit of all platform-specific paths.
Unraid's real event name is unmounting_disks, not disks_unmounting — the
mismatch meant emhttp_event never found this hook, so array_stopping.sh
never ran on real array stop.