Skip to main content

harn_guard/
lib.rs

1//! `harn-guard` — the downloadable on-device prompt-injection classifier for
2//! Harn (security Layer 2).
3//!
4//! This crate is the **management layer**: a catalog of upstream, already-hosted
5//! models ([`catalog`]), an on-disk store that installs and verifies them
6//! ([`store`]), and selector resolution ([`resolve_dir`]). It hosts nothing,
7//! bundles no weights, and makes no network calls itself — the CLI downloads
8//! from the catalog's upstream URLs on the user's machine and hands the bytes to
9//! [`GuardStore::install`] for SHA-256 verification + atomic install.
10//!
11//! The heavy inference runtime (ONNX) lives behind the off-by-default `neural`
12//! feature, so the default binary never links a model runtime. When built
13//! without it, [`load_classifier`] is a no-op returning `None` and the runtime
14//! keeps using the always-available built-in heuristic classifier. When built
15//! with it, a host installs [`load_classifier`] into the runtime's lazy loader
16//! seam (`harn_vm::security::set_injection_classifier_loader`), which fires the
17//! first time a `local-ml` policy scores untrusted content.
18
19pub mod catalog;
20mod error;
21#[cfg(any(feature = "neural", test))]
22mod neural_math;
23pub mod resolve;
24pub mod store;
25
26#[cfg(feature = "neural")]
27mod neural;
28
29pub use catalog::{CatalogFile, CatalogModel, ModelFormat, DEFAULT_MODEL};
30pub use error::{GuardError, Result};
31pub use resolve::resolve_dir;
32pub use store::{sha256_hex, GuardStore, Manifest, ManifestFile};
33
34#[cfg(feature = "neural")]
35pub use neural::OnnxInjectionClassifier;
36
37/// Load the installed model named/located by `selector` into a boxed
38/// [`InjectionClassifier`](harn_vm::security::InjectionClassifier), or `None` if
39/// the neural backend is unavailable, the model is not installed, or it fails to
40/// load. Never panics or registers anything itself — the caller (typically the
41/// runtime's lazy loader seam) decides what to do with the result.
42///
43/// Without the `neural` feature this always returns `None`, so the built-in
44/// heuristic classifier stays active and no model runtime is linked.
45#[cfg(feature = "neural")]
46pub fn load_classifier(
47    base_dir: &std::path::Path,
48    selector: &str,
49) -> Option<Box<dyn harn_vm::security::InjectionClassifier>> {
50    let store = GuardStore::new(base_dir);
51    let dir = match resolve_dir(&store, selector) {
52        Ok(Some(dir)) => dir,
53        Ok(None) => return None,
54        Err(error) => {
55            eprintln!("[harn-guard] cannot resolve guard model `{selector}`: {error}");
56            return None;
57        }
58    };
59
60    // Prefer the installed manifest's recorded id + format; fall back to ONNX
61    // for a bring-your-own directory selector.
62    let (model_id, format) = match store.read_manifest(selector) {
63        Ok(manifest) => (manifest.name, manifest.format),
64        Err(_) => (selector.to_owned(), ModelFormat::Onnx),
65    };
66
67    match neural::OnnxInjectionClassifier::load(&dir, &model_id, format) {
68        Ok(classifier) => Some(Box::new(classifier)),
69        Err(error) => {
70            eprintln!("[harn-guard] failed to load guard model `{model_id}`: {error}");
71            None
72        }
73    }
74}
75
76/// Stub for builds without the `neural` feature: the runtime keeps the heuristic.
77#[cfg(not(feature = "neural"))]
78pub fn load_classifier(
79    _base_dir: &std::path::Path,
80    _selector: &str,
81) -> Option<Box<dyn harn_vm::security::InjectionClassifier>> {
82    None
83}