Soju User Delete Hash

By Susam Pal on 14 Feb 2026

I have recently switched from ZNC to Soju as my IRC bouncer. I already like it quite a bit. No more manually maintaining a configuration file for my various IRC connections.

On Debian 13 (trixie), it is quite straightforward the setup is. Here are some example commands that show how straightforward it is to set it up.

sudo apt-get update
sudo apt-get -y install soju
sudo sojuctl user create -username soju -password YOUR_SOJU_PASSWORD
sudo sojuctl user run soju network create -name bnc1 -addr irc.libera.chat -nick YOUR_NICK -pass YOUR_NICK_PASSWORD

The above commands installs and configures Soju 0.9.0 on Debian 13.2. Note that YOUR_SOJU_PASSWORD is a placeholder for a new password you must choose for your Soju user. Then, on your IRC client, you can connect to Soju and connect to IRC networks via it. For example, here's how we can connect to Soju from Irssi:

/network add -nick YOUR_NICK -user soju/bnc1 net1
/server add -tls -network bnc1 YOUR_SOJU_HOST 6697 YOUR_SOJU_PASSWORD
/connect net1

You can also set up multiple connections to IRC networks via the same instance of Soju. All you need to do is repeat the Soju commands above to create new networks say bnc2, bnc3, etc. and then repeat the configuration for your IRC client with new network names, say, net2, net3. These network names are completely user-defined names, so you could name them anything you want. The names bnc2, net2, etc. here are only examples.

One thing that caught my attention while creating and deleting Soju users was that the delete command asks for a confirmation, like so:

$ sudo sojuctl user delete soju
To confirm user deletion, send "user delete soju 4664cd"
$ sudo sojuctl user delete soju 4664cd
deleted user "soju"

That confirmation token 4664cd for a user never changes, no matter how many times we create or delete it. The confirmation token is not saved in the Soju database, as can be confirmed here:

$ sudo sqlite3 -table /var/lib/soju/main.db 'SELECT * FROM User'
+----+----------+--------------------------------------------------------------+-------+----------+------+--------------------------+---------+--------------------------+--------------+
| id | username |                           password                           | admin | realname | nick |        created_at        | enabled | downstream_interacted_at | max_networks |
+----+----------+--------------------------------------------------------------+-------+----------+------+--------------------------+---------+--------------------------+--------------+
| 1  | soju     | $2a$10$yRj/oYlR2Zwd8YQxZPuAQuNo2j7FVJWeNdIAHF2MinYkKLmBjtf0y | 0     |          |      | 2026-02-16T13:49:46.119Z | 1       |                          | -1           |
+----+----------+--------------------------------------------------------------+-------+----------+------+--------------------------+---------+--------------------------+--------------+

Surely, then, the confirmation token is derived from the user definition? Yes, indeed it is. As can be confirmed from the source code here. Quoting the most relevant part from the source code:

hashBytes := sha1.Sum([]byte(username))
hash := fmt.Sprintf("%x", hashBytes[0:3])

Indeed if we compute the same hash ourselves, we get the same token:

printf soju | sha1sum | head -c6; echo
4664cd

This allows us to automate the two step Soju user deletion process into a single command:

sudo sojuctl user delete soju "$(printf soju | sha1sum | head -c6)"

But of course, the implementation of the confirmation token may change in future and Soju helpfully outputs the deletion command with the confirmation token when we first invoke it without the token, so it is perhaps more prudent to just take that output and feed it back to Soju, like so:

sudo sojuctl $(sudo sojuctl user delete soju | sed 's/.*"\(.*\)"/\1/')
Comments | #shell | #irc