Modern, full-featured XMPP for WeeChat — OMEMO, MAM history, PubSub microblogging, HTTP upload, and terminal image previews — in a C++23 plugin you can reload without restarting WeeChat.
Requires WeeChat 4.3 or newer. Source builds require C++23, CMake 3.22+, and matching WeeChat development headers; CMake checks their installed version.
Fork of bqv/weechat-xmpp
Original author: Tony Olagbaiye <bqv@fron.io>
Site: ekollof.github.io/xepher · Repo: github.com/ekollof/xepher
| OMEMO | Axolotl-namespace E2EE for PMs (MUC experimental); BTBV trust; /omemo fingerprint |
| History | XEP-0313 MAM + LMDB cache across reconnects |
| Social | PubSub microblog (Movim-compatible); optional feed buffers |
| Files | XEP-0363 upload, SFS/ESFS, stickers; Kitty/cell inline previews via icat with left-click to open |
| MUC | Full XEP-0045: join, admin, bookmarks, moderation, reactions |
| UX | Receipts/read markers, typing, corrections, replies, status bar encryption item |
| Forms | Guided ad-hoc commands, MUC configuration/registration, and account registration with defaults, choices, validation, and masked private fields |
⭐ Star the repo if Xepher is useful — it helps others find it.
💬 Join the MUC: xepher@conference.hackerheaven.org · Discussions
Packages (recommended) — grab the latest from Releases:
# Debian / Ubuntu
sudo dpkg -i xepher_*_amd64.deb
# Fedora
sudo dnf install ./xepher-*-*.x86_64.rpm
# Arch Linux
sudo pacman -U xepher-*-x86_64.pkg.tar.zst
# Void
sudo xbps-install -R . xepher-*.xbps # or install the .xbps from Releases
# Alpine
sudo apk add --allow-untrusted xepher-*-r0.apkFrom source (Linux; on BSD use gmake):
git clone --depth 1 https://github.com/ekollof/xepher.git
cd xepher
make install-deps # sudo; detects distro
make # Release build → ./xmpp.so
make install # plugin + icat.py/feed_compose.py with Python autoload/plugin load xmpp
/account add work user@example.com secret
/account connect work
After upgrades: make install, then /plugin reload xmpp (reconnect accounts).
More detail: Installation wiki · full deps and platforms below.
Binary packages for v0.8.1+ (current: v0.12.0) are on GitHub Releases. Pick your distro:
| Platform | Package |
|---|---|
| Debian / Ubuntu | xepher_*_amd64.deb |
| Fedora / RHEL | xepher-*-fc*.x86_64.rpm |
| Arch Linux | xepher-*-x86_64.pkg.tar.zst |
| Void Linux | xepher-*_x86_64.xbps |
| Alpine Linux | xepher-*-r0.apk |
Avoid v0.8.0 Fedora/Alpine artifacts (~1 GB packaging bug). Use v0.8.1 or newer.
| Library | Type | Linux | FreeBSD | OpenBSD | macOS (Homebrew) |
|---|---|---|---|---|---|
| libstrophe (≥ 0.12) | runtime | ✅ package | ✅ pkg install libstrophe |
✅ pkg_add libstrophe |
✅ brew install libstrophe |
| libxml2 | runtime | ✅ package | ✅ pkg install libxml2 |
✅ pkg_add libxml |
✅ brew install libxml2 |
| lmdb | runtime | ✅ package | ✅ pkg install lmdb |
✅ pkg_add lmdb |
✅ brew install lmdb |
| libomemo-c | runtime | ✅ package | ✅ pkg install libomemo-c |
✅ pkg_add libomemo-c |
|
| libsignal-protocol-c | runtime | ✅ package | ✅ pkg install libsignal-protocol-c |
✅ pkg_add libsignal-protocol-c |
|
| gpgme | runtime | ✅ package | ✅ pkg install gpgme |
✅ pkg_add gpgme |
✅ brew install gpgme |
| libfmt | runtime | ✅ package | ✅ pkg install libfmt |
✅ pkg_add fmt |
✅ brew install fmt |
| clang/clang++ + C++23 standard library (see below) | build | ✅ package | ✅ pkg install llvm |
✅ base system / pkg_add |
✅ brew install llvm |
| gmake | build | — (GNU make default) | ✅ pkg install gmake |
✅ pkg_add gmake |
✅ brew install make |
| bison | build | ✅ package | ✅ pkg install bison |
✅ pkg_add bison |
✅ brew install bison |
| flex | build | ✅ package | ✅ pkg install flex |
✅ pkg_add flex |
✅ brew install flex |
| doctest (header-only) | test | ✅ vendored in deps/doctest/ |
— | — | — |
| WeeChat >= 4.3 | headers + runtime | ✅ package | ✅ pkg install weechat |
✅ pkg_add weechat |
✅ brew install weechat |
On FreeBSD and OpenBSD, run ./install-deps.sh (or build from the port skeletons in
packaging/freebsd/ and packaging/openbsd/). Use gmake instead of make.
The plugin is developed and tested on Linux (Arch, Debian/Ubuntu, Fedora, Void, Alpine). CI builds packages for all five via GitHub Actions on each release tag.
macOS (Apple Silicon and Intel) receives best-effort support via Homebrew.
The build system auto-detects the Homebrew prefix and sets PKG_CONFIG_PATH
accordingly. See macOS build instructions below.
FreeBSD, OpenBSD, and NetBSD receive best-effort support: the build system and scripts have been ported to POSIX sh and BSD-compatible make, but these platforms are not routinely tested. Known considerations:
- Use
gmakeinstead ofmakeon BSD (BSD make has different syntax). - Build backend: CMake 3.22+ with Ninja (
build/). The rootmakefileis a thin wrapper; packaging and CI still callgmake PACKAGE_BUILD=1 weechat-xmpp. - Build deps: Clang/Clang++ with a compatible C++23 standard library (see toolchain requirements), cmake, ninja, bison, flex, git.
- Default toolchain: Clang/Clang++. The makefile sets
CC=clangandCXX=clang++on all platforms (Homebrew LLVM on macOS). NetBSD 9.x ships Clang 7 and cannot build — install a current LLVM toolchain and compatible C++ standard library from pkgsrc rather than relying on the OS version alone. libsignal-protocol-candlibomemo-care packaged on FreeBSD and OpenBSD; on NetBSD they may still need to be built from pkgsrc source.- Default builds use Release (
-O2 -DNDEBUG). UseDEBUG=1for dev builds (-O0 -DDEBUG+ 176 doctests). UseASAN=1for AddressSanitizer (-fsanitize=address;-lasan -lrton Linux only). Combine:gmake DEBUG=1 ASAN=1. - The
.sourceELF section embedding step (objcopy --add-section) is Linux-only, off by default (usemake releaseorEMBED_SOURCE=1), skipped on BSD, and skipped in distribution builds (PACKAGE_BUILD=1). - Debug doctests link the normal
xmpp.so(no double full compile). Coverage instrumentation is only built formake coverage.
Both the compiler and its selected C++ standard library headers and
library must support the features Xepher uses: std::expected, ranges,
std::span, and std::string::contains. CMake checks these during configuration
and stops with a toolchain diagnostic if they are unavailable.
- Linux with libstdc++: use Clang 19 or newer with GCC 13 or newer
C++ development headers and libstdc++. Clang normally uses the system GCC
standard library; installing a newer Clang does not necessarily update it.
Clang 19 enables libstdc++'s
std::expectedsupport through its updated concepts feature macro (LLVM release notes). - Toolchains using libc++ (including macOS/BSD): the library needs C++23
support, including
std::expected(available in libc++ 16+; see libc++ status). Use a matching, current LLVM toolchain; the CMake feature check is authoritative.
These are feature baselines, not a claim that every compiler/platform combination has been tested. The previous Clang 13/14 minimum in this README was incorrect.
If compilation reports no member named 'to' in namespace 'std::ranges'
(issue #20), update the checkout:
Xepher collects ranges with std::ranges::copy (and formats joins with fmt) and does not require
std::ranges::to (which first appeared in
libstdc++ 14). Clang 19/20 with a suitable
older libstdc++ can therefore build without upgrading to libstdc++ 14 just for
that function. If std::expected is missing instead, update the selected
compiler/library pair.
The earlier GCC 12 header recommendation was too low for the complete plugin:
std::ranges::contains, std::views::enumerate, and std::views::chunk require
newer library support. CMake checks these features as well. GCC 13 headers are
the libstdc++ baseline; upgrading to GCC 14 solely for ranges::to is unnecessary.
Select versioned compilers explicitly as make command-line arguments:
make CC=clang-20 CXX=clang++-20 DEBUG=1For an isolated toolchain check with direct CMake, use a fresh build directory:
cmake -S . -B /tmp/xepher-clang20 -G Ninja \
-DCMAKE_C_COMPILER=clang-20 -DCMAKE_CXX_COMPILER=clang++-20The canonical build uses CMake + Ninja under build/. Day-to-day commands go
through the thin makefile wrapper (same interface as before the CMake migration).
git clone --depth 1 git@github.com:ekollof/xepher.git
cd xepher
git submodule update --init --recursive
make install-deps # installs system packages (requires sudo)
make # optimized plugin (no doctests)
make DEBUG=1 # dev build + 176 doctests and form editor smoke test
make test # doctests only (CTest)
make tools # optional: dump_mam_db / dump_omemo_db LMDB inspectors
make install # atomic install to ~/.local/share/weechat/plugins/ — do NOT run as root
# safe while WeeChat is running; then /plugin reload xmppOn BSD, replace make with gmake throughout.
Doctest is vendored under deps/doctest/ (v2.5.2). make DEBUG=1 or make test
runs 176 doctests (handler slices, StanzaView, IQ builders, port stubs) without a
system package. When weechat-headless is available, it also runs an isolated
form editor smoke test covering input masking, history suppression, rejection
and retry, cancellation, and cleanup without connecting any XMPP accounts.
With Python 3 and the OpenSSL CLI available, CTest also runs
tests/test_registration_tls.py using Python's standard-library unittest.
It starts local mock servers and isolated WeeChat instances to verify that
registration sends no query or credentials without STARTTLS or with an
untrusted certificate. These tests skip if loopback port 5222 is occupied;
they never connect to an existing listener or create real accounts.
Plain make skips these checks.
To build a distribution-style plugin locally (same as packages):
make PACKAGE_BUILD=1 weechat-xmppTo embed sources into the plugin (for the make release dump workflow):
make EMBED_SOURCE=1 weechat-xmpp
# or: make releaseDirect CMake (IDEs, clangd, or when you prefer presets over the wrapper):
cmake --preset dev
cmake --build --preset dev # xmpp.so, then doctests (xepher_dev)
ctest --preset dev # doctests onlySee CMakePresets.json for release, asan, and package profiles.
make install-deps automatically detects your distribution (Debian/Ubuntu,
Fedora/RHEL, Arch, openSUSE, Void, Alpine, Gentoo, FreeBSD, OpenBSD, NetBSD,
macOS) and installs the required packages (including cmake and ninja).
Note:
libsignal-protocol-candlibomemo-care not in Homebrew core and must be built from source before the plugin will compile.
Step 1 — Install Homebrew packages:
./install-deps.sh # detects macOS and runs the brew install blockOr manually:
brew install llvm cmake ninja make bison flex libstrophe libxml2 lmdb gpgme fmt curl openssl weechatStep 2 — Build libsignal-protocol-c from source:
git clone https://github.com/signalapp/libsignal-protocol-c
cd libsignal-protocol-c
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$(brew --prefix) .
make && make install
cd ..Step 3 — Build libomemo-c from source:
git clone https://github.com/gkdr/libomemo-c
cd libomemo-c
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$(brew --prefix) .
make && make install
cd ..Step 4 — Build Xepher:
git clone --depth 1 git@github.com:ekollof/xepher.git
cd xepher
git submodule update --init --recursive
make
make test
make installThe build system uses -dynamiclib instead of -shared and plain -g instead
of -gdwarf-4 on macOS automatically. install-deps.sh installs Homebrew
llvm; the makefile defaults to that Clang. No extra CXX= flags are needed.
Debugger: On macOS the make debug target uses lldb with
DYLD_INSERT_LIBRARIES instead of gdb/LD_PRELOAD.
WeeChat 4.3 or newer is required for both building and running Xepher.
CMake checks the installed development package's version through
pkg-config weechat and rejects versions below 4.3 during configuration.
Install the matching WeeChat development headers and rebuild the plugin after
a WeeChat plugin API change; WeeChat checks plugin API compatibility when loading.
The minimum C++ language standard remains C++23.
/account add work user@example.com mypassword
/account connect work
Use /help account for full details.
Add the xmpp_encryption bar item to your status bar to see the current
buffer's encryption mode (🔒OMEMO / 🔒PGP / empty for plaintext). In MUCs it may show "🔒OMEMO (pending)" while fetching occupant bundles.
/set weechat.bar.status.items "[time],[buffer_last_number],[buffer_plugin],buffer_number+:+buffer_name+(buffer_modes)+{buffer_nicklist_count}+buffer_zoom+buffer_filter,xmpp_encryption,[lag],[hotlist],completion"
-
Enable typing in WeeChat:
/set typing.look.enabled_nicks on /set typing.look.enabled_self on /set typing.look.input_min_chars 2 /save typing -
Add the typing bar item:
/set weechat.bar.typing.items "typing" /save
Sent PM messages show delivery and encryption status as aligned prefixes at the
start of the message text (WeeChat's private tag hides the nick column for
your own lines). Examples: ⌛ hello, ✓ 🔒 hello. Status updates
automatically when the recipient acknowledges the message:
| Glyph | Meaning |
|---|---|
| ⌛ | Pending — sent, no receipt yet |
| ✓ | Delivered — recipient's client received it (XEP-0184) |
| ✓✓ | Read — recipient marked it displayed (XEP-0333) |
A colored Unicode symbol (for example ◉) may appear before a contact's bare
JID in PM buffers. That is a deterministic avatar badge derived from their
XEP-0084 PEP avatar image — not an
OMEMO device ID. Contacts without a published avatar show only the colored JID.
The account nickname is automatically added as a highlight word to PM and FEED buffers so messages containing it trigger a notification. MUC rooms use WeeChat's native nick-based highlighting instead.
To disable this behaviour (e.g. if your short nickname causes false highlights):
/set xmpp.look.highlight_words off
/save
When a message contains an <html> body, the plugin renders it with WeeChat
color/attribute support instead of raw markup.
| Element | Rendered as |
|---|---|
<b>, <strong> |
Bold |
<i>, <em> |
Italic |
<u> |
Underline |
<del>, <s>, <strike> |
Darkgray |
<code>, <tt> |
Gray monospace |
<pre> |
Gray-wrapped preformatted block |
<a href="...">text</a> |
text (blue URL) |
<img alt="..."/> |
[alt] placeholder |
<br> |
Newline |
<blockquote> |
Green > prefix |
<span style="color:..."> |
Terminal color (16 named + #rgb hex) |
Incoming: When a message contains <rdf:Description> (XEP-0511) carrying
OpenGraph metadata, a compact preview is rendered in darkgray:
alice: Check this out! https://example.com/article
The Best Webpage This is a great webpage and you will really like it https://example.com/canonical-url [img]
To disable incoming previews:
/set xmpp.look.incoming_link_preview off
Outgoing: URLs in your messages automatically trigger a background fetch; a metadata stanza is sent as a follow-up so the recipient can render a preview even without server-side injection. To disable:
/set xmpp.look.outgoing_link_preview off
Opens an interactive picker showing your last 20 non-retracted sent messages so you can select which one to correct, then pre-fills the input bar with the original text ready for editing.
/edit # open interactive picker — select message, edit in input bar
/edit-to <id> <text> # correct a specific message by ID (used internally by picker)
How it works:
- Type
/editwith no arguments to open the interactive picker. - Navigate with
↑/↓and pressEnterto select the message you want to correct. - The picker closes and the input bar is pre-filled with
/edit-to <id> <original text>. Edit the text and pressEnterto send. - A correction stanza is sent referencing the original message ID. The local
buffer updates the line with an
[edit]prefix.
Notes:
- In MUC rooms, correction uses the server-assigned stanza-id (XEP-0359) when available, falling back to the origin-id.
- Recipients who do not support XEP-0308 will receive the correction as a new
message with a
[CORRECTION]fallback body. - You must be connected and inside a chat buffer (PM or MUC) to use
/edit.
Example:
you: Hello eweryone!
/edit
[interactive picker opens — select the message, press Enter]
> input bar is pre-filled: /edit-to abc123 Hello eweryone!
> edit to: /edit-to abc123 Hello everyone! [Enter]
you: [edit] Hello everyone!
Deletes one of your own messages from the current buffer.
/retract
Takes no arguments. Opens an interactive picker showing your last 20
non-retracted messages. Navigate with ↑/↓, press Enter to retract the
selected message, or q/Esc to cancel.
How it works:
- An interactive picker opens listing your last 20 sent messages (most recent at the top).
- Select the message to retract and press
Enter. - A retraction stanza is sent referencing that message's ID. In MUC rooms the server-assigned stanza-id (XEP-0359) is used, which is required by the spec.
- Supporting clients replace the original message with a tombstone. The local
buffer shows
[Message deleted]in place of the original text. - A
storehint (XEP-0334) is included so MAM archives record the retraction, and other devices that sync history will also see the tombstone.
Notes:
- Retraction is not a guarantee: recipients who already received the message
keep it. Clients that do not support XEP-0424 will instead display the
fallback body:
[user] retracted a previous message, but it's unsupported by your client. - You cannot retract another user's message with
/retract. Use/moderate(XEP-0425) for that (requires MUC moderator role). - You must be connected and inside a chat buffer (PM or MUC) to use
/retract.
Example:
you: I should not have said that
/retract
[interactive picker opens — select the message, press Enter]
-- xmpp: message retraction sent
you: [Message deleted]
Sends an emoji reaction to the last received message in the current buffer.
/react <emoji>
How it works:
- Type
/reactfollowed by one or more emoji (the entire argument after/reactis used as the reaction string). - The plugin finds the most recent message in the buffer that was not sent
by you (identified by having an
id_tag and noself_msgtag). - A reaction stanza is sent referencing that message's ID.
Notes:
- Reactions target the last incoming message, not your own messages.
- In MUC rooms, the stanza-id (XEP-0359) is used when available.
- Multiple emoji in a single argument are sent as one reaction (e.g.,
/react 👍❤️sends the two-emoji string as a single reaction). - Clients that do not support XEP-0444 ignore the reaction stanza silently.
- You must be connected and inside a chat buffer to use
/react.
Examples:
/react 👍
/react ❤️
/react 😂
/react 🎉
Sends a reply with a message reference so supporting clients can display a quoted preview.
/reply # open interactive picker to choose which message to reply to
/reply <text> # reply immediately to the last received message
/reply-to <id> <text> # reply to a specific message by ID (used internally by picker)
How /reply works (no arguments — interactive picker):
- An interactive picker opens listing the last 20 messages received from others (non-own, non-retracted).
- Navigate with
↑/↓and pressEnterto select. - The picker closes and the input bar is pre-filled with
/reply-to <id>. - Type your reply text and press
Enterto send.
How /reply <text> works (with text):
The plugin finds the most recent non-own message and sends a reply directly, without opening the picker. Useful for quick replies.
Notes:
- Clients that do not support XEP-0461 will display the plain message text without the quoted context.
- Incoming replies render a quote line from the referenced buffer message.
If that message is no longer in the buffer, the sender's XEP-0428 fallback
quote is shown instead of a generic
[reply]label. - In MUC rooms, the MUC-assigned stanza-id (XEP-0359) is used when present,
as required by the spec for rooms with
stanza-idsupport. - You must be connected and inside a chat buffer to use
/reply.
Examples:
/reply # opens picker
/reply Thanks, that helped! # immediate reply to last received message
Removes another user's message from a MUC room (requires moderator role).
/moderate [reason]
Opens an interactive picker showing the last 20 non-retracted messages in
the room. Navigate with ↑/↓, press Enter to moderate the selected
message, or q/Esc to cancel. The optional reason is forwarded in the
moderation stanza.
How it works:
- An interactive picker opens listing the last 20 messages in the MUC buffer (both own and others', excluding already-retracted messages).
- Select the message to moderate and press
Enter. - A moderation stanza is sent referencing the server-assigned stanza-id (XEP-0359), which is required by XEP-0425 for MUC moderation.
- Supporting clients replace the original message with a moderation tombstone.
Notes:
- Requires MUC moderator role. The server will reject the request otherwise.
- Use
/retractto delete your own messages instead. - Moderation is not a guarantee: clients that do not support XEP-0425 may still show the original message.
- Must be used inside a MUC buffer (not a PM channel).
Examples:
/moderate
/moderate Spam message
/moderate Violates community guidelines
Publish and interact with microblog posts on PubSub services such as
Movim and Libervia/Salut-à-Toi.
XMPP microblogging is XMPP's equivalent of a federated social timeline — posts
are Atom entries stored on PubSub nodes; contacts who subscribe to your
urn:xmpp:microblog:0 PEP node receive your posts in real-time.
Feed content is rendered with full visual formatting: XHTML via the XEP-0071 renderer, HTML stripped to plain text, and plain-text entries processed through a lightweight Markdown renderer (headings, bold, italic, inline code, fenced blocks, blockquotes, lists, links, images, strikethrough, horizontal rules).
Note: XEP-0277 is Deferred and XEP-0472 is Experimental. Server support varies. Movim and Libervia implement the full stack; ejabberd supports the underlying XEP-0060 mechanics.
Short aliases (#N):
When you fetch a feed, each item is assigned a short #N alias shown in green.
Use it in place of the full item-id in any write command:
/feed reply #3 That's a great point!
/feed repeat #3 Great content
/feed comments #3
/feed retract #3
Publishing a post:
# Long form
/feed post movim.eu urn:xmpp:microblog:0 Hello from WeeChat!
/feed post movim.eu myblog --open My public post # --open = public node
/feed post movim.eu myblog --title My Headline -- Body text # with Atom title
# Short form from a feed buffer (service/node inferred)
/feed post Hello everyone!
/feed post -- https://example.com check this out # '--' avoids JID-like parse
# Compose in $EDITOR (requires feed_compose.py)
/feed post --edit
Sends an Atom <entry> to the PubSub node with pubsub#type=urn:xmpp:microblog:0
and the urn:xmpp:pubsub-social-feed:1 publish-option required by XEP-0472.
Failed publishes are reported in the buffer with the server error condition.
Embedding media in a post (XEP-0363 + XEP-0447):
Top-level microblog posts (not comments) support Jinja2-style embed tags that
upload files via HTTP File Upload and insert XEP-0447 <file-sharing> children
into the Atom entry:
{{ embed "photo.jpg" }} # inline image → 
{{ embed "photo.jpg" alt="sunset" }} # with alt text
{{ attach "paper.pdf" }} # download link → [paper.pdf](url)
{{ attach "paper.pdf" alt="My Paper" }}
{{ video "demo.mp4" }} # inline video → 
{{ video "demo.mp4" alt="Demo" }}
embedandvideosetdisposition='inline';attachsetsdisposition='attachment'- Files are uploaded one at a time (async); the Atom entry is published only when all uploads have completed
- On any upload failure the post is aborted and a draft is saved to
$weechat_data_dir/xmpp/drafts/<account>-<timestamp>.mdwith YAML frontmatter; the draft path is printed in the feed buffer - Embed tags are not supported in comments nodes; attempting them prints an error
Receiving clients that support XEP-0447 (Conversations, Dino, Gajim) render the media natively. Xepher displays a summary line for each attachment:
[Image: photo.jpg (image/jpeg, 1.2 MB) https://…]
[File: paper.pdf (application/pdf, 340 KB) https://…]
Replying to a post (threaded):
/feed reply movim.eu urn:xmpp:microblog:0 abc123 That's a great point!
/feed reply #3 That's a great point! # short form using alias
/feed reply #3 --edit # compose reply in $EDITOR
Adds a thr:in-reply-to element (RFC 4685) referencing the original item.
Clients such as Movim render threaded conversations from these references.
Boosting / repeating a post (XEP-0472 §4.5):
/feed repeat movim.eu urn:xmpp:microblog:0 abc123
/feed repeat movim.eu urn:xmpp:microblog:0 abc123 Great post!
/feed repeat #3 # short form using alias
/feed repeat #3 Great content! # short form with comment
Publishes a new entry with <link rel='via'> pointing to the original item.
An optional comment is included in the entry body.
Retracting (deleting) a post:
/feed retract movim.eu urn:xmpp:microblog:0 abc123
/feed retract #3 # short form using alias
Sends a PubSub retract IQ. Server errors are reported in the buffer.
Fetching comments for a post:
/feed comments movim.eu urn:xmpp:microblog:0 abc123
/feed comments #3 # short form using alias
If the post carried a <link rel='replies'> element (XEP-0277 §4.1), this
fetches the linked comments node into a dedicated FEED buffer. The hint is shown
in the feed buffer as Comments: /feed comments #N. Run
/feed movim.eu urn:xmpp:microblog:0 first if no hint is shown.
Subscribing and unsubscribing:
/feed subscribe news.movim.eu Phoronix
/feed unsubscribe news.movim.eu Phoronix
/feed subscriptions news.movim.eu
Sends XEP-0060 subscribe/unsubscribe IQ stanzas. Feedback (success or error condition) is printed in the originating buffer.
Receiving posts from contacts:
Incoming PEP pushes from contacts' urn:xmpp:microblog:0 nodes are
automatically rendered in the WeeChat buffer for that contact, showing the
author name, timestamp, content body, and — when present — threaded reply
references (thr:in-reply-to), boost/repeat provenance (Repeated from:),
comments links (Comments: /feed comments #N), category tags, enclosure URLs,
and geolocation. XHTML and HTML content is rendered with WeeChat formatting.
Duplicate items (already seen via IQ fetch) are suppressed via LMDB deduplication.
Reading a microblog feed:
/feed movim.eu urn:xmpp:microblog:0
/feed movim.eu urn:xmpp:microblog:0 --limit 5
/feed movim.eu urn:xmpp:microblog:0 --before <item-id> # older page (RSM)
/feed movim.eu urn:xmpp:microblog:0 --latest # reset cursor; newest page
The RSM cursor is persisted in LMDB. Use --latest to go back to the newest
page after paging back through older items.
Auto-discovering your server's PubSub services:
At connect time the plugin queries your server's disco#items (XEP-0030) to
find all components, then queries disco#info on each one. Any component
reporting identity category='pubsub' is remembered. You can then use:
/feed # fetch subscriptions from all discovered services
/feed discover # list discovered services without fetching
/feed discover --all # fetch every node from every discovered service
/feed close # close all open feed buffers (does not unsubscribe)
Closing feed buffers:
/feed close dismisses every open FEED buffer without unsubscribing from PubSub
nodes. Closed feeds do not receive pubsub push updates until opened again via
/feed.
Disabling feeds entirely:
If you do not use microblogging and want to prevent feed buffers from appearing:
/set xmpp.look.feeds off
When off: no feed buffers are created or restored on connect, pubsub pushes for
feed nodes are ignored, and /feed is disabled (except /feed close to dismiss
buffers that were already open). Default is on. Re-enable with
/set xmpp.look.feeds on.
Legacy OMEMO PEP nodes (eu.siacs.conversations.axolotl.*) are never
misclassified as feeds; this option is for users who want no PubSub timeline
buffers at all.
Run /roomconfig in a MUC buffer to fetch and edit the server's complete room
configuration form (owner-only, WeeChat 4.3+). This includes server-supported
settings such as room name, description, logging, maximum occupants, and owners.
The editor uses the same controls described below: Enter keeps a default,
:edit NUMBER revisits a field, :review shows the answers, and :submit saves.
Hidden fields and unchanged defaults are preserved; passwords are masked and
input is excluded from command history. Opt-in raw XML logs include submissions.
Changes are confirmed only after the server accepts them. A rejection keeps
your answers for correction and retry. :cancel sends protocol cancellation;
cancelling initial configuration of a newly created locked room destroys that
room, as required by XEP-0045. /close closes the editor locally without sending
anything. Disconnecting closes editors. /setmodes remains available for quick
mode changes; avoid changing the same room concurrently in another client.
Run /mucregister in a MUC buffer to open the room's registration form
(WeeChat 4.3+). /mucregister MyNick prefills the offered nickname field but
still opens the form for review; it does not automatically submit it. Fill in
the fields requested by the room, then use :submit to register or :cancel
to send form cancellation. /close sends nothing. Required fields are checked,
hidden tokens and multiple values are preserved, and private fields are masked.
Registration is confirmed only after server acceptance; rejection keeps your
answers available for correction. Disconnecting closes the editor.
/mucregister query displays registration information without opening an editor
or submitting anything, including an already-registered nickname when supplied
by the server. Hidden and private form values are not displayed as plain text.
The command picker uses the endpoint JID advertised for each command.
/adhoc example.com # list available commands
/adhoc example.com announce # execute a command (opens its form editor)
/adhoc example.com announce <id> subject=Hello body=World
/adhoc example.com announce <id> --action=next subject=Hello
/adhoc example.com announce <id> --action=prev
/adhoc example.com announce <id> --action=complete body=World
/adhoc example.com announce <id> --action=cancel
Input forms open in a dedicated xmpp.form.* buffer (WeeChat 4.3 or newer).
Enter values in the order offered by the server; pressing Enter keeps the
current default. Required fields are marked *. Boolean fields accept true,
false, 1, or 0; list fields display numbered choices. For multiple values,
enter quoted strings separated by spaces, or paste separate lines. Selected
list values retain the server's option order.
The editor accepts these controls without a leading slash:
| Control | Effect |
|---|---|
:edit NUMBER |
Revisit a displayed field |
:review |
Review current answers |
:clear |
Unset the current field explicitly |
:empty |
Set the current text field to an empty string |
:omit |
Omit an optional field, leaving its server value unchanged |
:submit [ACTION] |
Submit using the server default or an allowed action (next, prev, complete) |
:cancel |
Cancel the ad-hoc command without sending form values |
/close |
Close the local editor without sending anything |
Use :: for a value beginning with a literal colon. Hidden fields are returned
unchanged and fixed fields remain read-only. Private fields are masked in both
the input bar and review; form input is excluded from WeeChat command history.
Submissions still appear in opt-in raw XML protocol logs. Server rejection keeps
your answers available for correction and retry. Completing a command,
disconnecting, or unloading the plugin closes its editors. Result forms,
including multi-row reports, are displayed read-only in the account buffer.
The manual commands above remain available alongside the interactive editor.
Required fields are marked with *. Each executing response lists its allowed
actions and default. Omitting --action (or choosing execute) uses that default;
cancel is always available and sends no form payload. Hidden fields such as
FORM_TYPE and server tokens are returned automatically. Repeat a field name for
multiple values, for example users=alice@example.org users=bob@example.org.
An empty value uses field=; = inside a value is preserved. Quote arguments
containing spaces using WeeChat's command argument quoting. Include the required
visible fields when submitting a form. Completed and canceled sessions end;
session state is cleared on disconnect. This is requester support; Xepher does
not host commands for other clients.
/account list
/account add <name> <jid> <password>
/account connect <account>
/account disconnect <account>
/account reconnect <account>
/account delete <account>
/account register <name> <jid> <password> # create account on server (XEP-0077)
/account unregister <account> # cancel account on server (XEP-0077)
/account password <account> <new-password> # change password in-band (XEP-0077)
/account register negotiates STARTTLS with certificate verification before
requesting registration fields. When the server supplies a data form, it opens
the interactive editor (WeeChat 4.3+). Username and password fields without
defaults are filled from the command; password fields are masked. The username
must match the requested JID. An edited password is saved with the account only
after the server confirms success. Other fields, such as email or invitation
codes, use the server's form. Only the completed data form is submitted, with
hidden fields preserved; legacy username/password siblings are not included.
Use :submit to register or :cancel to abandon the attempt. Server errors keep
answers available for correction. Closing the editor, a connection failure,
plugin unload, or the ten-minute form deadline ends the attempt. No account is
added until registration succeeds. The initial connection has a 30-second
deadline. Unsupported CAPTCHA/media challenges
require the server's web registration instead. Flat username/password
registration remains supported. The password in the initial command can appear
in command history; the editor itself excludes its input from history.
| Command | Description |
|---|---|
/enter <jid> |
Join a MUC room |
/join <jid> |
Alias for /enter |
/open <jid> |
Open a direct chat (PM) |
/query <jid> [msg] |
Alias for /open |
/msg <text> |
Send a message to the current buffer |
/me <text> |
Send a /me action (XEP-0245) |
/ephemeral <seconds> <text> |
Send an ephemeral message that disappears after N seconds (XEP-0466) |
/notify [<jid>] [always|on-mention|never] |
Get or set per-chat notification preference (XEP-0492) |
/invite [--mediated] <jid> [reason] |
Invite a user to the current MUC (XEP-0249 direct by default; --mediated for XEP-0045 §7.8.2) |
/decline [room inviter [reason]] |
Decline a pending mediated MUC invitation (XEP-0045 §7.8.2; account buffer) |
/selfping |
Verify MUC membership (XEP-0410; also runs automatically every 15 min) |
/edit |
Picker: choose a sent message to correct (XEP-0308) |
/edit-to <id> <text> |
Correct a specific message by ID — used by the /edit picker |
/retract |
Picker: choose a sent message to delete (XEP-0424) |
/moderate [reason] |
Picker: choose a MUC message to moderate (XEP-0425) |
/react <emoji> |
React to the last received message (XEP-0444) |
/reply |
Picker: choose a message to reply to (XEP-0461) |
/reply <text> |
Reply to the last received message (XEP-0461) |
/reply-to <id> <text> |
Reply to a specific message by ID (XEP-0461) |
/spoiler [hint:] <text> |
Send a spoiler message (XEP-0382) |
/buzz |
Send attention request to current PM contact (XEP-0224) |
/whois [jid] |
Retrieve vCard information (XEP-0054) |
/setvcard <field> <value> |
Publish a vCard field (XEP-0054) |
/setavatar <filepath> |
Publish avatar image (XEP-0084) |
/setvcard preserves other vCard fields, including photos and addresses. For
email and telephone fields it changes the first entry and keeps additional
entries; organization edits preserve organization units. Success is reported
only after the server acknowledges publication.
| Command | Description |
|---|---|
/enter <jid> |
Join a MUC room (creates it if it doesn't exist on the server) |
/join <jid> |
Alias for /enter |
/create <room@server> [nick] [--reserved] |
Create a new MUC room (XEP-0045 §10.1). With --reserved the room is created locked so the owner can configure it via /setmodes / /affiliation / /destroy before letting anyone in. Without --reserved the room is unlocked with server defaults (instant room) as soon as the server signals status 201 |
/kick <nick> [reason] |
Kick a user from the MUC (requires moderator role) |
/ban <jid> [reason] |
Ban a user by JID (requires admin/owner role) |
/voice <nick> [reason] |
Grant voice in a moderated room (role=participant; moderator) |
/devoice <nick> [reason] |
Revoke voice (role=visitor; moderator) |
/op <nick> [reason] |
Grant moderator role (admin/owner) |
/deop <nick> [reason] |
Revoke moderator role back to participant (not a kick) |
/affiliation list [owner|admin|member|outcast] |
Query affiliation list (default: member) |
/affiliation set <jid> <aff> [--nick <nick>] [reason] [--confirm] |
Change affiliation; --nick sets or unsets (empty) reserved member nick |
/mucregister query |
Show room registration / reserved-nick info (XEP-0045 §7.10) |
/mucregister [nick] |
Open the room registration editor, optionally prefilling the nickname (WeeChat 4.3+); :submit applies |
/invite [--mediated] <jid> [reason] |
Invite a user (direct XEP-0249 by default; --mediated for XEP-0045 §7.8.2) |
/decline [room inviter [reason]] |
Decline a pending mediated MUC invitation (XEP-0045 §7.8.2) |
/topic [text] |
Set or clear the room topic |
/nick [newnick] |
Change your nickname in the current MUC |
/names |
List all known occupants in the current MUC with IRC-style role/affiliation prefixes (same symbols as the nicklist; sorted by rank then nick) |
/modes |
Display the MUC room modes and metadata (XEP-0045 §6.4/6.5). The mode flags alone also appear in the buffer's status-bar modes slot IRC-style (e.g. +miP for a moderated, members-only, persistent room) — automatically updated on join and on status-104 config-change notifications |
/setmodes [+/-][m][i][k][p][P][N][S] [secret] [--confirm] |
Set/clear room mode flags (XEP-0045 §10.2, owner-only). Without --confirm prints the planned diff |
/roomconfig |
Edit the server's full room configuration form in the current MUC buffer (owner-only, WeeChat 4.3+) |
/destroy [<reason> [<alt-jid> [<alt-password>]]] [--confirm] |
Destroy the current MUC room (XEP-0045 §10.7, owner-only). Irreversible — use --confirm to apply |
Each nick in a MUC room displays a prefix indicating their role or affiliation (XEP-0045). Affiliation takes precedence over role when both apply.
| Prefix | XEP-0045 role/affiliation | Meaning |
|---|---|---|
~ |
affiliation: owner | Room owner — full control |
& |
affiliation: admin | Administrator — can grant/revoke roles |
@ |
role: moderator | Can kick and mute participants |
% |
affiliation: member | Registered member (voice in members-only rooms) |
+ |
role: participant | Can send messages (standard occupant) |
? |
role: visitor | Read-only in a moderated room |
! |
affiliation: outcast | Banned from the room |
. |
(none) | No role or affiliation set yet |
| Command | Description |
|---|---|
/block <jid> [...] |
Block one or more JIDs (XEP-0191) |
/unblock [jid ...] |
Unblock JIDs, or all if no argument given |
/blocklist |
List all blocked JIDs |
Note: OMEMO for non-anonymous MUC rooms (XEP-0384 §5.8 multi-recipient) is
implemented but experimental and lightly tested. Use at your own risk. See
docs/planning-muc-omemo.md for architecture and status. PM OMEMO remains the
primary tested path.
MUC requirements: The room must be non-anonymous — every occupant's real
bare JID must be visible (check with /modes; N in the mode string). OMEMO is
refused in semi-anonymous or fully anonymous rooms.
MUC prefetch: When OMEMO is enabled in an eligible MUC, device lists and
bundles for occupants are fetched in the background so the first encrypted message
can be sent once the bar leaves 🔒OMEMO (pending). Passive prefetch establishes
Signal sessions quietly; it does not send key-transport groupchat stanzas into
the room (avoiding spurious undecryptable messages for strangers). Key-transport
is sent only when explicitly needed — e.g. /omemo kex or decrypt recovery after
you have exchanged encrypted traffic with that contact.
Trust model: This plugin uses Blind Trust Before Verification (BTBV) TOFU, matching Gajim's default behaviour. Trust levels are stored per {jid, device_id} in LMDB:
| Level | Name | Meaning |
|---|---|---|
BLIND |
Auto-trusted | First device seen for this JID (default) |
VERIFIED |
Manually verified | User ran /omemo trust <jid> [device-id] |
UNTRUSTED |
Explicitly distrusted | User ran /omemo distrust <jid> [device-id]; device is excluded from encryption |
UNDECIDED |
New device | A new device for a JID that already has VERIFIED or UNTRUSTED keys |
Only BLIND and VERIFIED devices receive encrypted key material. UNTRUSTED and
UNDECIDED devices are skipped. MUC occupants follow the same BTBV policy as PM
contacts; verify important contacts with /omemo trust.
The same trust gate applies to your other devices and to key transports. Device-list
refreshes never promote an UNDECIDED identity to trusted. The current PEP device
list determines outgoing recipients: removed devices stop receiving keys, while
their sessions remain available for archive decryption. Archived messages cannot
add removed devices back to that list.
PM and MUC messages both use the legacy Axolotl wire format (flat <key> elements
under <header>), as specified by XEP-0384 version 0.3.0. OMEMO:2 is unsupported.
Consumed pre-keys are replenished for payloadless key transports as well as chat
messages; bundle publication waits until account catch-up completes when necessary.
If a contact reinstalls their client (same device id, new identity key), the old session is dropped and BTBV is reapplied. Previously BLIND contacts are accepted again automatically. If you had VERIFIED or UNTRUSTED any of their devices, the new key is UNDECIDED until you run /omemo fingerprint <jid> and /omemo trust <jid> [device-id]. Signal Error: untrusted identity (-1010) means that gate fired.
| Command | Description |
|---|---|
/omemo |
Enable OMEMO for the current buffer. Works for both PMs and non-anonymous MUCs (MUC OMEMO support is experimental and untested — see planning doc). |
/omemo check |
Verify OMEMO bundle is published |
/omemo republish |
Republish legacy OMEMO bundle nodes (axolotl namespace) |
/omemo status |
Show device ID and status |
/omemo reset-keys |
Reset key database (forces renegotiation) |
/omemo fetch [jid] [device-id] |
Force devicelist/bundle refresh |
/omemo kex [jid] [device-id] |
Force key transport now |
/omemo fingerprint [jid] |
Show colon-hex fingerprint(s) with BTBV trust status ([BLIND]/[VERIFIED]/[UNTRUSTED]/[UNDECIDED]) |
/omemo devices <jid> |
List known OMEMO devices for a JID |
/omemo trust <jid> [device-id] |
Mark key(s) as VERIFIED (manual trust) |
/omemo distrust <jid> [device-id] |
Mark key(s) as UNTRUSTED (blocks encryption to that device) |
/pgp [keyid|status|reset] |
Manage PGP encryption |
/plain |
Disable encryption (use plaintext) |
MUC OMEMO troubleshooting: MUC encryption is experimental. Common issues:
no key for our device <id>— the sender's client did not include a<key rid='…'>for your device in the<keys jid='your@jid'>wrapper. Ask the sender to refresh their devicelist (or re-send after you run/omemo republish). This is separate from session-bootstrap problems on your side.OMEMO (pending)never clears — occupant bundles are still fetching; wait or run/omemo fetch <occupant-jid>for stuck contacts. Sending is blocked until the counter reaches zero.session bootstrap failed … no cached bundle— first send often happens before the PEP bundle fetch finishes. A follow-up send (or/omemo fetch//omemo kex) after the bundle arrives is expected to succeed.Signal Error: untrusted identity (-1010)— the peer's identity key is not trusted (they reset the app, or you haveUNDECIDED/UNTRUSTEDon that device). Check/omemo fingerprint <jid>, then/omemo trustif the new fingerprint is yours to accept.omemo: signed prekey rotated to id N— our own signed prekey aged out and was republished. Normal; not a peer error.- Garbled
No session for:in logs — was a libsignal logging bug with stale peer addresses during MUC decrypt recovery (fixed in6aa24fd). Upgrade and restart WeeChat; enable/set xmpp.look.debug onandtools/correlate_omemo_xml.sh --account <name>when filing bugs. - Spurious FEED buffers from OMEMO PEP — legacy axolotl nodes are filtered;
disable all feed buffers with
/set xmpp.look.feeds offif you do not use microblogging. - Room must be non-anonymous —
/modesshould includeN; OMEMO is refused in semi-anonymous or fully anonymous rooms.
See docs/planning-muc-omemo.md and GitHub issue #6
for architecture notes and ongoing MUC testing.
/upload [filename] # interactive picker if no filename given
Uploads a file via HTTP File Upload (XEP-0363) and announces it in the current channel using a three-layer stanza for maximum client compatibility (implemented with the fluent stanza builder per project rules for XEP support):
- XEP-0447 Stateless File Sharing (
urn:xmpp:sfs:0,disposition='inline') — used by Conversations ≥ 2.10, Dino, and Gajim to display inline image previews without the user having to click a link. Includes XEP-0446<file>(media-type, name, size, sha-256 hash, width/height for images) + sources. - XEP-0385 SIMS (
urn:xmpp:sims:1) — older inline media sharing, understood by clients that pre-date XEP-0447. - XEP-0066 OOB (
jabber:x:oob) — plain URL fallback; renders as a clickable link in any client that does not understand the above.
For OMEMO-enabled channels the file is AES-256-GCM encrypted before upload (XEP-0448
ESFS); the stanza carries the <encrypted> block with b64 key/iv + cipher hash and
the visible body uses the aesgcm://...#ivkey convention so supporting clients can
decrypt. The outer <file> metadata always describes the original plaintext.
For JPEG and PNG images the plugin automatically detects <width> and <height>
from the file's binary headers (JPEG SOF markers, PNG IHDR chunk) and includes
them in the XEP-0446 <file> element so receiving clients can pre-size preview
boxes before the image finishes downloading.
Incoming XEP-0447 file-sharing stanzas sent by other clients are also parsed and displayed, deduplicated against any SIMS or OOB element for the same URL so the file appears only once per message.
XEP-0231 Bits of Binary (small images, plaintext only): On channels without
OMEMO, /upload on an image/* file of 8 KiB or less is sent as BoB instead
of HTTP upload (Movim sticker interop). The message carries XHTML-IM
<img src='cid:sha1+…@bob.xmpp.org'/>; payloads of 1 KiB or less also include
inline <data xmlns='urn:xmpp:bob'/>, and larger ones are IQ-hosted from
bob_hosted when peers fetch the cid. The plugin advertises urn:xmpp:bob in
entity capabilities. OMEMO-enabled channels still use HTTP upload with XEP-0448
ESFS for images — outbound BoB inside encrypted messages is not implemented.
The weechat-icat script is vendored in scripts/icat.py (built from
https://github.com/trygveaa/weechat-icat). Enabling xmpp.look.icat causes the
plugin to automatically display received images inline using the Kitty graphics
protocol:
- Outgoing uploads — the local file is shown via
/icatimmediately after the upload completes and the sharing link is posted (HTTP upload or XEP-0231 BoB). - Incoming SFS / SIMS / OOB — unencrypted image URLs are passed to
/icatwhen the message is received (including MAM replay). - Incoming encrypted uploads (XEP-0448 ESFS) — after the file is decrypted and
saved to
~/Downloads/xmpp/<jid>/<date>/,/icatis invoked on the local path. - XEP-0449 stickers —
<sticker xmlns='urn:xmpp:stickers:0'/>with sibling SFS image payloads; the[File: …]suffix is suppressed when icat displays the image. - XEP-0514 custom emoji —
<emoji xmlns='urn:xmpp:markup:emoji:0'/>spans in XEP-0394 markup resolved against sibling SFS/SIMS file hashes. - XEP-0231 BoB (Movim stickers) — XHTML-IM
<img src='cid:…@bob.xmpp.org'/>references are IQ-fetched from the sender, cached under~/.local/share/weechat/xmpp/bob_cache/<account>/, and displayed inline (the[Sticker]placeholder is not shown when icat is enabled).
With WeeChat mouse mode enabled (/mouse enable), left-click an image row
to open its downloaded local asset in the default desktop application via
xdg-open. This requires the updated scripts/icat.py; reload it after updating
(/script reload icat.py). Missing or removed assets produce an error.
Only image MIME types (image/*) trigger icat. Other file types are announced
normally without inline display.
Note: The plugin passes -print_immediately to icat so placeholder lines are
printed synchronously, directly under the message that shared the image. Kitty
terminals correctly scroll these image cells together with the buffer text.
On non-Kitty terminals chafa probes the display itself (see scripts/icat.py):
real iterm/kitty/sixels graphics are written to the tty asynchronously when the
background encode finishes, otherwise symbol art is printed into the buffer.
Blank rows are reserved under the message and the pixels are drawn at those
screen rows (cursor-addressed, no refresh after), so images follow their links
like Kitty placeholders do. When the buffer is hidden, scrolled, or the image
is taller than the chat area, drawing falls back to the cursor position.
Scrolling erases sixel pixels before the repaint (tracked per buffer, so other
windows are untouched); scrolling back to the bottom — or switching back to
the buffer — redraws images whose slots provably did not move. Unlike buffer
text, pixels cannot follow content that shifted, so images stay gone once new
lines arrive after a scroll.
During MAM replay, image URLs are never passed directly to icat (async HTTP downloads would land out of order). Instead the plugin resolves each image to a local file first — LMDB cache hit, synchronous download, or synchronous ESFS decrypt — then invokes icat on that path immediately after the message line. Live delivery still uses icat's async URL fetch, but also caches downloaded paths for faster MAM replay on reconnect.
Setup:
make install-scriptsInside WeeChat:
/script load icat.py
/set xmpp.look.icat on
Requires a terminal that supports the Kitty graphics protocol (e.g. Kitty, WezTerm, or foot).
Also requires Python PIL/Pillow (pip install Pillow or python-pillow package).
/mam [days] # fetch history (default: 7 days)
/mam prefs # show MAM preferences (XEP-0441)
/mam prefs default <always|never|roster> # set default archiving policy
/mam prefs always <jid> # add JID to always-archive list
/mam prefs never <jid> # add JID to never-archive list
If recent messages are missing, run /mam 3 in the affected chat to re-fetch
the last three days. Reconnect history checkpoints use the query's end time,
so time spent loading history does not create a gap in the next fetch.
Interrupted fetches are cleared before a fresh connection starts; successful
stream resumption preserves them. Closing a chat cancels its pending history
work. Global catch-up shares the configured MAM concurrency
limit with chat fetches. Unstable archive pages do not advance reconnect
checkpoints, so a later catch-up can query them again.
When account catch-up overlaps a chat's history fetch, account results wait for
the chat fetch to finish before display. This keeps recent replies below older
history instead of leaving them near the top of the replay.
| Command | Description |
|---|---|
/disco [jid] |
Discover services and features (XEP-0030) |
/disco summary |
Cached server discovery: domain features, components, derived capabilities |
/disco summary refresh |
Re-query domain disco#info + disco#items, then print summary |
/adhoc <jid> [node] [id] [--action=ACTION] [field=value ...] |
Discover and execute ad-hoc commands, navigate stages, or cancel (XEP-0050) |
/roster |
Display contact list |
/roster add <jid> [name] |
Add a contact |
/roster del <jid> |
Remove a contact |
/list [keywords] |
Search public MUC rooms (XEP-0433) |
/feed |
Fetch subscriptions from all auto-discovered PubSub services (XEP-0060) |
/feed close |
Close all open feed buffers (does not unsubscribe) |
/feed discover |
List auto-discovered PubSub services |
/feed discover --all |
Fetch every node from every discovered service |
/feed <service-jid> |
Fetch all subscribed nodes on a service |
/feed <service-jid> --all |
Discover and fetch all nodes via disco#items |
/feed <service-jid> <node> |
Fetch a specific node into a dedicated buffer |
/feed ... --limit N |
Override the per-node item limit (default: 20) |
/feed ... --before <id> |
Fetch items older than <id> (XEP-0059 RSM paging) |
/feed ... --latest |
Clear saved RSM cursor; return to the newest page |
/feed subscribe <service> <node> |
Subscribe to a PubSub node |
/feed unsubscribe <service> <node> |
Unsubscribe from a PubSub node |
/feed subscriptions <service> |
List subscribed nodes on a service |
/feed post <service> <node> [--open] [--title <t>] <text> |
Publish a microblog Atom entry (XEP-0472); --title sets Atom <title> |
/feed post [--open] [--title <t>] <text> |
Short form: post from a feed buffer (service/node inferred) |
/feed post --edit |
Open $EDITOR via feed_compose.py; YAML frontmatter for optional title |
/feed post -- <text> |
-- separator: body starts with JID-like word or URL |
/feed reply <service> <node> <item-id|#N> <text> |
Reply with thr:in-reply-to threading (no title) |
/feed reply #N <text> |
Short form reply using item alias |
/feed reply #N --edit |
Compose reply in $EDITOR via feed_compose.py (no frontmatter) |
/feed repeat <service> <node> <item-id> [comment] |
Boost/repeat a post (XEP-0472 §4.5) |
/feed repeat #N [comment] |
Short form boost using item alias |
/feed retract <service> <node> <item-id> |
Retract (delete) a published post |
/feed retract #N |
Short form retract using item alias |
/feed comments <service> <node> <item-id|#N> |
Fetch comments node for a post (XEP-0277) |
/feed comments #N |
Short form fetch comments using item alias |
/bookmark |
List bookmarks (picker opens room with saved nick) |
/bookmark add [jid] [name] |
Add a bookmark (preserves existing nick on re-add) |
/bookmark nick <jid> <nick> |
Set preferred MUC nick for a bookmark |
/bookmark del <jid> |
Remove a bookmark |
/bookmark autojoin <jid> <on|off> |
Toggle autojoin |
After connect, /disco summary output is also printed automatically to the
account buffer (~6 seconds later, once component disco#info has finished) and
written to ~/.local/share/weechat/logs/xmpp.account.<name>.weechatlog — useful
when collecting support logs.
Rooms with autojoin enabled are joined in the background on connect (no buffer
switch). Use /enter or /join manually to join and switch to the room buffer.
| Command | Description |
|---|---|
/ping [jid] |
Send XMPP ping (XEP-0199) |
/mood [mood [text]] |
Publish mood via PEP (XEP-0107) |
/activity [category[/specific] [text]] |
Publish activity via PEP (XEP-0108) |
/xml <stanza> |
Send raw XML (advanced/debug) |
/xmpp |
Show plugin version |
/trap |
Trigger debug breakpoint (developers only) |
Several commands open an in-buffer interactive picker when invoked without arguments. Picker key bindings:
| Key | Action |
|---|---|
↑ / ↓ |
Navigate up/down |
Enter |
Confirm selection |
q / Esc |
Cancel and return to previous buffer |
Three complementary mechanisms are available for diagnosing protocol issues. All are opt-in and off by default so normal users see no extra noise.
Routes internal protocol messages (PEP events, avatar updates, vCard
auto-fetches, OMEMO device lists and session bootstrap, stream management,
client state, upload service discovery) to a dedicated xmpp.debug buffer
(xmpp.debug in WeeChat buffer notation) instead of cluttering account
buffers.
/set xmpp.look.debug on
The buffer is created lazily on first use. Each line carries a dim
[file:line] source prefix so you can trace back to the exact code path.
To jump to the debug buffer:
/buffer xmpp.debug
To turn it off again (messages stop appearing; buffer remains open):
/set xmpp.look.debug off
Appends every inbound (RECV) and outbound (SEND) XML stanza to a
per-account log file on disk. Useful when you need the full wire-level picture
for protocol analysis or when filing bug reports.
/set xmpp.look.raw_xml_log on
Log files are written to:
~/.local/share/weechat/xmpp/raw_xml_<account>.log
Each entry is formatted as:
[YYYY-MM-DD HH:MM:SS] SEND|RECV <stanza-name>
<full xml text>
Turn off to stop writing (existing log file is kept):
/set xmpp.look.raw_xml_log off
Controls whether PubSub microblog feed buffers (/feed, XEP-0060 / XEP-0472) are
enabled. On by default.
/set xmpp.look.feeds off # disable feed buffers and /feed (except /feed close)
/set xmpp.look.feeds on # re-enable (default)
See Microblogging — /feed for details.
On each fresh connect, the plugin prints a cached server capability matrix to the
account buffer after component discovery completes (~6 seconds). The same text is
available on demand with /disco summary or /disco summary refresh. Include
the account log (xmpp.account.<name>.weechatlog) when reporting server-specific
issues.
When diagnosing OMEMO key transport or session bootstrap issues, the
correlate_omemo_xml.sh helper cross-references WeeChat log events with raw
XML stanzas so you can see what the plugin received at the exact moment an
error occurred:
tools/correlate_omemo_xml.sh --account <account>This is most useful when xmpp.look.raw_xml_log is also enabled, since the
helper reads from raw_xml_<account>.log.
Companion Python scripts live in scripts/. User-level make install installs
both icat.py and feed_compose.py and enables autoload with relative symlinks.
Use make install-scripts to install/update them without compiling the plugin.
The default data directory is ${XDG_DATA_HOME:-$HOME/.local/share}/weechat;
override it with make install-scripts WEECHATHOME=/path/to/weechat.
System/root plugin installation does not modify a user's script directory.
Direct CMake users can run cmake --build build --target install-scripts;
configure -DXEPHER_WEECHAT_HOME=/path/to/weechat to override the destination.
WeeChat's Python plugin must be enabled. Autoload takes effect at startup;
in a running WeeChat, use /python load icat.py and
/python load feed_compose.py, or /python reload <script>.py for loaded scripts.
icat.py also requires Pillow; script installation does not install Python dependencies.
Opens a temporary Markdown file in your editor. For posts the file
includes a YAML frontmatter block with an optional title: field — fill it in
to set the Atom <title> headline, or leave it blank for a body-only post.
For replies and when run from a comments buffer the frontmatter is
omitted automatically (comments carry no Atom title). On save the content is
placed in the WeeChat input bar as a /feed post … or /feed reply … command
ready to review and send.
The easiest way to invoke it is with the --edit flag:
/feed post --edit # new post in $EDITOR
/feed reply #3 --edit # reply in $EDITOR
Or install and invoke directly:
make install-scriptsInside WeeChat:
/alias add fc /feed-compose
/key bind meta-e /feed-compose
See scripts/README.md for full configuration options.
Displays shared images inline using the Kitty graphics protocol. Vendored from
https://github.com/trygveaa/weechat-icat (MIT license). Used automatically
when xmpp.look.icat is enabled.
Requires Python PIL/Pillow (pip install Pillow or python-pillow package).
make install-scriptsPull requests and issues are welcome — look for
good first issue and
help wanted.
See the Contributing wiki page and
AGENTS.md for architecture and style guidance.
- C++23 throughout — match existing style in the file you edit.
- Port abstraction — use
xmpp::StanzaViewfor inbound stanzas, fluentstanza::specbuilders for outbound stanzas, andweechat::UiPort/BufferPort/LineStorePortfor WeeChat output. Rawxmpp_stanza_get_*andweechat_printfbelong only in hook/adapter glue. - Tests — run
make DEBUG=1ormake test(176 doctests plus the optional form editor and Python TLS tests) after changes; manual WeeChat testing for integration behaviour. - Releases — see Releasing wiki;
pushing a
v*tag triggers GitHub Actions to build and attach packages.
The official Xepher MUC is the place to ask questions, report usage issues that don't belong in the GitHub issue tracker, share tips, and follow announcements:
- JID:
xepher@conference.hackerheaven.org - Web link: xmpp:xepher@conference.hackerheaven.org?join
You can join with the plugin itself once any account is connected:
/xmpp enter xepher@conference.hackerheaven.orgOr from any other XMPP client (Conversations, Gajim, Dino, Monal, etc.) using the JID above.
Xepher aims for modern client interoperability, not a single suite checkbox. XEP-0459 (Compliance Suites 2022) is a useful baseline (Core + Advanced IM areas below), but the plugin goes well past that: OMEMO, SFS/ESFS, PubSub social feed, MDS, ephemeral messages, stickers/emoji markup, OpenGraph previews, channel search, and more — 50+ XEPs in total (see also DOAP.xml).
Legend: ✅ complete or production-usable · ⚡ experimental / partial · ⏳ planned
- ✅ XEP-0030: Service Discovery —
/disco summaryprints a cached capability matrix (domain features, components, upload/pubsub/MUC detection); auto-printed to the account log after connect - ✅ XEP-0045: Multi-User Chat —
/create, IRC-style admin (/kick,/ban,/voice,/devoice,/op,/deop), owner config (/roomconfigfull interactive form,/setmodes,/affiliation,/destroy), registration (/mucregister), direct and mediated invites (/invite,/decline) - ✅ XEP-0054: vcard-temp (retrieval via
/whois, publishing via/setvcard) - ✅ XEP-0077: In-Band Registration (
/account registerwith interactive data forms and verified TLS,unregister,password; CAPTCHA/media challenges unsupported) - ✅ XEP-0115: Entity Capabilities (persistent caching)
- ✅ XEP-0163: Personal Eventing Protocol
- ✅ XEP-0191: Blocking Command
- ✅ XEP-0198: Stream Management — negotiates SM before application stanzas; session resume with stanza retransmit; resume failure falls back to fresh
<enable/>; on resume skips redundant roster/presence/MAM/feed fetches; honors server<enabled location='…'/>for reconnect; closes stream on invalid peer ack (handled-count-too-high); graceful final ack before disconnect
- ✅ XEP-0245: The
/meCommand - ✅ XEP-0249: Direct MUC Invitations
- ✅ XEP-0308: Last Message Correction
- ✅ XEP-0363: HTTP File Upload
- ✅ XEP-0393: Message Styling
- ✅ XEP-0410: MUC Self-Ping
- ✅ XEP-0424: Message Retraction
- ✅ XEP-0425: Message Moderation
- ✅ XEP-0444: Message Reactions
- ✅ XEP-0461: Message Replies
- ✅ XEP-0004: Data Forms (interactive editor for Ad-Hoc Commands, MUC room configuration/registration, and account registration; all core field types, defaults, required fields, and result tables)
- ✅ XEP-0048: Bookmark Storage (Private XML)
- ✅ XEP-0049: Private XML Storage
- ✅ XEP-0050: Ad-Hoc Commands
- ✅ XEP-0059: Result Set Management (MAM paging)
- ✅ XEP-0066: Out of Band Data
- ✅ XEP-0071: XHTML-IM (bold, italic, underline, code, blockquotes, links, CSS colors)
- ✅ XEP-0085: Chat State Notifications (all 5 states)
- ✅ XEP-0092: Software Version
- ✅ XEP-0107: User Mood
- ✅ XEP-0108: User Activity
- ✅ XEP-0153: vCard-Based Avatars (receive; publishing not yet implemented)
- ✅ XEP-0172: User Nickname
- ✅ XEP-0184: Message Delivery Receipts
- ✅ XEP-0199: XMPP Ping
- ✅ XEP-0202: Entity Time
- ✅ XEP-0203: Delayed Delivery
- ✅ XEP-0223: Persistent Storage of Private Data via PubSub
- ✅ XEP-0224: Attention
- ✅ XEP-0280: Message Carbons
- ✅ XEP-0283: Moved (account migration notices)
- ✅ XEP-0292: vCard4 Over XMPP (fetch via
/whois; no publish support yet) - ✅ XEP-0297: Forwarded Messages
- ✅ XEP-0300: Cryptographic Hash Functions
- ✅ XEP-0313: Message Archive Management (with LMDB caching)
- ✅ XEP-0319: Last User Interaction in Presence
- ✅ XEP-0333: Chat Markers
- ✅ XEP-0334: Message Processing Hints
- ✅ XEP-0352: Client State Indication
- ✅ XEP-0359: Unique and Stable Stanza IDs
- ✅ XEP-0372: References
- ✅ XEP-0380: Explicit Message Encryption
- ✅ XEP-0382: Spoiler Messages
- ✅ XEP-0384: OMEMO Encryption (legacy axolotl namespace
eu.siacs.conversations.axolotlonly — OMEMO:2 /urn:xmpp:omemo:2is not supported; BTBV trust model with per-device LMDB trust store; random pre-key selection; own-device encryption for carbon-copy and self-message decryption; pre-key ID continuity across signed pre-key rotation; stale-session recovery via MAM bundle re-fetch) - ✅ XEP-0385: Stateless Inline Media Sharing (SIMS
<reference uri>extraction fixed forurn:xmpp:reference:0sources) - ✅ XEP-0392: Consistent Color Generation — SHA-1 nickname hashing and HSLuv
terminal palette mapping, checked against the specification's reference vectors.
Enabled by default. Set
/set xmpp.look.consistent_colors offto use WeeChat'sweechat.color.chat_nick_colorspalette instead (a single entry gives static coloring); reconnect to refresh cached nickname colors after changing it. - ✅ XEP-0402: PEP Native Bookmarks
- ✅ XEP-0422: Message Fastening
- ✅ XEP-0428: Fallback Indication
- ✅ XEP-0437: Room Activity Indicators (Deferred — subscribe via
<rai xmlns='urn:xmpp:rai:0'/>presence to own bare JID on connect; incoming<activity>notifications displayed in account buffer) - ✅ XEP-0446: File Metadata Element (image dimensions sent with uploads)
- ✅ XEP-0447: Stateless File Sharing (inline previews for Conversations/Dino/Gajim)
- ⚡ XEP-0231: Bits of Binary (Experimental — receive: inline
<data xmlns='urn:xmpp:bob'/>and XHTML-IMcid:…@bob.xmpp.orgimage refs; IQ-fetch uncached payloads; local cache + weechat-icat; send: plaintext/uploadof images ≤8 KiB as BoB + XHTML-IM with sha1 cid, inline data ≤1 KiB, IQ-get hosting;urn:xmpp:bobin caps; OMEMO outbound BoB not implemented) - ⚡ XEP-0449: Stickers (Experimental — receive:
<sticker xmlns='urn:xmpp:stickers:0'/>with SFS images; inline via weechat-icat; sticker pack pubsub not implemented) - ⚡ XEP-0514: Emoji Markup (Experimental — receive: custom emoji spans in XEP-0394 markup resolved against sibling SFS/SIMS hashes; inline via weechat-icat)
- ✅ XEP-0490: Message Displayed Synchronization
- ✅ XEP-0511: Link Metadata (incoming previews + outgoing OpenGraph)
- ⚡ XEP-0277: Microblogging over XMPP (Deferred — publish/reply/retract via
/feed post|reply|retract; receive PEP microblog push events; Atom metadata: title, author, categories, enclosures, geolocation, replies links;/feed commentsfetches comments nodes) - ⚡ XEP-0394: Message Markup (Experimental — receive-only:
<markup xmlns='urn:xmpp:markup:0'>parsed and rendered to WeeChat colour codes;<span emphasis>→ italic,<span strong>→ bold,<span code>→ cyan,<span deleted>→ grey,<bcode>→ grey block,<bquote>→ green-prefixed lines,<list>/<li>→ bullet markers; takes precedence over XEP-0393 when present; advertised in caps) - ⚡ XEP-0413: Order-By for MAM (Experimental —
orderwithby='creation'is used when the service advertises support; ascending order is retained for paging) - ⚡ XEP-0442: Pubsub MAM (Experimental, client role) — discovers archive support on feed reconnect. The first restore loads the latest 20 items; later reconnects fetch every page after the saved archive cursor, using explicit query IDs. The checkpoint advances only after the complete catch-up is stable; malformed or repeating cursors stop paging and leave the previous checkpoint intact. A stale cursor triggers one recovery from the archive beginning, which can retrieve substantial history; other query errors fall back to current PubSub items. Feed deduplication avoids repeated display. Closing a feed cancels its queries, and late archive notifications cannot reopen it as a PM buffer.
- ⚡ XEP-0452: MUC Mention Notifications (Experimental — receive side:
<addresses type='mentioned'>notification messages from MUC service detected and forwarded message body rendered to the MUC buffer with highlight, so missed @mentions surface even when not present in the room) - ⚡ XEP-0441: MAM Preferences (Experimental —
/mam prefsdisplays current server default policy and always/never JID lists;/mam prefs default <always|never|roster>sets default;/mam prefs always <jid>//mam prefs never <jid>add JIDs to the respective list) - ⚡ XEP-0466: Ephemeral Messages (Experimental — send:
/ephemeral <seconds> <message>attaches<ephemeral timer='N'/>+<no-permanent-store/>hint; receive: timer value displayed as[⏱ Ns]prefix, message automatically tombstoned after N seconds;urn:xmpp:ephemeral:0advertised in caps) - ⚡ XEP-0492: Chat Notification Settings (Experimental — per-chat notification preference (
always/on-mention/never) stored in XEP-0402 bookmark<extensions><notify>;/notify [<jid>] [always|on-mention|never]gets or sets the preference and re-publishes the bookmark) - ⚡ XEP-0448: Encrypted File Sharing (Experimental — send: when
/upload-ing a file to an OMEMO-enabled channel, the file is AES-256-GCM encrypted before HTTP PUT; the XMPP message carries<file-sharing>with<encrypted xmlns='urn:xmpp:esfs:0' cipher='urn:xmpp:ciphers:aes-256-gcm-nopadding:0'>containing the base64 key/IV/hash; receive:<encrypted xmlns='urn:xmpp:esfs:0'>detected inside SFS<sources>, cipher attribute validated againsturn:xmpp:ciphers:aes-256-gcm-nopadding:0(other ciphers skipped per §4), ciphertext downloaded in a background thread, decrypted with AES-256-GCM, saved to~/Downloads/<filename>;urn:xmpp:esfs:0advertised in caps) - ⚡ XEP-0472: Pubsub Social Feed (Experimental — publishes
pubsub#type=urn:xmpp:microblog:0, advertisesurn:xmpp:pubsub-social-feed:1in caps;thr:in-reply-to@refuses real Atom entry IRI; feed-level<feed>metadata items rendered; XHTML/HTML Atom content properly rendered) - ⚡ XEP-0433: Extended Channel Search (Searcher role; Search Service role not implemented)
- ⏳ XEP-0153: vCard-Based Avatars (publishing own avatar)
- ⏳ XEP-0398: Avatar Conversion
Plugin reload:
/plugin reload xmppis supported. It disconnects accounts, closes XMPP buffers, and re-inits from config (reconnect afterward).make installreplacesxmpp.sovia temp+rename so a running WeeChat is safe until you reload; do not overwrite the plugin with a plaincpwhile loaded.
Licensed under the Mozilla Public License 2.0.
See LICENSE or https://www.mozilla.org/en-US/MPL/2.0/
Original project © Tony Olagbaiye — https://github.com/bqv/weechat-xmpp
