Linux face authentication via PAM — persistent daemon, IR camera support, ONNX inference.
The Windows Hello equivalent for Linux. Visage authenticates sudo, login, and any
PAM-gated service using your face — no subprocess spawn, no interpreter startup, no model reload.
Built in Rust by Sovren Software. Ships standalone on any Linux system.
Visage runs as a persistent daemon: SCRFD face detection and ArcFace recognition are loaded once at startup via ONNX Runtime, and camera ownership is held across auth requests. Compare to Howdy — Python subprocess per auth attempt, 2–3s cold start, no IR emitter integration.
Built in Rust for memory safety throughout the authentication path. Integrates via standard Linux-PAM — no kernel patches, no modified sudo.
v0.4.0 — feature-complete, running on real hardware.
Enrollment, verification, PAM integration for sudo and lock screens, systemd hardening,
D-Bus access control, package lifecycle and suspend/resume are implemented and tested end
to end. sudo visage onboard takes a fresh machine to working face auth in one command.
Since v0.3.6: one-command onboarding, the first hardware validation of passive liveness, a
configurable PAM timeout, the first integration tests, Fedora RPM packaging, and three more
IR emitter quirks. v0.4.0 added PreviewFrame, so an enrolling client can show you what the
camera sees, and a pre-install hardware check you can run before installing anything. See
CHANGELOG for the full history.
⚠️ Keep a password fallback. Do not make this your only authentication factor yet. On its first hardware spoof validation, passive liveness did not discriminate: a hand-held phone screen displaced more than two genuine live attempts, and the identity stage matched that same photo. Every PAM stack shipped here falls through to the password, and it should stay that way. Details: Known Limitations and the threat model.
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ pam_visage │────▶│ visaged │────▶│ IR Camera │
│ (PAM module)│ D-Bus│ (daemon) │ │ + Emitter │
└─────────────┘ └──────┬───────┘ └──────────────┘
│
┌──────▼───────┐
│ visage-core │
│ SCRFD+ArcFace│
│ (ONNX) │
└──────────────┘
| Crate | Type | Purpose |
|---|---|---|
visaged |
Binary | System daemon — owns camera, D-Bus API, IR emitter control |
pam-visage |
cdylib | Thin PAM module — calls daemon over D-Bus |
visage-cli |
Binary | CLI tool — enroll, verify, test, diagnostics |
visage-core |
Library | Face detection (SCRFD) + recognition (ArcFace) via ONNX |
visage-hw |
Library | Camera capture, IR emitter control, hardware quirks DB |
visage-models |
Library | ONNX model manifest, pinned SHA-256 checksums, integrity verification |
visage-ipc |
Library | The D-Bus client surface, defined once and shared by every client |
visage-tui |
Binary | visage-enroll — enrollment with a live view of the camera |
Clone the repository and run the quickstart script. It handles dependency checks, building, packaging, installation, model download, face enrollment, and verification — from zero to working face auth in one command.
git clone https://github.com/sovren-software/visage.git
cd visage
./scripts/quickstart.shThe script validates your environment at each stage with clear pass/fail signals.
Requires Ubuntu 24.04 (amd64), Rust toolchain, a camera, and internet access.
Use --no-enroll for headless/CI builds.
For full instructions — configuration, troubleshooting, multi-user, removal — see the Operations Guide.
sudo apt install ./visage_*_amd64.deb
sudo visage onboard # models, enrollment, verification — one command
sudo echo "face auth works" # test — face first, password fallbackonboard downloads the ONNX models (~182 MB), captures several labelled angles with a
prompt between each, and verifies against the daemon before reporting success — so a
failed enrollment cannot look like a working one. It exits non-zero if verification does
not recognise you.
PAM is configured automatically via pam-auth-update.
sudo apt remove visage # removes binaries, disables PAM and service
sudo apt purge visage # also removes /var/lib/visage (models + face database)The quickstart script automates building from source. To build manually:
sudo apt install libpam0g-dev libdbus-1-dev
cargo install cargo-deb
cargo build --release --workspace
cargo deb -p visaged --no-build
sudo apt install ./target/debian/visage_*.deb# flake.nix
{
inputs.visage.url = "github:sovren-software/visage";
outputs = { self, nixpkgs, visage, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [
visage.nixosModules.default
{ services.visage.enable = true; }
];
};
};
}Then run onboarding:
sudo visage onboardThe NixOS module handles systemd, D-Bus policy, and PAM integration declaratively.
See packaging/nix/module.nix for all options (modelDir, camera, similarityThreshold, etc.).
git clone https://aur.archlinux.org/visage.git
# visage-git and visage-bin are also available
cd visage && makepkg -si
sudo visage onboard
# add --user <username> to onboard someone elsePAM requires a manual one-line edit on Arch — add before pam_unix.so in
/etc/pam.d/system-auth:
auth [success=done default=ignore] pam_visage.so
No published package yet — build the RPM from source:
cargo install cargo-generate-rpm
cargo build --release --workspace
cargo generate-rpm -p crates/visaged
sudo dnf install ./target/generate-rpm/visage-*.x86_64.rpm
sudo visage onboardFedora has no pam-auth-update and authselect owns system-auth, so PAM is configured
manually — the package ships the snippet at /usr/share/visage/pam.d/visage. Add before
pam_unix.so in /etc/pam.d/system-auth:
auth [success=done default=ignore] pam_visage.so
Tracking a COPR repository in #101.
- Installs
visaged(daemon),visage(CLI),visage-enroll(enrollment with a live camera view), andpam_visage.so(PAM module) - Enables the
visagedsystemd service andvisage-resume.service(suspend/resume) - Configures PAM (automatic on Ubuntu/NixOS, manual on Arch)
# Set up everything — models, enrollment, verification (start here)
sudo visage onboard
# Enrol with a live view of what the camera sees, so you can tell
# "too dark" from "off-centre" instead of guessing why a capture failed
sudo visage-enroll
# Verify interactively (exits 0 on match, 1 on no-match)
visage verify
# List enrolled models
visage list
# Show daemon status
visage status
# Remove a model
sudo visage remove <model-id># List cameras, VID:PID, and IR emitter quirk status
visage discoverOutput example:
/dev/video2 VID=0x04f2 PID=0xb6d9 quirk: ASUS Zenbook 14 UM3406HA IR Camera ✓
/dev/video4 VID=0x0bda PID=0x5850 no quirk (VID=0x0bda PID=0x5850)
# Test IR camera (default /dev/video2)
visage test
# Specify device and frame count
visage test --device /dev/video0 --frames 5Captures frames with the IR emitter active, applies dark-frame filtering and CLAHE
contrast enhancement, saves grayscale PGM files to /tmp/visage-test/, and prints
a summary. Requires the daemon to be running for emitter activation.
Visage works with USB UVC IR cameras — the class of IR / "Windows Hello" cameras
that appear as standard V4L2 devices under the uvcvideo kernel driver. No external
tools required: Visage includes built-in IR emitter activation via UVC extension unit
control, so there is no dependency on linux-enable-ir-emitter.
Pixel formats GREY (1 byte/pixel), YUYV (2 bytes/pixel), and Y16 (16-bit LE) are all supported and detected automatically at device open.
| Tier | Camera stack | Visage support | Examples |
|---|---|---|---|
| Supported | UVC IR (uvcvideo driver plus IR stream/emitter path) |
✅ Full support | ASUS ZenBook, ThinkPad T/X (pre-Gen 11), HP EliteBook (UVC IR configs), Dell Latitude (UVC IR configs), TUXEDO InfinityBook |
| Not secure-compatible | UVC RGB-only webcam | ❌ Testing only; not for PAM auth | ASUS ExpertBook B3302FEA/B5302FEA built-in 13d3:56ea |
| Not supported | Intel IPU6 / MIPI / libcamera | ❌ Not yet | Newer Dell XPS, ThinkPad Gen 11+ (some configs), Intel "AI PC" cameras |
| No IR camera | N/A | — | Framework, System76, Purism |
Not sure which your laptop has? Run visage discover — it detects the kernel
driver for each /dev/video* device and warns if an IPU6 camera is found. A
normal RGB UVC webcam is not enough for secure auth; see the tested
ASUS ExpertBook B3302FEA report
for an example of an incompatible uvcvideo camera.
ThinkPad note: ThinkPad T-series and X1 Carbon laptops frequently ship with a
separate USB UVC IR camera alongside the RGB webcam. These typically appear as a
second /dev/video* node under uvcvideo and work with Visage. However, newer
ThinkPad generations (Gen 11+) may use Intel IPU6 for the integrated camera stack.
IPU6 note: Intel IPU6 cameras require the proprietary Intel camera HAL and libcamera, not V4L2. Supporting them is a separate milestone (v0.4+).
Some cameras require a specific UVC control byte sequence to activate the IR emitter.
These are tracked in contrib/hw/ as TOML files embedded at compile time.
Confirmed quirk entries:
| File | Device | Source |
|---|---|---|
04f2-b6d9.toml |
ASUS Zenbook 14 UM3406HA | Verified on hardware |
04f2-b6d0.toml |
Lenovo ThinkPad P14s Gen 2a 21A0000RMX | Verified on hardware (community) |
174f-2454.toml |
Lenovo ThinkPad X1 Carbon Gen 9 20XW00FPUS | Verified on hardware |
174f-11a8.toml |
Lenovo ThinkPad P14s Gen 4 21HF | Verified on hardware (community) |
30c9-00c2.toml |
Lenovo ThinkBook 14 MP2PQAZG | Verified on hardware |
30c9-0120.toml |
HP OmniBook X Flip | Verified on hardware |
To add support for your camera, see contrib/hw/README.md.
For the full compatibility tier table and per-model notes, see docs/hardware-compatibility.md.
End-to-end acceptance test — CCX20, USB webcam /dev/video2, GREY format, CPU-only ONNX.
| Test | Result |
|---|---|
| Enroll, verify, match | ✅ similarity 0.87–0.90 |
| Daemon restart — data persists | ✅ |
Kill daemon — sudo falls back to password |
✅ |
apt install / remove / purge lifecycle |
✅ |
Systemd hardening (ProtectSystem=strict, char-video4linux rw) |
✅ |
| D-Bus access control (non-root enroll rejected) | ✅ |
| PAM stack (no terminal output on failure) | ✅ |
Suspend/resume via visage-resume.service |
✅ |
Latency: ~1.4s on USB webcam + CPU-only ONNX; median 2,273 ms on the 3277:0055 IR
module. The <500ms target is not met and is not close. An earlier version of this README
claimed "~200ms warm recognition" — that number was a prediction from the pre-build design
doc, never a measurement, and it is withdrawn.
Bugs fixed during testing: DeviceAllow glob, tokio::time::timeout panic in zbus context.
- Operations Guide ← start here: installation, configuration, troubleshooting
- Hardware Compatibility ← supported cameras, tiers, quirks
- Release Status & Known Limitations
- Architecture
- Threat Model
- Architecture Decisions ← 13 ADRs covering implementation, security, and governance decisions
- ADR 013 — Enrollment preview, and why the TUI ships
Visage is a PAM authentication module — security vulnerabilities have direct impact. Report security issues privately via GitHub Private Vulnerability Reporting. Do not open public issues for security bugs.
Full policy, scope, and response timeline: SECURITY.md.
Visage is feature-complete for facial authentication. Community contributions are focused on hardware validation (IR camera quirks) and distribution packaging.
See CONTRIBUTING.md for the full guide, including:
- The Adopt-a-Laptop program — test on your hardware, submit a report
- PR guidelines — merge strategy, review timeline, DCO sign-off
- Out-of-scope features — what we will and will not merge
- Packaging status by distribution
MIT