Skip to content

Releases: ventura8/Ubuntu-Hello

v1.2.8

Choose a tag to compare

@github-actions github-actions released this 08 Oct 21:24
c4556fc

Ubuntu Hello v1.2.8 - Keyring Repair

This release adds a one-click repair for a login keyring that stopped accepting your password, and detects the problem in Settings.


🔧 What's in this release

  • 🔑 Repair a login keyring that stopped unlocking. A login keyring can get damaged, or its password can drift away from your login password (seen after another app wrote to the keyring while it was open). Face login still works, but the keyring then rejects every password: the one Ubuntu Hello hands over and the one you type. Until now that failed silently and left you with keyring prompts. New sudo ubuntu-hello keyring repair stores your current password first, then renames the old login.keyring to login.keyring.broken-<date> (kept, never deleted). Your next login creates a fresh keyring that unlocks automatically. Passwords saved in the old keyring need to be entered again. GNOME Keyring only; KWallet users are pointed to System Settings.
  • ⚠️ Settings detects it for you. When auto-unlock is on and your login keyring is still locked in your running session, the Keyring tab shows a warning and a Repair Keyring button that runs the same repair after checking your password.
  • 📖 Troubleshooting. The README explains how to tell a broken keyring from a face-login problem: if your typed password is rejected too, the keyring is the cause.
  • 🧹 Code quality. Clears the SonarCloud findings in the PAM helper wait loop. The signal-retry logic is now one small function, and a new test interrupts a real wait with a signal to prove it retries.

Nine new translatable strings, translated in all 98 languages. No new config keys.


📥 Installation (from Release assets)

Verify downloads against SHA256SUMS on the Release page before installing. Download this release's assets into an empty/clean directory before running these commands — the wildcards below match by filename pattern, and leftover assets from another version in the same directory can make a package manager see multiple matches (or, for the AppImage command, pass a second matched file as a stray argument).

Format Command
Debian (amd64 + arm64) sudo apt install ./ubuntu-hello_*_$(dpkg --print-architecture).deb ./ubuntu-hello-gtk_*_$(dpkg --print-architecture).deb
Fedora (amd64 + arm64) sudo dnf install ./ubuntu-hello-[0-9]*fc44*.$(uname -m).rpm ./ubuntu-hello-gtk-*fc44*.$(uname -m).rpm
openSUSE (amd64 + arm64) sudo zypper install ./ubuntu-hello-[0-9]*lp160*.$(uname -m).rpm ./ubuntu-hello-gtk-*lp160*.$(uname -m).rpm
AppImage (amd64 + arm64) chmod +x Ubuntu-Hello-*-$(uname -m).AppImage && ./Ubuntu-Hello-*-$(uname -m).AppImage --install
Arch (amd64 only) sudo pacman -U ./ubuntu-hello-[0-9]*.pkg.tar.zst ./ubuntu-hello-gtk-*.pkg.tar.zst
Snap (amd64 only) sudo snap install --dangerous --classic ubuntu-hello_*_amd64.snap && sudo snap run ubuntu-hello.host-install
Flatpak (amd64 only) flatpak install --user ./com.github.ventura8.UbuntuHello-*.flatpak && flatpak run --command=ubuntu-hello-host-install com.github.ventura8.UbuntuHello

PPA (Ubuntu 22.04, 24.04 and 26.04). On 22.04 use the PPA: the .deb assets above are built on 26.04:

sudo add-apt-repository ppa:ventura8/ubuntu-hello && sudo apt update && sudo apt install ubuntu-hello

Optional source install: download install.sh from the v1.2.8 tag, review it, then run locally — do not pipe a moving branch URL directly to bash.

Full Changelog: v1.2.7...v1.2.8

v1.2.7

Choose a tag to compare

@github-actions github-actions released this 06 Oct 13:43
451aad1

Ubuntu Hello v1.2.7 - No More Login Hangs on a Stuck TPM

This is a reliability release: login can no longer hang on a stuck TPM or keyring helper when keyring/KWallet unlocking is enabled.


🔧 What's in this release

  • ⏱️ Login can no longer hang on a stuck keyring helper. When keyring/KWallet unlocking is enabled, the PAM module runs root helpers: the tpm2_* steps that unseal the keyring password after a face match, and keyring enable, which caches the password at login. It waited for them with no time limit, so a hung TPM device or resource manager froze the login. Each helper now has 30 seconds. It runs in its own process group, and if it is still running at the deadline the whole group is killed. Login then carries on without the keyring step: face auth is unaffected, and the keyring is just not unlocked or the password not cached that time.
  • 🧪 Tests. pam-main covers a helper that never exits, one that prints and then hangs (its partial output is kept), output held open by a background child, and a slow helper inside the limit.

No new config keys and no new or changed translatable strings.


📥 Installation (from Release assets)

Verify downloads against SHA256SUMS on the Release page before installing. Download this release's assets into an empty/clean directory before running these commands — the wildcards below match by filename pattern, and leftover assets from another version in the same directory can make a package manager see multiple matches (or, for the AppImage command, pass a second matched file as a stray argument).

Format Command
Debian (amd64 + arm64) sudo apt install ./ubuntu-hello_*_$(dpkg --print-architecture).deb ./ubuntu-hello-gtk_*_$(dpkg --print-architecture).deb
Fedora (amd64 + arm64) sudo dnf install ./ubuntu-hello-[0-9]*fc44*.$(uname -m).rpm ./ubuntu-hello-gtk-*fc44*.$(uname -m).rpm
openSUSE (amd64 + arm64) sudo zypper install ./ubuntu-hello-[0-9]*lp160*.$(uname -m).rpm ./ubuntu-hello-gtk-*lp160*.$(uname -m).rpm
AppImage (amd64 + arm64) chmod +x Ubuntu-Hello-*-$(uname -m).AppImage && ./Ubuntu-Hello-*-$(uname -m).AppImage --install
Arch (amd64 only) sudo pacman -U ./ubuntu-hello-[0-9]*.pkg.tar.zst ./ubuntu-hello-gtk-*.pkg.tar.zst
Snap (amd64 only) sudo snap install --dangerous --classic ubuntu-hello_*_amd64.snap && sudo snap run ubuntu-hello.host-install
Flatpak (amd64 only) flatpak install --user ./com.github.ventura8.UbuntuHello-*.flatpak && flatpak run --command=ubuntu-hello-host-install com.github.ventura8.UbuntuHello

PPA (Ubuntu 22.04, 24.04 and 26.04). On 22.04 use the PPA: the .deb assets above are built on 26.04:

sudo add-apt-repository ppa:ventura8/ubuntu-hello && sudo apt update && sudo apt install ubuntu-hello

Optional source install: download install.sh from the v1.2.7 tag, review it, then run locally — do not pipe a moving branch URL directly to bash.

Full Changelog: v1.2.6...v1.2.7

v1.2.6

Choose a tag to compare

@github-actions github-actions released this 06 Oct 09:23
09806ea

Ubuntu Hello v1.2.6 - Code Quality Cleanup

This is a maintenance release: it clears the last open SonarCloud issue in the PAM module and fixes a test that failed on arm64. Nothing changes for users.


🔧 What's in this release

  • 🧹 SonarCloud: no open issues. The root-helper function added in v1.2.5 (run_root_helper() in the PAM module) was flagged as too complex: a cognitive complexity of 40 against SonarCloud's limit of 25. It is now split into small helpers for pipe setup, stdin/stdout wiring, the uid-0 spawn, writing the input, reading the output and waiting for the child. Behaviour is unchanged.
  • 🧪 Test fix for arm64. The arm64 release build runs under qemu emulation, where starting a missing helper binary starts the child, which then exits 127, instead of failing at spawn. The test now accepts either way, because both mean the helper failed. This made the v1.2.5 GitHub Release build fail on arm64. The fix was released by re-tagging v1.2.5.

No product behaviour changes, no new config keys and no new or changed translatable strings. The v1.2.5 security fix is unchanged.


📥 Installation (from Release assets)

Verify downloads against SHA256SUMS on the Release page before installing. Download this release's assets into an empty/clean directory before running these commands — the wildcards below match by filename pattern, and leftover assets from another version in the same directory can make a package manager see multiple matches (or, for the AppImage command, pass a second matched file as a stray argument).

Format Command
Debian (amd64 + arm64) sudo apt install ./ubuntu-hello_*_$(dpkg --print-architecture).deb ./ubuntu-hello-gtk_*_$(dpkg --print-architecture).deb
Fedora (amd64 + arm64) sudo dnf install ./ubuntu-hello-[0-9]*fc44*.$(uname -m).rpm ./ubuntu-hello-gtk-*fc44*.$(uname -m).rpm
openSUSE (amd64 + arm64) sudo zypper install ./ubuntu-hello-[0-9]*lp160*.$(uname -m).rpm ./ubuntu-hello-gtk-*lp160*.$(uname -m).rpm
AppImage (amd64 + arm64) chmod +x Ubuntu-Hello-*-$(uname -m).AppImage && ./Ubuntu-Hello-*-$(uname -m).AppImage --install
Arch (amd64 only) sudo pacman -U ./ubuntu-hello-[0-9]*.pkg.tar.zst ./ubuntu-hello-gtk-*.pkg.tar.zst
Snap (amd64 only) sudo snap install --dangerous --classic ubuntu-hello_*_amd64.snap && sudo snap run ubuntu-hello.host-install
Flatpak (amd64 only) flatpak install --user ./com.github.ventura8.UbuntuHello-*.flatpak && flatpak run --command=ubuntu-hello-host-install com.github.ventura8.UbuntuHello

PPA (Ubuntu 22.04, 24.04 and 26.04). On 22.04 use the PPA: the .deb assets above are built on 26.04:

sudo add-apt-repository ppa:ventura8/ubuntu-hello && sudo apt update && sudo apt install ubuntu-hello

Optional source install: download install.sh from the v1.2.6 tag, review it, then run locally — do not pipe a moving branch URL directly to bash.

Full Changelog: v1.2.5...v1.2.6

v1.2.5

Choose a tag to compare

@github-actions github-actions released this 05 Oct 17:37
dbde1f7

Ubuntu Hello v1.2.5 - Root Helper Hardening (Security Fix)

This is a security release: it fixes a local privilege escalation in the keyring/KWallet unlocking path. Anyone with keyring unlocking enabled should upgrade.


🔧 What's in this release

  • 🔒 Security fix: root code execution through the keyring helpers. When keyring/KWallet unlocking was enabled, the PAM module started two root helpers through popen(): the TPM unseal steps after a face match, and ubuntu-hello keyring enable from pam_sm_setcred. That runs /bin/sh as uid 0 with the calling user's environment. The setuid program (su, sudo, …) has already had glibc remove the loader variables such as LD_PRELOAD, but PATH, PYTHONPATH and BASH_ENV were still there. The TPM step ran tpm2_* by bare name, so the shell found it through the caller's PATH. The ubuntu-hello wrapper ran Python without -E, so PYTHONPATH applied. Where /bin/sh is bash, BASH_ENV was read too, because the module had already set both uids to 0. A local user with keyring unlocking enabled could set one of these, run su $USER, authenticate with their own password or face, and have their code run as root without any sudo rights.
    • Every root helper now starts through one function, run_root_helper(). It uses posix_spawn with an absolute path and a fixed argument list, with no shell, and passes only the scrubbed environment compare.py already used: a fixed PATH plus checked locale variables.
    • The TPM unseal is now three direct runs of /usr/bin/tpm2_createprimary, tpm2_load and tpm2_unseal. The temporary context files are removed with unlink().
    • Sealing the keyring password at login runs python3 -E -s cli.py keyring enable -U <user> directly, with the password on stdin, instead of the /bin/sh ubuntu-hello wrapper. The username no longer passes through a shell string.
  • 🧪 Tests. pam-main now checks that LD_PRELOAD and PYTHONPATH set in the parent arrive empty in a root helper. It also runs the TPM unseal chain against fake tpm2_* tools, checking each step's arguments, failures and context-file cleanup, and runs keyring enable against a fake CLI. The keyring paths of try_set_keyring_authtok and setcred's handling of the helper's result are covered too.

Upgrade if you use keyring/KWallet unlocking. Nothing changes in how face login or keyring unlocking work: no new config keys and no new or changed translatable strings.


📥 Installation (from Release assets)

Verify downloads against SHA256SUMS on the Release page before installing. Download this release's assets into an empty/clean directory before running these commands — the wildcards below match by filename pattern, and leftover assets from another version in the same directory can make a package manager see multiple matches (or, for the AppImage command, pass a second matched file as a stray argument).

Format Command
Debian (amd64 + arm64) sudo apt install ./ubuntu-hello_*_$(dpkg --print-architecture).deb ./ubuntu-hello-gtk_*_$(dpkg --print-architecture).deb
Fedora (amd64 + arm64) sudo dnf install ./ubuntu-hello-[0-9]*fc44*.$(uname -m).rpm ./ubuntu-hello-gtk-*fc44*.$(uname -m).rpm
openSUSE (amd64 + arm64) sudo zypper install ./ubuntu-hello-[0-9]*lp160*.$(uname -m).rpm ./ubuntu-hello-gtk-*lp160*.$(uname -m).rpm
AppImage (amd64 + arm64) chmod +x Ubuntu-Hello-*-$(uname -m).AppImage && ./Ubuntu-Hello-*-$(uname -m).AppImage --install
Arch (amd64 only) sudo pacman -U ./ubuntu-hello-[0-9]*.pkg.tar.zst ./ubuntu-hello-gtk-*.pkg.tar.zst
Snap (amd64 only) sudo snap install --dangerous --classic ubuntu-hello_*_amd64.snap && sudo snap run ubuntu-hello.host-install
Flatpak (amd64 only) flatpak install --user ./com.github.ventura8.UbuntuHello-*.flatpak && flatpak run --command=ubuntu-hello-host-install com.github.ventura8.UbuntuHello

PPA (Ubuntu 22.04, 24.04 and 26.04). On 22.04 use the PPA: the .deb assets above are built on 26.04:

sudo add-apt-repository ppa:ventura8/ubuntu-hello && sudo apt update && sudo apt install ubuntu-hello

Optional source install: download install.sh from the v1.2.5 tag, review it, then run locally — do not pipe a moving branch URL directly to bash.

Full Changelog: v1.2.4...v1.2.5

v1.2.4

Choose a tag to compare

@github-actions github-actions released this 03 Oct 13:01
2b40dc0

Ubuntu Hello v1.2.4 - Coverage Above 90% in SonarQube Cloud

This is a quality release: SonarQube Cloud coverage goes from 87% to over 90%, with new tests for the PAM module's Enter-key device and theme detection. Nothing changes for users.


🔧 What's in this release

  • 🔎 SonarQube Cloud coverage above 90%. Sonar reported 87%, held down by the C++ PAM module's branch coverage (38%). Most of those branches were not decisions in the source: gcc adds an exception-unwinding edge for every call that may throw, and no test can choose to take one. The C++ report now leaves out those and the branches gcc proves unreachable (gcovr --exclude-throw-branches --exclude-unreachable-branches), which drops about 300 of them. Overall coverage is now 90.7%.
  • 🧪 New tests.
    • The PAM module's Enter-key device (enter_device.cc, used by the input workaround) had no tests. pam-main now runs it with libevdev's uinput calls stubbed: a refused /dev/uinput, a full Enter press, and a failed write at each step. It goes from 0% to 100%.
    • Theme detection: the light and "no answer" results for KDE, XFCE, MATE and LXQt, the GTK icon-theme fallback, and ThemeWatcher surviving a missing monitor tool, a broken pipe and errors on shutdown.

No product code changes, no new config keys and no new or changed translatable strings.


📥 Installation (from Release assets)

Verify downloads against SHA256SUMS on the Release page before installing. Download this release's assets into an empty/clean directory before running these commands — the wildcards below match by filename pattern, and leftover assets from another version in the same directory can make a package manager see multiple matches (or, for the AppImage command, pass a second matched file as a stray argument).

Format Command
Debian (amd64 + arm64) sudo apt install ./ubuntu-hello_*_$(dpkg --print-architecture).deb ./ubuntu-hello-gtk_*_$(dpkg --print-architecture).deb
Fedora (amd64 + arm64) sudo dnf install ./ubuntu-hello-[0-9]*fc44*.$(uname -m).rpm ./ubuntu-hello-gtk-*fc44*.$(uname -m).rpm
openSUSE (amd64 + arm64) sudo zypper install ./ubuntu-hello-[0-9]*lp160*.$(uname -m).rpm ./ubuntu-hello-gtk-*lp160*.$(uname -m).rpm
AppImage (amd64 + arm64) chmod +x Ubuntu-Hello-*-$(uname -m).AppImage && ./Ubuntu-Hello-*-$(uname -m).AppImage --install
Arch (amd64 only) sudo pacman -U ./ubuntu-hello-[0-9]*.pkg.tar.zst ./ubuntu-hello-gtk-*.pkg.tar.zst
Snap (amd64 only) sudo snap install --dangerous --classic ubuntu-hello_*_amd64.snap && sudo snap run ubuntu-hello.host-install
Flatpak (amd64 only) flatpak install --user ./com.github.ventura8.UbuntuHello-*.flatpak && flatpak run --command=ubuntu-hello-host-install com.github.ventura8.UbuntuHello

PPA (Ubuntu 22.04, 24.04 and 26.04). On 22.04 use the PPA: the .deb assets above are built on 26.04:

sudo add-apt-repository ppa:ventura8/ubuntu-hello && sudo apt update && sudo apt install ubuntu-hello

Optional source install: download install.sh from the v1.2.4 tag, review it, then run locally — do not pipe a moving branch URL directly to bash.

Full Changelog: v1.2.3...v1.2.4

v1.2.3

Choose a tag to compare

@github-actions github-actions released this 28 Sep 09:59
5cbc882

Ubuntu Hello v1.2.3 - Ubuntu 22.04 Support, PAM and CLI Fixes, Full Coverage in Sonar

This release adds support for Ubuntu 22.04 LTS (jammy): the PPA now publishes a jammy build (#17), and a new CI cell and the booted-OS tier keep it working. Tests written for this port found and fixed three real bugs in the PAM module and the CLI, including ubuntu-hello disable emptying config.ini. SonarQube Cloud now sees the C++ PAM module's coverage too.


🔧 What's in this release

  • 🐧 New: Ubuntu 22.04 (jammy) support (#17). On 22.04, apt update failed with "does not have a Release file" because the PPA only built noble and resolute. It now builds jammy too. Jammy ships meson 0.61, GTK 4.6, PyGObject 3.42 and Python 3.10, so:
    • The build no longer needs meson 0.64. install_data(preserve_path:) became a per-file install that keeps the cli/, recorders/ and rubberstamps/ layout.
    • Settings and the setup wizard load on GTK 4.6. GtkPicture:content-fit (4.8) is dropped at load time, where GTK 4.6's default scaling already looks the same. The wizard's GtkCenterBox children use typed <child> elements instead of the 4.10 *-widget properties.
    • Confirmation dialogs fall back to a small modal window where GTK has no Gtk.AlertDialog (4.10).
    • On 22.04 the dropdowns in Settings have no type-ahead search. PyGObject 3.42 cannot pass GTK the search expression, so they are plain dropdowns there. Everything else works the same.
    • The KDE theme probe installs kreadconfig5 where KF6 does not exist (libkf6config-bin | libkf5config-bin).
  • ✅ Verified on a booted 22.04. The booted-OS tier passes on a clean 22.04 cloud image. It covers the package install, face-auth PAM through sudo, the greeter rule, reboot persistence and purge. On 22.04 the polkit agent helper is setuid (polkit 0.105), so there is no sandbox to relax and the drop-in has no effect. The tier checks that instead of simulating a sandbox. v1.2.2 and older were built for 26.04 only, so on 22.04 there is no previous release to upgrade from.
  • 🐛 Fix: ubuntu-hello disable could empty config.ini. On a config without the disabled key, the in-place rewrite crashed halfway and left a 0-byte file. It also reported success without changing anything when the line was spelled disabled=false. It now uses the same section-aware, atomic editor as Settings. It adds the key when missing, keeps comments, and a failed write leaves the old file intact.
  • 🐛 Fix: the PAM module's "Unknown error" message read past its string. S("Unknown error: ") + status added the exit code to a char *, which is pointer arithmetic, not string concatenation. An unexpected compare exit code showed the user bytes from past the end of the message. It now prints the code.
  • 🐛 Fix: a PAM password task that never started could abort the host process. optional_task::stop() joined or cancelled a thread that did not exist when the task was never activated, for example the password watcher under pam_sm_open_session. It now returns early.
  • 🐛 Smaller fixes.
    • ubuntu-hello test no longer crashes on a model whose data list is empty, and prints its resolution as width × height.
    • compare.py no longer divides by zero on an empty camera frame.
  • 🔎 Coverage and duplication (SonarQube Cloud).
    • The Python gate now measures the same files Sonar does. The untested cli/*.py modules were never counted locally. 150+ new tests take the combined Python coverage to 97%.
    • The C++ PAM module now uploads gcov coverage (gcovr --sonarqube), with a new pam-main test for main.cc's gates, error reporting, helper sanitising and pam_sm_* entry points.
    • The one duplicated block (detect_desktop(), in both the theme probe and the wallet backend) is now shared.

No new config keys and no new or changed translatable strings.


📥 Installation (from Release assets)

Verify downloads against SHA256SUMS on the Release page before installing. Download this release's assets into an empty/clean directory before running these commands — the wildcards below match by filename pattern, and leftover assets from another version in the same directory can make a package manager see multiple matches (or, for the AppImage command, pass a second matched file as a stray argument).

Format Command
Debian (amd64 + arm64) sudo apt install ./ubuntu-hello_*_$(dpkg --print-architecture).deb ./ubuntu-hello-gtk_*_$(dpkg --print-architecture).deb
Fedora (amd64 + arm64) sudo dnf install ./ubuntu-hello-[0-9]*fc44*.$(uname -m).rpm ./ubuntu-hello-gtk-*fc44*.$(uname -m).rpm
openSUSE (amd64 + arm64) sudo zypper install ./ubuntu-hello-[0-9]*lp160*.$(uname -m).rpm ./ubuntu-hello-gtk-*lp160*.$(uname -m).rpm
AppImage (amd64 + arm64) chmod +x Ubuntu-Hello-*-$(uname -m).AppImage && ./Ubuntu-Hello-*-$(uname -m).AppImage --install
Arch (amd64 only) sudo pacman -U ./ubuntu-hello-[0-9]*.pkg.tar.zst ./ubuntu-hello-gtk-*.pkg.tar.zst
Snap (amd64 only) sudo snap install --dangerous --classic ubuntu-hello_*_amd64.snap && sudo snap run ubuntu-hello.host-install
Flatpak (amd64 only) flatpak install --user ./com.github.ventura8.UbuntuHello-*.flatpak && flatpak run --command=ubuntu-hello-host-install com.github.ventura8.UbuntuHello

PPA (Ubuntu 22.04, 24.04 and 26.04). On 22.04 use the PPA: the .deb assets above are built on 26.04:

sudo add-apt-repository ppa:ventura8/ubuntu-hello && sudo apt update && sudo apt install ubuntu-hello

Optional source install: download install.sh from the v1.2.3 tag, review it, then run locally — do not pipe a moving branch URL directly to bash.

Full Changelog: v1.2.2...v1.2.3

v1.2.2

Choose a tag to compare

@github-actions github-actions released this 24 Sep 20:29
a14b01f

Ubuntu Hello v1.2.2 - Verified Model Downloads, Keyring Seal Safety, SonarQube Cloud

This release closes an integrity gap in how the setup wizard fetches the face-recognition models, which it downloads and unpacks as root, and fixes four ways enabling keyring unlock could go wrong: losing the previous seal, choosing TPM sealing on a partial tpm2-tools install, accepting an empty piped password, and a Settings edit deleting comments from config.ini. Under the hood, the project now runs SonarQube Cloud static analysis locally and in CI, and went from 356 findings to none.


🔧 What's in this release

  • 🛡️ Security: the setup wizard now verifies the face models it downloads. When the models are missing, the wizard runs dlib-data/install.sh as root to fetch the three dlib archives and unpack them. It unpacked whatever arrived: there was no integrity check, and the github.com/.../raw/ URLs it used answer with a redirect. Each archive is now checked against a pinned SHA-256 before anything is unpacked. The pins are the same ones package-configure.sh already used for the packaged install path, so the two paths now agree where before only one of them verified. Redirects are also refused: the canonical raw.githubusercontent.com URLs serve the files directly, and wget gets --max-redirect=0 while curl gets --max-redirs 0 --proto-redir =https. A mismatch aborts the download, and the wizard reports it as a failed download.
  • 🔐 Fix: switching keyring unlock between TPM and software sealing could lose the seal. Each sealing path deleted the other mechanism's credential before writing its own, so a failure in between (a TPM command, encryption or a file write) left the user with no usable seal. The previous credential is now kept until its replacement is on disk.
  • 🧰 Fix: keyring enable could fail mid-seal on a partial tpm2-tools install. It chose TPM sealing when tpm2_createprimary and tpm2_unseal were present, but sealing also runs tpm2_create and restoring runs tpm2_load. All four are now required; if any is missing it falls back to software sealing.
  • 🚫 Fix: an empty piped password was sealed and reported as success. echo | ubuntu-hello keyring enable (EOF or a bare newline) sealed an empty string, but unsealing treats an empty result as failure, so the seal never unlocked anything. It is now rejected, as the interactive prompt already did.
  • 📝 Fix: a Settings edit deleted comments from config.ini. Replacing a key treated any indented line after it as part of the old value, including indented #/; comments. Comments are now kept, while the old value's real continuation lines are still dropped, so a stamp_rules update never leaves a stale rule behind.
  • 🔏 Fix: source installs set config.ini to 0744. The Meson install path gave the file an execute bit, diverging from every package path, from docs/SECURITY.md and from the booted-OS mode check. It is now 0644.
  • ⏱️ Fix: the auth window's user lookup could hang. Its last-resort loginctl call had no timeout, so a stalled logind could block the authentication window. It is now bounded at 5 seconds, like the Settings window's.
  • 🔎 New: SonarQube Cloud. ./scripts/ci-sonar.sh runs the analysis locally in a pinned Docker scanner, and CI scans as a step in the coverage job, with a quality gate on pushes to master and on pull requests from this repository (fork pull requests skip the scan, as they cannot read the SONAR_TOKEN secret). The project went from 356 findings to 0, with 0 bugs and 0 vulnerabilities and A ratings for reliability, security and maintainability. Most of the fixes above came out of that work or its code review. Where a rule misreads this project's idioms (GTK CSS selectors, kernel-mirroring v4l2 names, kwallet-pam's fixed PBKDF2 parameters, the PAM module's mandatory C-ABI casts), it is scoped per rule and per path, with the reason written next to it, and never suppressed in the source.
  • ⚙️ CI: runs once per pull request. push and pull_request both fired for every PR branch and landed in different concurrency groups, so every push ran the whole matrix twice. push now fires for master only. The booted-OS tier runs on same-repository release pull requests (feature/v*), and is guarded so a fork cannot trigger it.

No new config keys, no new or changed translatable strings, and no dependency changes. Desktop, greeter and wallet compatibility is unchanged: GNOME, KDE Plasma, XFCE, Cinnamon, MATE, Budgie and LXQt with GDM, SDDM and LightDM; GNOME Keyring (pam_gnome_keyring) and KWallet (pam_kwallet5).


📥 Installation (from Release assets)

Verify downloads against SHA256SUMS on the Release page before installing. Download this release's assets into an empty/clean directory before running these commands — the wildcards below match by filename pattern, and leftover assets from another version in the same directory can make a package manager see multiple matches (or, for the AppImage command, pass a second matched file as a stray argument).

Format Command
Debian (amd64 + arm64) sudo apt install ./ubuntu-hello_*_$(dpkg --print-architecture).deb ./ubuntu-hello-gtk_*_$(dpkg --print-architecture).deb
Fedora (amd64 + arm64) sudo dnf install ./ubuntu-hello-[0-9]*fc44*.$(uname -m).rpm ./ubuntu-hello-gtk-*fc44*.$(uname -m).rpm
openSUSE (amd64 + arm64) sudo zypper install ./ubuntu-hello-[0-9]*lp160*.$(uname -m).rpm ./ubuntu-hello-gtk-*lp160*.$(uname -m).rpm
AppImage (amd64 + arm64) chmod +x Ubuntu-Hello-*-$(uname -m).AppImage && ./Ubuntu-Hello-*-$(uname -m).AppImage --install
Arch (amd64 only) sudo pacman -U ./ubuntu-hello-[0-9]*.pkg.tar.zst ./ubuntu-hello-gtk-*.pkg.tar.zst
Snap (amd64 only) sudo snap install --dangerous --classic ubuntu-hello_*_amd64.snap && sudo snap run ubuntu-hello.host-install
Flatpak (amd64 only) flatpak install --user ./com.github.ventura8.UbuntuHello-*.flatpak && flatpak run --command=ubuntu-hello-host-install com.github.ventura8.UbuntuHello

PPA remains available:

sudo add-apt-repository ppa:ventura8/ubuntu-hello && sudo apt update && sudo apt install ubuntu-hello

Optional source install: download install.sh from the v1.2.2 tag, review it, then run locally — do not pipe a moving branch URL directly to bash.

Full Changelog: v1.2.1...v1.2.2

v1.2.1

Choose a tag to compare

@github-actions github-actions released this 21 Sep 14:52
137c6c9

Ubuntu Hello v1.2.1 - Clean-Install Fix, Settings Env Hardening, Booted-OS + Footage Test Tiers

This release fixes the 1.2.0 package failing to configure on a clean Ubuntu 26.04, closes a local root-execution path through the polkit-elevated Settings process, stops the PAM module from hijacking the host process's syslog identity, makes the confirmations config migration run for package installs, corrects the mode of config.ini, and adds the two test tiers that found all of it: a booted-OS tier (a clean Ubuntu, Fedora or Arch under QEMU/KVM with an emulated TPM) and a recorded-footage tier (the real recognition loop on real camera frames, no face pixels kept). Plural-form translations are now linted and declared in every catalog.


🔧 What's in this release

  • 📦 Fix: fresh install failed on a clean Ubuntu 26.04. The package builds dlib from source (the archive has no python3-dlib) and the toolchain it needs was only Recommended. apt never upgrades an already-installed package to satisfy a Recommends, so on an image whose python3 lagged the archive python3-dev was silently skipped, the build died on Python.h: No such file or directory, dpkg stayed half-configured and every login fell through to the password. Developer machines that already had the headers never saw it. The toolchain is now a hard dependency unless the distro's dlib is installed (python3-dlib | python3-dev, | python3-pip, | cmake, | g++); Fedora/openSUSE gain python3-devel / gcc-c++ / Pillow, Arch gcc / python-pillow. Reproduced and verified on a clean cloud image.
  • 🛡️ Security fix: the elevated Settings process accepted arbitrary environment variables from argv. ubuntu-hello-gtk forwards the desktop's display and locale variables across pkexec as --env-KEY=value arguments and restored every such argument into its environment as root. polkit pins only the interpreter and the script path, so a local user who can pass the auth_admin prompt could append --env-PATH=/tmp/evil:… or --env-LD_PRELOAD=… and have the root process — which spawns ubuntu-hello, sudo, gsettings and loginctl by bare name — execute their binary as root. The restore now accepts only the fixed display/locale allowlist that elevate() sends (one shared constant for sender and receiver), drops everything else with a stderr notice, and as root pins PATH to a fixed system value before any helper is spawned. Reported by an automated security review; unit tests cover the injected PATH/LD_*/PYTHON*/HOME cases.
  • 🪵 Fix: the PAM module hijacked the host's syslog identity. openlog("pam_ubuntu_hello") without closelog() tagged every later line of sudo or the display manager as pam_ubuntu_hello. The identity is now scoped to the module's own logging (SyslogIdentityScope).
  • ⚙️ Fix: the confirmations migration never ran for package installs. It lived in the source installer only; .deb/RPM/Arch upgrades left the key out and fell back to a single frame. It now lives in config_ensure.py and package-configure.sh runs it on every install and upgrade — hand edits, comments and a hand-tightened threshold are kept, verified on a real 1.1.6 → 1.2.1 apt upgrade.
  • 🔏 Fix: config.ini was installed executable (0755 → 0644); docs/SECURITY.md now describes the real /etc/ubuntu-hello/ layout (0755 directory, secrets in 0700 subdirectories).
  • 🔐 Fix: on Fedora/Arch the module could break password authentication. The authselect/Arch PAM line used [success=end default=ignore]; end is a Debian pam-auth-update macro, not raw PAM syntax, so Linux-PAM let the module's skip return (AUTHINFO_UNAVAIL for SSH / no camera / disabled) surface — an SSH or camera-less login failed instead of falling through to the password. Now [success=done default=ignore]. Found with a live before/after on a booted Fedora guest.
  • 👻 Fix: face auth spawned a GTK overlay that crashed on a headless box. compare.py started the on-screen auth overlay unconditionally, but under PAM there is no display, so on a server or SSH sudo it crashed on open and left an apport report every time (auth still fell back to the password). The overlay is now spawned only when a display is reachable.
  • 🪟 Fix: the post-install wizard crashed on a headless machine. It assumed DISPLAY=:0; installing over SSH started GTK with no display and left an apport crash report. It now launches only when a display actually exists.
  • 🧩 Fix: the Arch PAM module failed to load, breaking face auth entirely on Arch. It links libINIReader.so.0 (libinih) and libevdev, but those were build-only deps, so pacman never installed them and PAM could not dlopen the module. Both are now runtime dependencies.
  • 🏗️ Fix: the Arch package could not build dlib and then skipped the security setup. Its deps listed gcc/cmake but not make, so cmake could not build dlib, and that failure aborted the post-install before PAM and the polkit drop-in were written. make is now a dependency and the dlib build runs last and non-fatally on every format, so the security config is always applied.
  • 🧹 Fix: dnf remove left /usr/lib64/ubuntu-hello behind (untracked __pycache__); the shared pre-remove step now sweeps every lib dir and drops the emptied directory.
  • 🖥️ New: booted-OS test tier (scripts/vm/, tests/vm/). A clean Ubuntu 26.04, Fedora 44 or Arch cloud image boots under QEMU/KVM with a swtpm TPM 2.0 and scenario scripts run inside over SSH: install (PAM line, drop-in, tmpfiles, catalogs, dlib, file modes), config migration, sudo through PAM with a synthetic model (fails closed without a camera, wrong password refused, caller environment never inherited, syslog identity kept), the greeter HARD RULE (pam_probe.py runs the real PAM stack for gdm-password and counts prompts: with workaround = off the module never asks itself), the polkit helper sandbox with a real TPM seal/unseal, a real reboot, purge (nothing left, no crash reports), and an upgrade from the previous GitHub release. One lib.sh holds what differs per distro. UH_VM_PKG_DIR tests packages built from the tree; CI runs a matrix job per distro on release branches. Every fix above was found by it.
  • 🎞️ New: recorded-footage test tier (tests/footage/). Replays real-camera clips through compare.py's actual scan loop, the real dlib detector/predictor/encoder and the real nod rubber stamp: enrollment on real frames, the same face at Secure from another clip, no face never passing, the agreeing-frames gate, a real nod passing and holding still failing fail-closed, Secure margin. Privacy: only de-identified per-frame signals exist (dark flag, face rect, five landmarks, descriptor), the pixels were shredded, the signals stay off the repository, and CI uses a synthetic set with the measured shape.
  • 🌍 i18n: plural forms. ngettext() entries were invisible to the lint and an untranslated plural shipped everywhere with the lint green; plural entries are now linted and every catalog header carries the language's Plural-Forms rule so Python's gettext picks the right form. Catalogs are refreshed for 1.2.1 (template version, source references).

No new config keys or strings. Runtime dependencies: the dlib build toolchain is now a proper dependency where the distro ships no dlib. Desktop / greeter / wallet compatibility is unchanged.


📥 Installation (from Release assets)

Verify downloads against SHA256SUMS on the Release page before installing. Download this release's assets into an empty/clean directory before running these commands — the wildcards below match by filename pattern, and leftover assets from another version in the same directory can make a package manager see multiple matches (or, for the AppImage command, pass a second matched file as a stray argument).

Format Command
Debian (amd64 + arm64) sudo apt install ./ubuntu-hello_*_$(dpkg --print-architecture).deb ./ubuntu-hello-gtk_*_$(dpkg --print-architecture).deb
Fedora (amd64 + arm64) sudo dnf install ./ubuntu-hello-[0-9]*fc44*.$(uname -m).rpm ./ubuntu-hello-gtk-*fc44*.$(uname -m).rpm
openSUSE (amd64 + arm64) sudo zypper install ./ubuntu-hello-[0-9]*lp160*.$(uname -m).rpm ./ubuntu-hello-gtk-*lp160*.$(uname -m).rpm
AppImage (amd64 + arm64) chmod +x Ubuntu-Hello-*-$(uname -m).AppImage && ./Ubuntu-Hello-*-$(uname -m).AppImage --install
Arch (amd64 only) sudo pacman -U ./ubuntu-hello-[0-9]*.pkg.tar.zst ./ubuntu-hello-gtk-*.pkg.tar.zst
Snap (amd64 only) sudo snap install --dangerous --classic ubuntu-hello_*_amd64.snap && sudo snap run ubuntu-hello.host-install
Flatpak (amd64 only) flatpak install --user ./com.github.ventura8.UbuntuHello-*.flatpak && flatpak run --command=ubuntu-hello-host-install com.github.ventura8.UbuntuHello

PPA remains available:

sudo add-apt-repository ppa:ventura8/ubuntu-hello && sudo apt update && sudo apt install ubuntu-hello

Optional source install: download install.sh from the v1.2.1 tag, review it, then run locally — do not pipe a moving branch URL directly to bash.

Full Changelog: v1.2.0...v1.2.1

v1.2.0

Choose a tag to compare

@github-actions github-actions released this 17 Sep 09:25
79e8227

Ubuntu Hello v1.2.0 - Keyring-Unseal Fix, Second-Model Wizard, Auth Notifications, GTK 4 + Hardening

This release fixes TPM-sealed keyring unlock under polkit, teaches the setup wizard to enroll a second face model for different lighting, adds native desktop notifications for every face-auth attempt (with the diagnostics behind the result), moves Settings / wizard / overlay to GTK 4, hardens how the root PAM module launches its compare.py helper, and refreshes every product and CI dependency pin to its latest final release. The hardening was in response to an automated vulnerability-scanner report of a potential local privilege escalation via a user-controlled PATH. On review the chain was not exploitable as described (details below), but the code relied on an unspecified corner of libc behaviour, so it is now explicit and no longer depends on it.


🔒 What's in this release

  • Reported: pam_ubuntu_hello.so spawned compare.py as root with posix_spawnp(..., envp = nullptr) and never dropped privileges; compare.py then started the auth overlay with subprocess.Popen(["ubuntu-hello-gtk", ...]) — a bare-name PATH lookup. The scanner concluded that a local user could plant ~/.local/bin/ubuntu-hello-gtk and have it executed as root the moment face auth started (e.g. su <enrolled-user>).
  • Why it was not exploitable: on glibc/Linux, posix_spawnp() with a NULL envp gives the child an empty environment, not the caller's — verified with a small test program (FOO=bar ./t → env prints nothing). With no PATH set at all, both glibc's execvp() and Python's subprocess fall back to /bin:/usr/bin, so ubuntu-hello-gtk already resolved to /usr/bin/ubuntu-hello-gtk; ~/.local/bin was never searched. The premise of an "inherited environment" was wrong.
  • Hardened anyway — PAM module: the helper is now started with posix_spawn() and an explicit minimal environment (PATH=/usr/sbin:/usr/bin:/sbin:/bin only), so the calling user's PATH, PYTHONPATH, LD_PRELOAD, … can never reach the root helper regardless of libc behaviour. The interpreter runs as python3 -E -s (ignore PYTHON* variables, no user site-packages). -I was deliberately not used: it would also drop the script directory from sys.path, which would break compare.py's sibling imports (snapshot, paths_factory, recorders, …).
  • Hardened anyway — compare.py: the auth overlay is now spawned via an absolute path (GTK_BIN_PATH = "/usr/bin/ubuntu-hello-gtk"), matching the existing BUSCTL_PATH pattern. No child of the root helper is resolved by bare name any more.
  • Docs: docs/SECURITY.md gains a "No Inherited Environment / PATH" bullet; docs/architecture/README.md §3.1 and AGENTS.md now describe the explicit-env spawn and -E -s flags.
  • 🔑 Fix: TPM-sealed keyring unlock failed under polkit (worked at GDM): every pkexec / App Center prompt logged Failed to unseal keyring password from TPM. polkit ≥ 126 runs polkit-agent-helper-1 as a hardened transient unit (DevicePolicy=strict, ProtectSystem=strict); the shipped drop-in only re-allowed the camera and /dev/uinput, so /dev/tpmrm0 was Operation not permitted and the transient .ctx files under /etc/ubuntu-hello/tpm-keys/ hit a read-only filesystem. All three drop-in writers (install.sh, package-configure.sh, install_config.py) now add DeviceAllow=char-tpm rw, DeviceAllow=/dev/tpm0 rw, ReadWritePaths=/etc/ubuntu-hello/tpm-keys. Reproduced layer-by-layer with systemd-run under the unit's exact hardening and verified live: PAM_AUTHTOK set successfully for keyring unlocking on a real polkit prompt. Regression tests in test_install_download.py / test_packaging_release.py.
  • 🧑‍🎨 Setup wizard: second face model under different lighting: a single model recorded in daylight often lands just past the certainty threshold in the evening under lamps (or vice-versa), which shows up as intermittent "timeout reached" instead of a clear error (root-caused on real hardware: certainty 3.37–3.6 vs 3.5). Slide 4 now runs two passes: after the first scan it re-labels itself "Add a second face model (recommended)", explains why (lighting) and how (change the room light / desk lamp, turn away from the window, put on the glasses you sometimes wear), restarts the live preview and offers Scan second model. Either pass can be skipped, but not both: skipping pass 1 makes pass 2 required (Skip hidden, "Add a face model (required)"); a failed second scan is non-fatal (retry or skip) whereas a failed only-scan still stops the wizard. Models are labelled Setup lighting 1 / Setup lighting 2. Settings also shows a warning bar on the Models tab while exactly one model is enrolled, with an Add a second model action. 18 new strings translated in all 98 catalogs. Unit tests + real-GTK E2E (UH_REAL_GTK=1) cover both passes and both skip paths.
  • 🧑‍🎨 Guided multi-sample enrollment (Windows Hello style): a model was one descriptor from one frame, which is why logins landed just past the threshold intermittently. ubuntu-hello add now guides you through six small head movements (about a minute) and keeps up to 13 descriptors per model; the wizard and Settings show the live prompt and progress (the scan no longer blocks the UI), and the same prompt is mirrored on a notification card right under the camera. The optional second scan stays, now recommended for glasses on/off, a change of facial hair or different lighting. Fixes the winning-model lookup for multi-sample models.
  • 🔔 New: native desktop notifications for every face-auth attempt (notify.py, org.freedesktop.Notifications): one card updated in place — "Face authentication · sudo — 👀 Looking for your face…" stays on screen for the whole scan (critical urgency: GNOME never auto-hides it), then becomes Face recognized · sudo — ✓ Full Name · 1.3 s (removed after success_linger = 3 s) or Face not recognized — ✗ No match in 8 s · 💡 add a model in this light (stays in the list). Titles name the requester (sudo, su, authorization, screen unlock) because the PAM module now forwards the PAM service to compare.py. [notifications] details = true adds model, certainty/threshold, frames, dark frames, camera, pid, timeout, load. The notification id is persisted per user so consecutive attempts reuse the same card; a detached helper closes the success card and acts as a watchdog for a helper that died mid-scan. Layout follows GNOME Shell 50's rules read from its source (one-line body, <b> only; no desktop-entry/transient; low urgency never banners). Because compare.py runs as root with an empty environment, cards are sent on the target user's session bus by dropping to that uid before exec'ing the absolute /usr/bin/gdbus; greeter logins, missing gdbus, or any error silently disable it — never the auth result. Verified live on GNOME 50 / Wayland, including a real polkit prompt: the polkit helper drop-in now sets ProtectHome=tmpfs + BindReadOnlyPaths=-/run/user (yes hid /run/user, so no card could ever be delivered from Settings / App Center / pkexec; /home and /root stay empty inside the unit, only the runtime dir is bound back — security review) and ReadWritePaths=-/run/ubuntu-hello for the root-only id state, created at boot by a new tmpfiles.d/ubuntu-hello.conf. Strings translated in all 98 catalogs; libglib2.0-bin / glib2 added to core runtime deps.
  • ⚙️ Settings → Notifications tab: switches for Show notifications, Notification sounds and Include diagnostics ([notifications] enabled/sound/details), written with a new comment-preserving config_edit.set_option() (the bare-key ubuntu-hello set is ambiguous between sections). "Add new user" removed from the Models tab. Wizard: Back button on every page after the first (Cancel only on page 1) with per-page cleanup — camera scan cancelled and single device list on re-entry, chosen camera kept selected, IR test capture released, download not restarted, second scan resumed instead of a third model. First scan is required, only the second can be skipped; the two-scan plan is explained up front.
  • 🔐 New: Settings → Security tab, agreeing-frame matching and recalibrated levels: a face used to be accepted on the first frame that fell under the match threshold. A scan takes dozens of independent looks at the camera, so the deciding frame was the minimum of many draws and a single blurred or oddly lit frame from a stranger could carry the whole attempt. New [video] confirmations requires several separate frames to agree before the match is trusted (Fast 2, Balanced 3, Secure 4); the real user reaches the count in well under a second. The thresholds were also recalibrated from measured logins rather than one sample — the previous Secure value matched no frame at all once the room light changed, and with a single enrolled model the distance to the same face drifts by about 0.4 between lighting conditions. Levels are now Fast 3.5 / Balanced 3.0 / Secure 2.6, with strictness carried by the frame count. A new Settings → Security tab exposes the three levels plus an opt-in Ask me to nod to confirm switch, which the setup wizard's security page now offers too (its last page also saves on Finish rather than on arrival, so a level changed there is no longer lost) (a photo held up to the camera cannot nod; a video replay still can, the documented limit of challenge-response). Nod detection was rewritten around anchored nose displacement, the challenge prompt is mirrored to the notification card because the auth overlay can never open a display under PAM, and a liveness check that crashes now denies. Upgrades keep their own threshold and only gain the new key; a hand-tightened config stays on a single frame so the migration cannot break an existing...
Read more

v1.1.6

Choose a tag to compare

@github-actions github-actions released this 25 Aug 13:50
13e7521

Ubuntu Hello v1.1.6 - Session-Idle Self-Abort (Best-Effort) + Dependency Updates

This release adds a best-effort self-abort for a running face scan when the session genuinely goes idle, plus packaging/CI dependency updates. It does not fully resolve face recognition continuing to run after Esc at a GNOME/GDM lock screen — read the "Known limitation" bullet below before relying on it for that.


🐛 What's in this release

  • New: session-idle self-abort: with the shipped default core.workaround = off, nothing in the PAM module was ever told to cancel a running compare process — the only existing cancel path only fires when a concurrent password-watch thread wins the race, and that only exists when a workaround is enabled.
  • Deliberately did not widen PAM-side password watching to add this. AGENTS.md carries an explicit HARD RULE over that exact code, guarding against a worse prior incident: doing so previously broke GDM login outright (selecting a user immediately bounced back to the account list).
  • Added a same-process signal instead: compare.py now runs a daemon thread that polls its own login1 session's IdleHint via busctl (absolute path, since this runs as root during PAM auth) roughly twice a second. On an observed false → true transition (edge-triggered — an already-idle session at scan start, the normal case when waking a lock screen, does not trigger this) it sends the process SIGTERM so the existing main-thread handler does cleanup, rather than touching the camera directly from the watcher thread. Fails open if busctl/logind are unavailable or return anything other than exactly b true/b false. New config: core.abort_on_session_idle (default true).
  • ⚠️ Known limitation, confirmed by live testing: after building this, live D-Bus capture across a real Esc-at-lock-screen repro (system bus + user session bus, multiple rounds) showed IdleHint never transitions on GNOME/GDM for that scenario — Esc only toggles GNOME Shell's internal password-prompt-vs-idle-clock view, with no external signal we could find (logind IdleHint, xdg-desktop-portal Inhibit, org.gnome.ScreenSaver.ActiveChanged all stayed unchanged; WakeUpScreen/Mutter.IdleMonitor fire but also on ordinary interaction, so using them risks aborting legitimate scans). On GNOME/GDM this is currently a dormant safety net, not a fix for Esc. Kept because it's harmless and may help elsewhere.
  • Also this release: bumped the Flatpak runtime/SDK to 25.08 and several pinned GitHub Actions (upload-artifact, download-artifact, setup-buildx-action, setup-qemu-action) to their current latest releases, plus CI hardening (explicit least-privilege permissions: on the lint/coverage/compat jobs, and exposing the GitHub Actions cache runtime variables that buildx's type=gha cache backend needs) and two thread-safety fixes in the idle watcher found in PR review — including a race where a late busctl reading arriving after a concurrent successful/failed exit could override the decided exit code with abort code 12.
  • Verified: confirmed busctl's output format directly against a real login1 session on real hardware, matching the parser exactly. Full unit test coverage for the idle-state probe (including malformed-output and absolute-path cases) and the edge-triggered watcher thread; full test suite passes.

📥 Installation (from Release assets)

Verify downloads against SHA256SUMS on the Release page before installing. Download this release's assets into an empty/clean directory before running these commands — the wildcards below match by filename pattern, and leftover assets from another version in the same directory can make a package manager see multiple matches (or, for the AppImage command, pass a second matched file as a stray argument).

Format Command
Debian (amd64 + arm64) sudo apt install ./ubuntu-hello_*_$(dpkg --print-architecture).deb ./ubuntu-hello-gtk_*_$(dpkg --print-architecture).deb
Fedora (amd64 + arm64) sudo dnf install ./ubuntu-hello-[0-9]*fc44*.$(uname -m).rpm ./ubuntu-hello-gtk-*fc44*.$(uname -m).rpm
openSUSE (amd64 + arm64) sudo zypper install ./ubuntu-hello-[0-9]*lp160*.$(uname -m).rpm ./ubuntu-hello-gtk-*lp160*.$(uname -m).rpm
AppImage (amd64 + arm64) chmod +x Ubuntu-Hello-*-$(uname -m).AppImage && ./Ubuntu-Hello-*-$(uname -m).AppImage --install
Arch (amd64 only) sudo pacman -U ./ubuntu-hello-[0-9]*.pkg.tar.zst ./ubuntu-hello-gtk-*.pkg.tar.zst
Snap (amd64 only) sudo snap install --dangerous --classic ubuntu-hello_*_amd64.snap && sudo snap run ubuntu-hello.host-install
Flatpak (amd64 only) flatpak install --user ./com.github.ventura8.UbuntuHello-*.flatpak && flatpak run --command=ubuntu-hello-host-install com.github.ventura8.UbuntuHello

PPA remains available:

sudo add-apt-repository ppa:ventura8/ubuntu-hello && sudo apt update && sudo apt install ubuntu-hello

Optional source install: download install.sh from the v1.1.6 tag, review it, then run locally — do not pipe a moving branch URL directly to bash.

Full Changelog: v1.1.5...v1.1.6