How I learned to stop worrying and love WhatsApp... not really. This is how I've set up my own XMPP service to be able to talk to WhatsApp using the slidge-whatsapp transport module.

WhatsApp Pain

I wish I didn't have to use WhatsApp, I really do, I wish people would switch to more freedom giving services.
But that isn't the case... "everyone uses WhatsApp", so, long story short, if I want to stay in touch with people, I need some way of communicating with WhatsApp, at least for now.

Luckily, there are ways...

History

For a long time I've had a Matrix (synapse) server that it set up to use the mautrix-whatsApp bridge. This has worked well for many years, and I don't really have any complaints with it specifically. However, Matrix are making some... questionable choices, and honestly, I only use Matrix for the WhatsApp bridge - running an entire server just for this purpose feels a little... wasteful.

However, I do also run my own XMPP service, and I use this service for communicating with a handful of people. So, if I could get a WhatsApp "bridge" working with XMPP, I will have one app to communicate with almost everyone I know!

Hosting an XMPP server

I run my own ejabberd server, and I'll have another article about how I do this at some point. But for the purposes of this article the choice of XMPP service is not massively important. The WhatsApp transport module is known to work with both Prosody and eJabberD, so chosing one of those will be best.

Installing a WhatsApp Transport Module

There's a handful of WhatsApp transport modules available for XMPP, but some have seen little maintenance over the last few years. One that still appears to be in active development is slidge-whatsapp, part of a suite of modules based upon the slidge gateway library.

Typically, we should just need to install slidge-whatapp via the command

pipx install slidge-whatsapp

However, at the time of writing, this does not build on FreeBSD.

If you're using Linux, or have otherwise managed to install slidge-whatsapp with pipx, then you can probably skip on to the Configuration

First Error - puccinialin

We'll first be given the error:

$ pipx install slidge-whatsapp
Fatal error from pip prevented installation. Full pip output in file:
    /path/to/pipx/logs/cmd_2025-06-16_10.02.59_pip_errors.log

pip seemed to fail to build package:
    slixmpp<2,>=1.10.0

Some possibly relevant errors from pip install:
    error: subprocess-exited-with-error
    ModuleNotFoundError: No module named 'puccinialin'

You may think installing puccinialin will solve this, but unfortunately it's not really that causing the issue. Puccinialin is a Rust bootstrapper, so the problem is we don't have rust installed. We do a pkg install rust and try again.

Second Error - Go

We also have some GoLang dependencies, so if go isn't installed we'll hit this error:

$ pipx install slidge-whatsapp
...
pip failed to build package:
    slidge-whatsapp

Some possibly relevant errors from pip install:
    error: subprocess-exited-with-error
    RuntimeError: Cannot find the go executable in $PATH. Make you sure install golang, via your package manager or https://go.dev/dl/
    subprocess.CalledProcessError: Command '['/root/.local/pipx/venvs/slidge-whatsapp/bin/python', 'build.py']' returned non-zero exit status 1.
    ERROR: Failed to build installable wheels for some pyproject.toml based projects (slidge-whatsapp)

We pkg install go and carry on...

Third Error - GoPy fails to build

slidge-whatsapp has a dependency on go-python's gopy module. At the time of writing, gopy will not build on FreeBSD, so pipx install slidge-whatsapp will fail.

$ pipx install slidge-whatsapp
...
pip failed to build package:
    slidge-whatsapp

Some possibly relevant errors from pip install:
    error: subprocess-exited-with-error
    subprocess.CalledProcessError: Command '['go', 'install', 'github.com/go-python/gopy@master']' returned non-zero exit status 1.
    subprocess.CalledProcessError: Command '['/root/.local/pipx/venvs/slidge-whatsapp/bin/python', 'build.py']' returned non-zero exit status 1.
    ERROR: Failed to build installable wheels for some pyproject.toml based projects (slidge-whatsapp)

However, the reason it doesn't build is quite simple - there's no defined process for in GoPy's main*.go files for FreeBSD that sets some basic variables up.
We can fix this by editing one file in the gopy repo, then build and install it manually.

$ git clone https://github.com/go-python/gopy
$ cd gopy

Find and edit 2 lines in ./main_unix.go so they change from this

//go:build (linux && !android) || dragonfly || openbsd
// +build linux,!android dragonfly openbsd

to this

//go:build (linux && !android) || dragonfly || openbsd || freebsd 
// +build linux,!android dragonfly openbsd freebsd

I have made a pull request on the gopy repo which will change this upstream. In time, this step should no longer be necessary.

You can then build and install gopy with the following command (you will of course need the go language installed too)

$ go install .

Installing Slidge-WhatsApp from Source

We now have gopy installed as a go library, but we cannot use pipx any more as the slidge-whatsapp build script has a hard link to install gopy from their repo, and does not look at our local version.
Another simple fix, we clone the slidge-whatsapp repo, make a change to the build script, and build it locally.

$ git clone https://codeberg.org/slidge/slidge-whatsapp
$ cd slidge-whatsapp

In ./build.py, find this line:

subprocess.run(["go", "install", "github.com/go-python/gopy@master"], check=True)

and comment it out so it doesn't run.

#subprocess.run(["go", "install", "github.com/go-python/gopy@master"], check=True)

As with the change to gopy previously, this should only be a temporary requirement until the aforementioned pull request gets accepted.

Slidge recommend (if not using docker containers) that slidge-whatsapp builds and runs in a python venv - which is a very sensible recommendation. There's a number of ways of doing this, but using uv is a single command way of doing it.

# Install uv
# As root
(root)# pkg install uv

# Non root
# Try to build slidge-whatsapp
$ uv sync --frozen --all-groups --all-extras
More Errors...

We're nearly there! But, uv is failing with some more errors. The error messages are long, and require a bit of scrolling to find the issue, but you may see something like this if you look:

...
not found
          6 | #include <mupdf/fitz.h>
            |          ^~~~~~~~~~~~~~
      1 error generated.
...

or

...
      ld: error: unable to find library -lgumbo
      ld: error: unable to find library -lmujs
      clang: error: linker command failed with exit code 1 (use -v to see invocation)
...

and many others. I've gone through them so you don't have to...

We need some dependencies, so let's install them.
Also, I've found some souce code has trouble compiling with clang; there's probably a way to solve it but as a shortcut let's just install gcc and build using that, setting the CC envvar when running the uv command.

(root)# pkg install gcc gmake mupdf gumbo mujs
$ CC=gcc uv sync --frozen --all-groups --all-extras

Built!

slidge-whatsapp should now be available in $BUILD_DIR/.venv/bin/slidge-whatsapp, ready for use.
A couple of other runtime dependencies will be required though, so let's install them now.

(root)# pkg install py311-sqlite3 ffmpeg nginx

sqlite3 for database connectivity, ffmpeg for media handling and nginx for media publishing.

Finally, configuration!

The slidge website has some decent documentation, and setting things up boils down to a handful of steps:

  1. Configure slidge-whatsapp to communicate with eJabberD.
  2. Configure eJabberD to allow slidge-whatsapp, and set privileges.
  3. Set up nginx for publishing the sent media.

Configure slidge-whatsapp

A simple slidge-whatsapp configuration will look like this.

# /usr/local/etc/slidge-whatsapp.ini
server=localhost  # you can use your xmpp address here, but communication 
                  # is unencrypted so make sure it's either on the same host,
                  # or you can encrypt the transmissions between the services
                  
secret=v3rYSecur3 # secret to authenticate the slidge service to ejabberd
                  # you can just run `openssl rand -base64 32` to get something decent
              
jid=slidge-whatsapp.my.xmpp.host # should be a subdomain of the ejabberd address

no-upload-path=/var/lib/slidge/attachments # where media will be stored

no-upload-url-prefix=https://my.xmpp.host/slidge/ # the address where an XMPP client
                                                  # can find the media

Configure eJabberD

In the ejabberd configuration, we add a new listener, set up an acl, rules and some module privileges.

# /usr/local/ejabberd/ejabber.yml
#...
listen:
  #...
  -
    port: 5347
    module: ejabberd_service
    hosts:
      slidge-whatsapp.my.xmpp.host:
        password: v3rYSecur3 # Same as the secret in slidge-whatsapp.ini
        
#...

acl:
  #...
  slidge_acl:
    server:
      - `slidge-whatsapp.my.xmpp.host`

#...

access_rules:
  #...
  slidge_rule:
    allow: slidge_acl
    
#...

modules:
  #...
  mod_roster:
    versioning: true
  #...
  mod_privilege:
    roster:
      bost: slidge_rule
    message:
      outgoing: slidge_rule

Configure nginx

The reason we need nginx is documented here.

I will put a basic nginx configuration server block below. If you have many sites in the same nginx configuration, you should adjust this to your needs.
Please also configure certificates for your site here, you should already have them available for use with ejabberd anyway.

# /usr/local/etc/nginx/nginx.conf
#...
    server {
                listen 80;
                root /var/www/html;  # if you already have nginx serving files…

                # the section below is for slidge
                location /slidge {
                        #  Must be the same value as slidge's no-upload-path
                        alias /var/lib/slidge/attachments/;
                }
           }
#...


Run!

And there we have it. We can restart ejabberd and run slidge-whatsapp with command parameter to use the config file:

$ slidge-whatsapp -c /usr/local/etc/slidge-whatsapp.ini

You can also use an init script to run this as a daemon. My init script is in my rc.d scripts repo

We should them start to communicate with each other in their respective log files.

Connect clients

All that's left is to connect up our XMPP client, and we'll be good to go. The Documentation on Slidge's website talks through this, but to summarise, you can either send a message saying "register" to the slidge-whatsapp.my.xmpp.host account, or in something like gajim, go to Accounts > $Account > Discover Services, select "WhatsApp (slidge)" and click "Register". Then just follow the standard WhatsApp "Linked Devices" process.

Done!

And that's that. If things aren't working, we can just check the output of the slidge-whatsapp command (or the log file if you're using daemon) and solve what's going wrong.


Step-by-Step. Commands only

The whole thing, narrowed down to a handful of commands:

  1. Install Dependencies
(root)# pkg install rust go gcc gmake git py311-sqlite3 ffmpeg nginx mupdf mujs gumbo uv
  1. Build gopy (may become irrelevant in the future)
$ git clone https://github.com/go-python/gopy
$ cd gopy
$ sed -i '' 's/|| openbsd/|| openbsd || freebsd/' main_unix.go
$ sed -i '' 's/dragonfly openbsd/dragonfly openbsd freebsd/' main_unix.go
$ go install .
  1. Build slidge-whatsapp
$ git clone https://codeberg.org/slidge/slidge-whatsapp
$ cd slidge-whatsapp
$ grep -v -E "subprocess.+gopy" build.py > build.py.patched
$ cp build.py.patched build.py
$ CC=gcc uv sync --frozen --all-groups --all-extras

  1. Configure slidge-whatsapp
  2. Configure ejabberd
  3. Configure nginx
  4. [Re]Start Services
  5. Connect Clients