Replace bare config snippets with actual tee -a commands so there is no way to mistake a file entry for a shell command to run directly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6.2 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.
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.)
Add a line to your screen locker's PAM service file (e.g. /etc/pam.d/i3lock). This is a config file entry — use tee -a to append it, not sudo directly:
Arch / standard (--prefix=/usr):
echo 'auth optional /usr/lib/ahfail/libahfail_pam.so' | sudo tee -a /etc/pam.d/i3lock
Fedora / RHEL (multilib):
echo 'auth optional /usr/lib64/ahfail/libahfail_pam.so' | sudo tee -a /etc/pam.d/i3lock
Debian / Ubuntu (multiarch):
echo 'auth optional /usr/lib/x86_64-linux-gnu/ahfail/libahfail_pam.so' | sudo tee -a /etc/pam.d/i3lock
Replace i3lock with your locker's service name (xscreensaver, lightdm, etc.). 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=:
echo 'auth optional /usr/lib/ahfail/libahfail_pam.so display_path=/usr/lib/ahfail/ahfail-display' | sudo tee -a /etc/pam.d/i3lock
macOS
Homebrew (recommended)
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 after the existing auth entries:
auth optional /usr/local/lib/ahfail/libahfail_pam.so
Customization
The sprite is the author's face on Nedry's body. To use your own:
- Replace the sprite frames in
assets/sprites/with your own PNG sequence. - Update
assets/ahfail.gresource.xmlif you add or remove files. - 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