Tuta AppImage - It cannot be started anymore

Dear Team,

I did install for a while Tuta (Tutanota) AppImage on the Template VM. I did download that AppImage from the Tuta Website Portal directly. I did add this file in the /home/user/Applications folder.
Then of course: chmod a+x tutanota-desktop-linux.AppImage, for making it runable.
Finally in the App VM settings, I could add this installed Tuta in the shown App Menu (via Qubes Manager). On that way after restarting the App VM it worked well. I could use Tuta App.
After a while, I cannot open that Tuta from the Qubes Application Menu anymore. No idea why. I just replaced it with the newest version of Tuta and it still does not work.
Any idea why?
Other option was, that I did solve is with Flatpak:

flatpak install flathub com.tutanota.Tutanota

After adding this installed Tuta (on based Flatpak) in the shown App Menu, it works. But now I have two “Tuta Mail” in the App Menu available from the Template VM.

Maybe you have any idea how to solve the problem with AppImage only? When it works with the AppImage again then I could remove that second installed option on based Flatpak?

Thank you for any help in advance.

Kind regards

1 Like

AppImage you install in app qube not template.
And it works.

1 Like

If you do that in a template, it won’t exist in an appvm. If it does for you, it means you did not what you described.

As tuta stopped working anyway this mean you have the appimage, try to execute it from a terminal and share us the output.

1 Like

Thanks a lot for brainstorming.

Indeed, with AppImage would not work for any App VM when AppImage has been installed on Template VM. All good then. If that is also correct. :grinning:
Or maybe it is because: chmod a+x tutanota-desktop-linux.AppImage?

I do not know how came that it does not work anymore on the App VM. I just installed Tuta from Flatpak recently and then I could select Tuta Mail app from the app list on App VM and it runs.

Just a check: On Template VM - I can run AppImage Tuta as well. It works.

I just wondered how I got Tuta Mail app in the list of the installed apps in the App VM which does not run. I am not sure if that worked once. Maybe I did install this via sudo apt install ... or with the .deb file a while ago.

Between: On Termplate VM with AppImage, this also is not working, when I want to have it on App VM:

On Template VM:

File: /home/user/.local/share/applications/tutanota-desktop.desktop

Content:

[Desktop Entry]
Name=Tuta Mail
Comment=The desktop client for Tuta Mail, the secure e-mail service.
GenericName=Mail Client
Keywords=Email;E-mail
Exec="/home/user/Applications/tutanota-desktop-linux.AppImage" %U
Terminal=false
Type=Application
Icon=tutanota-desktop
StartupWMClass=tutanota-desktop
MimeType=x-scheme-handler/mailto;
Categories=Network;
X-Tutanota-Version=266.250202.0
TryExec=/home/user/Applications/tutanota-desktop-linux.AppImage

or even on App VM:

File: /home/user/.local/share/applications/tutanota-desktop.desktop

Content:

[Desktop Entry]
Name=Tuta Mail
Comment=The desktop client for Tuta Mail, the secure e-mail service.
GenericName=Mail Client
Keywords=Email;E-mail
Exec="/home/user/Applications/tutanota-desktop-linux.AppImage" %U
Terminal=false
Type=Application
Icon=tutanota-desktop
StartupWMClass=tutanota-desktop
MimeType=x-scheme-handler/mailto;
Categories=Network;
X-Tutanota-Version=259.250108.1
TryExec=/home/user/Applications/tutanota-desktop-linux.AppImage

I assume, that this was not set up by Flatpak.

Thanks for your feedback!

1 Like

What is the command output when you execute the appimage in a terminal?

1 Like

Nothing you install in template user space is passed to appvm based on that template.
Nothing.

I’ve just installed python shutils with

pip install shutils

in a template and it’s installed in ‘~/.local/lib/python-3.12/site-packages/’ and it’s not visible in appvm based on that template.

So, for app to be visible in appvm you either install it in root in template or in user in app vm.

For appimages installed in ‘~/’ in appvm with .desktop file in ‘~/.local/share/aplications/’ you run in dom0 terminal:

qvm-sync-appmenus yourAppVMname

‘yourAppVMname’ in question must be running because dom0 must read .desktop file from it.

2 Likes

@solene Want to run this AppImage on Template VM?

1 Like

This would not make sense, templates do not have internet.

Try in an appvm where you want to use it

1 Like

I do install some needed apps on a Template VM for adding specific installed apps in different App VMs. Therefore I use Internet connection on a Template VM. It is fine. :slight_smile:
Between: I did try to run the downloaded Tuta AppImage in an Template VM and it works.

1 Like

Hi @solene Between: Why do you think, that it would not make sense? Is there any reason that Template VMs should not have Internet connection?

1 Like

There is a documentation entry about it: Why don’t template have network access?

2 Likes

@solene Indeed. Now I do understand it. Thanks for letting me know this. This also helps me to get it.
Link: How to install software | Qubes OS

1 Like

There is a http proxy you can use from a template to download files from the Internet, the template can get in touch with the proxy through Xen vchans, so it does not need to be connected to the Internet.

1 Like

@solene Is that the documentation about how to setup proxy for a cloned new template which I can switch to Internet with firewalls (sys-firewall) and back to no network (default / none): How to install software | Qubes OS.
Another question: When will it happen that I have proxy?

1 Like

I’m sorry I don’t understand what you mean. Templates should never be connected to the internet, they have a proxy by default to be able to download files from the internet without network access.

2 Likes

Also as the topic is about Tuta’s appimage, you should get the file in an appvm and use it from there.

The appimage has a self update mechanism that replace the file in place, it won’t work if you install it in a template where the user can’t modify the file.

2 Likes

That is fine. Thanks for letting me know about AppImage situation.

1 Like

@solene - Sorry for my confusing. :grinning: I did read the Qubes OS documentation (especially the part: “How to install software”) again and indeed there is a proxy by default in the Template VM (for debian-12-xfce) pre-configured. So I also did test it, on based: “Installing software from default repositories”. All good. I should install only officially apps from those Debian default repositories.

Between: I hope it is fine when I did install Brave there? With this commands:

sudo apt install curl
sudo curl -fsSLo /usr/share/keyrings/brave-browser-archive-keyring.gpg https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg] https://brave-browser-apt-release.s3.brave.com/ stable main"|sudo tee /etc/apt/sources.list.d/brave-browser-release.list
sudo apt update
sudo apt install brave-browser

And I also do understand the basis of Qubes OS. That is really great! Thank you for making me aware of this. :+1:

Good to know that on Template VM (debian-12-xfce) is proxy by default pre-configured and also available. I just was wondering when I did run there with this command: ping www.startpage.com in very beginning, so, no response, it was / is the failure failed in name resolution (because of proxy). :grinning:

What I have is now: A new Template VM cloned from (debian-12-xfce) which has a new name of the Template VM and this can be connected with Network access. See also the documentation: How to install software | Qubes OS, part: “Installing software from other sources” which can be risky. :ok_hand:

Feel free to correct me when I am wrong here.

1 Like
install_tutanota_appimage_bwrap.sh

#!/usr/bin/env bash

######################################################################
# File Name    : install_tutanota_appimage_bwrap.sh
# Description  : This script automates the setup of an application.
#                It downloads the latest version from a specified source
#                using curl, installs it to the user's home directory,
#                creates directories, provides an updater script and a
#                remover script. It also runs the application inside a
#                custom bubblewrap sandbox.
#                NOTE: 1) When you first launch the keyring, you'll be
#                asked to set a password to secure store your credentials
#                locally.
# Dependencies : debian-based system, curl, jq, fuse, libfuse2, gnome-keyring, bubblewrap
# Usage        : • Make the script executable with
#                  chmod +x install_tutanota_appimage_bwrap.sh
#                • Run the script with:
#                  bash install_tutanota_appimage_bwrap.sh
#                • To update the AppImage, run:
#                  bash $HOME/.local/share/tutanota/update.sh
#                • To remove the AppImage, run:
#                  bash $HOME/.local/share/tutanota/remove.sh
# Author       : Me and the bois
# License      : Free of charge, no warranty
# Last edited  : 2026-03-07
######################################################################

# Safety check
set -eu

# Define variables
APP_NAME_LOWER="tutanota-desktop"    # For desktop entry and script names
APP_NAME_UPPER="tutanota-desktop"    # For config directories
SOURCE_URL="https://api.github.com/repos/tutao/tutanota/releases"
HOME_DIR="$HOME/.local/share/$APP_NAME_UPPER"
APPIMAGE_FILE="$HOME_DIR/$APP_NAME_LOWER.AppImage"
DESKTOP_ENTRY="$HOME/.local/share/applications/$APP_NAME_LOWER.desktop"

# Function to generate the bwrap command for desktop entry
get_bwrap_cmd() {
  local DISPLAY_VALUE="${DISPLAY:-:0}"
  local USER_ID=$(id -u)
  local XDG_RUNTIME_DIR="/run/user/$USER_ID"

  # Bubblewrap rules as string for readability and proper quoting (change as needed)
  local cmd="unshare -pfr --user --mount --kill-child bwrap"
  cmd="$cmd --dev-bind /dev /dev"
  cmd="$cmd --die-with-parent"
  cmd="$cmd --new-session"
  cmd="$cmd --hostname host"
  cmd="$cmd --unshare-all"
  cmd="$cmd --share-net"
  cmd="$cmd --proc /proc"
  cmd="$cmd --ro-bind /bin /bin"
  cmd="$cmd --ro-bind /etc/alternatives /etc/alternatives"
  cmd="$cmd --ro-bind /etc/fonts /etc/fonts"
  cmd="$cmd --ro-bind /etc/resolv.conf /etc/resolv.conf"
  cmd="$cmd --ro-bind /etc/ssl /etc/ssl"
  cmd="$cmd --ro-bind /lib /lib"
  cmd="$cmd --ro-bind /lib64 /lib64"
  cmd="$cmd --ro-bind /sbin /sbin"
  cmd="$cmd --ro-bind /sys/dev/char /sys/dev/char"
  cmd="$cmd --ro-bind /sys/devices /sys/devices"
  cmd="$cmd --ro-bind /usr /usr"
  cmd="$cmd --ro-bind /usr/lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu"
  cmd="$cmd --ro-bind /usr/lib/x86_64-linux-gnu/dri /usr/lib/x86_64-linux-gnu/dri"
  cmd="$cmd --ro-bind /usr/share/fonts /usr/share/fonts"
  cmd="$cmd --ro-bind /usr/share/xdg-desktop-portal /usr/share/xdg-desktop-portal"
  cmd="$cmd --setenv DISPLAY \"$DISPLAY_VALUE\""
  cmd="$cmd --setenv XDG_DATA_HOME \"$HOME/.local/share\""
  cmd="$cmd --setenv XDG_CONFIG_HOME \"$HOME/.config\""
  cmd="$cmd --setenv XDG_CACHE_HOME \"$HOME/.cache\""
  cmd="$cmd --setenv XDG_RUNTIME_DIR \"$XDG_RUNTIME_DIR\""
  cmd="$cmd --setenv DBUS_SESSION_BUS_ADDRESS \"unix:path=$XDG_RUNTIME_DIR/bus\""
  cmd="$cmd --tmpfs /run"
  cmd="$cmd --tmpfs /tmp"
  cmd="$cmd --bind /tmp/.X11-unix /tmp/.X11-unix"
  cmd="$cmd --bind /var/tmp /var/tmp"
  cmd="$cmd --bind /etc/mtab /etc/mtab"
  cmd="$cmd --bind \"$XDG_RUNTIME_DIR\" \"$XDG_RUNTIME_DIR\""
  cmd="$cmd --bind \"$XDG_RUNTIME_DIR/bus\" \"$XDG_RUNTIME_DIR/bus\""
  cmd="$cmd --bind \"$XDG_RUNTIME_DIR/gvfsd\" \"$XDG_RUNTIME_DIR/gvfsd\""
  cmd="$cmd --bind \"$XDG_RUNTIME_DIR/gvfs\" \"$XDG_RUNTIME_DIR/gvfs\""
  cmd="$cmd --bind \"$HOME/Downloads\" \"$HOME/Downloads\""
  cmd="$cmd --bind \"$HOME/.local/share/$APP_NAME_UPPER\" \"$HOME/.local/share/$APP_NAME_UPPER\""
  cmd="$cmd --bind \"$HOME/.config/$APP_NAME_UPPER\" \"$HOME/.config/$APP_NAME_UPPER\""
  cmd="$cmd --bind \"$HOME/.cache/$APP_NAME_UPPER\" \"$HOME/.cache/$APP_NAME_UPPER\""
  cmd="$cmd --bind \"$HOME/.cache/$APP_NAME_UPPER\" \"$HOME/.$APP_NAME_LOWER\""
  cmd="$cmd --bind \"$APPIMAGE_FILE\" \"$APPIMAGE_FILE\""
  cmd="$cmd /bin/sh -c '\"$APPIMAGE_FILE\" --no-sandbox'"

  echo "$cmd"
}

# Function to check dependencies
check_dependencies() {
  local dependencies=("curl" "jq" "fusermount" "gnome-keyring" "bwrap")
  for cmd in "${dependencies[@]}"; do
    if ! command -v "$cmd" > /dev/null 2>&1; then
      echo "Error: $cmd is not installed. Please install it first."
      exit 1
    fi
  done

  # Check for a running secret service via D-Bus
  gnome-keyring-daemon --start >/dev/null 2>&1
  sleep 3
  if ! dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames 2>/dev/null | grep -q org.freedesktop.secrets; then
        echo "WARNING: No secret service (org.freedesktop.secrets) is running on the session D-Bus."
        echo "Password saving may fail."
  fi
  echo "All dependencies are checked."
}

# Function to create directories
create_directories() {
  mkdir -p "$HOME_DIR"
  mkdir -p "$HOME/.local/share/applications"
  mkdir -p "$HOME/.local/share/$APP_NAME_UPPER"
  mkdir -p "$HOME/.config/$APP_NAME_UPPER"
  mkdir -p "$HOME/.cache/$APP_NAME_UPPER"
}

# Function to download the latest AppImage with hash verification
download_appimage() {
  echo "Fetching latest release information..."

  # Fetch latest release information
  RELEASE_JSON=$(curl -sL -H "Accept: application/vnd.github.v3+json" "$SOURCE_URL")

  # 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
    exit 1
  fi

  # Get the latest release with jq
  LATEST_RELEASE=$(echo "$RELEASE_JSON" | jq -r '[.[] | select(.assets | length > 0)][0]')

  # Extract the AppImage asset
  APPIMAGE_ASSET=$(echo "$LATEST_RELEASE" | jq -r '.assets[] | select(.name | test(".*linux.*\\.AppImage$"))')

  if [[ "$APPIMAGE_ASSET" == "null" || -z "$APPIMAGE_ASSET" ]]; then
    echo "Error: Could not find the package in latest release"
    exit 1
  fi

  # Extract download URL and hash
  VERSION_URL=$(echo "$APPIMAGE_ASSET" | jq -r '.browser_download_url')
  EXPECTED_HASH=$(echo "$APPIMAGE_ASSET" | jq -r '.digest // ""' | sed 's/sha256://')
  ASSET_NAME=$(echo "$APPIMAGE_ASSET" | jq -r '.name')

  echo "Found: $ASSET_NAME"
  echo "Downloading from: $VERSION_URL"

  # Download the AppImage
  if ! curl -L -o "$APPIMAGE_FILE" "$VERSION_URL"; then
    echo "ERROR: Download failed" >&2
    exit 1
  fi

  # Hash verification
  echo "Verifying integrity..."

  if [[ -n "$EXPECTED_HASH" ]]; then
    echo "Expected hash: $EXPECTED_HASH"
    ACTUAL_HASH=$(sha256sum "$APPIMAGE_FILE" | cut -d' ' -f1)
    echo "Actual hash:   $ACTUAL_HASH"

    if [[ "$EXPECTED_HASH" != "$ACTUAL_HASH" ]]; then
      echo "ERROR: Hash verification failed!" >&2
      echo "Hashes do not match!" >&2
      rm -f "$APPIMAGE_FILE"
      exit 1
    fi
    echo "Hash verification passed"
  else
    echo "WARNING: No hash provided in release data, exiting..." >&2
    exit 1
  fi

  chmod +x "$APPIMAGE_FILE"
  echo "Download complete"
}

# Function to configure desktop entry
configure_files() {
    echo "Configuring desktop entry..."

    # Generate the bwrap command
    BWRAP_CMD=$(get_bwrap_cmd)

    # Create directory for desktop entry
    mkdir -p "$HOME/.local/share/applications"

    # Define desktop entry path
    DESKTOP_ENTRY_SRC="$HOME/.local/share/applications/$APP_NAME_LOWER.desktop"

    # Write the desktop entry
    cat > "$DESKTOP_ENTRY_SRC" <<EOF
[Desktop Entry]
Version=1.0
Type=Application
Name=$APP_NAME_UPPER
Exec=$BWRAP_CMD
Icon=$APP_NAME_LOWER
Terminal=false
EOF

  # Copy to Desktop entry
  if [[ -d "$HOME/Desktop" ]]; then
    cp "$DESKTOP_ENTRY" "$HOME/Desktop/"
    update-desktop-database "$HOME/.local/share/applications" 2>/dev/null || true
  fi

  echo "Desktop entry configured"
}

# Create mimeapps.list with default MIME type associations
configure_mimeapps_list() {
echo "Configuring MIME type associations..."

  # Array of MIME types and their desktop files
  declare -A MIME_SETTINGS=(
  ["x-scheme-handler/mailto"]="tutanota-desktop.desktop"
  )

  # Set each default application
  for mime_type in "${!MIME_SETTINGS[@]}"; do
    desktop_file="${MIME_SETTINGS[$mime_type]}"
      xdg-mime default "$desktop_file" "$mime_type" >/dev/null 2>&1
  done

  echo "Default associations configured"
}

# Function to create updater script
create_updater_script() {
  cat > "$HOME_DIR/update.sh" <<EOF
#!/bin/sh
set -u

APP_NAME_LOWER="$APP_NAME_LOWER"
APP_NAME_UPPER="$APP_NAME_UPPER"
SOURCE_URL="$SOURCE_URL"
HOME_DIR="$HOME_DIR"
APPIMAGE_FILE="$APPIMAGE_FILE"

echo "Checking for updates..."

# Check dependencies
if ! command -v jq > /dev/null 2>&1; then
  echo "Error: jq is required but not installed."
  exit 1
fi

# Fetch latest release information
RELEASE_JSON=\$(curl -sL -H "Accept: application/vnd.github.v3+json" "\$SOURCE_URL")

# Get the latest release with jq
LATEST_RELEASE=\$(echo "\$RELEASE_JSON" | jq -r '[.[] | select(.assets | length > 0)][0]')

# Extract the AppImage asset
APPIMAGE_ASSET=\$(echo "\$LATEST_RELEASE" | jq -r '.assets[] | select(.name | test(".*linux.*\\\\.AppImage$"))')

if [ "\$APPIMAGE_ASSET" = "null" ] || [ -z "\$APPIMAGE_ASSET" ]; then
  echo "Error: Could not find the package in latest release"
  exit 1
fi

# Extract download URL and hash using jq
VERSION_URL=\$(echo "\$APPIMAGE_ASSET" | jq -r '.browser_download_url')
EXPECTED_HASH=\$(echo "\$APPIMAGE_ASSET" | jq -r '.digest // ""' | sed 's/sha256://')
ASSET_NAME=\$(echo "\$APPIMAGE_ASSET" | jq -r '.name')

echo "Found: \$ASSET_NAME"

echo "Downloading from: \$VERSION_URL"

# Ask for confirmation
printf "\n"
printf "Do you want to update? (y/N): "
read -r REPLY
printf "\n"
if [ ! "\$REPLY" = "y" ] && [ ! "\$REPLY" = "Y" ]; then
  echo "Update cancelled."
  exit 0
fi

# Create temp directory
TEMP_DIR="\$(mktemp -d)"
cd "\$TEMP_DIR" || exit 1

# Download the new AppImage
curl -L -o "\$APP_NAME_LOWER.AppImage" "\$VERSION_URL" || { echo "Error: Download failed"; rm -rf "\$TEMP_DIR"; exit 1; }

# Hash verification
if [ -n "\$EXPECTED_HASH" ]; then
  echo "Verifying integrity..."
  ACTUAL_HASH=\$(sha256sum "\$APP_NAME_LOWER.AppImage" | cut -d' ' -f1)

  if [ "\$EXPECTED_HASH" != "\$ACTUAL_HASH" ]; then
    echo "Error: Hash verification failed! Update aborted."
    rm -f "\$APP_NAME_LOWER.AppImage"
    rm -rf "\$TEMP_DIR"
    exit 1
  fi
  echo "Hash verification passed"
fi

# Replace with new AppImage
mv "\$APP_NAME_LOWER.AppImage" "\$APPIMAGE_FILE"
chmod +x "\$APPIMAGE_FILE"

# Cleanup
rm -rf "\$TEMP_DIR"

echo "Update completed successfully!"
EOF
  chmod +x "$HOME_DIR/update.sh"
}

# Function to create the remover script
create_remover_script() {
  cat > "$HOME_DIR/remove.sh" <<EOF
#!/bin/sh
set -e
echo "Removing application..."
read -p "Are you sure? (y/N): " -n 1 -r
echo
if [[ ! \$REPLY =~ ^[Yy]\$ ]]; then
  echo "Cancelled."
  exit 0
fi
rm -f "\$HOME/Desktop/$APP_NAME_LOWER.desktop"
rm -f "\$HOME/.local/share/applications/$APP_NAME_LOWER.desktop"
rm -rf "$HOME_DIR"
echo "Application removed."
echo "Note: Config files in ~/.config/$APP_NAME_UPPER and cache in ~/.cache/$APP_NAME_UPPER were kept."
EOF
  chmod +x "$HOME_DIR/remove.sh"
}

# Main execution
echo -e "Starting installation...\n"

check_dependencies
create_directories
download_appimage
configure_files
configure_mimeapps_list
create_updater_script
create_remover_script

echo -e "\nInstallation complete!"

1 Like