First, install Java 26 on the template that your signal-cli AppVM will be based on.
For Whonix Workstation or Debian:
export https_proxy=http://127.0.0.1:8082/
wget https://download.oracle.com/java/26/latest/jdk-26_linux-x64_bin.deb
sudo dpkg -i jdk-26_linux-x64_bin.deb
For Fedora:
export https_proxy=http://127.0.0.1:8082/
wget https://download.oracle.com/java/26/latest/jdk-26_linux-x64_bin.rpm
sudo rpm -i jdk-26_linux-x64_bin.rpm
Shutdown the template after the JDK 26 installation is complete.
Now create an AppVM based on this updated template and open a terminal inside it.
Download the latest signal-cli by running the following command:
wget https://github.com/AsamK/signal-cli/releases/download/v0.14.2/signal-cli-0.14.2.tar.gz
Extract the downloaded archive with this command:
tar xvf signal-cli-0.14.2.tar.gz
Run the appropriate commands below to add signal-cli to your PATH.
For Whonix Workstation 18:
echo 'export PATH="$PATH:$HOME/signal-cli-0.14.2/bin/"' >> ~/.zshrc
source ~/.zshrc
For Fedora or Debian-based AppVMs:
echo 'export PATH="$PATH:$HOME/signal-cli-0.14.2/bin/"' >> ~/.bashrc
source ~/.bashrc
Check the installed version with:
signal-cli --version
To register a Signal account on signal-cli, solving a captcha challenge on a browser is required. Both the browser and the terminal must connect to the Signal server using the same IP address; otherwise the Signal server will mark the captcha as invalid.
Similarly, in the case of virtual numbers, sometimes (not always) the virtual number does not receive the verification code if your IP address (in the case of Tor, the IP address of the Exit node) is not from the same country as the virtual number. That’s why fixing the exit node to a static IP is required to register a Signal account on signal-cli using Tor.
Fixing the exit node in sys-whonix requires root privileges and in Whonix 18 you can’t do it in normal user mode. So follow these steps:
ExitNodes {gb}
StrictNodes 1
{gb} is for Great Britain (United Kingdom). This is just an example. Use the country of your virtual number instead. For example {nl} for Netherlands, {ca} for Canada, etc. {gb} or whatever the country code is with the IP address like this:
ExitNodes 212.38.189.186
StrictNodes 1
Now your Tor is configured to register a Signal account using signal-cli.
Caution: Don’t do any other internet activity behind Tor after this, just use it to register the Signal account. Otherwise all of your internet traffic will go through a single exit node.
signal-cli -a PHONENUMBER register --captcha "CAPTCHACODE"
+ in place of PHONENUMBER. CAPTCHACODE. " before and after the captcha code.Caution: The captcha code is only valid for less than a minute, so do this process within this time.
signal-cli -a PHONENUMBER verify CODE
CODE. signal-cli listAccounts
If you have only one account in your signal-cli, you will not need to provide your phone number every time you give a command. Otherwise you will have to provide your phone number every time you give a command.
The first command you need to provide after registration is this:
signal-cli receive
If you have multiple accounts in your signal-cli then:
signal-cli -a PHONENUMBER receive
Other configuration commands:
To set your name:
signal-cli updateProfile --name "YOUR NAME"
To enable read receipts (This is required to enable for your linked desktop device too, you can’t enable it from the linked desktop device.):
signal-cli updateConfiguration --read-receipts true
To enable typing indicators:
signal-cli updateConfiguration --typing-indicators true
To enable link previews:
signal-cli updateConfiguration --link-previews true
Signal Desktop app shows a QR code to link it with the parent device. This code is displayed for 30 seconds. You need to extract the QR code from the QR image and paste it in the signal-cli command within these 30 seconds.
If your Signal Desktop is in a separate VM than your signal-cli (which is recommended) then the steps will increase. You will need to take a screenshot of the QR image. The screenshot file will be saved in dom0. You need to copy it to your signal-cli VM from dom0. Then you need to give the image to an online QR scanner to extract the code. Then you will type the command in signal-cli to link the desktop device and paste the QR code there.
All this must be done within 30 seconds or the QR code will change. So here’s the trick:
Don’t open Signal Desktop app yet.
signal-cli addDevice --uri "
cd ~/Pictures
qvm-copy-to-vm signal-cli qr.png
signal-cli is the name of your signal-cli VM, while qr.png is the screenshot file name that you will generate shortly.) qr.png in the default location which is the Pictures directory in dom0. qr.png file from /home/user/QubesIncoming/dom0/. " and after pasting the code type another " and press Enter. The command will look like this:
signal-cli addDevice --uri "QRCODE"
QRCODE.Signal Desktop app will start linking with the signal-cli.
You normally don’t need to use signal-cli, you just need to link it with your desktop client. But it is required that the receive command should run frequently in signal-cli because:
So for frequently running the receive command we’ll put the receive command in a script and make it autostart on login.
Signal-cli doesn’t save messages on local storage but it does store attachments, stories, avatars and stickers. Similarly when the parent device and all the linked devices receive the message the Signal server deletes the message but the server doesn’t delete the attachments, stories, avatars and stickers. It automatically deletes them after 45 days from the server.
So there is no need to download these attachments, stories, avatars and stickers. They will just use disk space and will be of no use. So we’ll skip them in the receive command.
Create a signal-receive.sh file and open it in editor:
nano signal-receive.sh
Paste the following in the editor:
On Whonix Workstation 18:
qterminal -e "zsh -ic 'signal-cli receive --ignore-attachments --ignore-stories --ignore-avatars --ignore-stickers; signal-cli receive --ignore-attachments --ignore-stories --ignore-avatars --ignore-stickers; exit;'"
On Debian or Fedora:
xfce4-terminal -e "bash -ic 'signal-cli receive --ignore-attachments --ignore-stories --ignore-avatars --ignore-stickers; signal-cli receive --ignore-attachments --ignore-stories --ignore-avatars --ignore-stickers; exit;'"
The command is ignoring all the attachments, stories, avatars and stickers. I provided the command two times because while using it behind Tor sometimes Tor takes time creating new circuits and the command times out in that period. So giving the command two times is for fail-safe. This command will open a new terminal, start receiving messages from server, display them on the terminal, do it two times then exit the terminal.
Make it executable:
chmod +x signal-receive.sh
Now to run this script on VM startup create an autostart folder in .config directory:
mkdir -p ~/.config/autostart
Create a desktop file to run the signal-receive.sh script on VM startup:
nano ~/.config/autostart/signal-receive.desktop
Paste the following:
[Desktop Entry]
Type=Application
Exec=/bin/bash -c "sleep 300 && /home/user/signal-receive.sh"
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
Name=Signal Receive
Comment=Runs signal-cli receive at startup
I’ve added a 300 seconds delay to run the script. Because we’ll set this VM to boot on startup. So the 300 is for safety so that the system, internet and Tor will connect properly and then this script will run. Otherwise if we run it immediately on startup the commands will probably time out.
Now in the signal-cli VM settings check the boot on startup checkbox.
It will be best if after running the receive script the signal-cli VM shuts down automatically because it will be of no use after that. For that check the “Shutdown when Idle for more than 15 minutes” checkbox in your signal-cli VM settings.
If this checkbox is disabled you need to install the package in your template first.
Open your template’s terminal and install it with the following command:
For Whonix & Debian:
sudo apt install qubes-app-shutdown-idle
For Fedora:
sudo dnf install qubes-app-shutdown-idle
Shutdown the template after installation.
Now check the “Shutdown when idle for more than 15 minutes” checkbox.
After these settings, on your system startup the signal-cli process will run in the following steps:
With this setup the user will not need to do anything with the signal-cli. It will become like a service that automatically runs and stops.
During registration sometimes Signal doesn’t accept VOIP virtual numbers of some countries. It will show an authentication error. You need to change the country and use a virtual number of some other country, or use a virtual number that is not VOIP.
Sometimes the Tor exit node that you are using gets blocked. As a result signal-cli always displays “Captcha Invalid” or “Authentication Error”. Change the exit node.
If your initial try of registration failed and you changed the virtual number, the signal-cli will keep the failed number in the database. Then you will not be able to use signal-cli account commands (like receive or account configuration commands) without providing -a PHONENUMBER.
To check if you have failed phone numbers give the following command:
signal-cli listAccounts
This command will display your registered number and also the failed numbers.
To delete the failed numbers give the following command:
signal-cli -a PHONENUMBER deleteLocalAccountData --ignore-registered
Replace PHONENUMBER with your failed phone number in signal-cli.
The --ignore-registered option will ignore the registered number if you have provided the registered number accidentally instead of the failed number.