The version, the changelog entry, the plan and the README. Nothing else,
which is what makes this readable as a release rather than as work.

CHANGELOG.md's 1.0.0 entry is assembled from every version below it, as
that file has said it would be since it was written: those shipped as a
running deployment rather than as releases, and this is what they add up
to. It is also what an administrator reads -- /admin/updates takes release
notes out of the annotated tag, so the tag message is this entry.

It says what arrived, then the part worth reading: the nine things that
had shipped looking correct and were found by five audit passes. Then
where the edges are, because a first release should say what it does not
do before somebody finds out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-07 15:26:18 +02:00
parent e7294e5b20
commit dce54eb2de
4 changed files with 165 additions and 19 deletions
+93
View File
@@ -16,6 +16,99 @@ for 1.0.0 have something to be assembled from.
## Unreleased ## Unreleased
## 1.0.0
The first release. Every version before it shipped as a running deployment
rather than as a release; this is what those add up to, and the point at which
it is worth somebody else installing.
**What it is.** A self-hosted web interface for OpenAI-compatible endpoints.
Server-rendered, no build step, no CDN, one SQLite file. Point it at whatever
you run — llama.cpp, LM Studio, vLLM, Ollama, OpenRouter, OpenAI — and it works
the same.
### What arrived since 0.8.1
- **Things that happen because time passed.** Say "every Monday at nine" and a
model sets it up itself, against the same recurrence rule the manual form
uses. A run can file a **report** you read later, send you a message, or work
in a chat of its own.
- **News that finds you.** A dot in the sidebar, a count in the tab title while
you are looking elsewhere, and **web push** so a schedule firing at seven in
the morning reaches a browser that is shut. Opt-in per device, and the one
thing here that contacts an outside service — `services/push.py` says so
plainly and says what it costs.
- **Helpers.** A reply can hand a self-contained piece of work to another model
that runs on its own and reports back, several at once. A helper cannot ask
questions, cannot send helpers of its own, changes nothing unless asked, and
on a machine runs only a fixed list of read-only commands.
- **Drawing.** Point it at a ComfyUI and a model can make images, against
workflow templates and defaults you set — size, steps, sampler, scheduler,
checkpoint. It reviews its own result and can try again.
- **Semantic search.** Pick an embedding model and library search fuses keyword
and meaning, so *"how do I get paid"* finds a document that says *"invoicing"*.
Choosing none is not a degraded mode: it is byte-for-byte the keyword search
that was always there, with nothing written and no requests made.
- **Quotas and sharing.** Monthly tokens, concurrent replies, agent wall clock,
images a day, helpers a reply — resolved by maximum across a person's groups,
with zero meaning *no limit*. Documents, notes, skills and reports can be
handed to a group or a person, read-only, with a *Shared with me* filter
everywhere. And a screen that answers **"what can this account actually do?"**
by naming where each permission came from.
- **Make it yours.** Name, tagline, logo, favicon and launcher icons; the
Middle-earth wording is editable data; custom themes defined as a set of
colours rather than a stylesheet.
- **Install it and update it.** A Dockerfile, a Proxmox container script, and an
`/admin/updates` page showing what is running, what is available and what
changed between. The button that applies an update is opt-in and cannot do the
work itself — it writes a file that a systemd unit picks up, because a web
application that can restart its own service is one whose worst day is much
worse.
### The part worth reading
Five audit passes went into this release rather than one, and they found things
that had shipped looking correct. These are the entries somebody stops working
around a bug because of:
- **Every model was told the time in a zone with no name** — on any account that
had not chosen one, which is every account by default.
- **A helper could write files and run programs on a remote machine,
unattended, in a mode that promises to change nothing.** `find` was on the
read-only command list, and `find -fprintf` writes a file.
- **Two ways to get root out of the update helper**, one of which needed no
compromise at all: root ran a script the unprivileged service account owns,
and an update fetches that script as that account.
- **Deleting a chat left every file it held on disk** — attachments, generated
images, all of it, with nothing that would ever look at them again.
- **Folder nesting was fully built, documented in the README, and reachable by
nothing.** So was moving a chat into a folder.
- **The terminal silently stopped accepting input after a reconnect**, while
output kept arriving so the panel looked healthy.
- **On the Messages screen, half the keyboard shortcuts did nothing**, because
two scripts were loaded twice and each toggle ran twice.
- **The prompt preview could not show two thirds of what it previews.**
- **Hints and timestamps failed the contrast minimum in both themes.**
### Where the edges are
Stated because they are the things worth knowing before you rely on it:
- **Nothing executes on the machine LLeMbas runs on.** Agent chats run their
commands over SSH on a host you choose, and the security of an agent chat is
the security of that host. There is no sandbox here and that is deliberate —
`PLAN.md` records the one that was designed and dropped, and why.
- **One worker.** The generation registry, the terminal sessions and the
schedule ticker are all in-process. Two workers means two tickers and every
schedule firing twice.
- **A restart abandons replies in flight**, keeping whatever each had.
- **Schema changes are additive.** New tables and columns apply themselves at
startup; renames and drops are manual. The upgrade path is tested from an
0.8.1-shaped database with rows in it.
- **Sharing grants reading only.**
2283 tests on Python 3.11, 3.12 and 3.14.
## 0.9.13 ## 0.9.13
**The testing pass.** 2140 tests became 2283, and writing them found four bugs **The testing pass.** 2140 tests became 2283, and writing them found four bugs
+70 -18
View File
@@ -4,14 +4,16 @@ Where the project is, what is deliberately not built yet, and the decisions
that would be expensive to revisit. Kept current as work lands; the detail of that would be expensive to revisit. Kept current as work lands; the detail of
*how* things work lives in [`CLAUDE.md`](CLAUDE.md). *how* things work lives in [`CLAUDE.md`](CLAUDE.md).
**Status:** usable daily, and closing on 1.0.0. Streaming chat, attachments, **Status:** released. **1.0.0.** Streaming chat, attachments, reasoning, tool
reasoning, tool calling with web search, custom HTTP tools and MCP servers, calling with web search, custom HTTP tools and MCP servers, agent chats that
agent chats that work on a machine over SSH, a knowledge library, notes, memory work on a machine over SSH, helpers a reply can delegate to, a knowledge library
and skills, speech in and out, image generation over ComfyUI, users and groups, with keyword and semantic search, notes, memory and skills, speech in and out,
model administration, installable as an app, reports, messages, and scheduled image generation over ComfyUI, users, groups, quotas and sharing, model
work that runs on its own. 2088 tests, `ruff` clean. administration, branding, installable as an app, reports, messages, scheduled
work that runs on its own, web push, and updating from the web interface.
2283 tests on Python 3.11, 3.12 and 3.14; `ruff` clean.
What remains before the first stable release is written out below, in phases, How it got there is written out below, in phases,
under [The road to 1.0.0](#the-road-to-100). under [The road to 1.0.0](#the-road-to-100).
--- ---
@@ -566,28 +568,47 @@ seen working.
- [x] `/healthz`, which opens the database rather than only proving the socket - [x] `/healthz`, which opens the database rather than only proving the socket
is listening, and says nothing about what is here is listening, and says nothing about what is here
### Phase 8 — audit and finalization (`0.9.8` … `0.9.9`) ### Phase 8 — the audit, in five passes (`0.9.9` … `0.9.13`)
- [ ] Security review over the whole accumulated diff
- [ ] A sweep for the failure this codebase keeps cataloguing: a control that Five passes rather than one, each ending in a deploy. What each found is in
looks like it works — a verb against a route that does not serve it, a `CHANGELOG.md`; the shape of it is worth keeping here.
trigger bound where the event does not go
- [ ] Every harness fragment read as a model would read it - [x] **The main logic and the harness** (`0.9.9`). Every model was being told
- [ ] Focus, contrast and narrow widths across the admin screens the time in a zone with no name; the prompt preview could not show two
- [ ] Documentation, a fresh install, and an upgrade from an 0.8.x database thirds of what it previews; Plan mode was told to use a tool Plan mode
withdraws; reading one knowledge document could fill the whole window
- [x] **Functional bugs and unreachable features** (`0.9.10`). The four control
sweeps came back **clean** — 68 htmx verbs against 179 routes, zero
mismatches. What they found instead was one level up: folder nesting fully
built, documented in the README, and reachable by nothing; deleting a chat
leaving every file it held on disk
- [x] **Security** (`0.9.11`, `0.9.12`). Six findings. A helper could write files
and run programs unattended in a mode that promises to change nothing; an
SSH connection could be pointed at `0.0.0.0` and reach this host; **two
root escalations in the update helper**, one of which meant control of the
branch was control of root
- [x] **Testing** (`0.9.13`). 2140 tests to 2283, and four bugs that reading had
not found — three of them from driving the JavaScript under a DOM stub
- [x] Contrast, measured rather than eyeballed: `--ink-faint` failed the 4.5:1
minimum in **both** themes
- [x] Documentation, and `docs/notes/release-checklist.md` for the half a
machine cannot test
### Phase 9 — 1.0.0 ### Phase 9 — 1.0.0
- [ ] A commit that changes the version, `CHANGELOG.md`, this file and the - [x] A commit that changes the version, `CHANGELOG.md`, this file and the
README, and nothing else README, and nothing else
- [ ] A **signed annotated tag** whose message is the 1.0.0 changelog entry. - [x] A **signed annotated tag** whose message is the 1.0.0 changelog entry.
Not decoration: `/admin/updates` reads release notes out of the tag Not decoration: `/admin/updates` reads release notes out of the tag
object, so the tag message is what an administrator sees on that page object, so the tag message is what an administrator sees on that page
- [ ] The deployment moves to the `stable` channel, which has something to - [x] The deployment moves to the `stable` channel, which has something to
follow for the first time follow for the first time
--- ---
## After 1.0.0 ## After 1.0.0
Features:
- **OCR** for scanned PDFs - **OCR** for scanned PDFs
- **Conversation branching** — `Message.parent_id` exists unused; needs a UI for - **Conversation branching** — `Message.parent_id` exists unused; needs a UI for
choosing between versions, which is why rewind truncates for now choosing between versions, which is why rewind truncates for now
@@ -597,6 +618,37 @@ seen working.
- **Writable shares**, which need history and a merge story before they need a - **Writable shares**, which need history and a merge story before they need a
column column
Carried out of the 1.0.0 audit, deliberately. Each is real; each would change
what something *does* rather than fix what it claims to do, which is why none of
them landed in an audit:
- **A read-only helper is still told about tools it does not have.**
`resolve_tools` filters per tool and `harness._families` gates per family, so
a family survives on its readers while its writers are gone — and seven
fragments name fifteen withdrawn write tools. The principled fix is the split
`tool.skills` / `tool.skills_write` already demonstrates, applied to `notes`,
`report`, `schedule` and `agent_edits`. That is a prompt restructure. The cost
today is bounded: `{{tool_names}}` is authoritative and the model has it, so a
helper wastes at most one round finding out.
- **`tool.background` promises a notification that can be switched off.** It has
no `requires` for `agents.background_notify`, while the runner branches on
exactly that flag. One fragment, two behaviours. Same shape as the split above.
- **`ask_user` has no harness fragment**, alone among the families. All of its
guidance lives in its schema description, which is the one thing an
administrator cannot edit.
- **`Connection.extra_headers_json` is read on every request and written by no
form**, so its documented use — OpenRouter's `HTTP-Referer` — is unreachable.
Nothing advertises it, so nothing is currently untrue.
- **Four columns are written and never read**: `Chat.compacted_at`,
`User.last_login_at`, `Schedule.last_fire_at`, `Schedule.compiled_at`. Each is
bookkeeping somebody may want to surface; none is load-bearing.
- **Dependency floor.** `pyproject.toml` pins no upper bounds and
`deploy/update.sh` runs `pip install -e` on every update, so a breaking
upstream release arrives on a button press. pip's `only-if-needed` default
limits the blast radius, which is why this is a note rather than an emergency.
- **`deploy/lxc-install.sh` has never been executed.** There is no Proxmox host
here. It is reviewed and syntax-checked; that is not the same claim.
--- ---
## Known limits ## Known limits
+1
View File
@@ -8,6 +8,7 @@
</p> </p>
<p align="center"> <p align="center">
<img alt="Version 1.0.0" src="https://img.shields.io/badge/version-1.0.0-6B8E4E?style=flat-square">
<img alt="Python 3.11+" src="https://img.shields.io/badge/python-3.11%2B-3E6B7A?style=flat-square"> <img alt="Python 3.11+" src="https://img.shields.io/badge/python-3.11%2B-3E6B7A?style=flat-square">
<img alt="License GPL-3.0" src="https://img.shields.io/badge/license-GPL--3.0-C9A227?style=flat-square"> <img alt="License GPL-3.0" src="https://img.shields.io/badge/license-GPL--3.0-C9A227?style=flat-square">
<img alt="No Node required" src="https://img.shields.io/badge/build%20step-none-6B8E4E?style=flat-square"> <img alt="No Node required" src="https://img.shields.io/badge/build%20step-none-6B8E4E?style=flat-square">
+1 -1
View File
@@ -1,3 +1,3 @@
"""LLeMbas - a Middle-earth themed web UI for OpenAI-compatible LLM endpoints.""" """LLeMbas - a Middle-earth themed web UI for OpenAI-compatible LLM endpoints."""
__version__ = "0.9.13" __version__ = "1.0.0"