Planet Jabber

September 27, 2020

Monal IM

Push server outages

There have been two outages to the push server in the last two days. We are working on identifying the issue. It is related to some new code we are using that allows text and image previews in messages the Monal 4.8 rolls out. I apologize for the inconvenience the end result will hopefully make it worth it.

by Anu at September 27, 2020 19:35


Development News September 2020

🥳 This month’s post marks one year of Gajim Development News! The rework of Gajim’s Preferences window has been completed. We improved group chat invitations and made group chat settings easier to access. Also, joining a group chat is now much prettier.

Changes in Gajim

After reworking Gajim’s configuration backend, it was time to redesign the Preferences window. During this month, a lot of work went into improving the settings framework, which will simplify coding and reduce the amount of code necessary to add new settings. Many of these changes won’t become visible instantly, but they will help future development.

Gajim’s Notification settings for event handling have been split up. You can now decide if you want notifications to be shown in general, and if you want new message to be opened directly (without a notification icon in the contact list). All settings around sending Chat States (e.g. ‘Composing…') have been moved from the Preferences window to the Accounts window, completing the migration of account-related settings. These are just a few of many improvements which you’ll discover in the next release. These changes should make it easier for you to handle all of Gajim’s configuration possiblities.

Gajim’s new Preferences window

Gajim’s new Preferences window

Gajim uses a ‘Sync Threshold’ setting to decide how many messages should be synchronized when joining a chat. If you set a custom Sync Threshold, please make sure to check the setting after upgrading Gajim, since it could not be migrated.

Receiving a group chat invitation can sometimes be ambiguous. ‘Do I really want to join this chat, or should I decline the invitation?’ In order for you to make an informed decision, Gajim now shows some information about the chat (group chat’s picture, name, and description) before joining. Furthermore, many people want to join public group chats using a different nickname from the one they use for private group chats. Gajim now offers to choose a nickname directly before joining.

The new Group Chat Invitation window

The new Group Chat Invitation window

You already now that many group chat actions (for example Information, Change Nickname…) are shown on group chat ‘pages’. Group chat settings (e.g. Minimize on Close, Show Status Changes) are now organized on a new settings page. This reduces the menu layers needed to click through until you reached the setting you wanted to change, and it also puts all settings in a single place for a better overview.

By default, Gajim will keep your chat history forever. However, if you want to cleanup your chat history from time to time, Gajim now offers to automatically reduce the amount of baggage you’re carrying with you. Of course it’s you who decides for how long Gajim should keep your chat history.

Gajim’s features (protocol extensions, called XEPs) are now listed on The table is automatically generated from Gajim’s DOAP (Description of a Project) file, which reduces the amount of maintenance significantly.

What else happened

  • A/V menu entries are now updated (enabled/disabled) correctly when receiving the contact’s capabilities
  • If Gajim fails to join a group chat, it now offers a Retry button (and also ‘Forget Group Chat’)
  • When pasting images from the clipboard, a preview is shown before sending
  • A ‘Send message’ button is now available for chat windows
  • Notification for contact sign in/out has been removed
  • VCard and Avatar publishing code has been reworked
  • A bug has been fixed where the notification icon would stay after blocking/reporting someone who sent you a subscription request and a message (#10264)
  • There have been speed improvements for SQLite operations concerning chat message processing (#10027)
  • Escape key will not close chat windows by default in the future
  • Some shortcuts now use Primary (Ctrl/Cmd) instead of Alt (which is often used by Window Management): Change Subject (<Primary><Shift>S), Emoji Chooser (<Primary><Shift>M)

Plugin updates

Gajim’s URL Image Preview is now able to preview audio files. If you receive a voice message, you can play it directly from within the chat window. The Syntax Highlighter plugin now features a ‘Paste as Code’/‘Paste as Code Block’ entry for the chat input. ‘Paste as Code’ will surround the text you paste with backticks, which enables syntax highlighting.

Each chat message shows a little green shield icon if it is encrypted. Colors for these icons have been unified between OMEMO, OpenPGP, and incoming/outgoing messages. Additionally, some issues have been resolved with Gajim’s PGP/OpenPGP plugins on Flatpak.

Changes in python-nbxmpp

Support for VCard Temp (XEP-0054) has been added. Furthermore, the code for VCard and Avatar publishing has been reworked. Tasks (using Python Generators) have been introduced, which should simplify the flow of many operations in the future. So far, Pubsub, Avatars, and Discovery are using Tasks.

As always, feel free to join to discuss with us.


September 27, 2020 00:00

September 25, 2020 Notices

Migration Update

This morning's migration of the groupchat service was a success. We are now planning the migration of end-user accounts and we will post again when we are ready to complete that task.

September 25, 2020 00:00

September 24, 2020

Tigase Blog

Publishing and Subscribing with Halcyon

Publishing and Subscribing with Halcyon

As you recall, Halcyon is multiplatform XMPP library written in Kotlin. In a previous article: “A look at Halcyon” we had a look at basic concepts in library and we created a simple client.

This time we will dive into more complex stuff. We will create simple solution to monitoring temperature at home :-) In this article we will not focus on measuring temperature. We will create a command-line tool to publish temperature provided as parameter.

First letter in XMPP acronym is from the word “eXtensible”. There is a lot of extensions for the XMPP protocol. One of them is XEP-0060: Publish-Subscribe - specification for publish-subscribe functionality. We will use it to create our temperature monitor.

You need to use XMPP Server with PubSub component. You can use your deployment (for example Tigase XMPP Server or use one of the publicly available servers, for example and its PubSub component A PubSub node with unique name (to avoid conflicts) will have to be created in the PubSub component. Please note that node created with default configuration is open, which means that everyone can subscribe to it (but only you will be able to publish data there).

Data structure

First of all we have to create data structure. In our case, it will be as simple as possible:

<temperature timestamp="1597946187562">23.8</temperature>

timestamp is time represented as a number of milliseconds after January 1, 1970 00:00:00 GMT.

We can use DSL (defined in Halcyon) to create such XML fragment:

val payload = element("temperature") {
    attributes["timestamp"] = (Date()).time.toString()


Publisher is a simple XMPP client that connects to the server, sends information to PubSub component and immediately disconnects.

First of all, lets define global values to keep node name and PubSUB JID:

val PUBSUB_JID = "".toJID()
val PUBSUB_NODE = "temperature_in_my_house"

It cannot be called a good practice, but is good enough for us right now :-)

In the previous article we explained how to create a simple client. Now we will focus on PubSubModule. This module allows publishing and receiving events as well as managing PubSub nodes and subscriptions.

This is the main code that publishes events:

pubSubModule.publish(PUBSUB_JID, PUBSUB_NODE, null, payload).handle {
    success { request, iq, result ->
        println("YAY! Published with id=${result!!.id}")
    error { request, iq, errorCondition, s ->
        System.err.println("ERROR $errorCondition! $s")

But what if the PubSub node doesn’t exist (e.g. it wasn’t created yet)? It’s simple: we have to create it using method create():

pubSubModule.create(PUBSUB_JID, PUBSUB_NODE).handle {
    success { _: IQRequest<Unit>, _: IQ, _: Unit? -> println("Got it! Node created!") }
    error { _: IQRequest<PubSubModule.PublishingInfo>, _: IQ?, errorCondition: ErrorCondition, msgs: String? ->
            "OOPS! Cannot create node $errorCondition $msgs"

The question is: under what conditions we should call this part of code and automatically create the node? One of the possibilities would be moment when item publishing fails with error item-not-found.

pubSubModule.publish(PUBSUB_JID, PUBSUB_NODE, null, payload).handle {
    success { request, iq, result ->
        println("YAY! Published with id=${result!!.id}")
    error { request, iq, errorCondition, s ->
        if (errorCondition == ErrorCondition.ItemNotFound) {
            println("Node not found! We need to create it!")
            pubSubModule.create(PUBSUB_JID, PUBSUB_NODE).handle {
                success { _: IQRequest<Unit>, _: IQ, _: Unit? -> println("Got it! Node created!") }
                error { _: IQRequest<PubSubModule.PublishingInfo>, _: IQ?, errorCondition: ErrorCondition, msgs: String? ->
                        "OOPS! Cannot create node $errorCondition $msgs"
        } else System.err.println("ERROR $errorCondition! $s")

To simplify the code, publishing will not be repeated after node creation.

It is good to use client.waitForAllResponses() before disconnect(), to not break connection before all responses comes back.


Listener is also a client (it should works on different account) that subscribes to receiving events from specific nodes of PubSub component. PubSub items received by PubSubModule are distributed in the client as PubSubEventReceivedEvent in Event Bus. To receive those events you have to register an events listener:

client.eventBus.register<PubSubEventReceivedEvent>(PubSubEventReceivedEvent.TYPE) {
    if (it.pubSubJID == PUBSUB_JID && it.nodeName == PUBSUB_NODE) {
        it.items.forEach { item ->
            val publishedContent = item.getFirstChild("temperature")!!
            val date = Date(publishedContent.attributes["timestamp"]!!.toLong())
            val value = publishedContent.value!!
            println("Received update: $date :: $value°C")

Note, that this listener will be called on every received PubSub event (like OMEMO keys distribution, PEP events, etc). That’s why you need to check node name and JabberID of PubSub component.

Your client will not receive anything from PubSub if it does not subscribe to specific node. Because subscription is persistent (at least with default node configuration), client doesn’t need to subscribe every time it connects to the server. Though, it should be able to check if it’s subscribed to the specific node or not. For that, you need to retrieve list of subscribers and see if the JabberID of the client is on the list:

val myOwnJID = client.getModule<BindModule>(BindModule.TYPE)!!.boundJID!!
pubSubModule.retrieveSubscriptions(PUBSUB_JID, PUBSUB_NODE).response {
    if (!it.get()!!.any { subscription -> subscription.jid.bareJID == myOwnJID.bareJID }) {
        println("We have to subscribe")
        pubSubModule.subscribe(PUBSUB_JID, PUBSUB_NODE, myOwnJID).send()

NOTE: In this example we intentionally skipped checking response errors.

PubSub component can keep some history of published elements. We can retrieve that list easily:

pubSubModule.retrieveItem(PUBSUB_JID, PUBSUB_NODE).response {
    when (it) {
        is IQResult.Success -> {
            println("Previously published temperatures:")
            it.get()!!.items.forEach {
                val date = Date(it.content!!.attributes["timestamp"]!!.toLong())
                val value = it.content!!.value!!
                println(" - $date :: $value°C")
        is IQResult.Error -> println("OOPS! Error " + it.error)

Length of the history is defined in node configuration.

Sample output

Submitting new temperature in Publisher…: publishing

yields receiving notifications in Listener: listening


We presented a simple way to create a PubSub publisher and consumer. You can extend it: for example you can run publisher on Raspberry Pi connected to some meteo-sensors. Possible applications of PubSub component are limited only by your imagination.

All source codes for this article can be found in GitHub repository.

September 24, 2020 00:00

September 22, 2020

Prosodical Thoughts

Simple Anti-Spam Tips

You can take it as a sign of success of a network when it becomes worthwhile for spammers to set up camp. If you’re an active user of XMPP, there is a chance you’ve been unfortunate enough to receive spam in recent weeks. Spam has always been an occasional issue on the network, as with any network, website or internet service. However a few years ago spammers really started to take things more seriously on XMPP.

by The Prosody Team at September 22, 2020 09:30 Notices

Groupchat Migration

This Friday, 2020-09-25, starting around 14:30 UTC, your admin team plans to migrate the groupchat service to a new machine and server software. If all goes well the downtime will be limited to ~30 minutes or less. Please note that this will not affect one-to-one chats, only groupchat rooms. Thanks for your patience.

September 22, 2020 00:00

September 20, 2020

Ignite Realtime Blog

Smack 4.4.0-beta2 released

The Smack developers are happy to announce the availability the second beta release of Smack 4.4.0. We expect Smack 4.4.0-beta2 to be the last beta before the first release candidate is published. Please consider testing this release in your integration stages and report back any issues you may found. The more people are actively testing pre-releases, the less issues will remain in the actual release.

1 post - 1 participant

Read full topic

by Flow at September 20, 2020 13:25

Peter Saint-Andre

Beyond Binary

As political polarization proliferates, it's beneficial to explore realms of thought that are not limited to left vs. right and us vs. them. Personally, I'm partial to philosophy: it's impossible to reduce all of philosophical thinking to, say, Epicureans vs. Stoics without ignoring the deep and unique contributions of Platonists, Aristotelians, Taoists, Buddhists, Confucians, Hindus, Christians, humanists, skeptics, existentialists, and many more....

September 20, 2020 00:00

September 17, 2020

Monal IM

News betas up for 4.8

There are new betas for iOS and macOS. Among many other fixes there are improvements to message archive management (MAM) which should allow you to scroll through your entire history. In addition there is a button to scroll to the newest message (like signal and WhatsApp)

by Anu at September 17, 2020 12:37

September 15, 2020

Tigase Blog

Using STUN & TURN server with Tigase XMPP Server with XEP-0215 (External Service Discovery)

Communication with your family and friends is not only about instant chats. Audio and Video calls are quite important and sometimes, under unfavourable network configurations establishing a call may prove difficult. Luckily, with the help of STUN (Session Traversal Utilities for NAT) and TURN (Traversal Using Relays around NAT ) servers it’s no longer a problem

In the following guide we will show how to setup TURN and STUN servers with Tigase XMPP Server, so that compatible XMPP clients will be able to use them. Our installation supports not only them, but also XMPP MIX


We are assuming that you have installed your preferred TURN server and created an account on the TURN server for use by your XMPP server users and that you have installed and configured Tigase XMPP Server.

At the end of the article there is a short guide hot to quickly setup CoTURN server.

Enabling external service discovery (required only for Tigase XMPP Server 8.1.0 and earlier)

First you need to edit etc/config.tdsl file and:

  1. Add following line in the main section of the file:

    'ext-disco' () {}
  2. Add following line in the sess-man section of the file:

    'urn:xmpp:extdisco:2' () {}

so that your config file would look like this:

'ext-disco' () {}
'sess-man' () {
    'urn:xmpp:extdisco:2' () {}

Start Tigase XMPP Server

After applying changes mentioned above, you need to start Tigase XMPP Server or, in case if it was running, restart it.

Open Admin UI

Open web browser and head to http://<your-xmpp-server-and-port>/admin/ (for example: https://localhost:8080). When promped, log in by providing admin user credentials: bare JID (i.e.: user@domain) as the user and related password. Afterwards you’ll see main Web AdminUI screen:

web admin main page

and on that screen open Configuration group on the left by clicking on it.

Add external TURN service

After opening Configuration group (1) click on Add New Item (2) position which has ext-disco@… in its subtitle.

In the opened form you need to provide following detail: web admin add new turn item

  • Service - ID of the service which will be used for identification by Tigase XMPP Server (ie.
  • Service name - name of the service which may be presented to the user (ie. TURN server)
  • Host - fully qualified domain name of the TURN server or its IP address (ie.
  • Port - port at which TURN server listens (ie. 3478)
  • Type - type of the server, enter turn
  • Transport - type of transport used for communication with the server udp or tcp (usually udp)
  • Requires username and password - for notifying XMPP client that this service requires its username and password for XMPP service (leave unchecked)
  • Username - username required for authentication for TURN server (ie. turn-user)
  • Password - password required for authentication for TURN server (ie. turn-password)

After filling out the form, press Submit button (3) to send form and add a TURN server to external services for your server. Admin UI will confirm that service was added with the following result web admin add new item confirmation

Add external STUN service

While adding a TURN server is usually all what you need, in some cases you may want to allow your users to use also STUN. Steps are quite similar like on TURN server - after opening Configuration group (1) click on Add New Item (2) position which has ext-disco@… in its subtitle and in the opened form you need to provide following detail: web admin add new stun item

  • Service - ID of the service which will be used for identification by Tigase XMPP Server (ie.
  • Service name - name of the service which may be presented to the user (ie. STUN server)
  • Host - fully qualified domain name of the STUN server or its IP address (ie.
  • Port - port at which TURN server listens (ie. 3478)
  • Type - type of the server, enter stun
  • Transport - type of transport used for communication with the server udp or tcp (usually udp)
  • Requires username and password - for notifying XMPP client that this service requires its username and password for XMPP service (leave unchecked)
  • Username - username required for authentication for STUN server (if required)
  • Password - password required for authentication for STUN server (if required)


If you are using the same server for STUN and TURN (you usually will as TURN servers usually contain STUN functionality) you will fill the following form with almost the same details *(only use different Service field value, Type will be stun and most likely you will skip passing Username and Password - leaving them empty, the rest of the field values will be the same).

After filling out the form, press Submit button (3) to send form and add a STUN server to external services for your server. Admin UI will confirm that service was added with the following result web admin add new item confirmation

And now what?

Now you have fully configured your STUN/TURN server for usage with Tigase XMPP Server allowing XMPP clients connected to your server and compatible with XEP-0215: External Service Discovery to take full advantage of your STUN/TURN server ie. by providing better VoIP experience.

CoTURN installation

You can quickly setup CoTURN server using Docker. Please follow Docker installation on your operating system and then install CoTURN using Docker Hub (instrumentisto/coturn). The bare minimum required to run it looks like that (please update realm with your domain and external-ip with IP on which server should be accessible):

sudo docker run --name coturn -d --network=host --restart always  instrumentisto/coturn -n --log-file=stdout --min-port=49160 --max-port=49200 --external-ip=<external_ip> -a'

Subsequently, add user to CoTURN with password and domain:

sudo docker exec -i -t coturn turnadmin -a -u tigase -r -p Ajbk7Ck38nIobLVl

September 15, 2020 00:00

September 13, 2020

Prosodical Thoughts

Great Invitations

There are two kinds of servers on the XMPP network today: those with public registration, and those without. The servers that support registration generally allow you to create accounts via the web, or using your XMPP client (XEP-0077). The problem is that this opens your server up to the world. Even when you add CAPTCHAs and other defences, even the most careful XMPP public server admin will at some point see spammers registering accounts on their server.

by The Prosody Team at September 13, 2020 17:08

September 11, 2020

Monal IM

Monal push server upgrade

In preparation for Monal 4.8, the push server has been upgraded. In addition to the usual fixes that come with a software update in this case, people using the Monal 4.8 beta should now see a single notification for every message that comes in with the message text in the notification. For those of you who used Monal in iOS 12 this will be very familiar. This should be functionally similar to how things were in iOS 12. Sorry this took so long, huge thanks to Thilo for doing the heavy lifting here and implementing both the server and the iOS extension that powers this.

by Anu at September 11, 2020 02:37

September 10, 2020

Ignite Realtime Blog

Openfire Monitoring plugin version 2.1.0 has been released

The Ignite Realtime community is happy to announce that version 2.1.0 of the Monitoring plugin has been released!

This release is mainly a bugfix release. Message archiving functionality has been improved (fixes have been applied for both XEP-0136 and XEP-0313), full text search has been added to the personal message archives of users, and a variety of smaller bug fixes have been applied.

In this release, the feature of retrieving messages ‘out of order’ has been removed.

The new version of the plugin will become available for installation in the admin console of your instance of Openfire in the next few hours. Alternatively, it can be downloaded from its archive page right now.

Note that for instances that have a lot of archived messages in their database, the initial load of this plugin might require a significant amount of time, as some database updates will be executed. For reference: it took our test instance, that has approximately 11 million archived messages in an external database approximately 9 minutes to perform this update.

For other release announcements and news follow us on Twitter

1 post - 1 participant

Read full topic

by guus at September 10, 2020 19:42

Anmol Chaudhary

GSoC 2020 Ends - Final Words

With the rise in the usage of the internet, the need for digital communication is increasing day by day, this has only been accelerated by the current COVID-19 pandemic in 2020. More and more people are using chat applications to communicate with each other whether it is their loved ones or for discussing work with colleagues.

This summer I was involved in implementing In-Band Real Time Text (RTT) for Dino. Real Time Text allows transmission of text instantly just as the user is typing. This allows for faster and more engaging textual conversations just as telephonic calls but at the same time keeping the discretion of text-based communication.

To sum it up here’s what I did in order to make RTT work:

  • Message Comparision that generates the difference between two strings, in this case, used to generate a difference between the message being typed in real-time to generate necessary action elements. The algorithm is based on “Gestalt pattern matching” (Details, see here)

  • Sending and receiving real-time texts in both single and multi-user chats. The Real-Time Text has support for all action elements (insert, erase, wait) and events (new, edit, reset, init, cancel) mentioned in XEP-0301. The RTT are displayed on the conversation view along with traditional messages. In MUC at a given time, only 3 RTT are displayed in order to keep the User Interface clutter-free, the priority for which is determined by the affiliation of the member. (Details : here, here, here, here, and here)

  • RTT Setting radio toggle to toggle between Off/Receive only/Send and Receive. This is handled by using events “init” and “cancel”. (Details, see here and here)

  • Handling of stale messages that removes the RTT widget from UI if there is no update in a given time interval. This may be due to peer stopped typing or loss of sync. (Details, see here)

  • Multi-Device Support to sync input between different instances of Dino. With this, a user can start composing a message on one device and finish on another. (Details, see here)

  • Compatibility with Last Message Correction. With this, RTT is also displayed for the messages being corrected. The live correction is displayed in place of the old message received.

The PR for which is available here.

Overall it has been a great 3-month long journey. From working with a completely new language - Vala to fixing a ton of bugs in the process, and from discussions with mentor to the weekly meetings; I enjoyed my time with XSF.

Although GSoC is now officially over, I look forward to contributing more to XSF and Dino in particular in the future. Next I plan on implementing message retraction and message moderation.

Finally, I thank my mentors especially Marvin for guiding me through the summer. They have always been present to solve any problems I faced whether it was regarding discussion on how to implement something or for some bug fix. I also thank the XSF community for having me as a student developer this summer.

September 10, 2020 00:00

September 09, 2020

Prosodical Thoughts

Prosody 0.11.6 released

We are pleased to announce a new minor release from our stable branch. This release brings a collection of fixes and improvements added since the 0.11.5 release improving security, performance, usability and interoperability. This version continues the deprecation of using prosodyctl to start/stop Prosody if it is installed system-wide. You should use your init system’s appropriate commands to manage the Prosody process instead. You can silence the warnings with the ‘prosodyctl_service_warnings’ option.

by The Prosody Team at September 09, 2020 13:46

Ignite Realtime Blog

New Openfire plugin to help reduce spam!

The Openfire community is happy to announce the immediate availability of a new plugin: the blacklist spam plugin.

With the growing popularity of XMPP-based solutions, we’re also seeing an increase of a less desirable application of XMPP: the distribution of unsolicited messages over instant messaging (SPAM, or SPIM).

In order to help our Openfire community combat known spam, we have now released a new plugin the “Spam blacklist” plugin. This is configured with a URL that is used to lookup addresses of known SPAM distributors and block all messages sent by these addresses.

By default, the plugin is configured to read from a blacklist that is hosted on the website, and block all messages sent by any user on a domain listed there. The blacklist hosted by the Ignite Realtime community is a periodically updated copy of the list that is curated by the project. You can help combat spam by contributing to that project!

In the next few hours, the plugin will pop up in the list of available plugins on all Openfire instances. Alternatively, you can manually download and install the plugin from its archive page.

For other release announcements and news follow us on Twitter

3 posts - 2 participants

Read full topic

by guus at September 09, 2020 08:02

September 05, 2020

Peter Saint-Andre

Aristotle Research Report #14: The Faults of Aristotle

Although I think Aristotle was the greatest mind who ever lived, he did have his faults. In modern times, he is especially criticized for supposedly being anti-science, anti-democracy, pro-slavery, and anti-women. Let us briefly consider these charges....

September 05, 2020 00:00

September 04, 2020

Tigase Blog

BeagleIM 4.0 and SiskinIM 6.0 released

New versions of XMPP clients for Apple’s mobile and desktop platforms have been released. The biggest change is introduction of XMPP MIX - the modern way of chatting in groups (if you are looking for a server where you can use this new feature be sure to check our installation). It also significantly improves on audio/video calls.

Common changes

New XEPs:

Major changes

  • Redesigned channel joining UI
  • Optimized resource usage during the establishment of VoIP calls and improved quality of VoIP calls
  • Improved Markdown support
  • Improved message synchronization speed, reliability and add support for MAM version 2 (XEP-0313: Message Archive Management)
  • Started using MAM for MUC history synchronization on rejoin (if possible)
  • Improved support for CAPS (feature discovery)
  • New chats list view with a single section for 1-1 chats and group chats
  • Added a way to join hidden MUC rooms


  • Fixed issue with avatars in group chats not being properly displayed.
  • Added workaround for possible issue with message archive synchronization if server does not send <complete/> attribute
  • Fixed issue with channel info refresh

BeagleIM 4.0

UI adjustments:

  • MIX / group chat creation streamlined


  • Better VoIP connectivity


  • Message Correction, Retraction and quick replies


Major changes

  • Adjusted font size in chat history (message text is slightly bigger)
  • Improved support for XMPP URI (requires beagle-im-set-as-default helper app)
  • Improved behaviour of service discovery window (indentation of items, scrolling)
  • Improved handling of responses for <iq/> requests which should fix some OMEMO key publication issues.
  • Improved message text selection
  • Remembers size of chats/roster windows
  • Improved auto-completion of nicknames.
  • Stopped downloading link previews if message with that link was received from someone outside of roster.


  • Fixed issue with respecting global settings for Allow subscription and Request subscription
  • Fixed Typo in Authorization menu
  • Fixed issue with not showing notifications for incoming groupchat messages containing keywords
  • Improved scrolling behavior (scrolling to found or first unread message, scrolling on the opening of a chat and concurrency issue)
  • Fixed an issue with a chat message not being resized properly when window size was changed.
  • Fixed issue with text color in chat history
  • Fixed issue with invisible selection in the search dialog
  • Added possible workaround for crashes when preview is for a movie
  • Fixed crash when the server is not sending Content-Length for uploaded files.


  • Fixed issues with compilation on XCode 12
  • Improved layout in chat logs (internal change)

SiskinIM 6.0

  • Improved detection of server features and prompting to enable them in SiskinIM
  • Added request for background fetch on push notification and fixed crashes when push notification is tapped just after it is displayed (#siskinim-205, #siskinim-206)
  • Fixed issue with using wrong speaker during the VoIP call (#siskinim-211)
  • Added warning when access to microphone or camera is not granted during initialization of a video call (#siskinim-213)
  • Improved account removal mechanism (#siskinim-199)
  • Improved look of the QR codes (#siskinim-195)
  • Fixed an issue with not dismissing room creation view after room is created (#siskinim-230)
  • Fixed an issue with encrypting files with AESGCM send to MUC rooms when default encryption is set to OMEMO (#siskinim-237)

    siskin-chat-list siskin-chat


You can download both application from their respective app-stores: Beagle IM from macOS appstore and Siskin IM from iOS appstore and star them on GitHub: Siskin IM on GitHub and Beagle IM on GitHub

Join the discussion

You can discuss all-things-Tigase (including our client apps) on our groupchat:


September 04, 2020 00:00

September 03, 2020

Monal IM

Some usage stats for a random day

I don’t track users in any way but in the process of fixing a server issue I did do a count on the number of messages sent on a given day. Here they are in case anyone was curious. On the scale of modern apps, these are tiny numbers but it is nice to see lots of people using the service.

On a random day, 8/25/20 in a 24 hr period

  • Total Users: 103,065
  • Pushes sent: 56,768
  • Server to server (s2s) connections: 1,580

by Anu at September 03, 2020 02:04

September 02, 2020

Tigase Blog

A look at Halcyon

A look at Halcyon

Some time ago, we started developing multiplatform XMPP Library called Halcyon based on Kotlin Multiplatform by Jetbrains. Our plan is to allow using the same library in different target environments: JVM, Android, JavaScript and Native. Currently we are focused on JVM and JavaScript targets.

In this post we will try to show library design and example of usage.


Before you start

Because Halcyon isn’t published in any Maven repository (yet), you need to compile it yourself. We believe, it will not be a problem. The only two things you need to do is to clone repository and compile library:

git clone
cd halcyon
./gradlew publishToMavenLocal

Thats all. Now Halcyon is in your local Maven repository.

Let’s do something

We recommend using Gradle to build everything (except for towers and bridges maybe). You can also use Maven, it doesn’t matter. Just use one of them, to prevent problems with dependencies. Here is sample build.gradle.kts file, the most important this is to enable kotlin plugin and include Hayclon in the list of dependencies:

plugins {
    kotlin("jvm") version "1.3.61"

repositories {

dependencies {
    testCompile("junit", "junit", "4.12")

configure<JavaPluginConvention> {
    sourceCompatibility = JavaVersion.VERSION_1_8
tasks {
    compileKotlin {
        kotlinOptions.jvmTarget = "1.8"
    compileTestKotlin {
        kotlinOptions.jvmTarget = "1.8"

Let’s add some Kotlin code:

fun main(args: Array<String>) {
    val client = Halcyon()
    client.configure {
        userJID = "".toBareJID()
        password = "secret"

This simple code creates XMPP client, connects to XMPP server and then disconnects.

To show how to work with Halcyon library, we will by adding code to this small code base.


Halcyon is events-driven library. It means, that each part of library may publish event to event bus and all registered listeners will receive it.

Lets add some code to see what is being send and received over XMPP stream:

client.eventBus.register<ReceivedXMLElementEvent>(ReceivedXMLElementEvent.TYPE) { event ->
    println(">> ${event.element.getAsString()}")
client.eventBus.register<SentXMLElementEvent>(SentXMLElementEvent.TYPE) { event ->
    println("<< ${event.element.getAsString()}")

To listen for all events since the connection is started, we have to add this code before client.connectAndWait().

All events extend class tigase.halcyon.core.eventbus.Event, so you can easily find them all in your favourite IDE.

Each module may have it’s own set of events, so please check documentation or source code of modules of interest.


Now we will look at one of the most interesting things in XMPP: requests and responses.

XMPP protocol allows sending request to another entity and receive response. Why is it so exciting? Because we can ping other clients, or ask for their local time! Ok, stop joking. Of course above examples are true, but with request-response we can do much more than simple sending messages: we can manage our contacts list, we can manage Multi User Chatrooms, we can execute remote methods on server or other clients.

As an example we will ping other XMPP entity (it may be server or other client). First we need to get PingModule to be able to use its request builder:

val pingModule = client.getModule<PingModule>(PingModule.TYPE)!!

Ping module has method ping() which creates a request builder (note, that it doesn’t send anything yet!) configured to understand response and return it as object. Method ping() has optional parameter jid. If is not provided, then ping will be send to the server to which the client is connected.

val request =
request.response { result ->
    when (result) {
        is IQResult.Success -> println("Pong in ${result.get()!!.time} ms")
        is IQResult.Error -> println("Oops! Error ${result.error}")

There is also a different way to add response handler to the request:

request.handle {
    success { request, iq, result -> println("Pong in ${result!!.time} ms") }
    error { request, iq, errorCondition, message -> println("Oops! Error $errorCondition") }

Use the one that you prefer.

One more example: we will check list of features of our server:

val discoveryModule = client.getModule<DiscoveryModule>(DiscoveryModule.TYPE)!!"".toJID()).handle {
    error { request, iq, errorCondition, message -> println("Oops! Error $errorCondition") }
    success { request, iq, result ->
        println("Server JID: ${result!!.jid}")
        result!!.features.forEach { println(" - $it") }


This chapter will be very hard, mostly because MessageModule isn’t finished yet. We haven’t made a design decision yet - how this module should work. It is good for you though, because we can create message stanza from scratch! And it’s cool!

This is how message stanza look like:

    <body>Art thou not Romeo, and a Montague?</body>

Let’s try to create this stanza in Kotlin and send it.

var messageRequest = client.request.message {
    to = "".toJID()
    body = "Art thou not Romeo, and a Montague?"

The only thing currently implemented in MessageModule is MessageReceivedEvent, useful to handle all incoming message stanzas:

client.eventBus.register<MessageReceivedEvent>(MessageReceivedEvent.TYPE) { event ->
    println("Message from ${event.fromJID}: ${event.stanza.body}")

Roster and presence

Ok, we can send a message to anybody, but most of the time we want to send them to our friends. We need a list of our friends. Luckily such list is available out-of-box in XMPP protocol: it is called Roster.

It shouldn’t be a surprise, but to manage your roster you need RosterModule:

var rosterModule = client.getModule<RosterModule>(RosterModule.TYPE)!!

We can add (or update, with the same method) roster items, remove and list them.

val allRosterItems =

RosterItem contains JabberID of the contact, list of groups being assigned to, status of subscription (if contact is allowed to see our presence or not, and if we are allowed to see it’s presence).

Presence is “status of contact”. You can see if your contacts are online, offline or maybe you shouldn’t send any message to someone because he has “Do Not Disturb” status.

As an example, we will list all contacts from the roster and their presence: { rosterItem ->
    val presenceStanza = presenceModule.getBestPresenceOf(rosterItem.jid)
    println("${} <${rosterItem.jid}> : ${presenceStanza?.show ?: "Offline"}")


…for being here up to this point. We hope you enjoyed reading about Halcyon library, and you liked it even though it is not finished yet.

Please share you thoughts and ideas at our group chat or on library GitHub page.

September 02, 2020 00:00

September 01, 2020

Paul Schaub

PGPainless 0.1.0 released

After two years and a dozen alpha versions I am very glad to announce the first stable release of PGPainless! The release is available on maven central.

PGPainless aims to make using OpenPGP with Bouncycastle fun again by abstracting away most of the complexity and overhead that normally comes with it. At the same time PGPainless remains configurable by making heavy use of the builder pattern for almost everything.

Lets take a look at how to create a fresh OpenPGP key:

        PGPKeyRing keyRing = PGPainless.generateKeyRing()
                .simpleEcKeyRing("alice@wonderland.lit", "password123");

That is all it takes to generate an OpenPGP keypair that uses ECDH+ECDSA keys for encryption and signatures! You can of course also configure a more complex key pair with different algorithms and attributes:

                        .withKeyFlags(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)
                        .withKeyFlags(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)
                .withPassphrase(new Passphrase("password123".toCharArray()))

The API is designed in a way so that the user can very hardly make mistakes. Inputs are typed, so that as an example the user cannot input a wrong key length for an RSA key. The “shortcut” methods (eg. withDefaultAlgorithms()) uses sane, secure defaults.

Now that we have a key, lets encrypt some data!

        byte[] secretMessage = message.getBytes(UTF8);
        ByteArrayOutputStream envelope = new ByteArrayOutputStream();

        EncryptionStream encryptor = PGPainless.createEncryptor()
                .signWith(keyDecryptor, senderSecretKey)

        Streams.pipeAll(new ByteArrayInputStream(secretMessage), encryptor);
        byte[] encryptedSecretMessage = envelope.toByteArray();

As you can see there is almost no boilerplate code! At the same time, above code will create a stream that will encrypt and sign all the data that is passed through. In the end the envelope stream will contain an ASCII armored message that can only be decrypted by the intended recipients and that is signed using the senders secret key.

Decrypting data and/or verifying signatures works very similar:

        ByteArrayInputStream envelopeIn = new ByteArrayInputStream(encryptedSecretMessage);
        DecryptionStream decryptor = PGPainless.createDecryptor()
                .decryptWith(keyDecryptor, recipientSecretKey)

        ByteArrayOutputStream decryptedSecretMessage = new ByteArrayOutputStream();

        Streams.pipeAll(decryptor, decryptedSecretMessage);
        OpenPgpMetadata metadata = decryptor.getResult();

The decryptedSecretMessage stream now contains the decrypted message. The metadata object can be used to get information about the message, eg. which keys and algorithms were used to encrypt/sign the data and if those signatures were valid.

In summary, PGPainless is now able to create different types of keys, read encrypted and unencrypted keys, encrypt and/or sign data streams as well as decrypt and/or verify signatures. The latest additions to the API contain support for creating and verifying detached signatures.

PGPainless is already in use in Smacks OpenPGP module which implements XEP-0373: OpenPGP for XMPP and it has been designed primarily with the instant messaging use case in mind. So if you want to add OpenPGP support to your application, feel free to give PGPainless a try!

by vanitasvitae at September 01, 2020 08:37

August 30, 2020

Ignite Realtime Blog

Spark 2.9.2 Released

The Ignite Realtime community is happy to announce the availability of Spark version 2.9.2.

This release fixes offline messages and a few other issues. Full list can be found in the changelog.

We are thankful to all the contributors and encourage developers to get involved with Spark project by providing feedback in the forums or submitting pull requests on our GitHub page.

You can download Spark from the Downloads page. Below are the sha1 checksums:

44bedca5f8c49373e3a3454ae92857612a907714  spark_2_9_2.deb
9b2a36cb4bc5b620f6e34d0bbe52ad57f0ff1bee  spark_2_9_2.dmg
8df94552ada52a8c2a7da68ad596f4acd39d9ac4  spark_2_9_2.exe
b1a81744923f4aaf3d39ced95fea233b6077f93c  spark-2.9.2.rpm
4933144d2812c638effe95344dce304dbf820c0d  spark_2_9_2.tar.gz
21836f86e101a7e48fcbd44fa400d5862f0840ab  spark_2_9_2-with-jre.dmg
68e2ed4bd916bab17a0f963f748176d4d8514c29  spark_2_9_2-with-jre.exe

For other release announcements and news follow us on Twitter

1 post - 1 participant

Read full topic

by wroot at August 30, 2020 18:26


Development News August 2020

This month was all about configuration. Gajim offers a ton of configuration options, making it customizable to fit almost every users needs. The settings backend received an upgrade and a lot of code has been cleaned up, which should make it easier for you to handle all of Gajim’s configuration possiblities.

Changes in Gajim

A huge amount of code has been cleaned up around Gajim’s configuration backend. This was necessary in order to move from a configuration based on text files to a new settings backend powered by SQLite. Everything involving configuration (preferences, account settings, states) is now completely migrated to SQLite. Gajim is now able to store settings efficiently, and some quirks around default values have been solved. When you browse Gajim’s configuration via Advanced Configuration Editor, you’ll now only see settings which are not available in the Preferences or Accounts window. States (e.g. window size) have also been removed from ACE, which results in less clutter.

After all these changes under the hood, it is finally the time to rework the Preferences window. The Accounts window already features ‘ListBox’ settings, enabling Gajim to display settings in a tidy and clear fashion. We plan to fully migrate the Preferences window as well, which should make it easier for you to handle all of Gajim’s configuration possiblities.

A preview of the new Preferences window

A preview of the new Preferences window

What else happened

  • Linux: Gajim will now use GTK’s native Emoji chooser (with categories and recently used emojis)
  • Removed ‘Auto copy’ workaround for Ctrl+C usage in the chat window
  • Fixed a bug where dropping selected text on a chat window would fail
  • Fixed ‘Show status changes’ setting being ignored for group chats
  • Fixed a bug where removing a plugin would fail

Plugin updates

It is now possible to install the ‘Ayatana Appindicator integration’ plugin via Flatpak.

Changes in python-nbxmpp

Support for JID Escaping (XEP-0106) has been added, enabling users to join group chats containing special characters (e.g. ‘&’ in IRC channels). Furthermore, bookmarks parsing has been improved (nickname validation, password element for XEP-0402).

As always, feel free to join to discuss with us.


August 30, 2020 00:00

August 28, 2020

Peter Saint-Andre

Compounding Wisdom

This weekend, in honor of Warren Buffett's 90th birthday, I'd like to consider the phenomenon of compounding. It's a little-known fact that Buffett earned almost 90% of his $82 billion fortune after the age of 65. Yet supposedly he has been fascinated by compounding since the age of 10, when he had an epiphany about how wealth could grow over time through the steady effects of return on investment....

August 28, 2020 00:00

August 26, 2020

Peter Saint-Andre

Speaking Freely

Emerson was really onto something when he spoke about the high freedom of great conversation. I've been thinking about two more aspects of such freedom....

August 26, 2020 00:00