Browse the docs

By stack

Apps built the same way break the same way. Find the app’s stack (the .deb’s dependencies, readelf -d on the binary and strings usually tell), then go through its list: what to put in the manifest and wrapper, and what to test before the PR. Each item links to the page that explains it.

For agents

Electron

  • Runtime org.freedesktop.Platform, base: org.electronjs.Electron2.BaseApp at the same version; start with zypak-wrapper <binary> --ozone-platform-hint=auto "$@" (Folo).
  • unset ELECTRON_RUN_AS_NODE in the wrapper. A few apps exit on any ELECTRON_* variable (VS Code’s terminal exports ELECTRON_NO_ATTACH_CONSOLE); clear them all for those. Test with env -u ELECTRON_RUN_AS_NODE -u ELECTRON_NO_ATTACH_CONSOLE flatpak run … from an IDE terminal, or the test fails for a reason users never hit.
  • StartupWMClass: measured; it is the product name (desktop integration).
  • Media: --own-name=org.mpris.MediaPlayer2.chromium.* and TMPDIR for cover art, if the app plays media (media controls).
  • Saved passwords on Hyprland and Sway: --password-store=gnome-libsecret (keyrings).
  • Data in a ~/.dotdir (os.homedir()): grant it with :create or --persist (permissions).
  • A setuid chrome-sandbox in a .deb: clear the bit (chmod -R a-s).

Chromium-based browsers

  • Same base and zypak as Electron, plus CHROME_WRAPPER, ZYPAK_SPAWN_LATEST_ON_REEXEC=1 and ZYPAK_EXPOSE_WIDEVINE_PATH (zypak).
  • Check the profile directory name the browser really uses before pointing anything at it; branded builds differ from the main product.
  • StartupWMClass measured; CHROME_DESKTOP is ignored by branded builds.
  • User flags: read a <name>-flags.conf from the app’s config directory in the wrapper, the way native packages do.

Tauri (WebKitGTK)

  • Runtime org.gnome.Platform. Keep the .deb’s usr tree whole when it has usr/lib/<Name>/ (Debian packages).
  • Rendering: WEBKIT_DISABLE_DMABUF_RENDERER=1, unless the window is transparent or the app crashes on GNOME 51, then :-0 (WebKitGTK).
  • Name the launcher symlink after the app id, and use it as StartupWMClass (Flow).
  • Tray: the ayatana-stack prebuilt and xdg-run/tray-icon:create, only if it really has a tray (tray).
  • Clipboard plugin: --socket=x11 (copy and paste).
  • Single instance: grant <identifier>.SingleInstance (second instance).
  • Updater plugin: can’t be turned off from outside (updaters).
  • Use the identifier in tauri.conf.json as the app id.

Wails v3

  • Links WebKitGTK 6 and GTK 4, so org.gnome.Platform even if the .deb says webkit2gtk-4.1.
  • Aborts at start unless the app’s Linux.ApplicationID is the Flatpak id (WebKitGTK 6).
  • Tray uses the unique connection name; only the watcher grant.

GPUI (Zed-style) and other cosmic-text apps

  • A shipped fonts.conf listing every font directory, and FONTCONFIG_FILE pointing at it (fonts).
  • --talk-name=org.a11y.Bus (accessibility).
  • --device=dri for GPU rendering.

Slint

  • Check the Slint version in the binary first: up to 1.16 needs both the fonts.conf and SLINT_DEFAULT_FONT=<directory>; 1.17 and later needs only SLINT_FONT_PATH=<directory> (fonts).
  • SLINT_BACKEND=winit-software as a documented override for GPUs where rendering fails (meatshell).

Flutter

  • Usually a relocatable bundle in a tarball; the freedesktop runtime is enough. Tarballs often record a real uid: --no-same-owner (Vynody).
  • A compiled GApplication id different from the Flatpak id needs --own-name=<compiled id>, or the app exits silently (second instance).
  • Tray: --own-name=org.fluttertray.*; MPRIS uses an app-id prefix.
  • File associations: usually %F; many Flutter apps don’t parse file:// URIs (opening files).
  • Fonts work through fontconfig; no fonts.conf needed.

Qt

  • Prefer an AppImage that carries its own Qt on the freedesktop runtime over a .deb linked to the system Qt (choosing a format).
  • Bundled Qt with only the xcb plugin: --socket=x11, --unset-env=QT_QPA_PLATFORM, QT_QPA_PLATFORM="${QT_QPA_PLATFORM:-xcb}" (Mark Shot).
  • A bundled Qt may lack the portal platform theme; its file dialogs then see only the granted folders.

.NET

  • Self-contained .NET 5 and older look for OpenSSL 1.1 and abort on a runtime with only OpenSSL 3 (“No usable version of libssl”): the openssl-1.1-compat prebuilt, appended last to LD_LIBRARY_PATH. Check tfm in <App>.runtimeconfig.json; .NET 6 and later don’t need it.
  • Never delete a file listed in <App>.deps.json, even an unused native library: the host checks every entry at start and aborts if one is missing.
  • Avalonia tray: --own-name=org.kde.* (tray).

Java

  • Use the JRE upstream bundles, if it does; otherwise pin a JRE (Zulu, Temurin) as a second extra-data download and point the launcher at it.
  • user.home is the real $HOME, not redirected: settings in a ~/.app or ~/.config/<app> directory need --persist or a grant (permissions).
  • An embedded Chromium (JxBrowser, JCEF) brings the nested-sandbox problem (embedded Chromium).
  • HiDPI: -Dsun.java2d.uiScale in the app’s options file, or the app’s own setting.

Windows apps (Wine)

  • Packaged on Cellar: the vendor’s Windows installer is the managed extra-data download, and cellar-cli a second, fixed one outside the managed block. Cellar runs the installer, unmodified, into the user’s prefix on first launch.
  • Grant the folders the app works in (xdg-music for a player), not home.
  • Redirect the prefix’s desktop folder into the sandbox, or installers drop shortcuts on the host’s desktop.

Edit this page on GitHub