Web UI + HTTP API in front of the local Ollaya server
(POST /api/decide, GET /api/tags, GET /api/ps, /v1/*, …)
and its MCP server (ollaya mcp --http).
- Features
- Screenshots
- Requirements
- Installation — pick one path
- Configuration
- Auth quick reference
- API quick reference
- Layout
- Decide playground — pick a model, a built-in preset
(
triage,email,guard,moderation,router,agent) or your own questions; answers with probabilities, load/eval timings, routing info. - Model manager — list local + loaded models, pull / delete / copy / create (Modelfile-style), load / unload, default-model keep-alive policy (default stays loaded; others get a sliding 10m idle window refreshed by every request and unload only when idle; max-N-loaded enforcement, GPU-OOM CPU fallback).
- MCP control — start/stop
ollaya mcp --httplocally, or manage a remote Ollaya host over SSH (key auth, allowlisted commands), probe health, call tools (decide,list_models,show_model,pull_model), read resources (ollaya://models,ollaya://presets/*), client config snippets. - Users & security — session login for the UI, bearer API keys for
scripts,
admin/userroles. Admins manage users; users manage their own profile + keys. History and metrics are scoped per user. - Performance & log — totals, p50/p95, per-model/per-preset/per-day, Ollaya + MCP health history, searchable sqlite request log (full-text + model/preset/user/status/time filters) with retention (keep-N-days + max-rows cap, pruned automatically).
- Full API — everything the UI does is a
GET/POST /api/*call (route map atGET /api, curl examples in the UI's API tab).
Decide tab (above) plus the other main views:
- Ollaya installed and serving
(default
http://127.0.0.1:11435—ollaya serve, or it starts on demand). Tested against Ollaya 0.7.3 (/api/*,/v1/*,mcp --http). - Python 3.10+ (bare-metal path only) or Docker (docker path only).
Pick one of the three options below — not two on the same host (they'd fight over port 11524 and the
./datasqlite db).
Fastest way to try OWI — no git clone, no build. Published per release at ghcr.io/yokurnaz/owi.
⚠️ The image is smoke-tested (serves UI, login, decide via host Ollaya) but not yet battle-tested in daily use — bare metal remains the best-supported path for now.
mkdir -p data config
curl -s -o config/ollaya-webui.yaml \
https://raw.githubusercontent.com/YOKurnaz/owi/main/config/ollaya-webui.yaml
# same host as Ollaya (host networking reaches loopback Ollaya):
docker run -d --name ollaya-webui --restart unless-stopped --network host \
-e OWI_CONFIG=/config/ollaya-webui.yaml \
-e OWI_DB=/data/owi.db \
-e OLLAYA_BASE_URL=http://127.0.0.1:11435 \
-v ./config:/config:ro \
-v ./data:/data \
ghcr.io/yokurnaz/owi:0.2.1
# UI: http://<HOST-IP>:11524 (first login: admin / admin)Tags: :0.1.0 (pinned) or :latest (moves with releases).
Same caveats as the Docker path below (MCP, API key, sqlite volume).
Prefer containers? Install Docker instead.
git clone https://github.com/YOKurnaz/owi.git
cd owi
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
bash run.sh
# UI: http://<HOST-IP>:11524run.sh creates the venv on first run, restarts any running instance,
and prints a health check. Or start manually:
mkdir -p data config
nohup .venv/bin/python src/main.py > data/webui.log 2>&1 &First login is admin / admin — change it immediately in the User tab.
mkdir -p ~/.config/systemd/user
cp systemd/ollaya-webui.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now ollaya-webui
journalctl --user -u ollaya-webui -f
# UI: http://<HOST-IP>:11524See systemd/README.md for details
(KillMode=process keeps a managed MCP child alive across restarts).
Prefer a venv? Install Bare metal instead.
The image holds the OWI web UI only — Ollaya itself keeps running on the
host (or wherever OLLAYA_BASE_URL points). Steps:
Prebuilt image (each release): ghcr.io/yokurnaz/owi:0.2.1
(or :latest). Skip to step 2 with IMAGE=ghcr.io/yokurnaz/owi:0.2.1.
git clone https://github.com/YOKurnaz/owi.git
cd owi
# 1. Build the image (name it what you like) — or pull the release image:
# docker pull ghcr.io/yokurnaz/owi:0.2.1
docker build -t ollaya-webui:latest .
# 2. Prepare local dirs (config ships a default yaml; sqlite db, logs and
# saved settings live in ./data and survive image updates)
mkdir -p data config
# 3a. Easiest: compose (port, env, volumes, host-gateway all set).
# NOTE: compose as shipped assumes Ollaya is reachable at
# host.docker.internal:11435 — but Ollaya binds loopback by default
# (see Notes), so prefer --network host (3b) on the same machine,
# or give Ollaya a non-loopback bind first (bridge-networking note below).
docker compose up -d --build
docker compose logs -f ollaya-webui # Ctrl-C to detach
# UI: http://<HOST-IP>:11524 (first login: admin / admin)
# 3b. Or plain docker run (same thing, explicit). NOTE: with
# --network host the image's baked-in :11524 would clash with a
# bare-metal OWI on the same host — override the command's port:
docker run -d --name ollaya-webui --restart unless-stopped --network host \
-e OWI_CONFIG=/config/ollaya-webui.yaml \
-e OWI_DB=/data/owi.db \
-e OLLAYA_BASE_URL=http://127.0.0.1:11435 \
-v ./config:/config:ro \
-v ./data:/data \
ollaya-webui:latest \
sh -c "OWI_PORT=11599 exec uvicorn main:app --host 0.0.0.0 --port 11599"
# UI: http://<HOST-IP>:11599Notes:
- Ollaya listens on loopback (
127.0.0.1:11435) by default, so a container reaching it viahost.docker.internalgets connection refused — the host gateway (e.g.172.17.0.1) is not loopback. Two ways around it:- Same host (simplest): run the container with host networking
(
docker run --network host ..., drop-p/--add-host), thenOLLAYA_BASE_URL=http://127.0.0.1:11435works from inside. - Bridge networking: make Ollaya listen off-loopback, e.g.
sudo systemctl edit ollayawithEnvironment="OLLAYA_HOST=0.0.0.0:11435"(+OLLAYA_API_KEY=..., since it warns when exposed without a key), thenhost.docker.internalworks as written. If Ollaya runs on another machine, setOLLAYA_BASE_URL=http://<that-host>:11435instead.
- Same host (simplest): run the container with host networking
(
- If Ollaya needs a key (
OLLAYA_API_KEYset server-side), pass the same value as-e OLLAYA_API_KEY=...or save it in the UI header. - MCP start/stop from the UI only works in bare-metal mode (the container
has no
ollayabinary). Two alternatives: pointOLLAYA_MCP_ADDRat a host MCP server that is already listening — or configure a Remote host (MCP tab, admin): host + user + SSH key path, then Test SSH and run allowlisted presets (mcp status/start/stop/log,ollaya status/ps,gpu). Key auth only, no password stored. Compose vars:OWI_REMOTE_HOST/OWI_REMOTE_USER/OWI_REMOTE_KEY(+ mount the key into the container, seecompose.yaml). - SQLite db, request log and saved settings live in
./data(a volume), sodocker compose pull && docker compose up -d --buildkeeps everything. - Health: the image probes
GET /login(public); all/api/*need login. Rebuild after code changes:docker compose up -d --build.
Lowest priority first — UI settings (sqlite) win over env, env over the yaml file, yaml over built-ins:
| Setting | Env | YAML key | Default |
|---|---|---|---|
| Ollaya URL | OLLAYA_BASE_URL |
ollaya_base_url |
http://127.0.0.1:11435 |
| Ollaya API key | OLLAYA_API_KEY |
— (UI only) | unset |
| MCP address | OLLAYA_MCP_ADDR |
mcp_addr |
127.0.0.1:11436 |
| Default model | OWI_DEFAULT_MODEL |
default_model |
laya:latest |
| Max loaded | OWI_MAX_LOADED_MODELS |
max_loaded_models |
2 |
| Idle window (non-default) | OWI_IDLE_KEEP_ALIVE |
idle_keep_alive |
10m |
| Listen port | OWI_PORT |
— | 11524 |
| Config path | OWI_CONFIG |
— | config/ollaya-webui.yaml |
| DB path | OWI_DB |
— | data/owi.db |
Edit config/ollaya-webui.yaml, or change Ollaya URL / MCP address /
API key directly in the UI header (saved to sqlite, no restart).
B=http://<HOST-IP>:11524
# UI login (session cookie)
curl -s -c jar -X POST $B/api/auth/login \
-H 'Content-Type: application/json' -d '{"username":"admin","password":"..."}'
curl -s -b jar $B/api/auth/me | python3 -m json.tool
# API key for scripts (shown ONCE)
curl -s -b jar -X POST $B/api/keys \
-H 'Content-Type: application/json' -d '{"name":"n8n"}'
KEY=owi_...
curl -s $B/api/decide -H "Authorization: Bearer $KEY" \
-H 'Content-Type: application/json' -d '{
"model": "laya", "preset": "triage",
"state": {"message": "You charged me twice, refund NOW!"}
}' | python3 -m json.tool
# admin: users
curl -s -b jar $B/api/users | python3 -m json.tool
curl -s -b jar -X POST $B/api/users \
-d '{"username":"ops","password":"...","role":"user"}'B=http://<HOST-IP>:11524
curl -s -b jar $B/api/health | python3 -m json.tool
curl -s -b jar $B/api/models | python3 -m json.tool
curl -s -b jar $B/api/policy | python3 -m json.tool
curl -s -b jar -X POST $B/api/mcp/start | python3 -m json.tool
curl -s -b jar $B/api/metrics | python3 -m json.toolFull endpoint list with curl examples is in the UI (§ API) and at
GET /api (machine-readable route map, needs login).
owi/
requirements.txt
run.sh # venv + start on :11524
src/main.py # FastAPI backend
src/static/ # single-page UI (+ login page)
systemd/ # example systemd user unit
config/ollaya-webui.yaml # fallback config (UI settings win)
data/ # sqlite db (owi.db), mcp.log, webui.log (gitignored)
docs/ # GitHub Pages site (screenshot + install steps)
Dockerfile / compose.yaml # OWI web-UI image (Ollaya stays on the host)





