Im using SimpleX. I’m having trouble placing the icons and getting appimage to open directly. The “disposable template appvm” ¹ ² ³ has also caused me some doubts and I don’t know if I’ve implemented it correctly. Could an experienced user check this?
Old version
#!/bin/bash
#######################################################################
# File Name : whonix-simplex-dvm.sh
# Description : This script creates a SimpleX qube and template based
# in Whonix DisposableVM template just with persistent
# identity across disposables. It downloads and verify
# the hash of the last SimpleX AppImage and uses sys-
# whonix NetVM. For ease of use the script aggregates
# shortcuts to application menu.
# Dependencies : curl
# Usage : • Transfer this script from appvm to dom0 with:
# [user@dom0 ~]$ qvm-run --pass-io appvm 'cat ~/whonix-simplex-dvm.sh' > ~/whonix-simplex-dvm.sh
# • Make the script executable with:
# [user@dom0 ~]$ chmod +x ~/whonix-simplex-dvm.sh
# • Run the script with:
# [user@dom0 ~]$ bash ~/whonix-simplex-dvm.sh
# • To update, run the script in whonix-simplex-template:
# bash ~/update-whonix-simplex-dvm.sh
# Author : Me and the bois
# License : Free of charge, no warranty
#######################################################################
# Safety check
set -eu
# Define Variables
APP_NAME="simplex-chat.AppImage"
APP_NAME_MENU="SimpleX Chat"
APP_DESKTOP="/usr/share/applications/simplex-chat.desktop"
BASE_TEMPLATE="whonix-workstation-17"
DISP_TEMPLATE_APPVM="whonix-simplex-template"
DISPVM_INSTANCE="whonix-simplex-dvm"
NET_VM="sys-whonix"
DOWNLOAD_URL="https://api.github.com/repos/simplex-chat/simplex-chat/releases/latest"
SIMPLEX_PERSISTENT_DIR="/rw/config/simplex-persistent"
SIMPLEX_CONFIG_DIR="$HOME/.config/simplex"
# Verify base template exists
if ! qvm-check "$BASE_TEMPLATE" >/dev/null 2>&1; then
echo -e "\nError: Base template $BASE_TEMPLATE not found!" >&2
exit 1
fi
# Update base template using qubesctl
echo -e "\nUpdating base template..."
sudo qubesctl --show-output --skip-dom0 --targets="$BASE_TEMPLATE" state.sls update.qubes-vm
# Create new AppVM based on Whonix template
echo -e "\nCreating $DISP_TEMPLATE_APPVM based on $BASE_TEMPLATE..."
qvm-create --class AppVM --property template="$BASE_TEMPLATE" --property label=gray "$DISP_TEMPLATE_APPVM"
echo -e "\nShutting down $DISP_TEMPLATE_APPVM to save changes..."
qvm-shutdown --wait "$DISP_TEMPLATE_APPVM"
# Configure it as DisposableVM template
echo -e "\nConfiguring $DISP_TEMPLATE_APPVM as DisposableVM template..."
qvm-prefs "$DISP_TEMPLATE_APPVM" template_for_dispvms True
qvm-prefs "$DISP_TEMPLATE_APPVM" netvm "$NET_VM"
qvm-features "$DISP_TEMPLATE_APPVM" appmenus-dispvm 1
# Install dependencies for new template
echo -e "\nInstalling dependencies..."
qvm-run -u root "$DISP_TEMPLATE_APPVM" 'apt update -y && apt install -y curl'
# Setup persistent storage
echo -e "\nConfiguring persistent storage..."
qvm-run -u root "$DISP_TEMPLATE_APPVM" "mkdir -p '$SIMPLEX_PERSISTENT_DIR' && \
chown user:user '$SIMPLEX_PERSISTENT_DIR' && \
ln -s '$SIMPLEX_PERSISTENT_DIR' '$SIMPLEX_CONFIG_DIR'"
# Download function
download_appimage() {
echo -e "\nFetching release info from GitHub..."
# Get release JSON with --pass-io for reliable transfer
RELEASE_JSON=$(qvm-run --pass-io "$DISP_TEMPLATE_APPVM" "curl -sL '$DOWNLOAD_URL'")
# Extract download URL (more robust parsing)
DOWNLOAD_URL=$(echo -e "\n$RELEASE_JSON" | grep -o '"browser_download_url": *"[^"]*"' | \
grep -i 'simplex-desktop.*x86_64.*\.AppImage' | \
cut -d '"' -f 4 | head -n 1)
# Fallback to any AppImage if specific one not found
if [[ -z "$DOWNLOAD_URL" ]]; then
DOWNLOAD_URL=$(echo -e "\n$RELEASE_JSON" | grep -o '"browser_download_url": *"[^"]*"' | \
grep -i '\.AppImage"' | cut -d '"' -f 4 | head -n 1)
fi
if [[ -z "$DOWNLOAD_URL" ]]; then
echo -e "\nError: Could not find AppImage download URL in:" >&2
echo -e "\n$RELEASE_JSON" >&2
exit 1
fi
echo -e "\nDownloading AppImage from: $DOWNLOAD_URL"
if ! qvm-run --pass-io "$DISP_TEMPLATE_APPVM" "curl -L -o '$HOME/$APP_NAME' '$DOWNLOAD_URL'"; then
echo -e "\nDownload failed!" >&2
exit 1
fi
qvm-run "$DISP_TEMPLATE_APPVM" "chmod +x '$HOME/$APP_NAME'"
}
# Hash verification function
verify_hash() {
echo -e "\nExtracting expected hash from release info..."
# Get the release body text that contains the hashes
local release_body=$(echo "$RELEASE_JSON" | grep -zoP '"body":\s*".*?"' | cut -d'"' -f4 | tr -d '\0')
# Extract ONLY the AppImage hash from the release body
local expected_hash=$(echo "$release_body" | grep -o "SHA2-256(simplex-desktop-x86_64.AppImage)= [a-f0-9]*" | cut -d' ' -f2)
if [[ -z "$expected_hash" ]]; then
echo "Error: Could not extract AppImage hash from release info!" >&2
exit 1
fi
echo -e "\nCalculating actual hash of downloaded file..."
local actual_hash=$(qvm-run --pass-io "$DISP_TEMPLATE_APPVM" "sha256sum '$HOME/$APP_NAME' | cut -d' ' -f1")
echo -e "\nVerifying hashes..."
echo "Expected: $expected_hash"
echo "Actual: $actual_hash"
if [[ "$expected_hash" != "$actual_hash" ]]; then
echo "ERROR: Hash verification failed!" >&2
exit 1
fi
echo "Hash verification passed"
}
# Download and verify
download_appimage
verify_hash
# Create desktop entry and menu entry
setup_desktop_entry() {
echo -e "\nSetting up desktop entry and app menu integration..."
# Define the desktop filename using Qubes naming convention
local QUBES_DESKTOP_FILENAME="org.qubes-os.vm._whonix_dsimplex_dtemplate.simplex-chat.desktop"
local DESKTOP_FILE_PATH="/usr/share/applications/$QUBES_DESKTOP_FILENAME"
# Create the desktop entry file with proper contents
qvm-run "$DISP_TEMPLATE_APPVM" "cat > '/tmp/$QUBES_DESKTOP_FILENAME'" < /home/user/.config/menus/applications-merged/user-qubes-vm-directory_whonix_dsimplex_dtemplate.menu" <<EOF
Applications
qubes-vm-directory_whonix_dsimplex_dtemplate
qubes-vm-directory_whonix_dsimplex_dtemplate.directory
$QUBES_DESKTOP_FILENAME
org.qubes-os.vm._whonix_dsimplex_dtemplate.xfce4-terminal.desktop
org.qubes-os.vm._whonix_dsimplex_dtemplate.janondisttorbrowser.desktop
org.qubes-os.vm._whonix_dsimplex_dtemplate.systemcheck.desktop
org.qubes-os.vm._whonix_dsimplex_dtemplate.thunar.desktop
org.qubes-os.vm._whonix_dsimplex_dtemplate.anondist-torbrowser_update.desktop
org.qubes-os.qubes-vm-settings._whonix_dsimplex_dtemplate.desktop
org.qubes-os.vm._whonix_dsimplex_dtemplate.qubes-start.desktop
EOF
# Force menu refresh
echo -e "\nForce refreshing application menus..."
qvm-run "$DISP_TEMPLATE_APPVM" "/etc/qubes-rpc/qubes.GetAppmenus > /dev/null"
# Sync menus to dom0
echo -e "\nSyncing application menus to dom0..."
qvm-sync-appmenus "$DISP_TEMPLATE_APPVM"
echo -e "\nDesktop entry and app menu setup complete!"
}
# Call the function
setup_desktop_entry
# Create updater script in the template with proper variable handling
create_updater_script() {
echo -e "\nCreating updater in template...update only in the template!"
# Define the script content
local UPDATER_SCRIPT=$(cat < \$HOME/update-simplex.sh"
# Make executable
qvm-run "$DISP_TEMPLATE_APPVM" "chmod +x \$HOME/update-simplex.sh"
echo "Updater script created in $DISP_TEMPLATE_APPVM"
}
create_updater_script
echo -e "\nShutting down $BASE_TEMPLATE..."
qvm-shutdown --wait "$BASE_TEMPLATE"
echo -e "\nSyncing DISP_TEMPLATE_APPVM application menus..."
qvm-sync-appmenus "$DISP_TEMPLATE_APPVM"
# Finalize
echo -e "\nShutting down $DISP_TEMPLATE_APPVM to create $DISPVM_INSTANCE..."
qvm-shutdown --wait "$DISP_TEMPLATE_APPVM"
echo -e "\nCreating disposable VM..."
qvm-create --property template="$DISP_TEMPLATE_APPVM" --class DispVM --property label=red "$DISPVM_INSTANCE"
qvm-features "$DISPVM_INSTANCE" appmenus-dispvm ''
echo -e "\nInstallation complete!"
echo -e "\nUse '$DISPVM_INSTANCE' from Qubes application menu."
New version (work-in-progress)
#!/bin/bash
#######################################################################
# File Name : whonix-simplex-dvm.sh
# Description : This script creates a SimpleX qube and template based
# in Whonix DisposableVM template just with persistent
# identity across disposables. It downloads and verify
# the hash of the last SimpleX AppImage and uses sys-
# whonix NetVM. For ease of use the script aggregates
# shortcuts to application menu.
# Dependencies : curl
# Usage : • Transfer this script from appvm to dom0 with:
# [user@dom0 ~]$ qvm-run --pass-io appvm 'cat ~/whonix-simplex-dvm.sh' > ~/whonix-simplex-dvm.sh
# • Make the script executable with:
# [user@dom0 ~]$ chmod +x ~/whonix-simplex-dvm.sh
# • Run the script with:
# [user@dom0 ~]$ bash ~/whonix-simplex-dvm.sh
# • To update, run the script in whonix-simplex-template:
# bash ~/update-whonix-simplex-dvm.sh
# Author : Me and the bois
# License : Free of charge, no warranty
#######################################################################
# Safety check
set -eu
# Define Variables
APP_NAME="simplex-chat.AppImage"
APP_NAME_MENU="SimpleX Chat"
BASE_TEMPLATE="whonix-workstation-17"
DISP_TEMPLATE_APPVM="whonix-simplex-template"
DISPVM_INSTANCE="whonix-simplex-dvm"
NET_VM="sys-whonix"
DOWNLOAD_URL="https://api.github.com/repos/simplex-chat/simplex-chat/releases/latest"
SIMPLEX_PERSISTENT_DIR="/rw/bind-dirs/home/user/.config/simplex"
SIMPLEX_CONFIG_DIR="$HOME/.config/simplex"
# Verify base template exists
if ! qvm-check "$BASE_TEMPLATE" >/dev/null 2>&1; then
echo -e "\nError: Base template $BASE_TEMPLATE not found!" >&2
exit 1
fi
# Update base template using qubesctl
echo -e "\nUpdating base template..."
sudo qubesctl --show-output --skip-dom0 --targets="$BASE_TEMPLATE" state.sls update.qubes-vm
# Create new AppVM based on Whonix template
echo -e "\nCreating $DISP_TEMPLATE_APPVM based on $BASE_TEMPLATE..."
qvm-create --class AppVM --property template="$BASE_TEMPLATE" --property label=gray "$DISP_TEMPLATE_APPVM"
echo -e "\nShutting down $DISP_TEMPLATE_APPVM to save changes..."
qvm-shutdown --wait "$DISP_TEMPLATE_APPVM"
# Configure it as DisposableVM template
echo -e "\nConfiguring $DISP_TEMPLATE_APPVM as DisposableVM template..."
qvm-prefs "$DISP_TEMPLATE_APPVM" template_for_dispvms True
qvm-prefs "$DISP_TEMPLATE_APPVM" netvm "$NET_VM"
qvm-features "$DISP_TEMPLATE_APPVM" appmenus-dispvm 1
# Install dependencies for new template
echo -e "\nInstalling dependencies..."
qvm-run -u root "$DISP_TEMPLATE_APPVM" 'apt update -y && apt install -y curl'
# Setup persistent storage using bind-dirs
echo -e "\nConfiguring persistent storage with bind-dirs..."
qvm-run -u root "$DISP_TEMPLATE_APPVM" "mkdir -p '$SIMPLEX_PERSISTENT_DIR' && \
chown -R user:user '/rw/bind-dirs/home/user' && \
mkdir -p /rw/config/qubes-bind-dirs.d && \
echo '/home/user/.config/simplex' > /rw/config/qubes-bind-dirs.d/50_simplex.conf"
# Download function
download_appimage() {
echo -e "\nFetching release info from GitHub..."
# Get release JSON with --pass-io for reliable transfer
RELEASE_JSON=$(qvm-run --pass-io "$DISP_TEMPLATE_APPVM" "curl -sL '$DOWNLOAD_URL'")
# Extract download URL (more robust parsing)
DOWNLOAD_URL=$(echo -e "\n$RELEASE_JSON" | grep -o '"browser_download_url": *"[^"]*"' | \
grep -i 'simplex-desktop.*x86_64.*\.AppImage' | \
cut -d '"' -f 4 | head -n 1)
# Fallback to any AppImage if specific one not found
if [[ -z "$DOWNLOAD_URL" ]]; then
DOWNLOAD_URL=$(echo -e "\n$RELEASE_JSON" | grep -o '"browser_download_url": *"[^"]*"' | \
grep -i '\.AppImage"' | cut -d '"' -f 4 | head -n 1)
fi
if [[ -z "$DOWNLOAD_URL" ]]; then
echo -e "\nError: Could not find AppImage download URL in:" >&2
echo -e "\n$RELEASE_JSON" >&2
exit 1
fi
echo -e "\nDownloading AppImage from: $DOWNLOAD_URL"
if ! qvm-run --pass-io "$DISP_TEMPLATE_APPVM" "curl -L -o '$HOME/$APP_NAME' '$DOWNLOAD_URL'"; then
echo -e "\nDownload failed!" >&2
exit 1
fi
qvm-run "$DISP_TEMPLATE_APPVM" "chmod +x '$HOME/$APP_NAME'"
}
download_appimage
# Hash verification function
verify_hash() {
echo -e "\nExtracting expected hash from release info..."
# Get the release body text that contains the hashes
local release_body=$(echo "$RELEASE_JSON" | grep -zoP '"body":\s*".*?"' | cut -d'"' -f4 | tr -d '\0')
# Extract ONLY the AppImage hash from the release body
local expected_hash=$(echo "$release_body" | grep -o "SHA2-256(simplex-desktop-x86_64.AppImage)= [a-f0-9]*" | cut -d' ' -f2)
if [[ -z "$expected_hash" ]]; then
echo "Error: Could not extract AppImage hash from release info!" >&2
exit 1
fi
echo -e "\nCalculating actual hash of downloaded file..."
local actual_hash=$(qvm-run --pass-io "$DISP_TEMPLATE_APPVM" "sha256sum '$HOME/$APP_NAME' | cut -d' ' -f1")
echo -e "\nVerifying hashes..."
echo "Expected: $expected_hash"
echo "Actual: $actual_hash"
if [[ "$expected_hash" != "$actual_hash" ]]; then
echo "ERROR: Hash verification failed!" >&2
exit 1
fi
echo "Hash verification passed"
}
verify_hash
# Setting up entries with correct variable expansion, heredoc parsing and permissions
setup_desktop_entry() {
echo -e "\nSetting up desktop entry and app menu integration..."
# Define paths
local DESKTOP_FILENAME="qubes-dispvm-simplex-chat.desktop"
local DESKTOP_FILE_PATH="/usr/local/share/applications/$DESKTOP_FILENAME"
local MENU_FILE_PATH="/rw/config/xdg/menus/applications-merged/qubes-dispvm.menu"
local MENU_DIR="/rw/config/xdg/menus/applications-merged"
local APPS_DIR="/usr/local/share/applications"
# Create applications directory
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "mkdir -p '$APPS_DIR' && chmod 755 '$APPS_DIR'"; then
echo "ERROR: Failed to create applications directory" >&2
exit 1
fi
# Create menu directory in persistent storage
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "mkdir -p '$MENU_DIR' && chmod 755 '$MENU_DIR'"; then
echo "ERROR: Failed to create menu directory" >&2
exit 1
fi
# Configure bind-dirs using a different approach
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "mkdir -p /rw/config/qubes-bind-dirs.d"; then
echo "ERROR: Failed to create qubes-bind-dirs.d" >&2
exit 1
fi
# Create the bind-dirs config file using cat with a here-document
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "cat > /rw/config/qubes-bind-dirs.d/50_menus.conf" <&2
exit 1
fi
# Create desktop file
local DESKTOP_CONTENT="[Desktop Entry]
Version=1.0
Type=Application
Name=SimpleX Chat (DispVM)
GenericName=Secure Messenger
Comment=Private and secure messaging with SimpleX
Exec=qvm-run --dispvm=$DISP_TEMPLATE_APPVM \\\"\\\$HOME/$APP_NAME\\\"
Icon=internet
Terminal=false
Categories=Network;InstantMessaging;X-Qubes-VM;
StartupNotify=true
X-Qubes-VMName=$DISPVM_INSTANCE"
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "echo '$DESKTOP_CONTENT' > '$DESKTOP_FILE_PATH'"; then
echo "ERROR: Failed to create desktop file" >&2
exit 1
fi
# Set desktop file permissions
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "chown root:root '$DESKTOP_FILE_PATH' && chmod 644 '$DESKTOP_FILE_PATH'"; then
echo "ERROR: Failed to set desktop file permissions" >&2
exit 1
fi
# Create menu file
local MENU_CONTENT='
Applications
DisposableVMs
qubes-dispvm.directory
qubes-dispvm-simplex-chat.desktop
'
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "echo '$MENU_CONTENT' > '$MENU_FILE_PATH'"; then
echo "ERROR: Failed to create menu file" >&2
exit 1
fi
# Create symlink - simplified approach
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "mkdir -p /etc/xdg/menus/applications-merged"; then
echo "ERROR: Failed to create applications-merged directory" >&2
exit 1
fi
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "ln -sf '$MENU_FILE_PATH' '/etc/xdg/menus/applications-merged/qubes-dispvm.menu'"; then
echo "ERROR: Failed to create menu symlink" >&2
exit 1
fi
# Update desktop database
if ! qvm-run -u root "$DISP_TEMPLATE_APPVM" "update-desktop-database '$APPS_DIR'"; then
echo "ERROR: Failed to update desktop database" >&2
exit 1
fi
# Verification
echo -e "\nVerification:"
qvm-run -p "$DISP_TEMPLATE_APPVM" "ls -l '$DESKTOP_FILE_PATH' '$MENU_FILE_PATH'"
qvm-run -p "$DISP_TEMPLATE_APPVM" "ls -l /etc/xdg/menus/applications-merged/"
qvm-run -p "$DISP_TEMPLATE_APPVM" "cat /rw/config/qubes-bind-dirs.d/50_menus.conf"
echo -e "\nDesktop entry and app menu setup complete in template!"
}
setup_desktop_entry
create_updater_script() {
echo -e "\nCreating updater in template...update only in the template!"
# Generate updater script content
local UPDATER_CONTENT=$(cat < \$HOME/update-simplex.sh &&
chown user:user \$HOME/update-simplex.sh &&
chmod 700 \$HOME/update-simplex.sh
"
echo "Updater script created in $DISP_TEMPLATE_APPVM"
}
create_updater_script
# Finalize
echo -e "\nShutting down $BASE_TEMPLATE..."
qvm-shutdown --wait "$BASE_TEMPLATE"
# Start template to sync menus
echo -e "\nStarting $DISP_TEMPLATE_APPVM to sync menus..."
qvm-start "$DISP_TEMPLATE_APPVM" && sleep 5
# Sync menus while template is running
echo -e "\nSyncing application menus to dom0..."
qvm-sync-appmenus "$DISP_TEMPLATE_APPVM"
# Shutdown template to create disposable
echo -e "\nShutting down $DISP_TEMPLATE_APPVM to create $DISPVM_INSTANCE..."
qvm-shutdown --wait "$DISP_TEMPLATE_APPVM"
# Create disposable VM
echo -e "\nCreating disposable VM..."
qvm-create --property template="$DISP_TEMPLATE_APPVM" --class DispVM --property label=red "$DISPVM_INSTANCE"
qvm-features "$DISPVM_INSTANCE" appmenus-dispvm ''
# Final menu update targeting the disposable VM
echo -e "\nUpdating application menus in dom0..."
qvm-appmenus --update --force "$DISPVM_INSTANCE"
echo -e "\nInstallation complete!"
SimpleX References:
- SimpleX Chat
- SimpleX Chat (Blog)
- SimpleX Chat (GitHub)
- SimpleX Public Groups List
- SimpleX Directory Service
- Unofficial Simplex Directory Service
- Crawler HQ (Unofficial Group)
- oSoWoSo - SimpleX Groups (GitHub)
- Anarsec - SimpleX Chat
Qubes References:
- About disposables:
- Confused about persistent and disposable VMs - General Discussion
- Default-dvm & whonix-workstation-17-dvm in apps tab? - User Support
- Running Tor Browser in Qubes Template or Disposable Template - Whonix Wiki
- Disposable customization - Qubes OS Documentation
- How to use disposables - Qubes OS Documentation
- About app menu: