# SPDX-FileCopyrightText: 2026 Gundu Labs # SPDX-License-Identifier: GPL-3.0-or-later # Template for the source RPM consumed by Copr. `just srpm` fills the ${...} placeholders, so # version and scriptlet bodies share one source with the nfpm packages. %global extension_uuid gaze@gundulabs.com # Built with -Cdebuginfo=0, so there is nothing to split out. %global debug_package %{nil} Name: gaze Version: 0.2.10 Release: 3%{?dist} Summary: Daemon, CLI, and PAM integration for Gaze License: GPL-3.0-or-later URL: https://gaze.gundulabs.com Vendor: Gundu Labs Source0: %{name}-%{version}.tar.gz Source1: vendor.tar.zst Source2: cargo-vendor-config.toml Patch0: gaze-pr-503.patch # Gaze and ONNX Runtime are currently built on these architectures. ExclusiveArch: x86_64 aarch64 BuildRequires: cargo BuildRequires: rust BuildRequires: gcc BuildRequires: gcc-c++ BuildRequires: clang BuildRequires: clang-devel BuildRequires: pkgconfig BuildRequires: onnxruntime-devel >= 1.22 BuildRequires: opencv-devel BuildRequires: libv4l-devel BuildRequires: pam-devel BuildRequires: tpm2-tss-devel BuildRequires: gtk4-devel BuildRequires: libadwaita-devel BuildRequires: gstreamer1-devel BuildRequires: gstreamer1-plugins-base-devel BuildRequires: openssl-devel BuildRequires: checkpolicy BuildRequires: policycoreutils BuildRequires: desktop-file-utils BuildRequires: systemd-rpm-macros BuildRequires: zstd Requires: pam Recommends: authselect %description Gaze is a face authentication system for Linux. This package contains the gazed daemon, the gaze command line client, the PAM modules, and the authselect profile used to wire face authentication into system-auth. %package gui Summary: GTK4/Adwaita GUI for Gaze Recommends: %{name}%{?_isa} = %{version}-%{release} %description gui Graphical enrollment and configuration front end for Gaze. It talks to gazed over the system bus, so install the gaze package as well. %package gnome-extension Summary: GNOME Shell extension for Gaze Requires: gnome-shell Recommends: %{name}%{?_isa} = %{version}-%{release} Recommends: policycoreutils %description gnome-extension GNOME Shell extension and GDM integration that start face authentication from the GNOME lock screen and login screen. %package hyprlock Summary: Hyprlock PAM integration for Gaze Recommends: %{name}%{?_isa} = %{version}-%{release} %description hyprlock PAM service files that let hyprlock authenticate with Gaze, either on its own or simultaneously with a password. %package kde Summary: KDE Plasma lock screen and System Settings integration for Gaze Recommends: %{name}%{?_isa} = %{version}-%{release} Recommends: %{name}-gui%{?_isa} = %{version}-%{release} %description kde Runs Gaze in the biometric PAM slot KScreenLocker starts before you touch anything, so the KDE Plasma lock screen unlocks on a face match with no key press, and adds a Face Unlock entry to System Settings. Ships no PAM service file of its own: plasma-workspace owns those, so they are edited in place. %prep %autosetup -p1 tar --zstd -xf %{SOURCE1} mkdir -p .cargo cp -p %{SOURCE2} .cargo/config.toml %build # Mirrors the `opencv_env` probe in the Justfile: the opencv crate only looks # for the opencv4/opencv pkg-config names. if ! pkg-config --exists opencv4 && ! pkg-config --exists opencv && pkg-config --exists opencv5; then export OPENCV_PKGCONFIG_NAME=opencv5 fi # Split like `just build-rust`: gazed's `detection` feature must not unify ONNX # Runtime into the clients, whose constructors crash on non-AVX2 CPUs. ORT_LIB_LOCATION="%{_libdir}" \ ORT_PREFER_DYNAMIC_LINK=1 \ RUSTFLAGS="-Cdebuginfo=0" \ cargo build --release --locked --offline -p gaze RUSTFLAGS="-Cdebuginfo=0" \ cargo build --release --locked --offline -p gaze-cli -p gaze-gui -p pam-gaze -p pam-gaze-grosshack checkmodule -M -m -o gaze-gdm-camera.mod packaging/selinux/gaze-gdm-camera.te semodule_package -o gaze-gdm-camera.pp -m gaze-gdm-camera.mod %install install -Dpm 0755 target/release/gazed %{buildroot}%{_bindir}/gazed install -Dpm 0755 target/release/gaze %{buildroot}%{_bindir}/gaze install -Dpm 0644 packaging/config/config.toml %{buildroot}%{_sysconfdir}/gaze/config.toml install -Dpm 0644 packaging/config/com.gundulabs.Gaze.conf %{buildroot}%{_sysconfdir}/dbus-1/system.d/com.gundulabs.Gaze.conf install -Dpm 0644 packaging/config/com.gundulabs.gaze.policy %{buildroot}%{_datadir}/polkit-1/actions/com.gundulabs.gaze.policy install -Dpm 0644 packaging/config/gazed.service %{buildroot}%{_unitdir}/gazed.service install -Dpm 0755 target/release/libpam_gaze.so %{buildroot}%{_libdir}/security/pam_gaze.so install -Dpm 0755 target/release/libpam_gaze_grosshack.so %{buildroot}%{_libdir}/security/pam_gaze_grosshack.so install -dm 0755 %{buildroot}%{_datadir}/authselect/vendor/gaze cp -p packaging/authselect/custom/gaze/* %{buildroot}%{_datadir}/authselect/vendor/gaze/ install -Dpm 0755 target/release/gaze-gui %{buildroot}%{_bindir}/gaze-gui install -Dpm 0644 packaging/gui/com.gundulabs.Gaze.desktop %{buildroot}%{_datadir}/applications/com.gundulabs.Gaze.desktop install -Dpm 0644 packaging/gui/com.gundulabs.Gaze.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/com.gundulabs.Gaze.svg install -Dpm 0644 packaging/gui/com.gundulabs.Gaze.metainfo.xml %{buildroot}%{_datadir}/metainfo/com.gundulabs.Gaze.metainfo.xml install -Dpm 0644 gnome-shell-extension/metadata.json %{buildroot}%{_datadir}/gnome-shell/extensions/%{extension_uuid}/metadata.json install -Dpm 0644 gnome-shell-extension/extension.js %{buildroot}%{_datadir}/gnome-shell/extensions/%{extension_uuid}/extension.js install -Dpm 0644 gnome-shell-extension/prefs.js %{buildroot}%{_datadir}/gnome-shell/extensions/%{extension_uuid}/prefs.js install -Dpm 0644 packaging/config/org.gnome.shell.extensions.gaze.gschema.xml %{buildroot}%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.gaze.gschema.xml install -Dpm 0644 packaging/gdm/00-gaze-defaults %{buildroot}%{_sysconfdir}/dconf/db/gdm.d/00-gaze-defaults install -Dpm 0644 packaging/gdm/profile %{buildroot}%{_datadir}/gaze/dconf-profile-gdm install -Dpm 0644 packaging/pam/gdm-face %{buildroot}%{_sysconfdir}/pam.d/gdm-face install -Dpm 0644 gaze-gdm-camera.pp %{buildroot}%{_datadir}/gaze/gaze-gdm-camera.pp install -Dpm 0755 packaging/kde/gaze-kde-pam %{buildroot}%{_bindir}/gaze-kde-pam install -Dpm 0644 packaging/kde/gaze-face-unlock.desktop %{buildroot}%{_datadir}/plasma/systemsettings/externalmodules/gaze-face-unlock.desktop install -Dpm 0644 packaging/pam/hyprlock-gaze %{buildroot}%{_sysconfdir}/pam.d/hyprlock-gaze install -Dpm 0644 packaging/pam/hyprlock-gaze-simultaneous %{buildroot}%{_sysconfdir}/pam.d/hyprlock-gaze-simultaneous %check desktop-file-validate %{buildroot}%{_datadir}/applications/com.gundulabs.Gaze.desktop %post %systemd_post gazed.service #!/bin/sh # SPDX-FileCopyrightText: 2026 Gundu Labs # SPDX-License-Identifier: GPL-3.0-or-later set -e # Regenerate PAM files after a Gaze profile update, but only when Gaze is # already selected. Never replace another active authselect profile. if command -v authselect >/dev/null 2>&1 && authselect current --raw 2>/dev/null | grep -q '^gaze\([[:space:]]\|$\)'; then authselect apply-changes >/dev/null 2>&1 || true fi # A display-manager greeter runs as xdm_t, which SELinux denies the camera by # default; the denial is silent and reads like a broken camera. Loaded here # because the base package owns the policy, whichever desktop is installed. if [ -f /usr/share/gaze/gaze-gdm-camera.pp ] && command -v semodule >/dev/null 2>&1; then semodule -i /usr/share/gaze/gaze-gdm-camera.pp >/dev/null 2>&1 || true fi if [ -d /run/systemd/system ]; then systemctl daemon-reload >/dev/null 2>&1 || true dbus-send --system --type=method_call --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.ReloadConfig >/dev/null 2>&1 || true systemctl restart polkit >/dev/null 2>&1 || true systemctl try-restart gazed >/dev/null 2>&1 || true fi %preun %systemd_preun gazed.service %postun %systemd_postun gazed.service %post gnome-extension #!/bin/sh # SPDX-FileCopyrightText: 2026 Gundu Labs # SPDX-License-Identifier: GPL-3.0-or-later set -e profile=/etc/dconf/profile/gdm insert_profile_db() { db="system-db:$1" after="$2" required="$3" grep -qxF "$db" "$profile" && return 0 if ! grep -qxF "$after" "$profile"; then [ "$required" = "required" ] || return 0 printf '%s\n' "$db" >>"$profile" return 0 fi tmp="${profile}.gaze-tmp" if awk -v db="$db" -v after="$after" ' { print } $0 == after && !inserted { print db; inserted = 1 } ' "$profile" >"$tmp"; then cat "$tmp" >"$profile" fi rm -f "$tmp" } ensure_gdm_dconf_profile() { template=/usr/share/gaze/dconf-profile-gdm [ -f "$template" ] || return 0 if [ ! -f "$profile" ]; then mkdir -p /etc/dconf/profile cp "$template" "$profile" return 0 fi insert_profile_db gdm user-db:user required [ -d /etc/dconf/db/distro.d ] && insert_profile_db distro system-db:gdm optional return 0 } ensure_gdm_dconf_profile || true if [ -d /run/systemd/system ]; then dconf update >/dev/null 2>&1 || true glib-compile-schemas /usr/share/glib-2.0/schemas >/dev/null 2>&1 || true if command -v semodule >/dev/null 2>&1; then semodule -i /usr/share/gaze/gaze-gdm-camera.pp >/dev/null 2>&1 || true fi fi %postun gnome-extension #!/bin/sh # SPDX-FileCopyrightText: 2026 Gundu Labs # SPDX-License-Identifier: GPL-3.0-or-later set -e case "${1:-}" in 1|2|upgrade|failed-upgrade) ;; *) rm -f /etc/dconf/db/gdm.d/99-gaze* 2>/dev/null || true ;; esac if [ -d /run/systemd/system ]; then dconf update >/dev/null 2>&1 || true glib-compile-schemas /usr/share/glib-2.0/schemas >/dev/null 2>&1 || true fi %post kde #!/bin/sh # SPDX-FileCopyrightText: 2026 Gundu Labs # SPDX-License-Identifier: GPL-3.0-or-later set -e if [ -x /usr/bin/gaze-kde-pam ]; then gaze_kde_status=0 /usr/bin/gaze-kde-pam enable || gaze_kde_status=$? else gaze_kde_status=1 fi if [ "$gaze_kde_status" -eq 0 ]; then cat <<'EOF' Gaze face unlock is enabled on the KDE Plasma lock screen. It runs in the slot KScreenLocker starts up front for biometrics, alongside the password field, so a face match unlocks without pressing a key. Lock your screen and look at the camera. The login greeter is separate and off by default. Unless your Plasma Login Manager ships an up-front biometric service, face auth there starts when you submit the login form, exactly as a fingerprint reader does on that screen. Turn it on with: sudo gaze-kde-pam enable-login Turn face unlock off again with: sudo gaze-kde-pam disable Docs: https://gaze.gundulabs.com/guide/kde EOF else cat <<'EOF' Gaze could not wire itself into KScreenLocker's biometric slot. Run this once KDE Plasma is installed: sudo gaze-kde-pam enable Docs: https://gaze.gundulabs.com/guide/kde EOF fi %postun kde #!/bin/sh # SPDX-FileCopyrightText: 2026 Gundu Labs # SPDX-License-Identifier: GPL-3.0-or-later set -e # Clean up only on real removal, not on upgrade. case "${1:-}" in 1 | 2 | upgrade | failed-upgrade | abort-upgrade | abort-install) exit 0 ;; esac lock_pam_files="/etc/pam.d/kde-fingerprint /etc/pam.d/kde-smartcard" login_pam_files="/etc/pam.d/plasmalogin /etc/pam.d/sddm" login_face_pam_file=/etc/pam.d/plasmalogin-fingerprint vendor_pam_dir=/usr/lib/pam.d state_dir=/etc/gaze login_flag="$state_dir/login-enabled" begin_marker='# BEGIN gaze (managed by gaze-kde; remove with `gaze-kde-pam disable`)' end_marker='# END gaze' strip_gaze_block() { target=$1 [ -f "$target" ] || return 0 grep -qF "$begin_marker" "$target" 2>/dev/null || return 0 tmp=$(mktemp) awk -v begin="$begin_marker" -v end="$end_marker" ' $0 == begin && !holding { holding = 1; held = ""; next } holding && $0 == end { holding = 0; next } holding { held = held $0 "\n"; next } { print } END { if (holding) printf "%s\n%s", begin, held } ' "$target" >"$tmp" if cmp -s "$target" "$tmp"; then rm -f "$tmp" return 0 fi staged="$target.gaze-staged.$$" cp -a "$target" "$staged" cat "$tmp" >"$staged" sync "$staged" 2>/dev/null || true mv -f "$staged" "$target" rm -f "$tmp" if command -v restorecon >/dev/null 2>&1; then restorecon "$target" >/dev/null 2>&1 || true fi } # Files Gaze created itself go away whole, restoring the vendor stack underneath # where there is one. The recorded checksum is what Gaze left behind, so a file # someone has edited since is kept, while a copy the vendor has changed underneath # is still removed rather than shadowing the new vendor stack for good. file_sum() { [ -f "$1" ] || return 1 if command -v sha256sum >/dev/null 2>&1; then sha256sum <"$1" | cut -d' ' -f1 else cksum <"$1" | cut -d' ' -f1,2 fi } # Decided before the block is stripped, since stripping is itself a change. untouched="" for target in $lock_pam_files $login_face_pam_file; do base=$(basename "$target") [ -f "$state_dir/$base.created-by-gaze" ] || continue recorded=$(cat "$state_dir/$base.installed-sha256" 2>/dev/null) || recorded= if [ -n "$recorded" ] && [ "$recorded" = "$(file_sum "$target" 2>/dev/null)" ]; then untouched="$untouched $target" fi done for target in $lock_pam_files $login_pam_files $login_face_pam_file; do strip_gaze_block "$target" done for target in $lock_pam_files $login_face_pam_file; do base=$(basename "$target") case " $untouched " in *" $target "*) rm -f "$target" ;; esac rm -f "$state_dir/$base.created-by-gaze" "$state_dir/$base.installed-sha256" \ "$state_dir/$base.vendor-sha256" done rm -f "$login_flag" "$state_dir/lock-disabled" cat <<'EOF' Gaze face unlock removed from the KDE Plasma lock screen and login greeter. Both are back to password-only authentication. EOF %post hyprlock #!/bin/sh # SPDX-FileCopyrightText: 2026 Gundu Labs # SPDX-License-Identifier: GPL-3.0-or-later set -e cat <<'EOF' Gaze hyprlock PAM service installed at /etc/pam.d/hyprlock-gaze To enable face unlock in hyprlock, add to ~/.config/hypr/hyprlock.conf: auth { pam { module = hyprlock-gaze } } For simultaneous face + password mode, use: module = hyprlock-gaze-simultaneous Docs: https://gaze.gundulabs.com/guide/hyprland EOF %postun hyprlock #!/bin/sh # SPDX-FileCopyrightText: 2026 Gundu Labs # SPDX-License-Identifier: GPL-3.0-or-later set -e cat <<'EOF' Gaze hyprlock PAM service removed. If your hyprlock.conf still references module = hyprlock-gaze, hyprlock will fall back to its default PAM service. Update hyprlock.conf to remove the reference. EOF %files %license LICENSE %doc README.md %{_bindir}/gazed %{_bindir}/gaze %dir %{_sysconfdir}/gaze %config(noreplace) %{_sysconfdir}/gaze/config.toml %config(noreplace) %{_sysconfdir}/dbus-1/system.d/com.gundulabs.Gaze.conf %{_datadir}/polkit-1/actions/com.gundulabs.gaze.policy %{_unitdir}/gazed.service %{_libdir}/security/pam_gaze.so %{_libdir}/security/pam_gaze_grosshack.so %{_datadir}/authselect/vendor/gaze/ %dir %{_datadir}/gaze %{_datadir}/gaze/gaze-gdm-camera.pp %files gui %license LICENSE %{_bindir}/gaze-gui %{_datadir}/applications/com.gundulabs.Gaze.desktop %{_datadir}/icons/hicolor/scalable/apps/com.gundulabs.Gaze.svg %{_datadir}/metainfo/com.gundulabs.Gaze.metainfo.xml %files gnome-extension %license LICENSE %{_datadir}/gnome-shell/extensions/%{extension_uuid}/ %{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.gaze.gschema.xml %config(noreplace) %{_sysconfdir}/dconf/db/gdm.d/00-gaze-defaults %config(noreplace) %{_sysconfdir}/pam.d/gdm-face %{_datadir}/gaze/dconf-profile-gdm %files hyprlock %license LICENSE %config(noreplace) %{_sysconfdir}/pam.d/hyprlock-gaze %config(noreplace) %{_sysconfdir}/pam.d/hyprlock-gaze-simultaneous %files kde %license LICENSE %{_bindir}/gaze-kde-pam %{_datadir}/plasma/systemsettings/externalmodules/gaze-face-unlock.desktop %changelog * Wed Aug 19 2026 Gundu Labs - 0.2.10-3 - Build against Fedora's onnxruntime-devel instead of bundling ONNX Runtime. * Wed Aug 19 2026 Gundu Labs - 0.2.10-2 - Apply upstream PR #503 to use ORT_ENABLE_ALL optimization level. * Sat Aug 15 2026 Gundu Labs - 0.2.10-1 - Release 0.2.10. See https://github.com/GunduLabs/gaze/releases