Contributing

Prerequisites

  • Rust 1.85+ (rustup update)
  • Linux with V4L2 support
  • A webcam (IR recommended; RGB works for development)
  • Podman (for container tests)

Building

cargo build --workspace

Workspace structure

Facelock is a Cargo workspace with 10 crates:

CrateTypePurpose
facelock-corelibConfig, types, errors, D-Bus interface, traits
facelock-cameralibV4L2 capture, auto-detection, preprocessing
facelock-facelibONNX inference (SCRFD + ArcFace)
facelock-storelibSQLite face embedding storage
facelock-daemonlibAuth/enroll logic, liveness, audit, rate limiting, handler
facelock-clibinUnified CLI (facelock binary, includes bench subcommand)
pam-facelockcdylibPAM module (libc + toml + serde + zbus only)
facelock-tpmlibOptional TPM-bound encryption for embeddings at rest
facelock-polkitbinPolkit authentication agent for face auth
facelock-test-supportlibMocks and fixtures for testing

Version is declared once in the root Cargo.toml and inherited via version.workspace = true. Inter-crate dependencies use relative paths.

Code style

  • Error handling: thiserror for library error types, anyhow in binaries. Return Result<T> over panicking. Never unwrap() in library code.
  • Logging: tracing for structured logging. Control verbosity via RUST_LOG env filter.
  • Tests: #[cfg(test)] modules in each source file.
  • Formatting: cargo fmt (default rustfmt settings).
  • Linting: cargo clippy --workspace -- -D warnings must pass with zero warnings.

Dependency rules

The PAM module (pam-facelock) must stay lightweight: libc, toml, serde, zbus only. No ort, no v4l, no facelock-core. This keeps the shared library small and avoids dragging heavy dependencies into every PAM-using process.

Each crate has a defined dependency boundary. See the Contracts chapter for the full table.

Testing

Tier 1: Unit tests (no hardware)

cargo test --workspace
cargo clippy --workspace -- -D warnings

Run these before every commit. They require no camera or models.

Tier 2: Hardware tests (camera + models)

cargo test --workspace -- --ignored

Requires a connected camera and downloaded models. These tests are marked #[ignore] and skipped by default.

Tier 3: Container tests (requires podman)

just test-pam          # PAM smoke tests (no camera)
just test-integration  # end-to-end with camera (daemon mode)
just test-oneshot      # end-to-end with camera (no daemon)
just test-shell        # interactive container shell for debugging

Container tests validate PAM integration without risking host lockout.

Tier 4: VM testing

Use a disposable VM with snapshots for testing PAM changes against real login flows.

Tier 5: Host PAM testing

Only after tiers 3--4 pass. Always keep a root shell open. Start with sudo only -- never add Facelock to login or display manager PAM until sudo works reliably.

All checks at once

just check  # runs test + clippy + fmt

Security considerations

Read the Security chapter before implementing any auth-related code. Key rules:

  • security.require_ir defaults to true. Never weaken this default.
  • Frame variance checks must remain in the auth path.
  • Model files are SHA256-verified at load time.
  • D-Bus message size limits are enforced by the bus daemon. Never allocate unbounded buffers.
  • D-Bus system bus policy restricts daemon access.
  • The PAM module logs all auth attempts to syslog.
  • Rate limiting is enforced in the daemon (5 attempts/user/60s default).

Contracts

Do not change binary names, paths, config keys, database schema, or auth semantics without updating the Contracts chapter.

Submitting changes

  1. Run just check (or at minimum cargo test --workspace && cargo clippy --workspace -- -D warnings).
  2. Run container tests if your change touches PAM, daemon, or IPC code.
  3. Keep commits focused. Separate refactoring from behavioral changes.
  4. Write clear commit messages that explain why, not just what.