SimpleX setup with dispvm

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?


After much coming and going, this is the final version:

whonix-simplex-named-disposable.sh

#!/bin/bash

#######################################################################
# File Name    : whonix-simplex-named-disposable.sh
# Description  : This script creates a named disposable SimpleX qube
#                based on Whonix Workstation template. It downloads
#                and verify the hash of the last SimpleX AppImage and
#                uses sys-whonix as NetVM.
# Dependencies : curl
# Usage        : • Transfer this script from appvm to dom0 with:
#                [user@dom0 ~]$ qvm-run -p appvm 'cat ~/whonix-simplex-named-disposable.sh' > ~/whonix-simplex-named-disposable.sh
#                • Make the script executable with:
#                [user@dom0 ~]$ chmod +x ~/whonix-simplex-named-disposable.sh
#                • Run the script with:
#                [user@dom0 ~]$ bash ~/whonix-simplex-named-disposable.sh
#                • To update, run the script in whonix-simplex-dvm (disposable template):
#                bash ~/update-simplex-chat.sh
# Author       : Me and the bois
# License      : Free of charge, no warranty
# Last edited  : 2025-10-11
#######################################################################

# Safety check
set -eu

# Define Variables
APP_NAME="simplex-chat"
BASE_TEMPLATE="whonix-workstation-17"
CUSTOM_TEMPLATE="whonix-simplex-template"
DISP_TEMPLATE="whonix-simplex-template-dvm"
NAMED_DISP_VM="disp-whonix-simplex"
NET_VM="sys-whonix"
SOURCE_URL="https://api.github.com/repos/simplex-chat/simplex-chat/releases/latest"

# Step 1: Install and update the Qubes Template
echo -e "\n[1/7] Checking for Qubes template..."
if ! qvm-check "$BASE_TEMPLATE" 2>/dev/null; then
    echo "Installing $BASE_TEMPLATE..."
    sudo qubes-dom0-update qubes-template-$BASE_TEMPLATE
fi

# Ensure template is shut down before updating
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true

# Update the template whether it was just installed or already existed
echo "Updating $BASE_TEMPLATE..."
sudo qubesctl --show-output --skip-dom0 --targets=$BASE_TEMPLATE state.sls update.qubes-vm

# Ensure Qubes base template is shut down before create
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true

# Step 2: Create custom base template
echo -e "\n[2/7] Creating custom template by cloning..."
qvm-clone "$BASE_TEMPLATE" "$CUSTOM_TEMPLATE"
qvm-prefs "$CUSTOM_TEMPLATE" label black

# Step 3: Install dependencies
echo -e "\n[3/7] Installing dependencies..."
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "echo 'TERM=xterm' >> /etc/environment"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "locale-gen en_US.UTF-8"

# Shutdown
qvm-shutdown --wait "$CUSTOM_TEMPLATE"

# Step 4: Create DVM template based on custom template
echo -e "\n[4/7] Creating DVM template..."
if ! qvm-check "$DISP_TEMPLATE" 2>/dev/null; then
    qvm-create --template "$CUSTOM_TEMPLATE" --label red "$DISP_TEMPLATE"
    qvm-prefs "$DISP_TEMPLATE" template_for_dispvms True
else
    echo "DVM template $DISP_TEMPLATE already exists, skipping creation."
fi

# Step 5: Download and verify in disposable template
echo -e "\n[5/7] Configuring $APP_NAME in disposable template..."

# Start template if not running
if ! qvm-check --running "$DISP_TEMPLATE"; then
    qvm-start "$DISP_TEMPLATE" && sleep 3
fi

# Fetch latest release information
echo "Fetching latest release information..."

RELEASE_JSON=""
if RELEASE_JSON=$(qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -sL '$SOURCE_URL'"); then
    # Check for rate limit error
    if echo "$RELEASE_JSON" | grep -q "API rate limit exceeded"; then
        echo -e "\nERROR: Failed to fetch release info from API (rate limited)" >&2
        qvm-shutdown --wait "$CUSTOM_TEMPLATE"
        exit 1
    fi
fi

# Extract download URL
DOWNLOAD_URL=$(echo "$RELEASE_JSON" | \
    grep -o '"browser_download_url": *"[^"]*"' | \
    grep -i "simplex-desktop-x86_64\.AppImage" | \
    cut -d '"' -f 4 | \
    head -n 1)

if [[ -z "$DOWNLOAD_URL" ]]; then
    echo "ERROR: Could not find download URL" >&2
    echo "Available browser_download_url entries:" >&2
    echo "$RELEASE_JSON" | grep -o '"browser_download_url": *"[^"]*"' >&2
    exit 1
fi

echo "Downloading from: $DOWNLOAD_URL"

# Download
qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -L -o '$HOME/$APP_NAME.AppImage' '$DOWNLOAD_URL'"

# Hash verification
echo "Verifying integrity..."

# Extract the hash from API
ASSET_NAME=$(basename "$DOWNLOAD_URL")
EXPECTED_HASH=$(echo "$RELEASE_JSON" | \
    grep -A 30 "\"name\":.*\"$ASSET_NAME\"" | \
    grep '"digest"' | \
    head -n 1 | \
    sed -n 's/.*"digest": *"sha256:\([a-f0-9]*\)".*/\1/p')

if [[ -z "$EXPECTED_HASH" ]]; then
    echo "ERROR: Could not extract hash from release info!" >&2
    echo "Looking for asset: $ASSET_NAME" >&2
    echo "Available assets:" >&2
    echo "$RELEASE_JSON" | grep -A 5 -B 5 "\"name\":.*\"$ASSET_NAME\"" >&2
    qvm-run -p "$DISP_TEMPLATE" "rm -f '$HOME/$APP_NAME.AppImage'"
    exit 1
fi

echo "Expected hash: $EXPECTED_HASH"
ACTUAL_HASH=$(qvm-run -p "$DISP_TEMPLATE" "sha256sum '$HOME/$APP_NAME.AppImage' | cut -d' ' -f1")
echo "Found hash:    $ACTUAL_HASH"

if [[ "$EXPECTED_HASH" != "$ACTUAL_HASH" ]]; then
    echo "ERROR: Hash verification failed!" >&2
    echo "Hashes do not match!" >&2
    qvm-run "$DISP_TEMPLATE" "rm -f '$HOME/$APP_NAME.AppImage'"
    exit 1
fi

echo "Hash verification passed"

# Adjust user permissions
qvm-run -u root "$DISP_TEMPLATE" "chown user:user '$HOME/$APP_NAME.AppImage'"
qvm-run "$DISP_TEMPLATE" "chmod +x '$HOME/$APP_NAME.AppImage'"

# Create desktop entry
echo "Creating desktop entry..."
DESKTOP_ENTRY="[Desktop Entry]
Version=1.0
Type=Application
Name=$APP_NAME
Comment=Private and secure open-source messenger
Exec=/home/user/$APP_NAME.AppImage
Icon=starred
Terminal=false
Categories=Network;Chat;
StartupNotify=true"

# Write desktop entry to the custom template
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
sleep 3

if ! qvm-run -p -u root "$CUSTOM_TEMPLATE" "
    mkdir -p /usr/share/applications && \
    cat > /usr/share/applications/$APP_NAME.desktop <&2
    exit 1
fi

# Updater script to use in disposable template
cat > /tmp/updater_script.sh <&2
    exit 1
fi

if [[ -z "\$dl_url" ]]; then
    echo "Error: No download URL found" >&2
    echo "Available browser_download_url entries:" >&2
    echo "\$release" | grep -o '"browser_download_url": *"[^"]*"' >&2
    exit 1
fi

echo "Downloading update from: \$dl_url"
curl --tlsv1.2 --http1.1 -L -o "\$HOME/\$APP_NAME.AppImage" "\$dl_url"

# Hash verification
echo "Verifying integrity..."

asset_name=\$(basename "\$dl_url")

# Extract the hash from API
expected_hash=\$(echo "\$release" | \\
    grep -A 30 "\"name\": *\"\$asset_name\"" | \\
    grep '"digest"' | \\
    head -n 1 | \\
    sed -n 's/.*"digest": *"sha256:\\([a-f0-9]*\\)".*/\\1/p')

if [[ -z "\$expected_hash" ]]; then
    echo "ERROR: Could not extract hash from release info!" >&2
    echo "Looking for asset: \$asset_name" >&2
    echo "Available assets:" >&2
    echo "\$release" | grep -A 5 -B 5 "\"name\": *\"\$asset_name\"" >&2
    rm -f "\$HOME/\$APP_NAME.AppImage"
    exit 1
fi

echo "Expected hash: \$expected_hash"
actual_hash=\$(sha256sum "\$HOME/\$APP_NAME.AppImage" | cut -d' ' -f1)
echo "Found hash:    \$actual_hash"

if [[ "\$expected_hash" != "\$actual_hash" ]]; then
    echo "ERROR: Hash verification failed!" >&2
    echo "Hashes do not match!" >&2
    rm -f "\$HOME/\$APP_NAME.AppImage"
    exit 1
fi

echo "Hash verification passed"

# Adjust permissions
chmod +x "\$HOME/\$APP_NAME.AppImage"
echo "Update complete and verified"
EOF

qvm-run -p "$DISP_TEMPLATE" "cat > /home/user/update-$APP_NAME.sh" < /tmp/updater_script.sh
qvm-run "$DISP_TEMPLATE" "chmod 700 /home/user/update-$APP_NAME.sh"
rm -f /tmp/updater_script.sh

qvm-shutdown --wait "$DISP_TEMPLATE"
echo "Template configuration complete"

# Step 6: Create named disposable VM instance
echo -e "\n[6/7] Creating named disposable VM instance..."
if ! qvm-check "$NAMED_DISP_VM" 2>/dev/null; then
    qvm-create --class DispVM --template "$DISP_TEMPLATE" --label red \
        --property netvm="$NET_VM" \
        --property include_in_backups=False \
        "$NAMED_DISP_VM"
    qvm-features "$NAMED_DISP_VM" appmenus-dispvm 1
else
    echo "Named disposable VM $NAMED_DISP_VM already exists, skipping creation."
fi

# Step 7: Configure menu items
echo -e "\n[7/7] Configuring menu items..."
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
qvm-sync-appmenus "$CUSTOM_TEMPLATE"
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
qvm-features "$CUSTOM_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$DISP_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$NAMED_DISP_VM" menu-items "$APP_NAME.desktop thunar.desktop xfce4-terminal.desktop"

# Finalize
echo -e "\nFinish!"

To make your settings permanent, open whonix-simplex-dvm (disposable template) and make the minimum changes to be saved when you open disp-whonix-simplex (named disposable vm). Also read this: bind-dirs.



SimpleX References

Qubes References

Qubes Persistence References

  • How to use disposables - Qubes OS Documentation
  • Disposable customization - Qubes OS Documentation
  • bind-dirs - Qubes OS Documentation
  • How can I securely persist sessions in dispVMs? - Qubes OS Forum
  • <li><a href="https://forum.qubes-os.org/t/confused-about-persistent-and-disposable-vms/27099/13">Confused about persistent and disposable VMs -  - Qubes OS Forum</a></li>
    <li><a href="https://forum.qubes-os.org/t/confused-about-persistent-and-disposable-vms/27099">Understanding Persistent and Disposable VMs - Qubes OS Forum</a></li>
    <li><a href="https://forum.qubes-os.org/t/default-dvm-whonix-workstation-17-dvm-in-apps-tab/25761"> Default-dvm & whonix-workstation-17-dvm in apps tab? - Qubes OS Forum</a></li>
    <li><a href="https://forum.qubes-os.org/t/whonix-ws-vs-whonix-gw/16302">Whonix Workstation vs Whonix Gateway Discussion</a></li>
    <li><a href="https://www.whonix.org/wiki/Tor_Browser/Advanced_Users#Running_Tor_Browser_in_Qubes_Template_or_Disposable_Template">Running Tor Browser in Qubes Template or Disposable Template - Whonix Wiki</a></li>
    <li><a href="https://forum.qubes-os.org/t/click-on-the-url-which-is-then-automatic-opened-in-a-new-disposable/24306">Clicking URLs in Disposable VMs - Qubes OS Forum</a></li>
    

Desktop Entry and Others References

I am also using SimpleX chat on qubeOS
my simplex qube is runing on a copy of whonix workstation as a template and sys-whonix as netvm
after that i just download simplex appimage and provide autorisation (like for every appimage) on it to make is working on my “simplex qube”
as you internet is working on Tor (sys-whonix) you have to set Simplex to use sockproxy for than you can go on settings, network and server and then use socks proxy
I hope this will help you !

1 Like

Interresting

In the context of Whonix (sys-whonix) everything is through Tor. You can enforce .onion-only mode (incognito mode via SOCKS). Its not clear to me if its important for Qubes setup, Simplex could use separate Tor circuits for connections from different profiles (transport isolation) only if SOCKS is enabled in the AppVM or Its just for Android?

Relevant point is here:

Simplex Documentation:

https://simplex.chat/blog/20220901-simplex-chat-v3.2-incognito-mode.html#incognito-mode :

“We have released support for using SOCKS proxy to access messaging servers via Tor, (…) It means that while your IP address was protected from the server, the whole Tor circuit could have been observed by some actors, and for some communication scenarios it is not desirable.
(…) all servers provided by SimpleX Chat now have dual addresses (one public and one .onion), and you can have your own servers available via two addresses as well (…) you should use the same format for the addresses of your servers.”

https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation :

“Even when you create different chat profiles you are still connecting to your contacts via the same device. Transport isolation, by default, makes connections belonging to the same profile use different TCP sessions, so while the server sees the same IP address, it doesn’t see it as the same client connection. If you are connecting via Tor using SOCKS proxy (e.g. Orbot app on Android) not only the app will use different TCP sessions, it will also use separate Tor circuits for connections from different profiles, preventing the servers and network observers seeing this as traffic coming from the same device.”

Simplex 6.3.3 Default Configuration:

About the script, Im almost there… Entries is commig, 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" <<'BIND_EOF'
binds+=( '/etc/xdg/menus' )
BIND_EOF
    then
        echo "ERROR: Failed to configure bind-dirs" >&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='<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
    "http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd">
<Menu>
    <Name>Applications</Name>
    <Menu>
        <Name>DisposableVMs</Name>
        <Directory>qubes-dispvm.directory</Directory>
        <Include>
            <Filename>qubes-dispvm-simplex-chat.desktop</Filename>
        </Include>
    </Menu>
</Menu>'

    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 <<'UP_EOF'
#!/bin/bash

# Update only in the template!

# Variables
APP_NAME="simplex-chat.AppImage"
DOWNLOAD_URL="https://api.github.com/repos/simplex-chat/simplex-chat/releases/latest"
UP_EOF
    )
    # Add functions
    UPDATER_CONTENT+="\n\n$(declare -f download_appimage | sed 's/^/    /')"
    UPDATER_CONTENT+="\n\n$(declare -f verify_hash | sed 's/^/    /')"
    UPDATER_CONTENT+="\n\n# Main execution\ndownload_appimage\nverify_hash\n"
    UPDATER_CONTENT+='echo -e "\nSimpleX Chat update completed successfully!"'

    # Transfer to template with proper permissions
    echo "$UPDATER_CONTENT" | qvm-run --pass-io "$DISP_TEMPLATE_APPVM" "
        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!"

regarding the script I am not the good one to help you.
I can just confirm than I had information than socks proxy is the best option regarding privacy and having onion address is always better.
It’s working well for me with the settings I previously explained

1 Like

Looking for the QubesOS group on SimpleX. Have seen references but unable to locate and join the actual group on SImpleX. Anyone able to assist with a link to the group? Or do I need to learn the secret handshake? Thanks.

Right!

And I remembered this one:

Yep, you is right. I actived the SOCKS proxy

Use SOCKS proxy (Android only)

This option makes the app connect via a SOCKS proxy that should be provided by some other app running on your device.

The most typical use for this is to run Orbot app that provides a SOCKS proxy to connect via Tor network, but it can be some other app and it can proxy connections via some other overlay network.

Use .onion hosts

Android

Use .onion hosts option is only available when Use SOCKS proxy is enabled. You can choose:
(…)

  • required: always use .onion hosts. Choose this option if your SOCKS proxy connects via Tor network and you want to avoid connections without Tor. In this case, if the SMP relay address does not include .onion host, the connection will fail.

We need to follow this relevant topic about the use of .onion hosts option independently of the use SOCKS proxy:

For while, we could use .onion hosts enabling “required” mode of SOCKS proxy in settings - SimpleX v 6.4.3.1 (117)


Reference

https://simplex.chat/docs/guide/app-settings.html#use-onion-hosts

The answear to my doubt about why it is more secure to connect to a .onion service whenever one is available, even if your system is configured to only use Tor is: the Rendezvous Point, a randomly selected node acts as a secure meeting point where no single node knows both ends.

To be short: the 6-hop rendezvous circuit with a blind middle point (the Rendezvous Point) makes it incredibly difficult for any single entity to link a client to a specific hidden service.

Reference

https://community.torproject.org/onion-services/overview/

I will continue working in the main topic later.

Here is: “named disposable AppImage SimpleX qube” with Whonix Workstation model that downloads, checks the hash of the latest SimpleX AppImage, and uses sys-whonix as NetVM. Includes the icon in the menu to directly open the AppImage.

It is in the testing phase...

Sometimes downloading with curl on GitHub doesn't work, so I'm studying the parameters --tlsv1.2 --http1.1. This is related to the “API rate limit” and errors related to HTTP/2.

The integrity verification procedure is not very good because the way the checksum is provided by the SimpleX project is poor.


whonix-simplex-named-disposable.sh

#!/bin/bash

#######################################################################
# File Name    : whonix-simplex-named-disposable.sh
# Description  : This script creates a named disposable SimpleX qube
#                based on Whonix Workstation template. It downloads
#                and verify the hash of the last SimpleX AppImage and
#                uses sys-whonix as NetVM.
# Dependencies : curl
# Usage        : • Transfer this script from appvm to dom0 with:
#                [user@dom0 ~]$ qvm-run -p appvm 'cat ~/whonix-simplex-named-disposable.sh' > ~/whonix-simplex-named-disposable.sh
#                • Make the script executable with:
#                [user@dom0 ~]$ chmod +x ~/whonix-simplex-named-disposable.sh
#                • Run the script with:
#                [user@dom0 ~]$ bash ~/whonix-simplex-named-disposable.sh
#                • To update, run the script in whonix-simplex-dvm (disposable template):
#                bash ~/update-simplex-chat.sh
# Author       : Me and the bois
# License      : Free of charge, no warranty
# Last edited  : 2025-09-19
#######################################################################

# Safety check
set -eu

# Define Variables
APP_NAME="simplex-chat"
BASE_TEMPLATE="whonix-workstation-17"
CUSTOM_TEMPLATE="whonix-simplex-template"
DISP_TEMPLATE="whonix-simplex-dvm"
NAMED_DISP_VM="disp-whonix-simplex"
NET_VM="sys-whonix"
GITHUB_API_URL="https://api.github.com/repos/simplex-chat/simplex-chat/releases/latest"

# Step 1: Install and update the Qubes Template
echo -e "\n[1/7] Checking for Qubes template..."
if ! qvm-check "$BASE_TEMPLATE" 2>/dev/null; then
    echo "Installing $BASE_TEMPLATE..."
    sudo qubes-dom0-update qubes-template-$BASE_TEMPLATE
fi

# Ensure template is shut down before updating
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true

# Update the template whether it was just installed or already existed
echo "Updating $BASE_TEMPLATE..."
sudo qubesctl --show-output --skip-dom0 --targets=$BASE_TEMPLATE state.sls update.qubes-vm

# Ensure Qubes base template is shut down before create
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true

# Step 2: Create custom base template
echo -e "\n[2/7] Creating custom template by cloning..."
qvm-clone "$BASE_TEMPLATE" "$CUSTOM_TEMPLATE"
qvm-prefs "$CUSTOM_TEMPLATE" label black

# Step 3: Install dependencies
echo -e "\n[3/7] Installing dependencies..."
qvm-run -p -u root "$CUSTOM_TEMPLATE" "echo 'TERM=xterm' >> /etc/environment"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "echo -e 'LANGUAGE=en_US.UTF-8\nLC_ALL=en_US.UTF-8\nLANG=en_US.UTF-8\nLC_CTYPE=en_US.UTF-8' >> /etc/locale.conf"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "echo -e 'LANGUAGE=en_US.UTF-8\nLC_ALL=en_US.UTF-8\nLANG=en_US.UTF-8\nLC_CTYPE=en_US.UTF-8' >> /etc/default/locale"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "/usr/sbin/locale-gen"

# Shutdown custom template to apply changes
qvm-shutdown --wait "$CUSTOM_TEMPLATE"

# Step 4: Create DVM template based on custom template
echo -e "\n[4/7] Creating DVM template..."
qvm-create --template "$CUSTOM_TEMPLATE" --label red "$DISP_TEMPLATE"
qvm-prefs "$DISP_TEMPLATE" template_for_dispvms True

# Step 5: Create named disposable VM instance
echo -e "\n[5/7] Creating named disposable VM instance..."
qvm-create --class DispVM --template "$DISP_TEMPLATE" --label red \
    --property netvm="$NET_VM" \
    --property include_in_backups=False \
    "$NAMED_DISP_VM"
qvm-features "$NAMED_DISP_VM" appmenus-dispvm 1

# Step 6: Download and verify in disposable template
echo -e "\n[6/7] Configuring $APP_NAME in disposable template..."

# Start template if not running
if ! qvm-check --running "$DISP_TEMPLATE"; then
    qvm-start "$DISP_TEMPLATE" && sleep 5
fi

# Download
echo "Fetching latest release information..."

RELEASE_JSON=""
if RELEASE_JSON=$(qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -sL '$GITHUB_API_URL'"); then
    # Check for rate limit error
    if echo "$RELEASE_JSON" | grep -q "API rate limit exceeded"; then
        echo "ERROR: Failed to fetch release info from GitHub (rate limited)" >&2
        exit 1
    fi
fi

# Extract download URL
DOWNLOAD_URL=$(echo "$RELEASE_JSON" | grep -o '"browser_download_url": *"[^"]*"' | \
    grep -i "simplex-desktop-x86_64\.AppImage" | cut -d '"' -f 4 | head -n 1)

if [[ -z "$DOWNLOAD_URL" ]]; then
    echo "ERROR: Could not find AppImage download URL" >&2
    exit 1
fi

echo "Downloading AppImage from: $DOWNLOAD_URL"

# Try download
qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -L -o '$HOME/$APP_NAME.AppImage' '$DOWNLOAD_URL'"

# Hash verification
echo "Verifying integrity..."

RELEASE_BODY=$(echo "$RELEASE_JSON" | grep -zo '"body": *"[^"]*"' | cut -d'"' -f4 | tr -d '\0')
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
    echo "release page content:" >&2
    echo "$RELEASE_BODY" >&2
    qvm-run "$DISP_TEMPLATE" "rm -f '$HOME/$APP_NAME.AppImage'"
    exit 1
fi

echo "Expected hash: $EXPECTED_HASH"
ACTUAL_HASH=$(qvm-run -p "$DISP_TEMPLATE" "sha256sum '$HOME/$APP_NAME.AppImage' | cut -d' ' -f1")
echo "Found hash:    $ACTUAL_HASH"

if [[ "$EXPECTED_HASH" != "$ACTUAL_HASH" ]]; then
    echo "ERROR: Hash verification failed!" >&2
    echo "Hashes do not match!" >&2
    qvm-run "$DISP_TEMPLATE" "rm -f '$HOME/$APP_NAME.AppImage'"
    exit 1
fi

echo "Hash verification passed"

# Adjust user permissions
qvm-run -u root "$DISP_TEMPLATE" "chown user:user '$HOME/$APP_NAME.AppImage'"
qvm-run "$DISP_TEMPLATE" "chmod +x '$HOME/$APP_NAME.AppImage'"

# Desktop entry (the root space works differently in the template)
echo "Creating desktop entry..."
DESKTOP_ENTRY="[Desktop Entry]
Version=1.0
Type=Application
Name=$APP_NAME
GenericName=$APP_NAME
Comment=Private and secure open-source messenger
Exec=/home/user/$APP_NAME.AppImage
Icon=starred
Terminal=false
Categories=Network;Chat;
StartupNotify=true"

if ! qvm-run -p -u root "$CUSTOM_TEMPLATE" "
    mkdir -p /usr/share/applications && \
    cat > /usr/share/applications/$APP_NAME.desktop <&2
    exit 1
fi

# Updater script to use in disposable template
cat > /tmp/updater_script.sh <&2
    exit 1
fi

dl_url=$(echo "$release" | grep -o '"browser_download_url": *"[^"]*"' | grep -i "simplex-desktop-x86_64\.AppImage" | cut -d '"' -f 4)

[[ -z "$dl_url" ]] && { echo "Error: No download URL found" >&2; exit 1; }

echo "Downloading update from: $dl_url"
curl --tlsv1.2 --http1.1 -L -o "$HOME/$APP_NAME.AppImage" "$dl_url"

# Hash verification
echo "Verifying integrity..."

release_body=$(echo "$release" | grep -zo '"body": *"[^"]*"' | cut -d'"' -f4 | tr -d '\0')
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
    echo "release page content:" >&2
    echo "$release_body" >&2
    rm -f "$HOME/$APP_NAME.AppImage"
    exit 1
fi

echo "Expected hash: $expected_hash"
actual_hash=$(sha256sum "$HOME/$APP_NAME.AppImage" | cut -d' ' -f1)
echo "Found hash:    $actual_hash"

if [[ "$expected_hash" != "$actual_hash" ]]; then
    echo "ERROR: Hash verification failed!" >&2
    echo "Hashes do not match!" >&2
    rm -f "$HOME/$APP_NAME.AppImage"
    exit 1
fi

echo "Hash verification passed"

# Adjust user permissions
chmod +x "$HOME/$APP_NAME.AppImage"
echo "Update complete and verified"
EOF

qvm-run -p "$DISP_TEMPLATE" "cat > /home/user/update-$APP_NAME.sh" < /tmp/updater_script.sh
qvm-run "$DISP_TEMPLATE" "chmod 700 /home/user/update-$APP_NAME.sh"
rm -f /tmp/updater_script.sh

qvm-shutdown --wait "$DISP_TEMPLATE"
echo "Template configuration complete"

# Step 7: Configure menu items
echo -e "\n[7/7] Configuring menu items..."
qvm-sync-appmenus "$CUSTOM_TEMPLATE"
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
qvm-features "$CUSTOM_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$DISP_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$NAMED_DISP_VM" menu-items "$APP_NAME.desktop thunar.desktop"

# Finalize
echo -e "\nFinish!"

To make your settings permanent, open whonix-simplex-dvm (disposable template) and make the minimum changes to be saved when you open disp-whonix-simplex (named disposable vm).

This script could be a base to integrate AppImages in Qubes.

1 Like

have you considered flatpak?

I consider Flatpak bloated. We can use bubblewrap with AppImage, but its easy to make mistakes and in Qubes we already have compartmentalization.

I don’t rule out bwrap as a good security layer if we want to use another application on the same disposable VM, but I need to think about it more carefully.

https://wiki.archlinux.org/title/Bubblewrap/Examples


Example with Firefox

 bwrap \
--symlink usr/lib /lib \
--symlink usr/lib64 /lib64 \
--symlink usr/bin /bin \
--symlink usr/bin /sbin \
--ro-bind /usr/lib /usr/lib \
--ro-bind /usr/lib64 /usr/lib64 \
--ro-bind /usr/bin /usr/bin \
--ro-bind /usr/lib/firefox /usr/lib/firefox \
--ro-bind /usr/share/applications /usr/share/applications \
--ro-bind /usr/share/gtk-3.0 /usr/share/gtk-3.0 \
--ro-bind /usr/share/fontconfig /usr/share/fontconfig \
--ro-bind /usr/share/icu /usr/share/icu \
--ro-bind /usr/share/drirc.d /usr/share/drirc.d \
--ro-bind /usr/share/fonts /usr/share/fonts \
--ro-bind /usr/share/glib-2.0 /usr/share/glib-2.0 \
--ro-bind /usr/share/glvnd /usr/share/glvnd \
--ro-bind /usr/share/icons /usr/share/icons \
--ro-bind /usr/share/libdrm /usr/share/libdrm \
--ro-bind /usr/share/mime /usr/share/mime \
--ro-bind /usr/share/X11/xkb /usr/share/X11/xkb \
--ro-bind /usr/share/icons /usr/share/icons \
--ro-bind /usr/share/mime /usr/share/mime \
--ro-bind /etc/fonts /etc/fonts \
--ro-bind /etc/resolv.conf /etc/resolv.conf \
--ro-bind /usr/share/ca-certificates /usr/share/ca-certificates \
--ro-bind /etc/ssl /etc/ssl \
--ro-bind /etc/ca-certificates /etc/ca-certificates \
--dir "$XDG_RUNTIME_DIR" \
--ro-bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse" \
--ro-bind "$XDG_RUNTIME_DIR/wayland-1" "$XDG_RUNTIME_DIR/wayland-1" \
--dev /dev \
--dev-bind /dev/dri /dev/dri \
--ro-bind /sys/dev/char /sys/dev/char \
--ro-bind /sys/devices/pci0000:00 /sys/devices/pci0000:00 \
--proc /proc \
--tmpfs /tmp \
--bind /home/example/.mozilla /home/example/.mozilla \
--bind /home/example/.config/transmission /home/example/.config/transmission \
--bind /home/example/Downloads /home/example/Downloads \
--setenv HOME /home/example \
--setenv GTK_THEME Adwaita:dark \
--setenv MOZ_ENABLE_WAYLAND 1 \
--setenv PATH /usr/bin \
--hostname RESTRICTED \
--unshare-all \
--share-net \
--die-with-parent \
--new-session \
/usr/bin/firefox

Final version.


⚠️ BWRAP DISCLAIMER ⚠️

Disclaimer: When configuring bwrap, I am not responsible for any misuse of the sandbox rules, the rules provided in the script are only examples. Bwrap is not a simple tool, as many bubblewrap arguments map 1:1 to low-level system calls and kernel mechanisms, but it is a powerful tool for defeating some exploitation routes in a specific security policy.

Everything passed to the sandboxed can be used to break out of it, for example a dbus-socket, which could be used to execute something via systemd. Also, as mentioned in a thread in Privacy Guides, a sandboxed webbrowser could be less able to enforce a security sandbox within itself.

Remember that Qubes already uses compartmentalization and amnesic systems as a security layer.


Simplex

whonix-simplex-named-disposable.sh

#!/bin/bash

#######################################################################
# File Name    : whonix-simplex-named-disposable.sh
# Description  : This script creates a named disposable SimpleX qube
#                based on Whonix Workstation template. It downloads
#                and verify the hash of the last SimpleX AppImage and
#                uses sys-whonix as NetVM.
# Dependencies : curl
# Usage        : • Transfer this script from appvm to dom0 with:
#                [user@dom0 ~]$ qvm-run -p appvm 'cat ~/whonix-simplex-named-disposable.sh' > ~/whonix-simplex-named-disposable.sh
#                • Make the script executable with:
#                [user@dom0 ~]$ chmod +x ~/whonix-simplex-named-disposable.sh
#                • Run the script with:
#                [user@dom0 ~]$ bash ~/whonix-simplex-named-disposable.sh
#                • To update, run the script in whonix-simplex-dvm (disposable template):
#                bash ~/update-simplex-chat.sh
# Author       : Me and the bois
# License      : Free of charge, no warranty
# Last edited  : 2025-10-11
#######################################################################

# Safety check
set -eu

# Define Variables
APP_NAME="simplex-chat"
BASE_TEMPLATE="whonix-workstation-17"
CUSTOM_TEMPLATE="whonix-simplex-template"
DISP_TEMPLATE="whonix-simplex-template-dvm"
NAMED_DISP_VM="disp-whonix-simplex"
NET_VM="sys-whonix"
SOURCE_URL="https://api.github.com/repos/simplex-chat/simplex-chat/releases/latest"

# Step 1: Install and update the Qubes Template
echo -e "\n[1/7] Checking for Qubes template..."
if ! qvm-check "$BASE_TEMPLATE" 2>/dev/null; then
    echo "Installing $BASE_TEMPLATE..."
    sudo qubes-dom0-update qubes-template-$BASE_TEMPLATE
fi

# Ensure template is shut down before updating
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true

# Update the template whether it was just installed or already existed
echo "Updating $BASE_TEMPLATE..."
sudo qubesctl --show-output --skip-dom0 --targets=$BASE_TEMPLATE state.sls update.qubes-vm

# Ensure Qubes base template is shut down before create
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true

# Step 2: Create custom base template
echo -e "\n[2/7] Creating custom template by cloning..."
qvm-clone "$BASE_TEMPLATE" "$CUSTOM_TEMPLATE"
qvm-prefs "$CUSTOM_TEMPLATE" label black

# Step 3: Install dependencies
echo -e "\n[3/7] Installing dependencies..."
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "echo 'TERM=xterm' >> /etc/environment"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "locale-gen en_US.UTF-8"

# Shutdown
qvm-shutdown --wait "$CUSTOM_TEMPLATE"

# Step 4: Create DVM template based on custom template
echo -e "\n[4/7] Creating DVM template..."
if ! qvm-check "$DISP_TEMPLATE" 2>/dev/null; then
    qvm-create --template "$CUSTOM_TEMPLATE" --label red "$DISP_TEMPLATE"
    qvm-prefs "$DISP_TEMPLATE" template_for_dispvms True
else
    echo "DVM template $DISP_TEMPLATE already exists, skipping creation."
fi

# Step 5: Download and verify in disposable template
echo -e "\n[5/7] Configuring $APP_NAME in disposable template..."

# Start template if not running
if ! qvm-check --running "$DISP_TEMPLATE"; then
    qvm-start "$DISP_TEMPLATE" && sleep 3
fi

# Fetch latest release information
echo "Fetching latest release information..."

RELEASE_JSON=""
if RELEASE_JSON=$(qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -sL '$SOURCE_URL'"); then
    # Check for rate limit error
    if echo "$RELEASE_JSON" | grep -q "API rate limit exceeded"; then
        echo -e "\nERROR: Failed to fetch release info from API (rate limited)" >&2
        qvm-shutdown --wait "$CUSTOM_TEMPLATE"
        exit 1
    fi
fi

# Extract download URL
DOWNLOAD_URL=$(echo "$RELEASE_JSON" | \
    grep -o '"browser_download_url": *"[^"]*"' | \
    grep -i "simplex-desktop-x86_64\.AppImage" | \
    cut -d '"' -f 4 | \
    head -n 1)

if [[ -z "$DOWNLOAD_URL" ]]; then
    echo "ERROR: Could not find download URL" >&2
    echo "Available browser_download_url entries:" >&2
    echo "$RELEASE_JSON" | grep -o '"browser_download_url": *"[^"]*"' >&2
    exit 1
fi

echo "Downloading from: $DOWNLOAD_URL"

# Download
qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -L -o '$HOME/$APP_NAME.AppImage' '$DOWNLOAD_URL'"

# Hash verification
echo "Verifying integrity..."

# Extract the hash from API
ASSET_NAME=$(basename "$DOWNLOAD_URL")
EXPECTED_HASH=$(echo "$RELEASE_JSON" | \
    grep -A 30 "\"name\":.*\"$ASSET_NAME\"" | \
    grep '"digest"' | \
    head -n 1 | \
    sed -n 's/.*"digest": *"sha256:\([a-f0-9]*\)".*/\1/p')

if [[ -z "$EXPECTED_HASH" ]]; then
    echo "ERROR: Could not extract hash from release info!" >&2
    echo "Looking for asset: $ASSET_NAME" >&2
    echo "Available assets:" >&2
    echo "$RELEASE_JSON" | grep -A 5 -B 5 "\"name\":.*\"$ASSET_NAME\"" >&2
    qvm-run -p "$DISP_TEMPLATE" "rm -f '$HOME/$APP_NAME.AppImage'"
    exit 1
fi

echo "Expected hash: $EXPECTED_HASH"
ACTUAL_HASH=$(qvm-run -p "$DISP_TEMPLATE" "sha256sum '$HOME/$APP_NAME.AppImage' | cut -d' ' -f1")
echo "Found hash:    $ACTUAL_HASH"

if [[ "$EXPECTED_HASH" != "$ACTUAL_HASH" ]]; then
    echo "ERROR: Hash verification failed!" >&2
    echo "Hashes do not match!" >&2
    qvm-run "$DISP_TEMPLATE" "rm -f '$HOME/$APP_NAME.AppImage'"
    exit 1
fi

echo "Hash verification passed"

# Adjust user permissions
qvm-run -u root "$DISP_TEMPLATE" "chown user:user '$HOME/$APP_NAME.AppImage'"
qvm-run "$DISP_TEMPLATE" "chmod +x '$HOME/$APP_NAME.AppImage'"

# Create desktop entry
echo "Creating desktop entry..."
DESKTOP_ENTRY="[Desktop Entry]
Version=1.0
Type=Application
Name=$APP_NAME
Comment=Private and secure open-source messenger
Exec=/home/user/$APP_NAME.AppImage
Icon=starred
Terminal=false
Categories=Network;Chat;
StartupNotify=true"

# Write desktop entry to the custom template
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
sleep 3

if ! qvm-run -p -u root "$CUSTOM_TEMPLATE" "
    mkdir -p /usr/share/applications && \
    cat > /usr/share/applications/$APP_NAME.desktop <&2
    exit 1
fi

# Updater script to use in disposable template
cat > /tmp/updater_script.sh <&2
    exit 1
fi

if [[ -z "\$dl_url" ]]; then
    echo "Error: No download URL found" >&2
    echo "Available browser_download_url entries:" >&2
    echo "\$release" | grep -o '"browser_download_url": *"[^"]*"' >&2
    exit 1
fi

echo "Downloading update from: \$dl_url"
curl --tlsv1.2 --http1.1 -L -o "\$HOME/\$APP_NAME.AppImage" "\$dl_url"

# Hash verification
echo "Verifying integrity..."

asset_name=\$(basename "\$dl_url")

# Extract the hash from API
expected_hash=\$(echo "\$release" | \\
    grep -A 30 "\"name\": *\"\$asset_name\"" | \\
    grep '"digest"' | \\
    head -n 1 | \\
    sed -n 's/.*"digest": *"sha256:\\([a-f0-9]*\\)".*/\\1/p')

if [[ -z "\$expected_hash" ]]; then
    echo "ERROR: Could not extract hash from release info!" >&2
    echo "Looking for asset: \$asset_name" >&2
    echo "Available assets:" >&2
    echo "\$release" | grep -A 5 -B 5 "\"name\": *\"\$asset_name\"" >&2
    rm -f "\$HOME/\$APP_NAME.AppImage"
    exit 1
fi

echo "Expected hash: \$expected_hash"
actual_hash=\$(sha256sum "\$HOME/\$APP_NAME.AppImage" | cut -d' ' -f1)
echo "Found hash:    \$actual_hash"

if [[ "\$expected_hash" != "\$actual_hash" ]]; then
    echo "ERROR: Hash verification failed!" >&2
    echo "Hashes do not match!" >&2
    rm -f "\$HOME/\$APP_NAME.AppImage"
    exit 1
fi

echo "Hash verification passed"

# Adjust permissions
chmod +x "\$HOME/\$APP_NAME.AppImage"
echo "Update complete and verified"
EOF

qvm-run -p "$DISP_TEMPLATE" "cat > /home/user/update-$APP_NAME.sh" < /tmp/updater_script.sh
qvm-run "$DISP_TEMPLATE" "chmod 700 /home/user/update-$APP_NAME.sh"
rm -f /tmp/updater_script.sh

qvm-shutdown --wait "$DISP_TEMPLATE"
echo "Template configuration complete"

# Step 6: Create named disposable VM instance
echo -e "\n[6/7] Creating named disposable VM instance..."
if ! qvm-check "$NAMED_DISP_VM" 2>/dev/null; then
    qvm-create --class DispVM --template "$DISP_TEMPLATE" --label red \
        --property netvm="$NET_VM" \
        --property include_in_backups=False \
        "$NAMED_DISP_VM"
    qvm-features "$NAMED_DISP_VM" appmenus-dispvm 1
else
    echo "Named disposable VM $NAMED_DISP_VM already exists, skipping creation."
fi

# Step 7: Configure menu items
echo -e "\n[7/7] Configuring menu items..."
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
qvm-sync-appmenus "$CUSTOM_TEMPLATE"
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
qvm-features "$CUSTOM_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$DISP_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$NAMED_DISP_VM" menu-items "$APP_NAME.desktop thunar.desktop xfce4-terminal.desktop"

# Finalize
echo -e "\nFinish!"
whonix-simplex-bwrap-named-disposable.sh

#!/bin/bash

#######################################################################
# File Name    : whonix-simplex-bwrap-named-disposable.sh
# Description  : This script creates a named disposable SimpleX qube
#                based on Whonix Workstation template. It downloads
#                and verify the hash of the last SimpleX AppImage and
#                uses sys-whonix as NetVM. It runs the application
#                using bubblewrap (bwrap) to allow unprivileged
#                users to use container features to defining its own
#                security model, and choosing appropriate bubblewrap
#                command-line arguments.
# Dependencies : curl, bubblewrap
# Usage        : • Transfer this script from appvm to dom0 with:
#                [user@dom0 ~]$ qvm-run -p appvm 'cat ~/whonix-simplex-named-disposable.sh' > ~/whonix-simplex-named-disposable.sh
#                • Make the script executable with:
#                [user@dom0 ~]$ chmod +x ~/whonix-simplex-named-disposable.sh
#                • Run the script with:
#                [user@dom0 ~]$ bash ~/whonix-simplex-named-disposable.sh
#                • To update, run the script in whonix-simplex-dvm (disposable template):
#                bash ~/update-simplex-chat.sh
# Author       : Me and the bois
# License      : Free of charge, no warranty
# Last edited  : 2025-10-11
#######################################################################

# Safety check
set -eu

# Define Variables
APP_NAME="simplex-chat"
BASE_TEMPLATE="whonix-workstation-17"
CUSTOM_TEMPLATE="whonix-simplex-template"
DISP_TEMPLATE="whonix-simplex-template-dvm"
NAMED_DISP_VM="disp-whonix-simplex"
NET_VM="sys-whonix"
SOURCE_URL="https://api.github.com/repos/simplex-chat/simplex-chat/releases/latest"

# Define bwrap rules
get_bwrap_cmd() {
  local DISPLAY_VALUE="${DISPLAY:-:0}"
  local USER_ID=$(id -u)
  local DOWNLOAD_DIR="${XDG_DOWNLOAD_DIR:-$HOME/Downloads}"

  cat </dev/null; then
    echo "Installing $BASE_TEMPLATE..."
    sudo qubes-dom0-update qubes-template-$BASE_TEMPLATE
fi

# Ensure template is shut down before updating
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true

# Update the template whether it was just installed or already existed
echo "Updating $BASE_TEMPLATE..."
sudo qubesctl --show-output --skip-dom0 --targets=$BASE_TEMPLATE state.sls update.qubes-vm

# Ensure Qubes base template is shut down before create
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true

# Step 2: Create custom base template
echo -e "\n[2/7] Creating custom template by cloning..."
qvm-clone "$BASE_TEMPLATE" "$CUSTOM_TEMPLATE"
qvm-prefs "$CUSTOM_TEMPLATE" label black

# Step 3: Install dependencies
echo -e "\n[3/7] Installing dependencies..."
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "echo 'TERM=xterm' >> /etc/environment"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "locale-gen en_US.UTF-8"

# Shutdown
qvm-shutdown --wait "$CUSTOM_TEMPLATE"

# Step 4: Create DVM template based on custom template
echo -e "\n[4/7] Creating DVM template..."
if ! qvm-check "$DISP_TEMPLATE" 2>/dev/null; then
    qvm-create --template "$CUSTOM_TEMPLATE" --label red "$DISP_TEMPLATE"
    qvm-prefs "$DISP_TEMPLATE" template_for_dispvms True
else
    echo "DVM template $DISP_TEMPLATE already exists, skipping creation."
fi

# Step 5: Download and verify in disposable template (we need to use userspace in dvm)
echo -e "\n[5/7] Configuring disposable template..."

# Start template if not running
if ! qvm-check --running "$DISP_TEMPLATE"; then
    qvm-start "$DISP_TEMPLATE" && sleep 3
fi

# Fetch latest release information
echo "Fetching latest release information..."

RELEASE_JSON=""
if RELEASE_JSON=$(qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -sL '$SOURCE_URL'"); then
    # Check for rate limit error
    if echo "$RELEASE_JSON" | grep -q "API rate limit exceeded"; then
        echo -e "\nERROR: Failed to fetch release info from API (rate limited)" >&2
        qvm-shutdown --wait "$CUSTOM_TEMPLATE"
        exit 1
    fi
fi

# Extract download URL
DOWNLOAD_URL=$(echo "$RELEASE_JSON" | \
    grep -o '"browser_download_url": *"[^"]*"' | \
    grep -i "simplex-desktop-x86_64\.AppImage" | \
    cut -d '"' -f 4 | \
    head -n 1)

if [[ -z "$DOWNLOAD_URL" ]]; then
    echo "ERROR: Could not find download URL" >&2
    echo "Available browser_download_url entries:" >&2
    echo "$RELEASE_JSON" | grep -o '"browser_download_url": *"[^"]*"' >&2
    exit 1
fi

echo "Downloading from: $DOWNLOAD_URL"

# Download
qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -L -o '$HOME/$APP_NAME.AppImage' '$DOWNLOAD_URL'"

# Hash verification
echo "Verifying integrity..."

# Extract the hash from API
ASSET_NAME=$(basename "$DOWNLOAD_URL")
EXPECTED_HASH=$(echo "$RELEASE_JSON" | \
    grep -A 30 "\"name\":.*\"$ASSET_NAME\"" | \
    grep '"digest"' | \
    head -n 1 | \
    sed -n 's/.*"digest": *"sha256:\([a-f0-9]*\)".*/\1/p')

if [[ -z "$EXPECTED_HASH" ]]; then
    echo "ERROR: Could not extract hash from release info!" >&2
    echo "Looking for asset: $ASSET_NAME" >&2
    echo "Available assets:" >&2
    echo "$RELEASE_JSON" | grep -A 5 -B 5 "\"name\":.*\"$ASSET_NAME\"" >&2
    qvm-run -p "$DISP_TEMPLATE" "rm -f '$HOME/$APP_NAME.AppImage'"
    exit 1
fi

echo "Expected hash: $EXPECTED_HASH"
ACTUAL_HASH=$(qvm-run -p "$DISP_TEMPLATE" "sha256sum '$HOME/$APP_NAME.AppImage' | cut -d' ' -f1")
echo "Found hash:    $ACTUAL_HASH"

if [[ "$EXPECTED_HASH" != "$ACTUAL_HASH" ]]; then
    echo "ERROR: Hash verification failed!" >&2
    echo "Hashes do not match!" >&2
    qvm-run "$DISP_TEMPLATE" "rm -f '$HOME/$APP_NAME.AppImage'"
    exit 1
fi

echo "Hash verification passed"

# Adjust permissions
qvm-run -u root "$DISP_TEMPLATE" "chown user:user '$HOME/$APP_NAME.AppImage'"
qvm-run "$DISP_TEMPLATE" "chmod +x '$HOME/$APP_NAME.AppImage'"

# Create desktop entry using bwrap rules
echo "Creating desktop entry..."

# Generate the bwrap command
BWRAP_CMD=$(get_bwrap_cmd)

# Create desktop entry
echo "Creating desktop entry..."
DESKTOP_ENTRY="[Desktop Entry]
Version=1.0
Type=Application
Name=$APP_NAME
Comment=Private and secure open-source messenger
Exec=$BWRAP_CMD
Icon=starred
Terminal=false
Categories=Network;Chat;
StartupNotify=true"

# Write desktop entry to the custom template
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
sleep 3

if ! qvm-run -p -u root "$CUSTOM_TEMPLATE" "
    mkdir -p /usr/share/applications && \
    cat > /usr/share/applications/$APP_NAME.desktop <&2
    exit 1
fi

# Updater script to use in disposable template
cat > /tmp/updater_script.sh <&2
    exit 1
fi

if [[ -z "\$dl_url" ]]; then
    echo "Error: No download URL found" >&2
    echo "Available browser_download_url entries:" >&2
    echo "\$release" | grep -o '"browser_download_url": *"[^"]*"' >&2
    exit 1
fi

echo "Downloading update from: \$dl_url"
curl --tlsv1.2 --http1.1 -L -o "\$HOME/\$APP_NAME.AppImage" "\$dl_url"

# Hash verification
echo "Verifying integrity..."

asset_name=\$(basename "\$dl_url")

# Extract the hash from API
expected_hash=\$(echo "\$release" | \\
    grep -A 30 "\"name\": *\"\$asset_name\"" | \\
    grep '"digest"' | \\
    head -n 1 | \\
    sed -n 's/.*"digest": *"sha256:\\([a-f0-9]*\\)".*/\\1/p')

if [[ -z "\$expected_hash" ]]; then
    echo "ERROR: Could not extract hash from release info!" >&2
    echo "Looking for asset: \$asset_name" >&2
    echo "Available assets:" >&2
    echo "\$release" | grep -A 5 -B 5 "\"name\": *\"\$asset_name\"" >&2
    rm -f "\$HOME/\$APP_NAME.AppImage"
    exit 1
fi

echo "Expected hash: \$expected_hash"
actual_hash=\$(sha256sum "\$HOME/\$APP_NAME.AppImage" | cut -d' ' -f1)
echo "Found hash:    \$actual_hash"

if [[ "\$expected_hash" != "\$actual_hash" ]]; then
    echo "ERROR: Hash verification failed!" >&2
    echo "Hashes do not match!" >&2
    rm -f "\$HOME/\$APP_NAME.AppImage"
    exit 1
fi

echo "Hash verification passed"

# Adjust permissions
chmod +x "\$HOME/\$APP_NAME.AppImage"
echo "Update complete and verified"
EOF

qvm-run -p "$DISP_TEMPLATE" "cat > /home/user/update-$APP_NAME.sh" < /tmp/updater_script.sh
qvm-run "$DISP_TEMPLATE" "chmod 700 /home/user/update-$APP_NAME.sh"
rm -f /tmp/updater_script.sh

qvm-shutdown --wait "$DISP_TEMPLATE"
echo "Template configuration complete"

# Step 6: Create named disposable VM instance
echo -e "\n[6/7] Creating named disposable VM instance..."
if ! qvm-check "$NAMED_DISP_VM" 2>/dev/null; then
    qvm-create --class DispVM --template "$DISP_TEMPLATE" --label red \
        --property netvm="$NET_VM" \
        --property include_in_backups=False \
        "$NAMED_DISP_VM"
    qvm-features "$NAMED_DISP_VM" appmenus-dispvm 1
else
    echo "Named disposable VM $NAMED_DISP_VM already exists, skipping creation."
fi

# Step 7: Configure menu items
echo -e "\n[7/7] Configuring menu items..."
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
qvm-sync-appmenus "$CUSTOM_TEMPLATE"
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
qvm-features "$CUSTOM_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$DISP_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$NAMED_DISP_VM" menu-items "$APP_NAME.desktop thunar.desktop xfce4-terminal.desktop"

# Finalize
echo -e "\nFinish!"

Issues: FUSE in Simplex bwrap setup

This version whonix-simplex-bwrap-named-disposable.sh requires FUSE permissions to be manually enabled in the bwrap rules. Look this:


Telegram

I took this opportunity to set up for Telegram binary as well.

whonix-telegram-named-disposable.sh

#!/bin/bash

#######################################################################
# File Name    : whonix-telegram-named-disposable.sh
# Description  : This script creates a named disposable Telegram qube
#                based on Whonix Workstation template. It downloads
#                and verify the hash of the latest Telegram binary
#                and uses sys-whonix as NetVM. Note that Telegram have
#                a native binary for updates.
# Dependencies : curl
# Usage        : • Transfer this script from appvm to dom0 with:
#                [user@dom0 ~]$ qvm-run -p appvm 'cat ~/whonix-telegram-named-disposable.sh' > ~/whonix-telegram-named-disposable.sh
#                • Make the script executable with:
#                [user@dom0 ~]$ chmod +x ~/whonix-telegram-named-disposable.sh
#                • Run the script with:
#                [user@dom0 ~]$ bash ~/whonix-telegram-named-disposable.sh
#                • To update, run the script in whonix-telegram-dvm (disposable template):
#                bash ~/update-telegram.sh
# Author       : Me and the bois
# License      : Free of charge, no warranty
# Last edited  : 2025-10-11
#######################################################################

# Safety check
set -eu

# Define Variables
APP_NAME="Telegram" # Absolute reference
APP_PATH="/home/user/telegram-desktop"
BASE_TEMPLATE="whonix-workstation-17"
CUSTOM_TEMPLATE="whonix-telegram-template"
DISP_TEMPLATE="whonix-telegram-template-dvm"
NAMED_DISP_VM="disp-whonix-telegram"
NET_VM="sys-whonix"
SOURCE_URL="https://api.github.com/repos/telegramdesktop/tdesktop/releases/latest"

# Step 1: Install and update the Qubes Template
echo -e "\n[1/7] Checking for Qubes template..."
if ! qvm-check "$BASE_TEMPLATE" 2>/dev/null; then
    echo "Installing $BASE_TEMPLATE..."
    sudo qubes-dom0-update qubes-template-$BASE_TEMPLATE
fi

# Ensure template is shut down before updating
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true

# Update the template whether it was just installed or already existed
echo "Updating $BASE_TEMPLATE..."
sudo qubesctl --show-output --skip-dom0 --targets=$BASE_TEMPLATE state.sls update.qubes-vm

# Ensure Qubes base template is shut down before create
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true

# Step 2: Create custom base template
echo -e "\n[2/7] Creating custom template by cloning..."
qvm-clone "$BASE_TEMPLATE" "$CUSTOM_TEMPLATE"
qvm-prefs "$CUSTOM_TEMPLATE" label black

# Step 3: Install dependencies
echo -e "\n[3/7] Installing dependencies..."
qvm-run -p -u root "$CUSTOM_TEMPLATE" "echo 'TERM=xterm' >> /etc/environment"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "locale-gen en_US.UTF-8"

# Install dependencies (change as needed)
echo "Installing dependencies..."
qvm-run -p -u root "$CUSTOM_TEMPLATE" "apt-get update && apt-get install -y \
    qt5-image-formats-plugins \
    libabsl20220623 \
    libavcodec59 \
    libavformat59 \
    libavutil57 \
    libglib2.0-0 \
    libglibmm-2.68-1 \
    libhunspell-1.7-0 \
    libjpeg62-turbo \
    libkf5coreaddons5 \
    liblz4-1 \
    libminizip1 \
    libopenal1 \
    libopus0 \
    libqrcodegencpp1 \
    libqt5core5a \
    libqt5gui5 \
    libqt5network5 \
    libqt5qml5 \
    libqt5quickwidgets5 \
    libqt5svg5 \
    libqt5waylandcompositor5 \
    libqt5widgets5 \
    librlottie0-1 \
    libsigc++-3.0-0 \
    libsrtp2-1 \
    libssl3 \
    libswresample4 \
    libswscale6 \
    libvpx7 \
    libwayland-client0 \
    libxcb-keysyms1 \
    libxcb-record0 \
    libxcb-screensaver0 \
    libxcomposite1 \
    libxdamage1 \
    libxrandr2 \
    libxxhash0 \
    zlib1g"

# Shutdown
qvm-shutdown --wait "$CUSTOM_TEMPLATE"

# Step 4: Create DVM template based on custom template
echo -e "\n[4/7] Creating DVM template..."
if ! qvm-check "$DISP_TEMPLATE" 2>/dev/null; then
    qvm-create --template "$CUSTOM_TEMPLATE" --label red "$DISP_TEMPLATE"
    qvm-prefs "$DISP_TEMPLATE" template_for_dispvms True
else
    echo "DVM template $DISP_TEMPLATE already exists, skipping creation."
fi

# Step 5: Download and verify in disposable template
echo -e "\n[5/7] Configuring disposable template..."

# Start template if not running
if ! qvm-check --running "$DISP_TEMPLATE"; then
    qvm-start "$DISP_TEMPLATE" && sleep 3
fi

# Fetch latest release information
echo "Fetching latest release information..."

RELEASE_JSON=""
if RELEASE_JSON=$(qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -sL '$SOURCE_URL'"); then
    # Check for rate limit error
    if echo "$RELEASE_JSON" | grep -q "API rate limit exceeded"; then
        echo -e "\nERROR: Failed to fetch release info from API (rate limited)" >&2
        qvm-shutdown --wait "$CUSTOM_TEMPLATE"
        exit 1
    fi
fi

# Extract download URL
DOWNLOAD_URL=$(echo "$RELEASE_JSON" | \
    grep -o '"browser_download_url": *"[^"]*"' | \
    grep "tsetup\..*\.tar\.xz" | \
    cut -d '"' -f 4 | \
    head -n 1)

if [[ -z "$DOWNLOAD_URL" ]]; then
    echo "ERROR: Could not find download URL" >&2
    echo "Available browser_download_url entries:" >&2
    echo "$RELEASE_JSON" | grep -o '"browser_download_url": *"[^"]*"' >&2
    exit 1
fi

echo "Downloading from: $DOWNLOAD_URL"

# Download
qvm-run -p "$DISP_TEMPLATE" "mkdir -p '$APP_PATH'"
qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -L -o '$APP_PATH/$APP_NAME.tar.xz' '$DOWNLOAD_URL'"

# Hash verification
echo "Verifying integrity..."

# Extract the hash from API
ASSET_NAME=$(basename "$DOWNLOAD_URL")
EXPECTED_HASH=$(echo "$RELEASE_JSON" | \
    grep -A 30 "\"name\":.*\"$ASSET_NAME\"" | \
    grep '"digest"' | \
    head -n 1 | \
    sed -n 's/.*"digest": *"sha256:\([a-f0-9]*\)".*/\1/p')

if [[ -z "$EXPECTED_HASH" ]]; then
    echo "ERROR: Could not extract hash from release info!" >&2
    echo "Looking for asset: $ASSET_NAME" >&2
    echo "Available assets:" >&2
    echo "$RELEASE_JSON" | grep -o '"name": *"[^"]*"' | head -10 >&2
    qvm-run -p "$DISP_TEMPLATE" "rm -f '$APP_PATH/$APP_NAME.tar.xz'"
    exit 1
fi

echo "Expected hash: $EXPECTED_HASH"
ACTUAL_HASH=$(qvm-run -p "$DISP_TEMPLATE" "sha256sum '$APP_PATH/$APP_NAME.tar.xz' | cut -d' ' -f1")
echo "Found hash:    $ACTUAL_HASH"

if [[ "$EXPECTED_HASH" != "$ACTUAL_HASH" ]]; then
    echo "ERROR: Hash verification failed!" >&2
    echo "Hashes do not match!" >&2
    qvm-run -p "$DISP_TEMPLATE" "rm -f '$APP_PATH/$APP_NAME.tar.xz'"
    exit 1
fi

echo "Hash verification passed"

# Extract and install
echo "Extracting files..."
qvm-run -p "$DISP_TEMPLATE" "tar -xf '$APP_PATH/$APP_NAME.tar.xz' -C '$APP_PATH/' --strip-components=1"
qvm-run -p "$DISP_TEMPLATE" "rm -f '$APP_PATH/$APP_NAME.tar.xz'"

# Telegram hidden feature for TDATA portable account storage in the same directory of the app binary
qvm-run -p "$DISP_TEMPLATE" "mkdir -p $APP_PATH/TelegramForcePortable"

# Adjust user permissions
qvm-run -p -u root "$DISP_TEMPLATE" "chown -R user:user '$APP_PATH'"
qvm-run -p "$DISP_TEMPLATE" "chmod +x '$APP_PATH/$APP_NAME'"
qvm-run -p "$DISP_TEMPLATE" "chmod +x '$APP_PATH/Updater'" # Absolute reference

# Create desktop entry
echo "Creating desktop entry..."
DESKTOP_ENTRY="[Desktop Entry]
Version=1.0
Type=Application
Name=$APP_NAME
Comment=New era of messaging
Exec=$APP_PATH -- %u
Icon=org.telegram.desktop
Terminal=false
Categories=Chat;Network;InstantMessaging;Qt;
StartupNotify=true
MimeType=x-scheme-handler/tg;x-scheme-handler/tonsite;"

# Write desktop entry to the custom template
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
sleep 3

if ! qvm-run -p -u root "$CUSTOM_TEMPLATE" "
    mkdir -p /usr/share/applications && \
    cat > /usr/share/applications/$APP_NAME.desktop <&2
    exit 1
fi

# Updater script to use in disposable template
cat > /tmp/updater_script.sh <&2
    exit 1
fi

if [[ -z "\$dl_url" ]]; then
    echo "Error: No download URL found" >&2
    echo "Available browser_download_url entries:" >&2
    echo "\$release" | grep -o '"browser_download_url": *"[^"]*"' >&2
    exit 1
fi

echo "Downloading update from: \$dl_url"
curl --tlsv1.2 --http1.1 -L -o "\$APP_PATH/\$APP_NAME.tar.xz" "\$dl_url"

# Hash verification
echo "Verifying integrity..."

asset_name=\$(basename "\$dl_url")

# Extract the hash from API
expected_hash=\$(echo "\$release" | \\
    grep -A 30 "\"name\": *\"\$asset_name\"" | \\
    grep '"digest"' | \\
    head -n 1 | \\
    sed -n 's/.*"digest": *"sha256:\\([a-f0-9]*\\)".*/\\1/p')

if [[ -z "\$expected_hash" ]]; then
    echo "ERROR: Could not extract hash from release info!" >&2
    echo "Looking for asset: \$asset_name" >&2
    echo "Available assets:" >&2
    echo "\$release" | grep -o '"name": *"[^"]*"' | head -10 >&2
    rm -f "\$APP_PATH/\$APP_NAME.tar.xz"
    exit 1
fi

echo "Expected hash: \$expected_hash"
actual_hash=\$(sha256sum "\$APP_PATH/\$APP_NAME.tar.xz" | cut -d' ' -f1)
echo "Found hash:    \$actual_hash"

if [[ "\$expected_hash" != "\$actual_hash" ]]; then
    echo "ERROR: Hash verification failed!" >&2
    echo "Hashes do not match!" >&2
    rm -f "\$APP_PATH/\$APP_NAME.tar.xz"
    exit 1
fi

echo "Hash verification passed"

# Clean, extract and update
echo "Extracting update..."
rm -f "\$APP_PATH/\$APP_NAME" "\$APP_PATH/Updater"
tar -xf "\$APP_PATH/\$APP_NAME.tar.xz" -C "\$APP_PATH/" --strip-components=1
rm -f "\$APP_PATH/\$APP_NAME.tar.xz"

# Adjust permissions
chmod +x "\$APP_PATH/\$APP_NAME"
chmod +x "\$APP_PATH/Updater"
echo "Update complete and verified"
EOF

qvm-run -p "$DISP_TEMPLATE" "cat > /home/user/update-$APP_NAME.sh" < /tmp/updater_script.sh
qvm-run -p "$DISP_TEMPLATE" "chmod 700 /home/user/update-$APP_NAME.sh"
rm -f /tmp/updater_script.sh

qvm-shutdown --wait "$DISP_TEMPLATE"
echo "Template configuration complete"

# Step 6: Create named disposable VM instance
echo -e "\n[6/7] Creating named disposable VM instance..."
if ! qvm-check "$NAMED_DISP_VM" 2>/dev/null; then
    qvm-create --class DispVM --template "$DISP_TEMPLATE" --label red \
        --property netvm="$NET_VM" \
        --property include_in_backups=False \
        "$NAMED_DISP_VM"
    qvm-features "$NAMED_DISP_VM" appmenus-dispvm 1
else
    echo "Named disposable VM $NAMED_DISP_VM already exists, skipping creation."
fi

# Step 7: Configure menu items
echo -e "\n[7/7] Configuring menu items..."
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
qvm-sync-appmenus "$CUSTOM_TEMPLATE"
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
qvm-features "$CUSTOM_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$DISP_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$NAMED_DISP_VM" menu-items "$APP_NAME.desktop thunar.desktop xfce4-terminal.desktop"

# Finalize
echo -e "\nFinish!"
whonix-telegram-bwrap-named-disposable.sh

#!/bin/bash

#######################################################################
# File Name    : whonix-telegram-bwrap-named-disposable.sh
# Description  : This script creates a named disposable Telegram qube
#                based on Whonix Workstation template. It downloads
#                and verify the hash of the latest Telegram binary
#                and uses sys-whonix as NetVM. It runs the application
#                using bubblewrap (bwrap) to allow unprivileged
#                users to use container features to defining its own
#                security model, and choosing appropriate bubblewrap
#                command-line arguments. Note that Telegram have a
#                native binary for updates and a hidden feature for
 #               TDATA portable account storage in the same directory
 #               of the app binary.
# Dependencies : curl, bubblewrap
# Usage        : • Transfer this script from appvm to dom0 with:
#                [user@dom0 ~]$ qvm-run -p appvm 'cat ~/whonix-telegram-bwrap-named-disposable.sh' > ~/whonix-telegram-bwrap-named-disposable.sh
#                • Make the script executable with:
#                [user@dom0 ~]$ chmod +x ~/whonix-telegram-bwrap-named-disposable.sh
#                • Run the script with:
#                [user@dom0 ~]$ bash ~/whonix-telegram-bwrap-named-disposable.sh
#                • To update, run the script in whonix-telegram-dvm (disposable template):
#                bash ~/update-telegram.sh
# Author       : Me and the bois
# License      : Free of charge, no warranty
# Last edited  : 2025-10-11
#######################################################################

# Safety check
set -eu

# Define Variables
APP_NAME="Telegram" # Absolute reference
APP_PATH="/home/user/telegram-desktop"
BASE_TEMPLATE="whonix-workstation-17"
CUSTOM_TEMPLATE="whonix-telegram-template"
DISP_TEMPLATE="whonix-telegram-template-dvm"
NAMED_DISP_VM="disp-whonix-telegram"
NET_VM="sys-whonix"
SOURCE_URL="https://api.github.com/repos/telegramdesktop/tdesktop/releases/latest"

# Define bwrap rules
get_bwrap_cmd() {
  local DISPLAY_VALUE="${DISPLAY:-:0}"
  local USER_ID=$(id -u)
  local DOWNLOAD_DIR="${XDG_DOWNLOAD_DIR:-$HOME/Downloads}"

  cat </dev/null; then
    echo "Installing $BASE_TEMPLATE..."
    sudo qubes-dom0-update qubes-template-$BASE_TEMPLATE
fi

# Ensure template is shut down before updating
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true

# Update the template whether it was just installed or already existed
echo "Updating $BASE_TEMPLATE..."
sudo qubesctl --show-output --skip-dom0 --targets=$BASE_TEMPLATE state.sls update.qubes-vm

# Ensure Qubes base template is shut down before create
qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true

# Step 2: Create custom base template
echo -e "\n[2/7] Creating custom template by cloning..."
if ! qvm-check "$CUSTOM_TEMPLATE" 2>/dev/null; then
    qvm-clone "$BASE_TEMPLATE" "$CUSTOM_TEMPLATE"
    qvm-prefs "$CUSTOM_TEMPLATE" label black
else
    echo "Custom template $CUSTOM_TEMPLATE already exists, skipping clone."
fi

# Step 3: Install dependencies
echo -e "\n[3/7] Installing dependencies..."
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "echo 'TERM=xterm' >> /etc/environment"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen"
qvm-run -p -u root "$CUSTOM_TEMPLATE" "locale-gen en_US.UTF-8"

# Install dependencies (change as needed)
echo "Installing dependencies..."
qvm-run -p -u root "$CUSTOM_TEMPLATE" "apt-get update && apt-get install -y \
    qt5-image-formats-plugins \
    libabsl20220623 \
    libavcodec59 \
    libavformat59 \
    libavutil57 \
    libglib2.0-0 \
    libglibmm-2.68-1 \
    libhunspell-1.7-0 \
    libjpeg62-turbo \
    libkf5coreaddons5 \
    liblz4-1 \
    libminizip1 \
    libopenal1 \
    libopus0 \
    libqrcodegencpp1 \
    libqt5core5a \
    libqt5gui5 \
    libqt5network5 \
    libqt5qml5 \
    libqt5quickwidgets5 \
    libqt5svg5 \
    libqt5waylandcompositor5 \
    libqt5widgets5 \
    librlottie0-1 \
    libsigc++-3.0-0 \
    libsrtp2-1 \
    libssl3 \
    libswresample4 \
    libswscale6 \
    libvpx7 \
    libwayland-client0 \
    libxcb-keysyms1 \
    libxcb-record0 \
    libxcb-screensaver0 \
    libxcomposite1 \
    libxdamage1 \
    libxrandr2 \
    libxxhash0 \
    zlib1g \
    bubblewrap"

# Shutdown
qvm-shutdown --wait "$CUSTOM_TEMPLATE"

# Step 4: Create DVM template based on custom template
echo -e "\n[4/7] Creating DVM template..."
if ! qvm-check "$DISP_TEMPLATE" 2>/dev/null; then
    qvm-create --template "$CUSTOM_TEMPLATE" --label red "$DISP_TEMPLATE"
    qvm-prefs "$DISP_TEMPLATE" template_for_dispvms True
else
    echo "DVM template $DISP_TEMPLATE already exists, skipping creation."
fi

# Step 5: Download and verify in disposable template
echo -e "\n[5/7] Configuring disposable template..."

# Start template if not running
if ! qvm-check --running "$DISP_TEMPLATE"; then
    qvm-start "$DISP_TEMPLATE" && sleep 3
fi

# Fetch latest release information
echo "Fetching latest release information..."

RELEASE_JSON=""
if RELEASE_JSON=$(qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -sL '$SOURCE_URL'"); then
    # Check for rate limit error
    if echo "$RELEASE_JSON" | grep -q "API rate limit exceeded"; then
        echo -e "\nERROR: Failed to fetch release info from API (rate limited)" >&2
        qvm-shutdown --wait "$CUSTOM_TEMPLATE"
        exit 1
    fi
fi

# Extract download URL
DOWNLOAD_URL=$(echo "$RELEASE_JSON" | \
    grep -o '"browser_download_url": *"[^"]*"' | \
    grep "tsetup\..*\.tar\.xz" | \
    cut -d '"' -f 4 | \
    head -n 1)

if [[ -z "$DOWNLOAD_URL" ]]; then
    echo "ERROR: Could not find download URL" >&2
    echo "Available browser_download_url entries:" >&2
    echo "$RELEASE_JSON" | grep -o '"browser_download_url": *"[^"]*"' >&2
    exit 1
fi

echo "Downloading from: $DOWNLOAD_URL"

# Download
qvm-run -p "$DISP_TEMPLATE" "mkdir -p '$APP_PATH'"
qvm-run -p "$DISP_TEMPLATE" "curl --tlsv1.2 --http1.1 -L -o '$APP_PATH/$APP_NAME.tar.xz' '$DOWNLOAD_URL'"

# Hash verification
echo "Verifying integrity..."

# Extract the hash from API
ASSET_NAME=$(basename "$DOWNLOAD_URL")
EXPECTED_HASH=$(echo "$RELEASE_JSON" | \
    grep -A 30 "\"name\":.*\"$ASSET_NAME\"" | \
    grep '"digest"' | \
    head -n 1 | \
    sed -n 's/.*"digest": *"sha256:\([a-f0-9]*\)".*/\1/p')

if [[ -z "$EXPECTED_HASH" ]]; then
    echo "ERROR: Could not extract hash from release info!" >&2
    echo "Looking for asset: $ASSET_NAME" >&2
    echo "Available assets:" >&2
    echo "$RELEASE_JSON" | grep -o '"name": *"[^"]*"' | head -10 >&2
    qvm-run -p "$DISP_TEMPLATE" "rm -f '$APP_PATH/$APP_NAME.tar.xz'"
    exit 1
fi

echo "Expected hash: $EXPECTED_HASH"
ACTUAL_HASH=$(qvm-run -p "$DISP_TEMPLATE" "sha256sum '$APP_PATH/$APP_NAME.tar.xz' | cut -d' ' -f1")
echo "Found hash:    $ACTUAL_HASH"

if [[ "$EXPECTED_HASH" != "$ACTUAL_HASH" ]]; then
    echo "ERROR: Hash verification failed!" >&2
    echo "Hashes do not match!" >&2
    qvm-run -p "$DISP_TEMPLATE" "rm -f '$APP_PATH/$APP_NAME.tar.xz'"
    exit 1
fi

echo "Hash verification passed"

# Extract and install
echo "Extracting files..."
qvm-run -p "$DISP_TEMPLATE" "tar -xf '$APP_PATH/$APP_NAME.tar.xz' -C '$APP_PATH/' --strip-components=1"
qvm-run -p "$DISP_TEMPLATE" "rm -f '$APP_PATH/$APP_NAME.tar.xz'"

# Telegram hidden feature for TDATA portable account storage in the same directory of the app binary
qvm-run -p "$DISP_TEMPLATE" "mkdir -p $APP_PATH/TelegramForcePortable"

# Adjust user permissions
qvm-run -p -u root "$DISP_TEMPLATE" "chown -R user:user '$APP_PATH'"
qvm-run -p "$DISP_TEMPLATE" "chmod +x '$APP_PATH/$APP_NAME'"
qvm-run -p "$DISP_TEMPLATE" "chmod +x '$APP_PATH/Updater'" # Absolute reference

# Create desktop entry using bwrap rules
echo "Creating desktop entry..."

# Generate the bwrap command
BWRAP_CMD=$(get_bwrap_cmd)

# Create desktop entry
DESKTOP_ENTRY="[Desktop Entry]
Version=1.0
Type=Application
Name=$APP_NAME
Comment=New era of messaging
Exec=$BWRAP_CMD
Icon=org.telegram.desktop
Terminal=false
Categories=Chat;Network;InstantMessaging;Qt;
StartupNotify=true
MimeType=x-scheme-handler/tg;x-scheme-handler/tonsite;"

# Write desktop entry to the custom template
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
sleep 3

if ! qvm-run -p -u root "$CUSTOM_TEMPLATE" "
    mkdir -p /usr/share/applications && \
    cat > /usr/share/applications/$APP_NAME.desktop <&2
    exit 1
fi

# Updater script to use in disposable template
cat > /tmp/updater_script.sh <&2
    exit 1
fi

if [[ -z "\$dl_url" ]]; then
    echo "Error: No download URL found" >&2
    echo "Available browser_download_url entries:" >&2
    echo "\$release" | grep -o '"browser_download_url": *"[^"]*"' >&2
    exit 1
fi

echo "Downloading update from: \$dl_url"
curl --tlsv1.2 --http1.1 -L -o "\$APP_PATH/\$APP_NAME.tar.xz" "\$dl_url"

# Hash verification
echo "Verifying integrity..."

asset_name=\$(basename "\$dl_url")

# Extract the hash from API
expected_hash=\$(echo "\$release" | \\
    grep -A 30 "\"name\": *\"\$asset_name\"" | \\
    grep '"digest"' | \\
    head -n 1 | \\
    sed -n 's/.*"digest": *"sha256:\\([a-f0-9]*\\)".*/\\1/p')

if [[ -z "\$expected_hash" ]]; then
    echo "ERROR: Could not extract hash from release info!" >&2
    echo "Looking for asset: \$asset_name" >&2
    echo "Available assets:" >&2
    echo "\$release" | grep -o '"name": *"[^"]*"' | head -10 >&2
    rm -f "\$APP_PATH/\$APP_NAME.tar.xz"
    exit 1
fi

echo "Expected hash: \$expected_hash"
actual_hash=\$(sha256sum "\$APP_PATH/\$APP_NAME.tar.xz" | cut -d' ' -f1)
echo "Found hash:    \$actual_hash"

if [[ "\$expected_hash" != "\$actual_hash" ]]; then
    echo "ERROR: Hash verification failed!" >&2
    echo "Hashes do not match!" >&2
    rm -f "\$APP_PATH/\$APP_NAME.tar.xz"
    exit 1
fi

echo "Hash verification passed"

# Clean, extract and update
echo "Extracting update..."
rm -f "\$APP_PATH/\$APP_NAME" "\$APP_PATH/Updater"
tar -xf "\$APP_PATH/\$APP_NAME.tar.xz" -C "\$APP_PATH/" --strip-components=1
rm -f "\$APP_PATH/\$APP_NAME.tar.xz"

# Adjust permissions
chmod +x "\$APP_PATH/\$APP_NAME"
chmod +x "\$APP_PATH/Updater"
echo "Update complete and verified"
EOF

qvm-run -p "$DISP_TEMPLATE" "cat > /home/user/update-$APP_NAME.sh" < /tmp/updater_script.sh
qvm-run -p "$DISP_TEMPLATE" "chmod 700 /home/user/update-$APP_NAME.sh"
rm -f /tmp/updater_script.sh

qvm-shutdown --wait "$DISP_TEMPLATE"
echo "Template configuration complete"

# Step 6: Create named disposable VM instance
echo -e "\n[6/7] Creating named disposable VM instance..."
if ! qvm-check "$NAMED_DISP_VM" 2>/dev/null; then
    qvm-create --class DispVM --template "$DISP_TEMPLATE" --label red \
        --property netvm="$NET_VM" \
        --property include_in_backups=False \
        "$NAMED_DISP_VM"
    qvm-features "$NAMED_DISP_VM" appmenus-dispvm 1
else
    echo "Named disposable VM $NAMED_DISP_VM already exists, skipping creation."
fi

# Step 7: Configure menu items
echo -e "\n[7/7] Configuring menu items..."
qvm-start --skip-if-running "$CUSTOM_TEMPLATE"
qvm-sync-appmenus "$CUSTOM_TEMPLATE"
qvm-shutdown --wait "$CUSTOM_TEMPLATE"
qvm-features "$CUSTOM_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$DISP_TEMPLATE" menu-items "$APP_NAME.desktop thunar.desktop debian-xterm.desktop xfce4-terminal.desktop"
qvm-features "$NAMED_DISP_VM" menu-items "$APP_NAME.desktop thunar.desktop xfce4-terminal.desktop"

# Finalize
echo -e "\nFinish!"

Issues: icons in Telegram setup

There is no icon because we dont created it inside the folder in the user space like: /home/user/.local/share/icons/hicolor/256x256/apps/org.telegram.desktop.png.


[7/7] Configuring menu items...
(...)
whonix-telegram-template: Creating Telegram
whonix-telegram-template: Failed to get icon for Telegram: No icon received
(...)

There is no desktop entry inside the user space until the first start of the Telegram binary:/home/user/.local/share/applications/org.telegram.desktop._XXXXXXXXXXXXXX.desktop.

Note that we created the desktop entry in the root space of the custom template: /usr/share/applications/Telegram.desktop.

The issue: Telegram could overwrite the desktop entry (with the bwrap rules) and also create an werid name to track the updater binary, see this reports:

About the list of Telegram binary dependencies:


# Less strict
apt show telegram-desktop
apt-cache depends telegram-desktop

# More strict
dpkg -i telegram-desktop_4.6.5+ds-2+b1_amd64.deb

About the Telegram TDATA setup:

You could safely use Telegram account in TDATA format using the TelegramForcePortable folder. You could purchase TDATA phone number to get a login code then ensure to close all existing sessions on the account and set a 2FA password.


References

Desktop Entry and Others References

Qubes Default Disposable References

Bubblewrap References

FUSE References

Telegram References

Telegram default Icon:

:warning:Scripts not rendering correctly in the forum!:warning:

The bwrap function caused a problem with the HTML rendering, part of the code simply disappeared.

The complete scripts, just change it to .sh :

whonix-simplex-bwrap-named-disposable.log (11.1 KB)
whonix-simplex-named-disposable.log (9.6 KB)
whonix-telegram-bwrap-named-disposable.log (13.2 KB)
whonix-telegram-named-disposable.log (11.4 KB)

is telegram still able to know that you are on qubes using this method?

Yes, as mentioned here:

Send Telegram Messages over Tor with Whonix ™

But I think we could circunvent it using bwrap rules,

  • Unix Time-sharing System Namespace: --unshare-uts option when invoking bwrap.
  • Custom Hostname: --setenv

For example

bwrap --unshare-uts --setenv HOSTNAME "custom"

I did little test, this also makes Telegram display: “XEN”.


I think a Linux guru who understands namespaces could help us create a fake system using bwrap.

Xen-specific system files:

/sys/hypervisor/uuid

/proc/xen

/dev/xen

/proc/cpuinfo

Like:

  --bind /tmp/fake-proc/cpuinfo /proc/cpuinfo 

Maybe we could consider other sandbox solutions, like Podman


I found these interesting references:

There is a solution to hide Telegram hypervisor VM using AppArmor. Maybe we could use bwrap --unbind, some day I will do tests.