Configuration and enrolment

Every key is documented in config/face-auth.toml.example, which is grouped: everyday settings, liveness, lockout, guards, install/hardware, advanced. This page covers which config sources are trusted where, enrolment, and choosing a recognition model.

Config sources

The authentication path and the unprivileged tools trust different things.

During PAM authentication (vinoauthface-auth: sudo, lock screen, login, polkit):

Source Effect
/etc/face-auth.toml (root-owned) Authoritative for everything
~/.config/face-auth.toml May only make authentication stricter, see below
FACE_AUTH_* environment Ignored entirely

For vinoauthface enroll and improve run as root (the normal case), only /etc/face-auth.toml and the command-line flags count: a ~/.config/face-auth.toml or FACE_AUTH_* carried over by sudo -E or doas keepenv is ignored. A --device must be an IR capture node, or the device already set in /etc/face-auth.toml.

For the offline tools, and enrolment as a normal user into your own --embeddings-dir, the usual layering applies: environment variables, then ~/.config/face-auth.toml, then /etc/face-auth.toml. Environment variable names follow the flat field names, so the capture timeout is FACE_AUTH_CAPTURE_TIMEOUT_MS and the liveness motion threshold is FACE_AUTH_LIVENESS_MOTION_THRESHOLD.

Example /etc/face-auth.toml:

threshold = 0.6
scan_duration_ms = 5000
liveness.preset = "standard"
guards.start_delay_ms = 100

Grouped keys

Related settings share a prefix: liveness.*, lockout.* and guards.* (when a scan runs at all: seat_check, abort_if_ssh, abort_if_lid_closed, start_delay_ms, start_delay_scope, require_confirmation_elevation). Write them dotted, as the example does, rather than as [liveness] headers: deploy.sh and pin-camera.sh append plain keys to the end of the file, and after a table header those would land inside the table.

The old flat names (liveness_motion_threshold, lockout_threshold, seat_check, …) still work. If both are set, the flat key wins.

liveness.preset is the quick way to set motion liveness:

Preset Motion threshold Residual threshold Use
off 0 0 No motion check. A photo or screen replay can unlock
standard 0.01 0 The default. Still faces pass by drifting over the window
strict 0.01 0.3 Also rejects a photo moved by hand; may fail if you sit very still

An individual liveness.* value overrides the preset for that value.

What a user may override at the login prompt

A user’s own config is read from their home (resolved via getent passwd), must be a regular file they own, and is applied as a narrowing overlay:

Key At the login prompt
threshold, detector_threshold, liveness.motion_threshold, liveness.residual_motion_threshold, liveness.preset, min_face_size_ratio Honoured only if >= the system value. A lower number is ignored, so preset = "off" in a user file does nothing
device Honoured only if the path is a real IR capture device on this machine (IR-looking sysfs name, or a physical greyscale sensor, and opens in a supported format)
scan_duration_ms, scan_interval_ms, capture_timeout_ms, liveness.grace_ms Honoured within built-in bounds
model_path, detector_model_path, embeddings_dir, pinned_camera_*, lockout.*, seal_embeddings, backend, npu_device, liveness.window_ms, bind_camera, guards.* Ignored: system policy only

This stops code running as you, which doesn’t know your password, from writing a permissive ~/.config/face-auth.toml and turning your next sudo into a root shell. To loosen matching, edit /etc/face-auth.toml as root.

Leave device unset

UVC cameras normally expose a metadata node right beside the capture node under the same name (/dev/video2 captures, /dev/video3 doesn’t). Auto-detection opens each candidate and takes the first that really is a capture device in a supported format. A hand-written path often gets this wrong. pin-camera.sh sets device for you. If nothing qualifies, there’s no fallback node: face auth declines and PAM asks for the password.

Enrolment

Face templates are root-owned, so enrolment needs root:

sudo vinoauthface enroll --user $USER    # replace templates with a new capture (30 frames)
sudo vinoauthface improve --user $USER   # append, for other lighting or angles

30 frames gives enough pose and expression variation from one sitting. Running improve in different lighting is the biggest gain beyond that. A user can store at most 256 embeddings; an enrolment or improve that would go past that fails before the camera opens, so re-enrol from scratch once the gallery is full.

Both take --user, --frames, --interval, --device, --threshold, --model and --embeddings-dir; -v works on either.

The command is vinoauthface, but the config files (face-auth.toml), FACE_AUTH_* variables, /var/lib/face-auth and the face-auth group keep their names.

Re-enrol after switching recognition model (vinoAuthFace refuses the old templates and says so) and after any change to the preprocessing pipeline. Templates from older versions of this fork or upstream may still match, but re-enrolling is the safer bet.

Enrolment isn’t user-writable on purpose: whatever can write a face template decides whose face unlocks that account. If your own login could rewrite it, so could anything running as you.

Recognition model: mbf vs r50

  mbf r50
Backbone MobileFaceNet ResNet50
Pack buffalo_sc buffalo_l
Size ~13 MB ~175 MB
Encode latency (NPU benchmark) ~1.2 ms/frame ~4.2 ms/frame
Genuine-match similarity (same benchmark) mean 0.815, min 0.759 mean 0.875, min 0.830

r50 gives a wider match margin for a few milliseconds per frame, which the camera-paced scan loop hides. deploy.sh picks r50 for an NPU (OpenVINO) build and mbf for a CPU (tract) build. An existing install keeps its model: the NPU default isn’t applied when the config already sets model_path or someone is enrolled. Choose explicitly with:

sudo FACE_AUTH_RECOGNITION_MODEL=r50 ./deploy.sh   # or mbf

Switching models requires re-enrolling. The two produce incompatible embedding spaces with the same 512-d shape, so comparing across them is meaningless rather than just less accurate. Every template file records the model that produced it, and vinoAuthFace refuses to authenticate or improve against a mismatch. Files from before this existed carry no tag and are treated as compatible.

Model sources and licensing: architecture.md.