feat: ONNX face detection, IR camera support, and PAM authentication

Wire up ONNX RetinaFace detector and MobileFaceNet embeddings in the CLI
and auth pipeline. Add IR camera detection for Windows Hello-style
"Integrated I" cameras and greyscale-only format heuristic. Add histogram
normalization for underexposed IR frames from low-power emitters.

- Add `onnx` feature flag to CLI crate forwarding to daemon
- Wire ONNX detector into `detect` command with fallback to simple detector
- Fix IR camera detection for Chicony "Integrated I" naming pattern
- Add `normalize_if_dark()` for underexposed IR frames in auth pipeline
- Load user config from ~/.config/linux-hello/ as fallback
- Update systemd service for IR emitter integration and camera access
- Add system installation script and ONNX runtime installer
- Update .gitignore for local dev artifacts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-02 15:04:52 +02:00
parent ac5c71c786
commit 8c478836d8
19 changed files with 2188 additions and 212 deletions

View File

@@ -60,12 +60,20 @@ async fn main() -> Result<()> {
info!("Face detection module initialized (placeholder)");
// Initialize authentication service
let auth_service = AuthService::new(config.clone());
let template_path = std::env::var("LINUX_HELLO_TEMPLATES")
.map(std::path::PathBuf::from)
.unwrap_or_else(|_| TemplateStore::default_path());
let auth_service = AuthService::with_paths(config.clone(), template_path.clone());
auth_service.initialize()?;
info!("Authentication service initialized");
info!("Authentication service initialized (Templates: {})", template_path.display());
// Start IPC server
let mut ipc_server = IpcServer::new(IpcServer::default_socket_path());
let socket_path = std::env::var("LINUX_HELLO_SOCKET")
.map(std::path::PathBuf::from)
.unwrap_or_else(|_| IpcServer::default_socket_path());
let mut ipc_server = IpcServer::new(socket_path.clone());
// Set authentication handler
let auth_service_for_auth = auth_service.clone();
@@ -82,22 +90,30 @@ async fn main() -> Result<()> {
});
// Set list handler
ipc_server.set_list_handler(move |user| async move {
let store = TemplateStore::new(TemplateStore::default_path());
store.list_templates(&user)
let template_path_for_list = template_path.clone();
ipc_server.set_list_handler(move |user| {
let template_path = template_path_for_list.clone();
async move {
let store = TemplateStore::new(template_path);
store.list_templates(&user)
}
});
// Set remove handler
ipc_server.set_remove_handler(move |user, label, all| async move {
let store = TemplateStore::new(TemplateStore::default_path());
if all {
store.remove_all(&user)
} else if let Some(l) = label {
store.remove(&user, &l)
} else {
Err(linux_hello_common::Error::Config(
"No label specified".to_string(),
))
let template_path_for_remove = template_path.clone();
ipc_server.set_remove_handler(move |user, label, all| {
let template_path = template_path_for_remove.clone();
async move {
let store = TemplateStore::new(template_path);
if all {
store.remove_all(&user)
} else if let Some(l) = label {
store.remove(&user, &l)
} else {
Err(linux_hello_common::Error::Config(
"No label specified".to_string(),
))
}
}
});
@@ -127,10 +143,10 @@ async fn main() -> Result<()> {
info!("Linux Hello Daemon ready");
info!("Listening for authentication requests...");
if dbus_enabled && dbus_server.is_connected() {
info!(" - IPC: {}", IpcServer::default_socket_path().display());
info!(" - IPC: {}", socket_path.display());
info!(" - D-Bus: org.linuxhello.Daemon");
} else {
info!(" - IPC: {}", IpcServer::default_socket_path().display());
info!(" - IPC: {}", socket_path.display());
}
// Start IPC server as a task