tinywebui 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -519
- package/bin/tinywebui.js +16 -7
- package/package.json +3 -3
- package/public/chat/attachments.js +5 -1
- package/public/chat/composer.js +16 -6
- package/public/core/access.js +5 -0
- package/public/index.html +53 -0
- package/public/panels/admin.js +42 -0
- package/public/panels/connectors.js +184 -0
- package/public/panels/settings.js +68 -4
- package/public/styles.css +16 -0
- package/src/access/policy.js +23 -3
- package/src/chat/runs.js +7 -2
- package/src/cli.js +14 -3
- package/src/config/approval.js +9 -2
- package/src/config/config.js +55 -12
- package/src/config/models.js +43 -2
- package/src/mcp.js +34 -6
- package/src/routes/admin.js +38 -1
- package/src/routes/auth.js +15 -3
- package/src/routes/chats.js +3 -3
- package/src/routes/settings.js +34 -3
- package/src/server.js +70 -9
- package/src/set-password.js +18 -7
- package/src/store/schema.js +1 -1
- package/src/tools/mcp_tool.js +311 -0
package/README.md
CHANGED
|
@@ -2,21 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
# TinyWebUI
|
|
4
4
|
|
|
5
|
+
<a href="https://tinysuite.dev">
|
|
6
|
+
<img src="assets/tinywebui-full-logo.png" alt="TinyWebUI" width="240" />
|
|
7
|
+
</a>
|
|
8
|
+
|
|
5
9
|
**A small, local-first chat workspace for any OpenAI-compatible model, with MCP tools.**
|
|
6
10
|
|
|
7
11
|
Part of [TinySuite](https://tinysuite.dev). Comes with [TinySearch](https://github.com/TinySuiteHQ/TinySearch) and [TinyContext](https://github.com/TinySuiteHQ/TinyContext) already connected.
|
|
8
12
|
|
|
9
13
|
[](https://tinysuite.dev)
|
|
10
14
|
[](https://www.npmjs.com/package/tinywebui)
|
|
15
|
+
[](https://www.npmjs.com/package/tinywebui)
|
|
11
16
|
[](https://github.com/TinySuiteHQ/TinyWebUI/actions/workflows/test.yml)
|
|
12
17
|
[](LICENSE)
|
|
13
18
|
[](https://nodejs.org)
|
|
19
|
+
[](https://github.com/TinySuiteHQ/TinyWebUI/releases)
|
|
20
|
+
[](https://github.com/TinySuiteHQ/TinyWebUI/commits/main)
|
|
21
|
+
[](https://discord.gg/mFFKF9bf5e)
|
|
22
|
+

|
|
14
23
|
|
|
15
24
|
<img src="docs/demo.gif" width="800" alt="TinyWebUI from a fresh start: a web research turn with a task list and a tool approval, questions about an attached document, the model asking which tone to use, then chat search, statistics, MCP servers, automations and settings">
|
|
16
25
|
|
|
17
26
|
<sub>From a fresh start, using DeepSeek V4.1 Flash through OpenRouter. The wait during the research turn is shown at 4× speed. <a href="docs/demo.mp4">Full-resolution video</a>.</sub>
|
|
18
27
|
|
|
19
|
-
[
|
|
28
|
+
**[Read the documentation → tinysuite.dev/docs/tinywebui](https://tinysuite.dev/docs/tinywebui/)**
|
|
20
29
|
|
|
21
30
|
</div>
|
|
22
31
|
|
|
@@ -26,539 +35,76 @@ TinyWebUI is the chat interface of [TinySuite](https://tinysuite.dev), a set of
|
|
|
26
35
|
|
|
27
36
|
It comes pre-loaded with two TinySuite MCP servers, so a new install can already search the web and remember things:
|
|
28
37
|
|
|
29
|
-
- **[TinySearch](https://github.com/TinySuiteHQ/TinySearch)** searches, reads and reranks the web locally
|
|
30
|
-
- **[TinyContext](https://github.com/TinySuiteHQ/TinyContext)** is a local long-term memory
|
|
31
|
-
|
|
32
|
-
You can add any other MCP server next to them, or remove them (see [MCP tools](#mcp-tools)).
|
|
33
|
-
|
|
34
|
-
Out of the box it is a personal workspace with no login. It can grow into a team deployment, with gateway sign-in, roles, per-user data and an admin panel, without changing anything else.
|
|
38
|
+
- **[TinySearch](https://github.com/TinySuiteHQ/TinySearch)** searches, reads and reranks the web locally. It doesn't need a search API key.
|
|
39
|
+
- **[TinyContext](https://github.com/TinySuiteHQ/TinyContext)** is a local long-term memory that recalls only what fits the token budget.
|
|
35
40
|
|
|
36
41
|
## Highlights
|
|
37
42
|
|
|
38
|
-
- **Durable conversations.**
|
|
39
|
-
- **Steer a turn while it runs.** Press Enter
|
|
40
|
-
- **
|
|
41
|
-
- **
|
|
42
|
-
- **
|
|
43
|
-
- **
|
|
44
|
-
- **
|
|
45
|
-
- **
|
|
46
|
-
- **
|
|
47
|
-
- **Scales up when needed.** You can add a password for remote access, or put it behind an SSO gateway for a team with roles and an admin panel.
|
|
48
|
-
- **Managed from files.** Every setting can come from version-controlled config, which hot-reloads, refuses typos and has a fingerprint you can check against a running instance.
|
|
43
|
+
- **Durable conversations.** Edit, retry, rewind, folders and full-text search. A running turn belongs to the server, so you can reload and come back to it.
|
|
44
|
+
- **Steer a turn while it runs.** Press Enter to reach the model at the next safe point, or Alt+Enter to hold a follow-up.
|
|
45
|
+
- **MCP over stdio, Streamable HTTP or SSE.** Calls that can change something wait for your approval.
|
|
46
|
+
- **Attachments.** Text and source files, PDFs, DOCX and images.
|
|
47
|
+
- **Hybrid local search** (BM25 plus a small on-device embedding model) over documents and past chats.
|
|
48
|
+
- **Long chats that stay affordable.** Cache-friendly prefixes, one-time compaction of large tool results, and per-round token statistics.
|
|
49
|
+
- **Automations.** Cron-scheduled prompts that run in a chat of your choice.
|
|
50
|
+
- **Private by default.** Interactive first runs ask for an owner password; a team can use an SSO gateway with roles and an admin panel.
|
|
51
|
+
- **Managed from files.** Version-controlled config that hot-reloads, refuses typos and has a fingerprint you can check against a running instance.
|
|
49
52
|
- **Themes.** Fall Fairy and Cyber Grid are included. See [theme authoring](public/themes/README.md) to write your own.
|
|
50
53
|
|
|
51
54
|
## Quick start
|
|
52
55
|
|
|
53
|
-
Requires **Node.js 22.13** or later. The bundled TinySearch and TinyContext servers also need **[uv](https://docs.astral.sh/uv/)
|
|
56
|
+
Requires **Node.js 22.13** or later. The bundled TinySearch and TinyContext servers also need **[uv](https://docs.astral.sh/uv/)**, which fetches them the first time they start.
|
|
54
57
|
|
|
55
58
|
```bash
|
|
56
59
|
npx tinywebui
|
|
57
60
|
```
|
|
58
61
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
npm install
|
|
63
|
-
npm start
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
Open <http://127.0.0.1:7777>. Use `--port` and `--host` to change where it listens.
|
|
67
|
-
|
|
68
|
-
> **Search model.** `npm start` first downloads the default embedding model (about 90 MB) into `models/`, once. `npx tinywebui` skips that step and uses keyword search until you run `npx tinywebui models pull fast`. See [Search](#search).
|
|
69
|
-
|
|
70
|
-
### Docker
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
cp example.tinywebui.config.json tinywebui.config.json # add your API key
|
|
74
|
-
docker compose up --build
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
The default image is Debian slim and has hybrid search, with the embedding model baked in at build time. `Dockerfile.lexical` builds a small Alpine image with keyword search only. Both images keep data in a `/data` volume and have a `/readyz` health check. The images don't include uv, so mount an `mcp.json` that connects to TinySearch and TinyContext over HTTP, for example running in their own containers, or one that leaves them out. [docs/deploy.md](docs/deploy.md) walks through a full scripted deployment.
|
|
78
|
-
|
|
79
|
-
### Development mode
|
|
80
|
-
|
|
81
|
-
`npm run dev` restarts the server when `src/` or `bin/` change. Files in `public/` are served as-is, so a browser refresh is enough for them.
|
|
82
|
-
|
|
83
|
-
## Configuration
|
|
84
|
-
|
|
85
|
-
Start from the example:
|
|
86
|
-
|
|
87
|
-
```bash
|
|
88
|
-
cp example.tinywebui.config.json tinywebui.config.json
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
> [!WARNING]
|
|
92
|
-
> `tinywebui.config.json` can hold your API key. It is gitignored, so keep it that way.
|
|
93
|
-
|
|
94
|
-
The Settings panel can change the model, system prompt, generation settings, tool budget, context controls and enabled tools, and it writes every change back to this file. The API key is never sent to the browser.
|
|
95
|
-
|
|
96
|
-
**OpenRouter:**
|
|
62
|
+
The first run asks you to create an owner password of at least 15 characters. Open <http://127.0.0.1:7777>, sign in, then connect a model. Put an endpoint and key in `tinywebui.config.json` (start from `example.tinywebui.config.json`) or use the Settings panel:
|
|
97
63
|
|
|
98
64
|
```json
|
|
99
65
|
{
|
|
100
66
|
"baseUrl": "https://openrouter.ai/api/v1",
|
|
101
67
|
"apiKey": "sk-or-...",
|
|
102
|
-
"model": "deepseek/deepseek-v4.1-flash"
|
|
103
|
-
"systemPrompt": "You are a terse, precise assistant.",
|
|
104
|
-
"maxToolRounds": 12
|
|
68
|
+
"model": "deepseek/deepseek-v4.1-flash"
|
|
105
69
|
}
|
|
106
70
|
```
|
|
107
71
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
```json
|
|
111
|
-
{
|
|
112
|
-
"baseUrl": "http://localhost:8080/v1",
|
|
113
|
-
"model": "qwen3-coder-30b-a3b-instruct"
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### Environment variables
|
|
118
|
-
|
|
119
|
-
| Variable | Effect |
|
|
120
|
-
| --- | --- |
|
|
121
|
-
| `TINYWEBUI_CONFIG` | Path to the config file (`.json`, or `.js`/`.mjs`, see [Deployment as code](#deployment-as-code)) |
|
|
122
|
-
| `TINYWEBUI_MCP` | Path to `mcp.json` (default: next to the config file) |
|
|
123
|
-
| `TINYWEBUI_DB` | Path to the SQLite database (default: `tinywebui.db` next to the config file) |
|
|
124
|
-
| `TINYWEBUI_API_KEY`, `OPENROUTER_API_KEY` | API key; overrides the file |
|
|
125
|
-
| `TINYWEBUI_BASE_URL`, `TINYWEBUI_MODEL` | Endpoint and model; override the file |
|
|
126
|
-
| `TINYWEBUI_RETRIEVAL_MODE` | `auto`, `lexical`, `dense` or `hybrid` (see [Search](#search)) |
|
|
127
|
-
| `TINYWEBUI_MODELS_DIR` | Where embedding models are stored (default `models/`) |
|
|
128
|
-
| `TINYWEBUI_PASSWORD` | Tier-2 password, for containers (see [Access](#access-three-tiers)) |
|
|
129
|
-
| `TINYWEBUI_SESSION_SECRET` | Signs session cookies. Set it so that sessions survive a restart |
|
|
130
|
-
| `TINYWEBUI_LOG_LEVEL` | `debug`, `info` (default), `warn`, `error` or `silent`. Audit events are always written |
|
|
131
|
-
|
|
132
|
-
Every log line is tagged with its area, for example `[tinywebui:mcp]`.
|
|
133
|
-
|
|
134
|
-
### Settings reference
|
|
135
|
-
|
|
136
|
-
| Setting | Default | What it does |
|
|
137
|
-
| --- | --- | --- |
|
|
138
|
-
| `maxToolRounds` | `12` | Tool-use budget for one user message. A model response that calls one or more tools uses one round. |
|
|
139
|
-
| `askUserTimeoutSeconds` | `120` | How long an `ask_user` question waits before the run continues on the model's own assumptions. `0` waits until the question is answered or the run is stopped. Automation runs never wait. |
|
|
140
|
-
| `toolApproval` | `"writes"` | Which tool calls need your approval: `"writes"`, `"all"` or `"off"`. See [Tool approval](#tool-approval). |
|
|
141
|
-
| `cache` | `true` | Prompt-cache shaping where the provider supports it. |
|
|
142
|
-
| `cacheMode` | `"auto"` | `auto`, `implicit` (stable prefix only), `explicit` (Anthropic `cache_control`), `rolling` (OpenRouter) or `off`. |
|
|
143
|
-
| `cacheTtl` | `"5m"` | Anthropic cache lifetime. `"1h"` costs more to write and suits chats with long pauses between turns. |
|
|
144
|
-
| `reasoningReplay` | `"auto"` | Which reasoning earlier assistant turns send back: `reasoning_details` (OpenRouter, Anthropic-style), `reasoning_content` on tool-call turns (direct DeepSeek), or `omit`. Set it for a gateway that `auto` doesn't recognise. |
|
|
145
|
-
| `compactThreshold` | `60000` | History tokens (system prompt and tool definitions excluded) at which older tool results are compacted. `0` disables it. |
|
|
146
|
-
| `keepTurns` | `2` | How many recent user turns keep their tool results in full. |
|
|
147
|
-
| `compactMinSaved` | `20000` | The minimum number of characters a compaction must remove to be worth the cache miss it causes. |
|
|
148
|
-
| `maxInlineChars` | `30000` | Tool results larger than this are stubbed for later turns as soon as they arrive. |
|
|
149
|
-
| `maxTurnChars` | `120000` | Tool results larger than this are stubbed even within the turn that fetched them. |
|
|
150
|
-
| `maxHistoryTokens` | `100000` | Hard window. Beyond it, the oldest turns stop being sent to the model, but they stay in the transcript. `0` disables it. |
|
|
151
|
-
| `llmCompaction` | `true` | Before the window drops turns, the chat's model summarises them. This costs one extra request each time the window moves. If the summary fails, the turns are simply dropped. |
|
|
152
|
-
| `compactionMaxTokens` | `8192` | Output limit for that summary. Reasoning models think within this limit, and a cut-off summary is rejected, so leave headroom. |
|
|
153
|
-
| `expandCharBudget` | `8000` | The most one `expand_context` call can return. |
|
|
154
|
-
| `timezone` | `""` | IANA zone used for today's date and for automations. Empty uses the server's zone. |
|
|
155
|
-
| `extraBody` | `{}` | Merged into every completion request. Gateway-specific options go here. |
|
|
156
|
-
| `dbPath` | `""` | Database location. Empty means `tinywebui.db` next to the config file. |
|
|
157
|
-
|
|
158
|
-
The model is told its full tool budget and today's date. The last tool result of each round ends with a note of the rounds left, and that note becomes a warning for the final two rounds. When the budget is spent, TinyWebUI makes one last request with tools disabled, so the model answers from the evidence it already has.
|
|
159
|
-
|
|
160
|
-
The full schema, including the access and retrieval settings, is in [docs/config.schema.json](docs/config.schema.json). You can also print it with `tinywebui schema`.
|
|
161
|
-
|
|
162
|
-
### Model catalog
|
|
163
|
-
|
|
164
|
-
By default, any model id works. To fix the list of models people can use, and to name and tune each one, add a `models` catalog:
|
|
165
|
-
|
|
166
|
-
```json
|
|
167
|
-
{
|
|
168
|
-
"model": "quick",
|
|
169
|
-
"models": [
|
|
170
|
-
{ "id": "quick", "label": "Quick", "description": "Everyday questions",
|
|
171
|
-
"model": "deepseek/deepseek-v4.1-flash", "temperature": 0.3 },
|
|
172
|
-
{ "id": "deep", "label": "Thorough", "model": "anthropic/claude-sonnet-5",
|
|
173
|
-
"systemPrompt": "You are a careful research assistant...", "maxToolRounds": 20,
|
|
174
|
-
"extraBody": { "reasoning": { "effort": "high" } } }
|
|
175
|
-
],
|
|
176
|
-
"access": { "roles": { "user": { "models": ["quick"] } } }
|
|
177
|
-
}
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
- **`id`** is the name everything else uses: the `model` setting, role model lists and each person's saved choice. **`model`** is the provider's id; if it is left out, the `id` is sent.
|
|
181
|
-
- **People only see `label` and `description`.** They can't type in an id, and the provider id never reaches the browser.
|
|
182
|
-
- **Per-model overrides.** An entry can set `systemPrompt`, `temperature`, `maxTokens`, `maxToolRounds`, `cacheMode`, `cacheTtl`, `reasoningReplay` and `extraBody`. These replace the global values for turns with that model, except `extraBody`, which is merged with the global one.
|
|
183
|
-
- **`"enabled": false`** hides an entry but keeps its settings. Anyone who had picked it falls back to the default model. The default must be an enabled entry.
|
|
184
|
-
- **The catalog is file-only.** Startup and reload refuse a catalog with an unknown key, a duplicate id, or a reference to an entry that doesn't exist.
|
|
185
|
-
|
|
186
|
-
## MCP tools
|
|
187
|
-
|
|
188
|
-
MCP servers are defined in `mcp.json` next to the config file, in the same shape that MCP desktop clients use.
|
|
189
|
-
|
|
190
|
-
### Pre-loaded: TinySearch and TinyContext
|
|
191
|
-
|
|
192
|
-
Until an `mcp.json` exists, TinyWebUI starts with these two TinySuite servers, launched by [uv](https://docs.astral.sh/uv/):
|
|
193
|
-
|
|
194
|
-
```json
|
|
195
|
-
{
|
|
196
|
-
"mcpServers": {
|
|
197
|
-
"tinysearch": {
|
|
198
|
-
"command": "uvx",
|
|
199
|
-
"args": ["--python", "3.12", "--from", "tinysuite-search[server]", "tinysearch"]
|
|
200
|
-
},
|
|
201
|
-
"tinycontext": {
|
|
202
|
-
"command": "uvx",
|
|
203
|
-
"args": ["--python", "3.12", "--from", "tinysuite-context[server]", "tinycontext"]
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
| Server | What the model gets |
|
|
210
|
-
| --- | --- |
|
|
211
|
-
| [TinySearch](https://github.com/TinySuiteHQ/TinySearch) | Web search, clean Markdown from pages, and a browser for pages that need clicking. It runs locally and needs no search API key. [Docs](https://tinysuite.dev/docs/tinysearch/). |
|
|
212
|
-
| [TinyContext](https://github.com/TinySuiteHQ/TinyContext) | Memory across chats: it saves short memories and recalls the relevant ones within a token budget. Stored locally in SQLite. |
|
|
213
|
-
|
|
214
|
-
The first start takes a little longer while uv fetches the packages. TinyContext also downloads its embedding model once.
|
|
215
|
-
|
|
216
|
-
**Without uv**, TinyWebUI still starts and everything else works. The two servers are just switched off, and it tells you why:
|
|
217
|
-
|
|
218
|
-
- **On the command line**, startup ends with `tinycontext, tinysearch off until uv is installed: https://docs.astral.sh/uv/`, and `tinywebui doctor` reports both as failed for the same reason.
|
|
219
|
-
- **In the app**, both servers show **needs uv** in the tools menu and the MCP panel, with an **Install uv** link.
|
|
220
|
-
|
|
221
|
-
Install uv and restart TinyWebUI; no other setup is needed. The restart matters because the uv installer adds itself to your `PATH` only for programs started after it.
|
|
222
|
-
|
|
223
|
-
Saving the MCP panel writes these servers to `mcp.json`. After that the file is in charge: edit it to change them, or remove them to leave them out. An empty `{ "mcpServers": {} }` means no servers at all.
|
|
224
|
-
|
|
225
|
-
### Adding servers
|
|
226
|
-
|
|
227
|
-
`example.mcp.json` has the two TinySuite servers plus a disabled HTTP example. Copy it to start, then add servers like these:
|
|
228
|
-
|
|
229
|
-
```bash
|
|
230
|
-
cp example.mcp.json mcp.json
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
```json
|
|
234
|
-
{
|
|
235
|
-
"mcpServers": {
|
|
236
|
-
"files": {
|
|
237
|
-
"command": "npx",
|
|
238
|
-
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
|
|
239
|
-
},
|
|
240
|
-
"research": {
|
|
241
|
-
"url": "http://127.0.0.1:8000/mcp",
|
|
242
|
-
"headers": { "Authorization": "Bearer ..." }
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
- **Stdio servers** take `command`, `args`, `env` and `cwd`.
|
|
249
|
-
- **Remote servers** take `url`, optional `headers`, and `"transport": "sse"` for SSE. The default transport is Streamable HTTP.
|
|
250
|
-
- **`"disabled": true`** keeps a server defined but disconnected.
|
|
251
|
-
|
|
252
|
-
The MCP panel validates and saves `mcp.json`, then reconnects without a restart. You can switch off single tools without disconnecting their server. The model sees tools under stable `server__tool` names.
|
|
253
|
-
|
|
254
|
-
### Tool approval
|
|
255
|
-
|
|
256
|
-
A tool call that can change something waits for you. It appears in the transcript with its arguments and three choices: **allow**, **always allow this tool** and **deny**.
|
|
257
|
-
|
|
258
|
-
- A tool counts as read-only only when its server marks it with the MCP `readOnlyHint` annotation. Any tool without that mark is treated as one that can write.
|
|
259
|
-
- `toolApproval` sets the policy: `"writes"` (default) asks before any call that isn't read-only, `"all"` asks before every call and `"off"` never asks. The tools panel can override it for a single tool (`confirmTools` / `autoApproveTools`).
|
|
260
|
-
- TinyWebUI's built-in tools never ask.
|
|
261
|
-
- Scheduled automation runs have no one to ask, so they refuse a call that would need approval and report it.
|
|
262
|
-
|
|
263
|
-
Calls from one round that are marked both `readOnlyHint` and `idempotentHint` run in parallel. Other calls run one at a time. Either way, the transcript and the results come out in the order the model asked for them.
|
|
72
|
+
`tinywebui.config.json` holds your password hash and API keys and is gitignored. Keep it that way. Use `npx tinywebui set-password` to change the password later.
|
|
264
73
|
|
|
265
|
-
|
|
74
|
+
`npx tinywebui` uses keyword search until you run `npx tinywebui models pull fast` (about 90 MB) to enable hybrid search.
|
|
266
75
|
|
|
267
|
-
|
|
268
|
-
| --- | --- |
|
|
269
|
-
| `read_document` | Searches or reads a document attached to the chat. |
|
|
270
|
-
| `search_chats` | Searches your earlier conversations (never the current one) and returns the questions with the answers they got. |
|
|
271
|
-
| `expand_context` | Searches or pages through the full output behind a compacted tool result. |
|
|
272
|
-
| `ask_user` | Asks you a question mid-turn, optionally with choices, and waits for the answer. |
|
|
273
|
-
| `manage_tasks` | Keeps a checklist for the current chat, shown in the right rail. |
|
|
274
|
-
| `manage_automation` | Creates, edits and runs scheduled [automations](#automations). |
|
|
275
|
-
|
|
276
|
-
## Chats and attachments
|
|
277
|
-
|
|
278
|
-
**Conversations** are stored in `tinywebui.db`. You can edit and resend a message, retry an answer, rewind, file chats into folders and search them all. The search covers your messages and the final answers, but not reasoning, tool calls or raw tool output, so the results stay relevant. An outline on the right edge lists the questions in a long chat, and clicking one jumps to it.
|
|
279
|
-
|
|
280
|
-
**Turns run on the server.** Reloading or disconnecting doesn't cancel a turn: reopen the chat to rejoin its stream, or press **stop**. While a turn is running, press **Enter** to steer it, or **Alt+Enter** to hold a follow-up that you send when you're ready.
|
|
281
|
-
|
|
282
|
-
**Documents.** Attach files with the paperclip, by dragging them onto the composer, or by pasting a long block of text. Text and source files, CSV, JSON, PDFs with extractable text and DOCX files are stored as documents for the chat. The model reads the relevant parts with `read_document` instead of getting the whole file in its context. Each document can be up to 5 MiB. Files you attached before can be reattached from the library without uploading them again.
|
|
283
|
-
|
|
284
|
-
**Images** are sent to the model with the message: up to eight per message, each up to 5 MiB. JPEG, PNG, WebP and GIF are sent as they are, and HEIC and AVIF are converted to JPEG. This needs a model and endpoint that accept OpenAI-style image content.
|
|
285
|
-
|
|
286
|
-
**Not supported:** OCR (scanned or image-only PDFs are refused with a clear error) and legacy `.doc` files (save them as `.docx` or `.pdf`).
|
|
287
|
-
|
|
288
|
-
## Search
|
|
289
|
-
|
|
290
|
-
`read_document` and `search_chats` share one retrieval engine. A chat turn is indexed as the question together with the answer it finally got, without the narration or tool calls in between.
|
|
291
|
-
|
|
292
|
-
Search is **hybrid** by default. SQLite full-text search (BM25) matches exact words, and a small local embedding model matches meaning, so a question about an "automobile" finds the paragraph about the "sedan". The two rankings are combined with Reciprocal Rank Fusion, using the same ONNX models and fusion as its TinySuite siblings [TinySearch](https://github.com/TinySuiteHQ/TinySearch) and [TinyContext](https://github.com/TinySuiteHQ/TinyContext). Everything runs locally.
|
|
293
|
-
|
|
294
|
-
- **Install.** `npm install` brings the embedding runtime (`onnxruntime-node` and `@huggingface/tokenizers`, both optional dependencies). The first `npm start` downloads the `fast` model through `tinywebui models ensure`. The Docker image includes it.
|
|
295
|
-
- **Fallback.** If the model or the runtime is missing (an offline first start, a platform where onnxruntime can't install, or `npm install --omit=optional`), the default `auto` mode runs BM25 only and logs why.
|
|
296
|
-
- **Keyword search only.** To skip hybrid search and the download, run `TINYWEBUI_RETRIEVAL_MODE=lexical npm start` or set `{ "retrieval": { "mode": "lexical" } }`.
|
|
297
|
-
- **Pinning the model.** `{ "retrieval": { "mode": "hybrid", "model": "fast", "modelSha256": "<sha256 from tinywebui models pull>" } }` refuses to start with any other model file.
|
|
298
|
-
- **No downloads at runtime.** Models only arrive through `tinywebui models pull` / `models ensure` or the Docker build. ONNX Runtime telemetry is switched off.
|
|
299
|
-
|
|
300
|
-
<details>
|
|
301
|
-
<summary><b>How retrieval works, and how to tune it</b></summary>
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
**Small-to-big retrieval.** Each document is split twice:
|
|
306
|
-
|
|
307
|
-
| | Passage | Chunk |
|
|
308
|
-
| --- | --- | --- |
|
|
309
|
-
| What it is | What `read_document` returns to the model | What gets embedded |
|
|
310
|
-
| Size | `passageSize` characters (default 1,800, about 400 tokens) | The embedding model's token limit: 256 for `fast`, 512 for the others |
|
|
311
|
-
| Overlap | `passageOverlap` characters (default 200) | `chunkOverlap` tokens (default 32) |
|
|
312
|
-
| Used for | BM25 (every mode) and the text the model reads | Dense matching (`dense`, `hybrid`) |
|
|
313
|
-
|
|
314
|
-
The question is compared with every small chunk, each passage takes its best chunk's score, and the model gets whole passages. Small chunks keep the match precise, and large passages give the model enough context to answer. Chunk size follows the model, so every part of a passage is embedded, even one longer than the model can read at once.
|
|
315
|
-
|
|
316
|
-
**Modes.**
|
|
317
|
-
- `auto` (default): `hybrid` when the model is installed, `lexical` when it isn't.
|
|
318
|
-
- `lexical`: BM25 only.
|
|
319
|
-
- `dense`: embeddings only.
|
|
320
|
-
- `hybrid`: both, fused.
|
|
321
|
-
|
|
322
|
-
An explicit `dense` or `hybrid` refuses to start without the model instead of falling back. A checksum that doesn't match `modelSha256` stops startup in every mode. `tinywebui models verify` and `tinywebui doctor` show which mode will run and why.
|
|
323
|
-
|
|
324
|
-
**Models.**
|
|
325
|
-
|
|
326
|
-
| Preset | Model | Notes |
|
|
327
|
-
| --- | --- | --- |
|
|
328
|
-
| `fast` (default) | all-MiniLM-L6-v2 | English, about 90 MB |
|
|
329
|
-
| `balanced` | bge-small-en-v1.5 | English |
|
|
330
|
-
| `quality` | bge-base-en-v1.5 | English |
|
|
331
|
-
| `multilingual` | granite-embedding-107m-multilingual (Apache-2.0) | About as fast as `fast`, 430 MB download |
|
|
332
|
-
|
|
333
|
-
For questions and documents in different languages, keyword matches rarely help, so `dense` mode or a higher `denseWeight` usually ranks better. A custom bundle works with `modelDir`.
|
|
334
|
-
|
|
335
|
-
**Embeddings are computed once.** Document chunks are embedded when the document is attached, and chat turns when each run ends. Older chats and documents are embedded in the background at startup. Vectors are stored in SQLite, and a query only embeds the question. Switching models re-embeds everything automatically. A turn whose answer changes is re-embedded, and deleting or rewinding removes its vectors.
|
|
336
|
-
|
|
337
|
-
**Tuning:**
|
|
338
|
-
- `passageSize` and `passageOverlap` set what the model gets back. Bigger passages give more context per hit, but fewer hits fit in one result.
|
|
339
|
-
- `chunkOverlap` sets how much neighbouring chunks share.
|
|
340
|
-
- `denseWeight` (default 0.5) and `rrfK` (default 60) control the fusion.
|
|
341
|
-
- `queryPrefix` and `documentPrefix` are for models that expect instructions. For bge, set `queryPrefix` to `"Represent this sentence for searching relevant passages: "`.
|
|
342
|
-
|
|
343
|
-
Changing these is safe: stored documents are re-split when the passage settings change, and re-embedded when the model or `chunkOverlap` changes.
|
|
344
|
-
|
|
345
|
-
**Adding a corpus.** The engine in `src/retrieval/retrieval.js` can search any source that describes its units through a small adapter (`units`, `candidates`, `lexical`, `hydrate`…). Register it with `retrieval.register(name, corpus)` and it gets lexical, dense and hybrid search, storage and backfill.
|
|
346
|
-
|
|
347
|
-
</details>
|
|
348
|
-
|
|
349
|
-
## Automations
|
|
350
|
-
|
|
351
|
-
An automation posts its instructions into a chat you choose, on a five-field cron schedule in an IANA timezone. You can create automations in the **automations** view, or ask the model to manage them with `manage_automation`.
|
|
352
|
-
|
|
353
|
-
- Runs use the current model and enabled tools, and appear both in the chat and in the automation's run history.
|
|
354
|
-
- **Run now** triggers one immediately. It waits for the chat's current turn to finish.
|
|
355
|
-
- Automations only run while TinyWebUI is running. A run that was missed, or that falls while the chat is busy, is skipped.
|
|
356
|
-
- No external notifications are sent.
|
|
357
|
-
|
|
358
|
-
## Long chats: caching and compaction
|
|
359
|
-
|
|
360
|
-
Tool results can be much larger than the conversation around them. TinyWebUI keeps every result in full in SQLite, but avoids sending it again and again:
|
|
361
|
-
|
|
362
|
-
1. **Cache-friendly prefixes.** Between compactions, history is append-only and serialized deterministically, so providers can reuse the matching prompt prefix.
|
|
363
|
-
2. **One-time compaction.** When history crosses `compactThreshold`, older tool results are replaced, once, with compact stubs. A stub holds an artifact ID, a hint about the structure, and the first and last lines verbatim. Older images are replaced with a short note.
|
|
364
|
-
3. **Nothing is lost.** The model can get the exact content back through `expand_context`.
|
|
365
|
-
4. **A moving window as a last resort.** If history is still over `maxHistoryTokens`, which usually happens in a very long chat with few tool calls, the oldest turns stop being sent. The cut falls on a user message and leaves a note that earlier conversation was omitted. With `llmCompaction`, those turns are first summarised (together with any earlier summary) into the system prompt. Rewinding to a point before the cut removes the summary.
|
|
366
|
-
|
|
367
|
-
**Provider specifics.** On OpenRouter, TinyWebUI sends a stable per-chat `session_id` so sticky routing keeps caches warm, and Claude gets the automatic rolling cache directive. Local and other endpoints get no OpenRouter-only fields. Set `cacheMode` only if your gateway needs a different cache dialect, or set `cache: false` to turn cache shaping off.
|
|
368
|
-
|
|
369
|
-
## Usage statistics
|
|
370
|
-
|
|
371
|
-
Every round in the transcript shows its token use, including cache reads and writes when the provider reports them. The **Statistics** view totals usage over time and by model, and can drill down from all time to a single day. Extra summary requests made by `llmCompaction` show up there too.
|
|
372
|
-
|
|
373
|
-
**Token attribution.** Each assistant request stores a counts-only breakdown of where its tokens went. The input side covers the operator prompt, harness, MCP guidance, tool schemas per server, conversation, reasoning sent back, tool history and results, and images. The output side covers reasoning, tool calls, intermediate text and the final answer.
|
|
374
|
-
|
|
375
|
-
- The provider's totals and reported reasoning counts are authoritative. Everything else is estimated at about four characters per token, with images counted as 1,500 tokens, and is marked `~`.
|
|
376
|
-
- The gap to the provider's total is shown as a signed difference and never rescaled away.
|
|
377
|
-
- Cache reads are part of the input, not an extra component.
|
|
378
|
-
- Requests made before attribution existed are not reconstructed.
|
|
379
|
-
|
|
380
|
-
## Access: three tiers
|
|
381
|
-
|
|
382
|
-
Choose a tier with `authMode`. Each tier keeps everything the previous one has, and switching tiers never hides existing data.
|
|
383
|
-
|
|
384
|
-
| Tier | `authMode` | For | Sign-in |
|
|
385
|
-
| --- | --- | --- | --- |
|
|
386
|
-
| 1. Just you | `"none"` (default) | You, on your own machine | None |
|
|
387
|
-
| 2. Just you, remotely | `"single"` | You, over a network | A password |
|
|
388
|
-
| 3. Users and admins | `"trusted-header"` | A team or organization | Google, Apple, Microsoft, any SSO, through a sign-in gateway |
|
|
389
|
-
|
|
390
|
-
### Tier 1: just you
|
|
391
|
-
|
|
392
|
-
There is no login, and every chat, setting and tool belongs to whoever can reach the page. Keep it bound to `127.0.0.1`.
|
|
393
|
-
|
|
394
|
-
### Tier 2: just you, remotely
|
|
76
|
+
From a clone, run `npm install && npm start`. With Docker, create the owner password in the persistent volume before starting the service:
|
|
395
77
|
|
|
396
78
|
```bash
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
- **Only a hash is stored.** The config holds an scrypt hash, never the password. In a container, set `"authMode": "single"` and pass the password in `TINYWEBUI_PASSWORD`.
|
|
402
|
-
- **Sessions** use HTTP-only signed cookies. Running `set-password` again ends every existing session.
|
|
403
|
-
- **Lockout.** After five wrong passwords, that address is locked out for 15 minutes.
|
|
404
|
-
- **HTTPS.** Serve it over HTTPS, for example behind a reverse proxy, whenever it is reachable from outside your machine.
|
|
405
|
-
|
|
406
|
-
### Tier 3: users and admins
|
|
407
|
-
|
|
408
|
-
TinyWebUI sits behind a **sign-in gateway** that handles the login, such as Cloudflare Access, oauth2-proxy, Authelia or Authentik. Any identity provider the gateway supports works. The gateway verifies each person and passes their identity in request headers:
|
|
409
|
-
|
|
410
|
-
```json
|
|
411
|
-
{
|
|
412
|
-
"authMode": "trusted-header",
|
|
413
|
-
"trustedProxyCidrs": ["172.20.0.0/16"],
|
|
414
|
-
"trustedUserIdHeader": "x-tinysuite-user-id",
|
|
415
|
-
"trustedEmailHeader": "x-tinysuite-email",
|
|
416
|
-
"trustedNameHeader": "x-tinysuite-name",
|
|
417
|
-
"trustedRoleHeader": "x-tinysuite-role",
|
|
418
|
-
"logoutUrl": "https://<team>.cloudflareaccess.com/cdn-cgi/access/logout",
|
|
419
|
-
"access": {
|
|
420
|
-
"bootstrapAdmins": ["<your gateway user id>"],
|
|
421
|
-
"newUsers": "approved"
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
- **Accounts create themselves** on a person's first request. They are keyed on the gateway's stable user ID, not on email, so a changed address keeps the same account.
|
|
427
|
-
- **Data is private to each user.** This covers chats, documents, folders, search, automations and usage. The server refuses any request for another user's data by ID, and a test matrix tries every route against another user's IDs.
|
|
428
|
-
- **The first admin comes from the config.** Anyone listed in `access.bootstrapAdmins` is always an approved admin.
|
|
429
|
-
- **Roles grant [features](#features).** For example, users can be limited to chatting while admins set up models and MCP servers. The gateway can send `admin` or `user` in the role header, but a decision recorded in the files outranks it. A browser can never promote itself.
|
|
430
|
-
- **Admins** get an **Admin** item under Statistics, where they can:
|
|
431
|
-
- approve pending users (set `access.newUsers: "pending"` to require approval);
|
|
432
|
-
- disable an account, which returns 403 immediately and stops that user's automations;
|
|
433
|
-
- change roles;
|
|
434
|
-
- read any user's chats, documents and live turns. This view is read-only, and each view is recorded in the audit log.
|
|
435
|
-
- **Settings belong to the operator.** Without the `settings` feature, the model, system prompt and approval rules are read-only, and credentials and the gateway address are hidden. Without `mcp`, the MCP servers are hidden. Each person picks their own model from their role's allowed list.
|
|
436
|
-
- **Audit log.** New accounts, rejected requests, role and status changes, config changes and admin views are written to stdout as `[tinywebui:audit]` JSON lines. Messages and secrets are never logged.
|
|
437
|
-
|
|
438
|
-
> [!IMPORTANT]
|
|
439
|
-
> Identity headers are trusted **only** from addresses in `trustedProxyCidrs`, based on the real network connection. `X-Forwarded-For` is ignored, and TinyWebUI refuses to start in this mode without that list. The gateway must remove any identity headers a client sends, and TinyWebUI must be reachable only through the gateway, with no published port.
|
|
440
|
-
|
|
441
|
-
## Deployment as code
|
|
442
|
-
|
|
443
|
-
Every part of a deployment, down to what each role can see, can be set up from files. An agent or a script can build it, commit it to git and hand it over, and the running app stays in step with the files.
|
|
444
|
-
|
|
445
|
-
### Configuring from JavaScript
|
|
446
|
-
|
|
447
|
-
`start()` takes the same settings, so you can run an instance without any files:
|
|
448
|
-
|
|
449
|
-
```js
|
|
450
|
-
import { start } from 'tinywebui';
|
|
451
|
-
|
|
452
|
-
const server = await start({
|
|
453
|
-
port: 7777,
|
|
454
|
-
config: { apiKey: process.env.OPENROUTER_API_KEY, model: 'anthropic/claude-sonnet-5' },
|
|
455
|
-
mcpServers: { files: { command: 'npx', args: ['-y', '@modelcontextprotocol/server-filesystem', '.'] } },
|
|
456
|
-
configFile: false, // or a path; false reads and writes no config file
|
|
457
|
-
dbPath: ':memory:',
|
|
458
|
-
});
|
|
459
|
-
// later: await server.shutdown();
|
|
79
|
+
docker compose build
|
|
80
|
+
docker compose run --rm -it tinywebui set-password
|
|
81
|
+
docker compose up -d
|
|
460
82
|
```
|
|
461
83
|
|
|
462
|
-
The
|
|
463
|
-
|
|
464
|
-
**Layers apply lowest first:** built-in defaults, the JSON config file, environment variables, then `config` from JavaScript. Keys set in JavaScript are locked: the Settings panel shows them read-only and the API refuses to change them. `tinywebui.config.json` is still read and written for everything else, unless the options set `configFile: false`. In that case changes last only as long as the process. Passing `mcpServers` replaces `mcp.json` and makes the MCP panel read-only.
|
|
465
|
-
|
|
466
|
-
### Three kinds of setting
|
|
467
|
-
|
|
468
|
-
| Kind | Where it lives | Can the UI change it? |
|
|
469
|
-
| --- | --- | --- |
|
|
470
|
-
| **File-only:** `authMode`, passwords, `trusted*`, `logoutUrl`, `baseUrl`, `apiKey`, `dbPath`, `access`, `models` | `tinywebui.config.js` or `tinywebui.config.json` | Never. The API refuses, even for admins. |
|
|
471
|
-
| **Locked:** anything set in `tinywebui.config.js`, including `mcpServers` | `tinywebui.config.js` | No. Shown read-only. |
|
|
472
|
-
| **Editable:** everything else | `tinywebui.config.json`, `mcp.json` | Yes, and every change is **written back to the file**. |
|
|
473
|
-
|
|
474
|
-
- **Freeze everything** with `frozen: true` in either file. Settings, tools and MCP servers then can't be changed from the UI or the API. You change the files and let them reload instead. User data and admins' decisions about users still work normally.
|
|
475
|
-
- **Mistakes fail loudly.** An unknown key (usually a typo), a value of the wrong type or a value outside its allowed set stops startup, reload, a UI save and `tinywebui validate`, and every problem is named. Nothing silently falls back to a default.
|
|
476
|
-
- **Admin decisions live in the files.** Approving, disabling or changing someone's role writes `access.users` in `tinywebui.config.json` before the database is touched. The files outrank the database: delete the database and restart, and every approval and ban is still in force.
|
|
477
|
-
- **Edits apply without a restart.** Changes to `tinywebui.config.json` or `mcp.json` are picked up automatically, or on `kill -HUP`. A reload is all or nothing: a file that doesn't parse or validate is rejected and logged, and the running config stays as it was. Changes to `tinywebui.config.js` or `authMode` need a restart.
|
|
478
|
-
- **Fingerprints.** The effective config has a hash that never includes secrets. It is printed at startup, shown in the Admin panel, returned by `/readyz` and included in config-related audit lines before and after each change. Running `tinywebui fingerprint` on the committed files gives the same value, so you can check that a running instance matches a given commit.
|
|
479
|
-
|
|
480
|
-
### A complete tier-3 setup
|
|
481
|
-
|
|
482
|
-
```js
|
|
483
|
-
// tinywebui.config.js: committed to git, holds no secrets
|
|
484
|
-
export default {
|
|
485
|
-
config: {
|
|
486
|
-
authMode: 'trusted-header',
|
|
487
|
-
trustedProxyCidrs: ['172.20.0.0/16'],
|
|
488
|
-
baseUrl: 'http://gateway:8080/v1', // your model gateway; users never see it
|
|
489
|
-
apiKey: process.env.MODEL_GATEWAY_KEY,
|
|
490
|
-
model: 'fast',
|
|
491
|
-
systemPrompt: 'You are the team assistant…', // locked: set here, so read-only in the UI
|
|
492
|
-
logoutUrl: 'https://team.cloudflareaccess.com/cdn-cgi/access/logout',
|
|
493
|
-
access: {
|
|
494
|
-
bootstrapAdmins: ['google-oauth2|1234567890'],
|
|
495
|
-
newUsers: 'pending',
|
|
496
|
-
roles: {
|
|
497
|
-
user: { features: ['chat', 'attachments', 'images', 'search', 'folders', 'model-picker'], models: ['fast', 'smart'] },
|
|
498
|
-
admin: { features: '*' }
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
},
|
|
502
|
-
mcpServers: { // locked: users can use these tools, nobody can edit them
|
|
503
|
-
search: { url: 'http://tinysearch:8000/mcp' },
|
|
504
|
-
context: { url: 'http://gateway:8080/context/mcp' }
|
|
505
|
-
}
|
|
506
|
-
};
|
|
507
|
-
```
|
|
84
|
+
The Compose example publishes port 7777 on the host's loopback interface. The Docker image also accepts `TINYWEBUI_PASSWORD` for a headless first start; use a password of at least 15 characters and keep the container's data volume. Without credentials, a headless start exits before opening a port. The runtime images contain Node.js but no npm/npx; connect MCP servers over HTTP or include their executables in a custom image.
|
|
508
85
|
|
|
509
|
-
|
|
86
|
+
The Compose example now stores config in its named data volume. If you used the previous bind-mounted `tinywebui.config.json`, copy its settings into the volume before starting, then run `set-password` there.
|
|
510
87
|
|
|
511
|
-
|
|
88
|
+
## Documentation
|
|
512
89
|
|
|
513
|
-
|
|
90
|
+
The full reference lives at **[tinysuite.dev/docs/tinywebui](https://tinysuite.dev/docs/tinywebui/)**:
|
|
514
91
|
|
|
515
|
-
|
|
|
92
|
+
| Topic | Covers |
|
|
516
93
|
| --- | --- |
|
|
517
|
-
|
|
|
518
|
-
|
|
|
519
|
-
|
|
|
520
|
-
|
|
|
521
|
-
|
|
|
522
|
-
|
|
|
523
|
-
|
|
|
524
|
-
|
|
|
525
|
-
|
|
|
526
|
-
|
|
|
527
|
-
|
|
|
528
|
-
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
**Defaults.** In tier 3, users get everything except `tools`, `settings`, `mcp`, `admin` and `oversight`, and admins get `*`. In tiers 1 and 2 you get everything except user management.
|
|
532
|
-
|
|
533
|
-
## CLI reference
|
|
534
|
-
|
|
535
|
-
Output on stdout is JSON or a single line, and logs go to stderr, so scripts can parse the output.
|
|
536
|
-
|
|
537
|
-
| Command | What it does |
|
|
538
|
-
| --- | --- |
|
|
539
|
-
| `tinywebui [start] [--port] [--host] [--config]` | Run the server |
|
|
540
|
-
| `tinywebui set-password` | Turn on tier 2 and set the password |
|
|
541
|
-
| `tinywebui validate [path]` | Prints `ok <fingerprint>`, or lists every problem and exits with code 1 |
|
|
542
|
-
| `tinywebui effective [--role user\|admin]` | What is in effect, how each setting can change, and what a role can see and do |
|
|
543
|
-
| `tinywebui fingerprint` | Hash of the effective config (never includes secrets) |
|
|
544
|
-
| `tinywebui schema` | JSON Schema for the config (also in [docs/config.schema.json](docs/config.schema.json)) |
|
|
545
|
-
| `tinywebui migrate [--check]` | Run schema migrations as an explicit step. `--check` exits with code 1 if the database is behind. Set `autoMigrate: false` to make this mandatory. |
|
|
546
|
-
| `tinywebui doctor` | Checks Node, config, database, model endpoint and each MCP server |
|
|
547
|
-
| `tinywebui users list` | Users, and the decisions pinned in the files |
|
|
548
|
-
| `tinywebui users set <id> [--role] [--status] [--clear]` | Record a decision in the config. A running server reloads it. |
|
|
549
|
-
| `tinywebui models pull <preset> [--dir]` | Download an embedding model (`fast`, `balanced`, `quality`, `multilingual`) |
|
|
550
|
-
| `tinywebui models ensure` | Download the configured model only if it is missing |
|
|
551
|
-
| `tinywebui models verify` | Load the configured model the same way startup does |
|
|
552
|
-
|
|
553
|
-
For orchestrators, `GET /healthz` reports that the process is answering, and `GET /readyz` reports that it can serve requests (`{"ready":true,"version":"…","fingerprint":"…"}`).
|
|
554
|
-
|
|
555
|
-
## Security and limits
|
|
556
|
-
|
|
557
|
-
- **Network exposure.** In tier 1, keep TinyWebUI bound to `127.0.0.1`. Use tier 2 or 3 before exposing it to a network.
|
|
558
|
-
- **MCP servers** run with whatever access you give them. Treat each configured server and its credentials as trusted infrastructure, and use [tool approval](#tool-approval) for anything that writes.
|
|
559
|
-
- **Model output is untrusted.** Model-written text reaches the page only as text or through a Markdown renderer that escapes first, and the server sends strict security headers.
|
|
560
|
-
- **Deleting a chat** also deletes its documents, tool output and search vectors.
|
|
561
|
-
- **Not included:** OCR, cloud sync and branching chat history.
|
|
94
|
+
| [Quick start](https://tinysuite.dev/docs/tinywebui/#quick-start) | npx, clone, Docker |
|
|
95
|
+
| [Configuration](https://tinysuite.dev/docs/tinywebui/#configuration) | Environment variables, every setting, the model catalog |
|
|
96
|
+
| [MCP tools](https://tinysuite.dev/docs/tinywebui/#mcp-tools) | Adding servers, tool approval, built-in tools |
|
|
97
|
+
| [Chats and attachments](https://tinysuite.dev/docs/tinywebui/#chats-and-attachments) | Documents, images, steering a turn |
|
|
98
|
+
| [Search](https://tinysuite.dev/docs/tinywebui/#search) | Hybrid retrieval, embedding models, tuning |
|
|
99
|
+
| [Automations](https://tinysuite.dev/docs/tinywebui/#automations) | Scheduled prompts |
|
|
100
|
+
| [Long chats](https://tinysuite.dev/docs/tinywebui/#long-chats-caching-and-compaction) | Caching and compaction |
|
|
101
|
+
| [Usage statistics](https://tinysuite.dev/docs/tinywebui/#usage-statistics) | Token use and attribution |
|
|
102
|
+
| [Access](https://tinysuite.dev/docs/tinywebui/#access) | Just you, a password, or a team behind SSO |
|
|
103
|
+
| [Deployment as code](https://tinysuite.dev/docs/tinywebui/#deployment-as-code) | JavaScript config, locked settings, roles and features |
|
|
104
|
+
| [CLI](https://tinysuite.dev/docs/tinywebui/#cli) | Every command |
|
|
105
|
+
| [Security and limits](https://tinysuite.dev/docs/tinywebui/#security-and-limits) | Exposure, MCP trust, what isn't included |
|
|
106
|
+
|
|
107
|
+
In this repository: [docs/deploy.md](docs/deploy.md) is a scripted container deployment, and [docs/config.schema.json](docs/config.schema.json) is the config schema (also `tinywebui schema`).
|
|
562
108
|
|
|
563
109
|
## Development
|
|
564
110
|
|
|
@@ -567,22 +113,7 @@ npm test # the whole suite, about 10 seconds
|
|
|
567
113
|
npm run dev # restart on changes in src/ and bin/
|
|
568
114
|
```
|
|
569
115
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
### Evals
|
|
573
|
-
|
|
574
|
-
`npm run eval` runs the tasks in `evals/tasks/` against the configured model and scores its behaviour. It checks whether the model uses tools instead of guessing, ignores instructions planted in tool output, reports failures honestly, respects a declined call, and more. Each task scripts its tools' results, so runs are comparable. The checks are deterministic (regex, call counts), except `judge`, which asks the model for a strict PASS or FAIL on one criterion.
|
|
575
|
-
|
|
576
|
-
```bash
|
|
577
|
-
npm run eval -- --repeat 3 --save baseline # record a reference run
|
|
578
|
-
# ...change the prompt or the harness...
|
|
579
|
-
npm run eval -- --repeat 3 --compare baseline
|
|
580
|
-
```
|
|
581
|
-
|
|
582
|
-
`--only name,words` runs a subset of tasks and `--model id` tries another model. Results go to `evals/results/`, which git ignores. To add a task, drop a JSON file into `evals/tasks/`; the existing tasks show every type of check.
|
|
583
|
-
|
|
584
|
-
> [!NOTE]
|
|
585
|
-
> Evals make real API calls to your configured endpoint, so they cost money.
|
|
116
|
+
[AGENTS.md](AGENTS.md) maps the codebase and says where new code goes. `npm run eval` runs model-behaviour evals from `evals/tasks/` and makes real API calls, so it costs money.
|
|
586
117
|
|
|
587
118
|
## License
|
|
588
119
|
|