- Emacs Lisp 99%
- Makefile 1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| tests | ||
| .dir-locals.el | ||
| .gitignore | ||
| AGENTS.md | ||
| conduit-observations.org | ||
| Makefile | ||
| plan.org | ||
| README.md | ||
| webui-api.el | ||
| webui-auth.el | ||
| webui-backend.el | ||
| webui-chat.el | ||
| webui-custom.el | ||
| webui-db.el | ||
| webui-list.el | ||
| webui-models.el | ||
| webui-pkg.el | ||
| webui-sync.el | ||
| webui-transient.el | ||
| webui-util.el | ||
| webui.el | ||
webui.el
An Open WebUI client for Emacs.
webui.el lets you chat with models served by an Open WebUI instance from the comfort of Emacs: start new streaming discussions, resume server-side conversations, and keep a local SQLite cache of your chat history for offline browsing and fast listing.
It is built in the spirit of eglot: minimal dependencies, built on Emacs' own capabilities wherever possible, no over-engineering.
Status
Version 0.1.0 — Phase 3 of the implementation plan:
authentication, REST client, SQLite cache, streaming chat, model
selection, incremental sync, and the chat browser are working.
File uploads, memory management, and full-text search arrive in
later phases (see plan.org §12).
Requirements
- Emacs 29.1+
- gptel — only its
gptel-requestlibrary, used as the streaming SSE transport - markdown-mode —
assistant-message rendering (
markdown-ts-modeis used automatically on Emacs 31.1+) curl(external binary) — multipart file uploads only (upcoming; not needed yet)
Installation
The package lives at forge.dursaf.xyz/tareef/webui.el.
With use-package and the :vc keyword (Emacs 30+, or a recent
standalone use-package):
(use-package webui
:vc (:url "https://forge.dursaf.xyz/tareef/webui.el.git" :rev :newest)
:commands (webui webui-new-chat webui-pull))
To clone over SSH instead, use
ssh://git@forge.dursaf.xyz/tareef/webui.el.git.
On Emacs 29 (no :vc support), clone the repo manually and add it
to your load-path:
git clone https://forge.dursaf.xyz/tareef/webui.el.git ~/src/webui.el
(add-to-list 'load-path "~/src/webui.el")
(require 'webui)
Quick start
-
Add a server (persists to your init file):
M-x webui-add-server RET https://chat.example.com RET you@example.com RET -
Log in (the password is stored in
auth-source; the short-lived JWT is cached in the local database, never in plaintext):M-x webui-login RET -
Open the chat browser:
M-x webui RET -
Pull your existing chats from the server:
M-x webui-pull RET -
Start a new discussion:
M-x webui-new-chat RET
Configuration
Everything lives under the webui customize group:
M-x customize-group RET webui RET
The main options:
(use-package webui
:custom
(webui-servers
'((:url "https://chat.example.com"
:user "me@example.com"
:display-name "Work")))
(webui-database-file "~/.emacs.d/webui.sqlite"))
webui-servers— list of server plists (:url,:user, optional:display-name). Multiple servers are supported.webui-database-file— location of the local SQLite cache (defaults to~/.emacs.d/webui.sqlite; set to nil for an in-memory, non-persisted cache).
Usage
Chat buffers
Chat buffers are ordinary markdown-mode buffers you can edit, save,
and search with standard Emacs commands. Each assistant response is
streamed in with fontification; Open WebUI features (web search,
memory, etc.) can be toggled per chat.
| Keybinding | Command |
|---|---|
C-c C-c |
Send the prompt |
C-c C-k |
Abort the stream |
C-c C-m |
Select model |
C-c C-f |
Toggle features |
Chat browser (*webui-list*)
A tabulated-list-mode buffer over the local cache, sorted by
last-updated.
| Keybinding | Command |
|---|---|
RET |
Open chat at point |
G |
Pull (sync) from server |
n |
Start a new chat |
s |
Search chats |
q |
Quit |
Other commands
M-x webui-menu— transient command menuM-x webui-login— sign in and store the JWTM-x webui-pull— incremental sync (a no-op pull is a single HTTP request thanks to a high-water mark)
How it works
Three thin layers:
- UI —
webui.el,webui-chat.el,webui-list.el,webui-transient.el - Domain —
webui-sync.el(pull/push),webui-chat.el(turn orchestration),webui-models.el - Storage / transport —
webui-db.el(builtinsqlitemodule, raw SQL, no ORM),webui-api.el(REST via builtinurl-retrieve),gptel-request(SSE chat streaming)
REST calls use url-retrieve; streaming chat rides on
gptel-request with Open WebUI's extra request fields injected via
the per-backend :request-params seam. The SQLite cache is the
source of truth for offline state; the server wins on conflict.
Development
make test # run the ERT suite (clean Emacs, no user config)
make compile # byte-compile all sources
make checkdoc # docstring conventions
make lint # package-lint (GNU ELPA readiness)
make clean # remove compiled artifacts
Design documents:
plan.org— architecture, module layout, DB schema, sync engine, phasing. The design authority.conduit-observations.org— the Open WebUI REST + streaming API contract distilled from the Conduit reference client (resources/conduit/).
License
GPL-3.0-or-later.