Files
gtk-ahfail/readme.md
Asger Geel Weirsøe 5817074f1a
Some checks failed
Test / test (push) Failing after 4m25s
Release / release (push) Failing after 6m37s
release: v0.9.0 pre-release — X11 PAM integration, shadow suppression, macOS cfg guards
- PAM module now correctly fires on failed attempts (must precede auth includes)
- ahfail-display: sprite-sized window replaces full-screen overlay
- ahfail-display: XGrabKeyboard unlock detection replaces process scanning
- ahfail-display: _COMPTON_SHADOW=0 suppresses picom shadow without config changes
- ahfail-display: flock single-instance guard prevents stacking on rapid failures
- X11-specific code guarded behind #[cfg(target_os = "linux")] for macOS builds
- Removed debug logging (dlog/plog) from display and PAM binaries
- README: PAM ordering requirement, supported locker table, roadmap section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 22:01:02 +02:00

9.0 KiB

Ah ah ah, you didn't say the magic word

On a failed lock-screen unlock attempt, this spawns a looping animation of the author's face photoshopped onto Dennis Nedry's body and plays the "ah ah ah, you didn't say the magic word" clip from Jurassic Park. Each wrong guess adds another sprite at a random screen position. Volume is forced to 100% on the first failure and restored when the screen unlocks.


Security disclaimer

This project hooks into PAM and/or your screen locker's plugin API. PAM sits directly in the authentication critical path — a bug in this module could lock you out of your system or, in the worst case, weaken authentication.

The author makes no guarantees about the security or correctness of this software. By installing it you accept that you are modifying a security-sensitive component of your system and take full responsibility for any vulnerabilities or instability that result. Use at your own discretion.


Platform support

The integration method differs by display server:

Platform Display server Module loaded How it works
Linux Wayland ahfail-module.so Loaded directly by gtklock via its module API
Linux X11 libahfail_pam.so + ahfail-display PAM cleanup hook spawns the display binary after each failed auth
macOS libahfail_pam.so + ahfail-display Same PAM approach, hooks into the screensaver PAM stack

The gtklock module is Wayland-only — it uses gtklock's internal window API to overlay sprites directly on the lock screen. The PAM module works on X11 and macOS by registering a cleanup callback that fires on each failed authentication attempt; it double-forks a standalone display binary (ahfail-display) that runs independently of the PAM stack.


Linux (x86_64) — binary install

Pre-built binaries for both Wayland and X11 are available on the releases page. The tarball contains all three files:

File Used by
ahfail-module.so Wayland — loaded by gtklock
libahfail_pam.so X11 — loaded by PAM
ahfail-display X11 — spawned by the PAM module
# Download and extract (replace v0.1.0 with the latest release)
curl -fsSL https://gitea.weircon.dk/agw/gtk-ahfail/releases/download/v0.1.0/ahfail-linux-x86_64.tar.gz \
  | sudo tar -xz -C /tmp/ahfail-install

# Wayland (gtklock)
sudo install -Dm755 /tmp/ahfail-install/ahfail-module.so /usr/lib/gtklock/ahfail-module.so

# X11 (PAM module + display binary)
sudo install -Dm755 /tmp/ahfail-install/libahfail_pam.so /usr/lib/ahfail/libahfail_pam.so
sudo install -Dm755 /tmp/ahfail-install/ahfail-display   /usr/lib/ahfail/ahfail-display

Then follow the Wayland or X11 configuration below.


Linux compile

Install dependencies and build from source

sudo pacman -S meson ninja rust gtk3 gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gtklock
meson setup builddir --prefix=/usr
meson compile -C builddir
sudo meson install -C builddir

--prefix=/usr matches Arch conventions (same as pacman) and ensures the compiled-in default path for ahfail-display matches where it is installed.

Installs:

  • /usr/lib/gtklock/ahfail-module.so — gtklock module
  • /usr/lib/ahfail/libahfail_pam.so — PAM module (for X11)
  • /usr/lib/ahfail/ahfail-display — standalone display binary

Usage Linux - wayland (only gtklock)

gtklock -m /usr/lib/gtklock/ahfail-module.so

Arguments

Pass module arguments after --:

gtklock -m /usr/lib/gtklock/ahfail-module.so -- --deadzone=860,440,200,200
  • --deadzone=X,Y,W,H — rectangle where sprites will not spawn (keep your password field clear)
  • --audio-uri=URI — override the default audio clip, e.g. file:///home/user/custom.mp3

Usage Linux - X11 (i3lock, xscreensaver, etc.)

The PAM module works with any X11 locker that authenticates via PAM. Supported lockers and their service file names:

Locker PAM service file
i3lock /etc/pam.d/i3lock
i3lock-color /etc/pam.d/i3lock-color
betterlockscreen /etc/pam.d/betterlockscreen
xscreensaver /etc/pam.d/xscreensaver
lightdm /etc/pam.d/lightdm

Add a line to the relevant file.

Important: The ahfail line must appear before auth include system-auth (or any equivalent include). If it comes after, PAM's internal flow control inside system-auth (pam_faillock with [default=die]) means our module is never reached on failed attempts — it only gets called on success.

Your PAM service file should look like this:

Arch / standard (--prefix=/usr):

#%PAM-1.0
auth optional /usr/lib/ahfail/libahfail_pam.so
auth include system-auth

Fedora / RHEL (multilib):

#%PAM-1.0
auth optional /usr/lib64/ahfail/libahfail_pam.so
auth include system-auth

Debian / Ubuntu (multiarch):

#%PAM-1.0
auth optional /usr/lib/x86_64-linux-gnu/ahfail/libahfail_pam.so
auth include system-auth

The full path is required — $(libdir)/ahfail is not in PAM's default search path.

If the display binary is not at the default location, add display_path=:

auth optional /usr/lib/ahfail/libahfail_pam.so display_path=/usr/lib/ahfail/ahfail-display

macOS

brew tap agw/ahfail https://gitea.weircon.dk/agw/homebrew-ahfail
brew install ahfail

After install, Homebrew prints the PAM line to add to /etc/pam.d/screensaverui (macOS 13+) or /etc/pam.d/screensaver (macOS 12 and earlier). That one-line edit is the only manual step.

To upgrade: brew upgrade ahfail.

Script install

git clone https://gitea.weircon.dk/agw/gtk-ahfail.git
cd gtk-ahfail
bash scripts/install-macos.sh

Installs Homebrew dependencies, builds from source, copies binaries to /usr/local/lib/ahfail/, and patches the screensaver PAM configuration automatically.

Manual install

brew install gtk+3 gstreamer gst-plugins-base gst-plugins-good meson ninja
# install Rust via https://rustup.rs if not present
meson setup builddir && meson compile -C builddir
sudo mkdir -p /usr/local/lib/ahfail
sudo cp builddir/libahfail_pam.so builddir/ahfail-display /usr/local/lib/ahfail/

Add to /etc/pam.d/screensaverui (macOS 13+) or /etc/pam.d/screensaver as the first auth line (before any auth include or auth required entries):

auth optional /usr/local/lib/ahfail/libahfail_pam.so

Uninstall

Linux — X11

bash scripts/uninstall-linux-x11.sh

Removes the ahfail line from common PAM service files (/etc/pam.d/i3lock, xscreensaver, lightdm, etc.) and removes the installed binaries. If builddir is present it uses ninja uninstall; otherwise it removes the known paths manually.

Linux — Wayland

No PAM config was modified. Just stop passing the module to gtklock and optionally remove the file:

sudo rm -f /usr/lib/gtklock/ahfail-module.so   # --prefix=/usr install
# or
sudo rm -f /usr/local/lib/gtklock/ahfail-module.so

macOS — Homebrew

sudo sed -i '' '/ahfail/d' /etc/pam.d/screensaverui   # or screensaver on macOS 12
brew uninstall ahfail

macOS — manual / script install

bash scripts/uninstall-macos.sh

Removes the ahfail line from the screensaver PAM file and deletes /usr/local/lib/ahfail/.


Customization

The sprite is the author's face on Nedry's body. To use your own:

  1. Replace the sprite frames in assets/sprites/ with your own PNG sequence.
  2. Update assets/ahfail.gresource.xml if you add or remove files.
  3. Rebuild with Meson.

To replace the audio clip, swap assets/audio/magic-word.mp3 and rebuild.


Development

cargo test                                        # unit + integration tests (needs display)
cargo test --test benchmarks -- --nocapture       # sprite placement benchmarks
cargo clippy                                      # lint
meson setup builddir && meson compile -C builddir # full build including .so
xvfb-run meson test -C builddir --verbose        # Meson tests headless

Roadmap

swaylock (Wayland)

The PAM module can be added to /etc/pam.d/swaylock and will fire on each failed attempt — but on Wayland the ahfail-display binary cannot draw on top of the lock screen. The Wayland session-lock protocol (ext-session-lock-v1) restricts rendering to the locker process itself, so the animation is suppressed by the compositor and only the audio plays.

Full support requires swaylock to expose a plugin API similar to gtklock's. There is an open request for this upstream. Once available, a dedicated swaylock module can be added alongside the existing gtklock one.

hyprlock (Wayland)

Same situation as swaylock — PAM fires correctly but the display is blocked by the compositor. hyprlock does not currently have a plugin/module API. Full animation support is pending upstream plugin support.