Face unlock for Linux that earns root.
IR-enforced anti-spoofing, sub-second daemon auth, optional TPM-sealed encryption. 100% local — your face never leaves the machine.
SCRFD detection finds faces, affine alignment normalizes geometry, and ArcFace produces a 512-dimensional embedding for cosine similarity matching.
V4L2 frame acquisition with auto-detection. Prefers IR cameras for anti-spoofing. CLAHE enhancement for consistent lighting.
SCRFD neural network locates faces and extracts 5-point landmarks. Affine alignment produces a normalized 112x112 crop.
ArcFace produces a 512-dim L2-normalized vector. Cosine similarity against stored embeddings determines match or reject.
Multiple independent layers protect against spoofing, tampering, and unauthorized access — without any configuration after install.
Enabled by default. Rejects RGB cameras that are trivially spoofed with a printed photo. IR captures skin texture invisible to screens.
Requires micro-movement between consecutive frames. Static photos produce near-identical embeddings and are rejected automatically.
5 attempts per user per 60 seconds by default. Prevents brute-force and rapid-retry attacks against the daemon.
ONNX model files are SHA256-verified at every load. Tampered models are rejected before any inference runs.
D-Bus system bus policy restricts daemon access. Only root and facelock group members can send messages to the daemon interface.
Every authentication attempt is logged to syslog with user, service, and outcome. Full audit trail in journald or /var/log/auth.log.
Three places it pays for itself the first day you install it.
Every sudo prompt becomes a glance at the camera. The daemon keeps ONNX models loaded — ~600ms on a cold camera, dropping to 150–200ms on back-to-back unlocks.
Drop-in PAM for Hyprlock, swaylock, and KDE Plasma's screen locker. Wake the display, look at the camera, you're back in.
GDM, SDDM, and LightDM (experimental). Camera initialization at the login screen is system-dependent — see docs for caveats.
A complete face authentication stack written in Rust, with sane defaults and the knobs you need when you outgrow them.
Enforces infrared cameras by default. Phone screens and printed photos lack IR skin texture, blocking the most common attack vector.
Persistent daemon keeps ONNX models loaded. ~600ms typical, dropping to ~150ms on back-to-back auths when the camera stays warm.
All processing happens on-device via ONNX Runtime. No cloud services, no network requests, no telemetry, no analytics. Your face data never leaves your machine, ever.
Drop-in PAM module for sudo, polkit, hyprlock, swaylock, KDE, and login managers. Installs as a single pam_facelock.so — the wizard wires the rest.
Optional TPM 2.0 support for encrypting face embeddings at rest. Hardware-bound keys ensure biometric data stays protected even if the disk is copied.
Choose persistent daemon mode for speed or oneshot mode for simplicity. The CLI auto-detects which mode is available.
Facelock is designed from the ground up to keep your biometric data private.
Face detection and recognition run entirely on your CPU or GPU via ONNX Runtime. No images or embeddings are ever transmitted over the network.
Zero analytics, tracking, or phone-home code. Models are downloaded once during facelock setup — after that, Facelock never contacts any server.
Face embeddings can be encrypted with AES-256-GCM, optionally sealed to your TPM. Even if someone copies your database, the biometric data is unreadable.
Every line of code is MIT/Apache-2.0 licensed. No proprietary blobs, no obfuscated network calls. Verify the privacy claims yourself.
Install the package for your distro and run sudo facelock setup. The wizard detects your camera, downloads models, sets up encryption, enrolls your face, and wires up PAM for sudo and your screen locker. No further commands needed.
Need finer control? See docs for manual model paths, custom PAM stacks, GPU provider tuning, and --non-interactive automation.
Howdy proved face auth on Linux was possible. Facelock is the production rewrite — Rust, hardened by default, and engineered to be safe to actually deploy.
| Feature | Facelock | Howdy |
|---|---|---|
| LanguageMemory-safe at compile time; no runtime crashes on bad input. | Rust | Python |
| Daemon modeAuth completes before you finish typing your password. | Yes (~150–600ms auth) | No (process per auth) |
| IR enforcementPhone screens and printed photos are rejected without configuration. | Default on | Supported, not enforced |
| Frame variance checkA still photo can't produce two different frames — rejected automatically. | Default on | No |
| TPM encryptionA copied disk image can't decrypt your face data. | Optional (sealed key) | No |
| Model verificationSwapped or tampered models fail to load instead of mis-authenticating. | SHA256 at every load | No |
| Rate limitingRapid-retry brute force is stopped at the daemon, not the PAM stack. | 5/user/60s default | No |
| D-Bus activationDaemon only runs when something asks for auth — zero idle cost. | Yes | No |
| Constant-time matchingMatch scores can't be reconstructed by timing the response. | subtle crate | No |
| GPU accelerationDrop auth latency further on supported hardware — no rebuild needed. | CUDA / ROCm / OpenVINO (runtime) | No |
| Audit loggingEvery attempt is grep-able and SIEM-friendly. | Structured JSONL + syslog | syslog only |
| systemd hardeningDaemon runs without write access to / and without ambient capabilities. | ProtectSystem, NoNewPrivileges, etc. | No |
| PAM module sizeTiny PAM module — no Python runtime in the auth path. | ~2MB (libc + toml + zbus) | Full Python runtime |