att_lock
An Android-style 4×4 dot pattern screen locker for wlroots-based Wayland
compositors (Sway, Hyprland, river, labwc, …). It uses the secure
ext-session-lock-v1 protocol, verifies the drawn pattern against a salted
hash stored in an XDG-compliant directory, and falls back to PAM password
authentication.
Features
- 4×4 dot grid, drawn by dragging with a pointer or touch — including Android's rule that intermediate dots on a straight line get captured.
- Rotation / any aspect ratio: the grid is a centred square sized from the
compositor-provided surface dimensions, so portrait, landscape and rotated
outputs all just work. HiDPI (integer
wl_outputscale) is honoured. - First-run setup: with no stored pattern (or
--setup) you draw the pattern twice to confirm it. It is stored as a salted yescrypt hash (falling back to sha512crypt) at$XDG_DATA_HOME/att_lock/pattern.hash(mode0600), never in plaintext. The hash uses cost factor 9 (~0.22 s per attempt) and covers your username as well as the pattern, so the same pattern on two accounts produces unrelated hashes. - PAM password fallback: start typing at the unlock screen to switch to a password field authenticated through PAM (for "forgot pattern").
- Background image via a command-line argument (PNG), scaled to cover.
- Secure by construction: if the process dies while locked, the compositor
keeps the session locked (a guarantee of
ext-session-lock-v1).
Build
The build system is Nix flakes.
# One-off build:
nix build # -> ./result/bin/att_lock
# Or a dev shell + meson:
nix develop
meson setup build
ninja -C build # -> ./build/att_lock
Dependencies (provided by the flake): wayland, wayland-scanner, cairo,
libxkbcommon, pam (linux-pam), libxcrypt, plus meson/ninja/pkg-config.
Usage
att_lock [options] [IMAGE]
-i, --image PATH Background image (PNG); also accepted positionally
-p, --pam-service NAME PAM service for the password fallback (default: login)
-m, --min-dots N Minimum dots in a pattern (default: 4)
--setup Force (re)creation of the stored pattern
-h, --help Show help
Examples:
att_lock ~/wallpaper.png # lock with a background
att_lock --setup # re-record the pattern, then unlock
att_lock -p att_lock -i bg.png # use a dedicated PAM service
Unlocking
- Draw your pattern (≥
--min-dotsdots) and release to unlock. - Type any character to switch to the password field; press Enter to authenticate via PAM, Esc to go back to the pattern.
After 4 failed attempts, further attempts are locked out for an escalating delay — 15 s, then 30 s, 60 s, 120 s, 240 s, capped at 5 minutes — with the remaining time shown on screen. The counter is shared between the pattern and the password field, so switching to the password does not sidestep an active lockout, and it is held in memory only (there is no way to restart the locker to clear it, since the compositor keeps the session locked regardless). The countdown uses a monotonic clock, so it cannot be skipped by changing the system time or by suspending the machine.
PAM configuration
The password fallback authenticates through PAM using the service named by
--pam-service (default login, which exists on most systems). For a
dedicated service, create one that stacks pam_unix:
NixOS (configuration.nix):
security.pam.services.att_lock = {}; # inherits sane defaults (pam_unix)
then run att_lock --pam-service att_lock.
Other distros (/etc/pam.d/att_lock):
auth include login
account include login
The pattern itself is not checked by PAM — it is verified against the local salted hash. PAM only backs the password fallback.
Wiring it into your compositor
Use it as your lock command, e.g. with swayidle:
swayidle -w \
timeout 300 'att_lock ~/wallpaper.png' \
before-sleep 'att_lock ~/wallpaper.png'
or bind it in Sway:
bindsym $mod+Escape exec att_lock ~/wallpaper.png
Notes & limitations
- A pattern is not a password. The lockout above throttles online guessing at
the lock screen, but it cannot help offline: even at cost factor 9 there are
only 43,680 possible 4-dot patterns, so anyone who obtains
pattern.hashcan search that space at their own pace. Raise--min-dotsif that matters to you; each extra dot multiplies the space by roughly an order of magnitude. - Patterns are also far more shoulder-surfable than typed passwords, and on touchscreens finger smudges can reveal them.
- Hash files written by versions before the
att_lock-hash-v3format are not readable; att_lock reports this and asks you to draw a new pattern. The PAM password fallback still works in the meantime. In particular, v3 is the format written since this project was renamed fromwlpattern: the account name and the project name are both baked into the hash input, so a pattern enrolled underwlpattern(in$XDG_DATA_HOME/wlpattern/) cannot be carried over and must be re-drawn once. - Background images must be PNG (loaded via Cairo). Convert other formats
first (e.g.
magick in.jpg out.png). - The pattern is rendered identically on every connected output; the live drag segment is drawn on the output receiving input.
- Rendering uses
wl_shm+ Cairo (no GPU/EGL dependency).
Layout
flake.nix Nix flake: package + dev shell
meson.build build definition
protocols/ext-session-lock-v1.xml vendored Wayland protocol
src/main.c Wayland client, input, unlock/setup logic
src/pattern.c grid geometry + pattern sequence model
src/render.c wl_shm buffers + Cairo drawing
src/auth.c hashing/storage (crypt) + PAM
src/att_lock.h shared types and interfaces
License
MIT. The vendored protocol XML is MIT (wlroots contributors).