Repository navigation
PLATFORMS
How the updater picks the right file for a machine, and what it does with it once verification passes.
Picking an artifact happens at runtime, on the user's machine, based on OS,
CPU architecture, and (on Linux) how the app was installed in the first
place. A Debian box wants the .deb. A machine running as an AppImage wants
the new AppImage, regardless of distro.
| Platform | Preferred order |
|---|---|
| Windows | installer .exe, .msi, .zip
|
| Debian / Ubuntu |
.deb, AppImage, tarball |
| Fedora / RHEL / SUSE |
.rpm, AppImage, tarball |
| Running as an AppImage | AppImage, tarball |
| Other Linux | AppImage, tarball, then native packages |
| macOS |
.dmg, .pkg, .zip, tarball |
If a release doesn't publish a format yet, selection falls through to the
next one on the list. An app shipping only .deb today keeps working
unchanged when it adds .rpm and .AppImage later - no updater change
needed.
Override this with assets.preference (global) or assets.linux.preference
(OS-specific) in config, or force one exact asset with assets.pattern, a
regex. See
examples/config.yaml
for the full list of settings.
Handles Inno Setup and NSIS installers, MSI packages, and zips. Silent flags
default to Inno Setup's (/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP-),
since that's what most small Windows apps ship, but the installer family is
detected from the file itself where possible, so an NSIS installer gets NSIS
flags (/S) instead. Override with install.installer_args if your
installer needs something different.
MSI exit code 3010 ("success, reboot required") and 1641 count as success, not failure.
Handles .deb, .rpm, AppImage, and tarballs. Packages install through the
system package manager (dpkg/apt, rpm/dnf) rather than by unpacking,
so the package database stays consistent and the app stays uninstallable
through normal means afterward. Only .deb is in wide production use today,
but .rpm and AppImage support needs no updater change to start working -
publish the asset and platform detection picks it up.
Handles .dmg, .pkg, and zipped .app bundles. A .dmg is mounted, the
.app inside is copied into place, and the image is detached. A .pkg is
handed to the system installer.
Downloaded bundles carry the com.apple.quarantine attribute, which macOS
uses to show a scary warning or refuse to launch. That attribute is cleared
once the bundle is in place - safe to do only because the artifact's
signature was already verified before this code ever runs it (see
SECURITY.md §1).
Every archive is attacker-controlled input until its signature is verified,
and a verified archive can still be malformed. Before extraction the updater
rejects absolute paths, path traversal (../..), and symlinks resolving
outside the destination. Extraction always goes to a scratch directory first
and is copied into place only once it completes, so a corrupt archive can't
leave a half-replaced install. Details:
SECURITY.md §5.
Two modes, both first-class:
-
silent(default) - unattended, no windows, no prompts. Right when the calling app already asked the user. -
interactive- the real vendor installer UI runs and the user clicks through it. Right when a publisher wants users to see licence terms or pick components.
Set with install.mode.
The updater never asks for admin rights speculatively. It checks whether the install directory is writable by the current user, and only re-launches elevated when it isn't - a per-user install updates silently, a Program Files install prompts once, exactly when needed.
- Windows:
ShellExecuteWwith therunasverb (the UAC prompt) - Linux:
pkexecin a desktop session,sudoon a terminal - No elevation path available (headless session, no
pkexec/sudo): reported as an error rather than hanging on a password prompt nobody can answer
Set with install.elevate: auto | always | never. Full reasoning:
SECURITY.md §4.
This is most of what separates a working updater from one that corrupts an install. The sequence is always: ask the process to exit politely, wait, escalate if needed, confirm the file locks are actually gone, install, then relaunch. Skipping "wait until the executable is unlocked" is the classic Windows failure mode where install fails because the old binary is still mapped into memory.
Controlled by process.calling_pid (usually passed as --calling-pid),
process.stop_timeout, and process.relaunch.
- README - platform support
- DEV_GUIDE for calling the updater from your app
- SECURITY.md for the full threat model
QUpdateTool2.0 - see LICENSE.txt
Get started
Features
Security