1.0.0
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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,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"
|
||||||
|
|||||||
Reference in New Issue
Block a user