READ THIS BEFORE implementing anything PAM-related.
Never install pam_facelock.so on the host or edit /etc/pam.d/* until validated in container. A broken PAM module can lock you out of sudo, login, and su.
cargo test --workspace
cargo clippy --workspace -- -D warningsCovers: config parsing, format conversion, NMS, cosine similarity, IPC serialization, SQLite CRUD, frame variance logic. No hardware, no root.
cargo test --workspace -- --ignoredRequires camera and downloaded ONNX models. Tests capture, model loading, full pipeline.
just test-arch-pam # Arch PAM smoke tests (no camera needed)
just test-arch-integration # full E2E with camera (daemon mode)
just test-arch-oneshot # full E2E with camera (no daemon)
just test-arch-dev-shell # interactive shell for manual testingContainer tests validate:
- PAM module loads without crashing
- Returns PAM_IGNORE when daemon unavailable
- Handles missing/invalid config
- Exports correct PAM symbols
- End-to-end: enroll → list → test → PAM auth → clear
- Both daemon and oneshot modes
Disposable VM with snapshots. USB camera passthrough for real hardware testing. Verify sudo, su, login scenarios with rollback safety.
Safety checklist:
- Open root shell in separate terminal — keep it open
sudo cp /etc/pam.d/sudo /etc/pam.d/sudo.facelock-backupsudo facelock setup --pam --service sudo- Test in NEW terminal:
sudo echo test - If broken, revert from root shell:
sudo cp /etc/pam.d/sudo.facelock-backup /etc/pam.d/sudo - Never modify
system-authorloginuntil sudo works perfectly
Emergency recovery: boot from USB, mount partition, remove PAM line, reboot.
export FACELOCK_CONFIG=dev/config.toml
cargo build --workspace
cargo run --bin facelock -- setup # download modelsAll CLI commands work without a daemon — the CLI falls back to direct mode silently:
facelock enroll
facelock test
facelock list
facelock devicesfacelock daemon &
facelock enroll # uses daemon (faster for repeated commands)
facelock test
kill %1 # stop daemonControl via RUST_LOG environment variable:
RUST_LOG=facelock_daemon=debug facelock daemon # verbose daemon
RUST_LOG=facelock_cli=debug facelock test # verbose CLIdev/config.toml — temp paths, no root, camera auto-detected:
[device]
max_height = 480
[daemon]
model_dir = "./models"
[storage]
db_path = "/tmp/facelock-dev.db"
[security]
require_ir = true
require_frame_variance = trueGitHub Actions at .github/workflows/ci.yml:
- Build + test + clippy + fmt check
- Container PAM smoke tests
Local full CI: bash test/run-tests.sh
| File | Purpose |
|---|---|
test/Containerfile |
Container image (Arch + pamtester) |
test/run-tests.sh |
CI script (unit + lint + PAM symbols) |
test/run-container-tests.sh |
PAM smoke tests |
test/run-integration-tests.sh |
E2E with camera (daemon) |
test/run-oneshot-tests.sh |
E2E with camera (oneshot) |
test/pam.d/facelock-test |
Test PAM config |
| Recipe | Description |
|---|---|
just test |
Unit tests |
just lint |
Clippy |
just check |
test + lint + fmt |
just test-arch-pam |
Arch container PAM smoke |
just test-arch-integration |
E2E daemon mode |
just test-arch-oneshot |
E2E oneshot mode |
just test-arch-dev-shell |
Interactive container |
just install |
System install (root) |