Skip to content

Microblogging

Emiel Kollof edited this page Mar 27, 2026 · 6 revisions

Microblogging (XEP-0277 / XEP-0472)

XMPP microblogging lets you publish short posts — analogous to Mastodon/Twitter toots/tweets — directly over XMPP via PubSub nodes. Posts are Atom entries stored on a PubSub service; contacts who subscribe to your urn:xmpp:microblog:0 PEP node receive them in real-time.

Status: XEP-0277 is Deferred; XEP-0472 is Experimental. Both are implemented in this plugin for the write side (post, reply, repeat, retract, subscribe, unsubscribe, comments) and the receive side (incoming PEP push events are rendered automatically with deduplication).


How it works

  • Posts live on a PubSub node (most commonly urn:xmpp:microblog:0 on a PEP service, or any XEP-0060 pubsub service).
  • Each post is an Atom entry (<entry xmlns='http://www.w3.org/2005/Atom'>).
  • XEP-0472 requires the publish-option pubsub#type=urn:xmpp:microblog:0 and the capability string urn:xmpp:pubsub-social-feed:1 to be advertised — both are handled automatically by this plugin.
  • Threaded replies use RFC 4685 thr:in-reply-to inside the Atom entry.
  • Boosts/repeats use <link rel='via'> (XEP-0472 §4.5).
  • Comments nodes are linked via <link rel='replies'> (XEP-0277 §3).

Compatible servers / clients

Software Role
Movim Full social feed — post, reply, retract, timeline
Libervia / Salut-à-Toi Full implementation
ejabberd PubSub mechanics work; no built-in social UI
Prosody + mod_pep PubSub node creation/subscription works

Short aliases (#N)

When you fetch a feed with /feed, each displayed item is assigned a short #N alias shown in green before the title (e.g. #1, #2, #3). You can use these aliases in place of the full item-id in any write command:

/feed reply #3 Nice post!
/feed repeat #3 Great content
/feed comments #3
/feed retract #3

Aliases are assigned per feed-buffer and reset when you reconnect or close WeeChat.


Commands

Publish a post

Long form (explicit service and node):

/feed post <service-jid> <node> [--open] [--title <title>] <text>

Short form (from a feed buffer — service/node inferred):

/feed post [--title <title>] <text>
/feed post --open <text>
/feed post -- <text>       # use '--' when body starts with a JID-like word
/feed post --edit          # open $EDITOR via feed_compose.py

The --open flag sets pubsub#access_model=open so the post is publicly readable without a subscription.

The --title flag sets the Atom <title> element (the headline shown by clients like Movim above the post body). If --title is omitted the <title> element is not emitted at all — the entry is body-only. Use this for microblog-style short posts that have no headline.

The --edit flag delegates to feed_compose.py: it opens your $EDITOR with a YAML frontmatter template where you can fill in an optional title and write a longer body. feed_compose.py must be loaded (see Composing longer posts below).

Use -- as an explicit separator when the post body starts with a word containing . or @ (e.g. a URL or username) to avoid the parser treating it as a service JID:

/feed post -- hello@world.example is a great site

Examples:

# Post to your own PEP microblog
/feed post movim.eu urn:xmpp:microblog:0 Hello from WeeChat!

# Public post
/feed post movim.eu urn:xmpp:microblog:0 --open This is a public post

# Post with a headline title
/feed post --title My Weekend Project -- Spent the weekend hacking on XMPP…

# Short form from a feed buffer
/feed post Hello everyone!

# Short form with body starting with a URL
/feed post -- https://example.com check this out

# Compose a longer post in $EDITOR (requires feed_compose.py)
/feed post --edit

Failed publishes are reported in the buffer with the server error condition (e.g. forbidden, item-not-found).


Reply to a post (threaded)

Long form:

/feed reply <service-jid> <node> <item-id|#N> [--open] <text>

Short form (from a feed buffer):

/feed reply #N [--open] <text>
/feed reply #N --edit     # compose reply in $EDITOR (no title frontmatter)

The reply is a new Atom entry containing thr:in-reply-to ref="<atom-id>". The ref attribute uses the Atom <id> URI of the original entry (cached from the most recent fetch). Clients such as Movim render threaded conversations from these references.

Note: Replies (comments) do not have titles — the --title flag is not available for /feed reply. When --edit is used for a reply, feed_compose.py opens the editor without a YAML frontmatter block.

Examples:

# Long form
/feed reply movim.eu urn:xmpp:microblog:0 abc-1234 That's a great point!

# Short form using alias
/feed reply #3 That's a great point!

# Compose reply in $EDITOR (requires feed_compose.py)
/feed reply #3 --edit

# Reply to a comments node item (XEP-0277)
/feed reply #3 Nice comment!

Boost / repeat a post (XEP-0472 §4.5)

Long form:

/feed repeat <service-jid> <node> <item-id> [comment]

Short form (from a feed buffer):

/feed repeat #N [comment]

Publishes a new Atom entry with <link rel='via' href='xmpp:…' ref='atom-id'/>. The ref attribute is set to the original entry's Atom <id> URI when known (cached from the most recent fetch), enabling receiving clients to resolve the full provenance chain.

An optional comment is included in the entry body.

Examples:

# Boost without comment
/feed repeat movim.eu urn:xmpp:microblog:0 abc-1234

# Boost with comment (long form)
/feed repeat movim.eu urn:xmpp:microblog:0 abc-1234 Great post, sharing this!

# Short form with alias
/feed repeat #3

# Short form with alias and comment
/feed repeat #3 Great content — everyone should read this

Retract (delete) a post

Long form:

/feed retract <service-jid> <node> <item-id>

Short form (from a feed buffer):

/feed retract #N

Sends a PubSub retract IQ with notify='true'. Subscribers receive a retraction notification; supporting clients remove the post from their timeline. Server errors (forbidden, item-not-found) are reported in the buffer.

Note: Retraction is best-effort. Contacts who already received the item may still have a local copy.

Examples:

/feed retract movim.eu urn:xmpp:microblog:0 abc-1234
/feed retract #3

Fetch comments for a post

/feed comments <service-jid> <node> <item-id|#N>
/feed comments #N    # short form from a feed buffer

If the post carries a <link rel='replies'> element (XEP-0277 §3), this opens a dedicated FEED buffer and fetches the comments node. The replies URI is cached when the post is first fetched or received via push.

If no comments link was advertised the command reports that no cached link is available — run /feed <service> <node> first to populate the cache.

When a post has a comments node, the feed buffer shows a hint:

  Comments: /feed comments #3

Examples:

/feed comments movim.eu urn:xmpp:microblog:0 abc-1234
/feed comments #3

Subscribe and unsubscribe

/feed subscribe <service-jid> <node>
/feed unsubscribe <service-jid> <node>
/feed subscriptions <service-jid>

Sends XEP-0060 subscribe/unsubscribe IQ stanzas to the service. Success and error feedback is printed in the buffer where the command was run (not always the account buffer). Errors include the XMPP error condition (e.g. not-authorized, item-not-found).

/feed subscribe news.movim.eu Phoronix
/feed unsubscribe news.movim.eu Phoronix
/feed subscriptions news.movim.eu

Reading a microblog feed

Use the standard /feed read commands:

/feed movim.eu urn:xmpp:microblog:0           # fetch last 20 posts
/feed movim.eu urn:xmpp:microblog:0 --limit 5 # fetch last 5 posts
/feed movim.eu urn:xmpp:microblog:0 --before <item-id>  # older page (RSM)
/feed movim.eu urn:xmpp:microblog:0 --latest  # clear saved cursor, fetch newest page

Each node opens in its own dedicated WeeChat buffer. The RSM cursor is persisted in LMDB so subsequent /feed calls automatically continue from where you left off. Use --latest to go back to the newest page after paging back through older items.

Deduplication

Items fetched via an explicit /feed command (IQ path) are always rendered, even if they were seen before — so repeated /feed invocations refresh the display. The seen-item set is still updated after each fetch.

Items delivered via a real-time PubSub push event are suppressed if the same item-id was already rendered (e.g. fetched moments earlier by an explicit /feed command). This prevents double-display of the same post when a server both returns it in an IQ result and immediately pushes it.


Receiving posts from contacts

When a contact publishes to their urn:xmpp:microblog:0 PEP node, the push event is delivered automatically and rendered in the WeeChat buffer for that contact. Each item displays:

  • Author name and timestamp
  • Title (bold) with #N alias in green
  • Body text
  • Thread reference (In reply to: #N or the raw item UUID)
  • Boost provenance (Repeated from: xmpp:…)
  • Comments hint (Comments: /feed comments #N) when a replies link is present
  • Category tags
  • Enclosure/attachment URLs
  • Geolocation
  • XHTML/HTML content rendered with WeeChat colors

Duplicate push-delivered items (already seen via an IQ fetch) are suppressed automatically.


Capability advertisement

The plugin advertises the following in its entity caps (XEP-0115) and disco#info response so social-feed-aware servers and clients can discover support:

  • urn:xmpp:microblog:0+notify — subscribe to contacts' microblog PEP nodes
  • urn:xmpp:pubsub-social-feed:1 — XEP-0472 social feed capability

Composing longer posts in $EDITOR

The plugin's /feed post and /feed reply commands accept text inline on a single line. For longer drafts — or when you simply prefer your editor — use the --edit flag together with the feed_compose.py companion script.

Install feed_compose.py

# one-time install (script ships in scripts/ inside the repo)
cp /path/to/repo/scripts/feed_compose.py \
   ~/.local/share/weechat/python/autoload/

Or load it without autoloading:

/script load /path/to/scripts/feed_compose.py

Usage via --edit

/feed post --edit                 # compose a new post in $EDITOR
/feed post --open --edit          # compose a public post in $EDITOR
/feed reply #3 --edit             # compose a reply in $EDITOR

When --edit is given, feed_compose.py is invoked automatically. A temporary Markdown file is opened in your $EDITOR. For posts the file starts with a YAML frontmatter block:

---
title:
---

Write your post body here.

Fill in title: to add an Atom <title> headline; leave it blank (or delete the frontmatter entirely) for a body-only post.

For replies the frontmatter is omitted entirely — comments have no titles.

Save and quit the editor — the content is placed in the WeeChat input bar as a complete /feed post … or /feed reply … command ready to send.

Direct invocation

The script also registers a /feed-compose command you can call directly:

/feed-compose [initial text]

Run it from inside a FEED buffer and the service JID and node are filled in automatically. Run it from any other buffer and you can add them before pressing Enter.

Suggested alias and key binding

/alias add fc /feed-compose
/key bind meta-e /feed-compose

Configuration

Option Default Description
editor "" Editor binary; falls back to $EDITOR, then vi
run_externally "false" Set to "true" for GUI or tmux-based editors (non-blocking)

Set options with /set plugins.var.python.feed_compose.<option> <value>.


See also

Clone this wiki locally