Files
2026-09-15 15:24:03 -04:00

8.0 KiB

Configuration

Everything you are meant to edit is in ../configs/. Nothing else in the tree needs changing to run this.

configs/
  owuitools.env            the stack's settings and its one required token   (gitignored)
  owuitools.env.example    the template to copy it from                      (tracked)
  tools.toml               which tools the assistant can see
  vms.toml                 the machines the VM sub-agent can be given
  vmd.env                  the VM daemon's settings, installed to /etc/owuivm/
  host.env.example         overrides for vm/host/install.sh                  (optional)
  assistant/
    system-prompt.md       what you paste into Open WebUI
  agent/
    params.env             the sub-agent's model, sampling and session bounds
    tools.toml             every one of the sub-agent's tool definitions
    prompt.md              its system prompt, shared by both machines
    prompt.ubuntu.md       the Ubuntu half
    prompt.windows.md      the Windows half
    prompt.screen.md       the part about looking at the screen
  services/
    gateway.nginx.conf         the ingress proxy
    egress.squid.conf          the one route to the internet
    searxng.settings.yml       the search back end
    vmgw.nginx.conf.template   the sub-agent's one path to Ollama

owuitools.env

Copy the example and fill it in:

cp configs/owuitools.env.example configs/owuitools.env
sed -i "s|^OWUITOOLS_AUTH_TOKEN=.*|OWUITOOLS_AUTH_TOKEN=$(openssl rand -hex 32)|" \
  configs/owuitools.env

.env in the project root is a symlink to it, because that is the file docker compose reads. Both are gitignored.

OWUITOOLS_AUTH_TOKEN is the only required setting. The same value goes in Open WebUI's tool connection, and in the vm_agent_progress filter's valve if you use it.

Setting Default What it does
OWUITOOLS_AUTH_TOKEN — Required. The bearer token Open WebUI presents.
OWUITOOLS_CONTACT owuitools@localhost Goes in the User-Agent. Wikimedia 403s without a real one — see setup/contact.md.
OWUITOOLS_MAX_RESPONSE_CHARS 12000 Ceiling on any one tool response. Open WebUI does not truncate tool results, so this is what stands between one greedy fetch and a blown context.
OWUITOOLS_PROBE_RATE_PER_HOUR 30 Per-user hourly budget for the four tools that touch a named host. 0 disables the limiter.
OWUITOOLS_LOG_LEVEL INFO
OWUITOOLS_VM_WAIT_SECONDS 7000 How long one vm_agent call may block. One of three clocks.
OWUITOOLS_GITHUB_TOKEN — Optional. setup/github-token.md
OWUITOOLS_STACKEXCHANGE_KEY — Optional. setup/stackexchange.md
OWUITOOLS_NVD_KEY — Optional. setup/nvd.md

tools.toml — the on/off switch

One line per tool. false removes it: the operation leaves the OpenAPI spec, so the model is never told it exists, and the endpoint stops answering.

That matters because the 22 schemas cost about 9,700 tokens on every turn of every conversation, whether or not a tool is used. A tool you will not use is a standing tax.

[tools]
web_search  = true
pcb_calc    = false     # not doing electronics

Tools needing setup carry a comment pointing at the page for it. After editing:

docker compose restart api

…and then re-save the tool connection in Open WebUI, which caches the spec it fetched. Both steps are needed.

A tool the file does not mention stays on, so an old config never silently removes a new tool. A name that does not exist is reported in the log and ignored.


vms.toml — the machines

[[machine]]
id   = "ubuntu"
name = "Ubuntu"
iso  = "ubuntu*.iso"

id is identity, not a label. It names the base disk, the systemd unit instance, the saved-machine directory, and the value the assistant passes as os — and it appears in regex allowlists inside the privileged launch scripts, which is what lets those scripts refuse a path. Changing it does not rename a machine; it describes one that does not exist.

name is yours. Change it here, or press Rename in the maintenance app. Renames made in the app are written to /var/lib/owuivm/registry/vm-names.toml and merged over the top, so the registry keeps its comments.

shell, project and example are prompt text, substituted into the sub-agent's tool descriptions so one set of definitions describes both machines.


agent/ — the second model

params.env holds its model, sampling and session bounds; see advanced-params.md.

tools.toml holds every one of its tool definitions — name, description and parameters. The descriptions are prompt text the model reads, and rewording one is often the most effective change available. It is bind-mounted, so:

docker compose restart vmagent

is enough — no rebuild.

What that file cannot do is give the agent a new capability. Dispatch lives in code, and a tool named there with nothing behind it is refused at startup, naming the file. There is an allowlist (DISPATCHABLE in vmagent/app/tools.py) precisely so that a config file cannot widen what the agent can reach.

The prompts beside it are loaded the same way. If prompt.md is missing the agent refuses to start: one that runs with no instructions still calls tools and still reports success, which is the worst failure available.


services/ — the supporting containers

Bind-mounted read-only into their containers. Restart the one you changed.

File Container Watch out for
gateway.nginx.conf owuitools-gw /tools/vm_agent has its own 7200s timeout; everything else keeps 240s
egress.squid.conf owuitools-egress The private-range denies are a security control, not tuning
searxng.settings.yml owuitools-searxng Set your own secret_key. The engine list is a measurement from one IP — see setup/searxng.md
vmgw.nginx.conf.template owuitools-vmgw The location = exact matches are load-bearing. Ollama is unauthenticated and serves DELETE /api/delete

Restarting netguard or vmguard strands the containers that share their network namespace. They keep running, keep passing their own healthchecks, and have no route to anything. Recreate the dependents too:

docker compose up -d --force-recreate netguard api searxng
docker compose up -d --force-recreate vmguard vmagent

Host-side settings

The VM sub-agent's daemon reads /etc/owuivm/vmd.env, created from ../configs/vmd.env by vm/host/install.sh — only if it is not already there, because the live copy holds the Gitea token. Edit the live file to change the running daemon.

configs/host.env (optional, gitignored) overrides what install.sh works out for itself. Copy host.env.example if any of its guesses are wrong for your machine.

Nothing in it is required — the installer derives the operator from sudo, the VM's memory and CPU from nproc and installed RAM, and defaults the pool to /Pool. What you are most likely to need is:

Setting Why
OWUIVM_POOL_ROOT Your ZFS pool's mountpoint. /Pool is this project's default and nothing more — zfs list shows yours. The installer creates <pool>/vmrun and <pool>/vmprojects if they are missing.
OWUIVM_GITEA_ALLOW The daemon runs IPAddressDeny=any. Gitea is not on the allowlist by default, because a fresh install has none.
OWUIVM_ALLOWED_CPUS Pin the VM to particular cores. Empty — the default — means no pinning.

These are read at install time and written into the units and scripts, so re-run sudo ./vm/host/install.sh after changing them. The installer refuses to install a file that still has an unrendered placeholder in it, so a value added to a template and not to the installer fails loudly rather than producing a unit that will not start.