* refactor: Rename project from NetGuardia to Mantis * fix: convert mantis-frontend from tracked files to submodule * wip * feat: Suricata integration stabilization and config unification Fix a series of bugs in the Suricata daemon integration and unify configuration so users interact only with config.toml. * docs: sur-001-c1 Suricata integration stabilization findings
5.0 KiB
Mantis
Network intrusion detection system combining eBPF packet capture with ML-based anomaly detection and Suricata/Snort rule matching.
Language Convention
- Conversation: Always use Traditional Chinese (繁體中文)
- Files, docs, comments, code: Always use English
- This applies to ALL generated content without exception
Dependency Policy
- ML inference: ort-tract (pure Rust, no native ORT binary) Do not switch to native ORT without explicit discussion.
- Async runtime: tokio only, do not add async-std or smol.
- Logging: macros::log! only, do not add log crate or use tracing:: directly.
- Locking: std::sync::Mutex is preferred; parking_lot::Mutex where performance matters.
- ort version is pinned to =2.0.0-rc.12, do not upgrade without checking ort-tract compatibility.
Workspace Structure
mantis/ - Main application (ML, eBPF userspace, HTTP API, WebSocket)
common/ - Shared types used by both userspace and eBPF programs
macros/ - Procedural macros: log!, traceable!, loggable!
ingress-ebpf/ - eBPF ingress packet capture program
egress-ebpf/ - eBPF egress packet capture program
mantis-frontend/ - Next.js web UI
Build
Full build requires eBPF toolchain and system libs (libelf, boost for vectorscan).
To type-check without eBPF (for ML/API changes):
SKIP_EBPF_BUILD=1 cargo check --package mantis
Ignore these expected errors when SKIP_EBPF_BUILD is set:
environment variable ARTIFACTCS_PATH not definedenvironment variable CSV_RECORD_PATH not definedenvironment variable RULES_DB_PATH not definedcouldn't read .../mantis-ingresscouldn't read .../mantis-egress
mantis Source Layout
src/
├── core/
│ ├── ebpf/ - XDP/AF_XDP packet capture, access control
│ └── infrastructure/- AppServices init, AppConfig, GeoIP, MLAlert
├── detection/
│ ├── ml/ - ML inference pipeline
│ │ ├── engine.rs - inference loop (tokio interval)
│ │ ├── flow_tracker.rs - per-flow packet aggregation
│ │ ├── feature_extractor.rs - flow -> feature vector
│ │ ├── inference.rs - sliding window + autoencoder MSE
│ │ ├── model_loader.rs - ort Session creation
│ │ ├── config_loader.rs - inference_config.json
│ │ ├── aggregator.rs - attack event dedup/aggregation
│ │ └── traffic_logger.rs - CSV recording mode
│ └── rule/ - Suricata/Snort rule matching (vectorscan)
├── model/
│ ├── error/ - one file per domain (ml, ebpf, http, rule, ...)
│ ├── log/ - one file per domain (ml, ebpf, http, rule, ...)
│ └── ml_detection.rs- shared ML types (FlowKey, DetectionResult, ...)
├── web/ - actix-web HTTP API and WebSocket endpoints
└── utils/ - packet parsing, logging setup, boot time
Macros (always use these)
log!
Logs an error or loggable value. Routes to tracing level automatically.
use macros::log;
log!(MLError::ModelLoadFailed { path });
log!(MLLog::InferenceCompleted { total_flows, anomaly, benign, duration_ms, throughput });
traceable!
Defines an error enum in src/model/error/<domain>.rs.
traceable! {
MLError {
#[no_source]
#[error("Failed to load ONNX model from: {path:?}")]
ModelLoadFailed { path: PathBuf } => tracing::Level::ERROR,
}
}
loggable!
Defines a log message enum in src/model/log/<domain>.rs.
loggable! {
MLLog {
#[error("ML artifacts loaded - {info}")]
ModelsLoaded { info: String } => tracing::Level::INFO,
}
}
Conventions
- New error variant -> add to
src/model/error/<domain>.rsusingtraceable! - New log message -> add to
src/model/log/<domain>.rsusingloggable! - Never use
println!,eprintln!, ortracing::directly — always uselog!() - Shared data types (structs, enums used across modules) go in
src/model/ - Comments in English only, no special characters
ML Pipeline
Inference runs on a tokio interval (default 5s):
eBPF packet -> FlowTracker (per-flow stats)
-> FlowFeatures (extract + normalize ~80 features)
-> Inference (sliding window buffer per src_ip)
-> ort Session (LSTM autoencoder, MSE score)
-> AttackAggregator -> MLAlert (WebSocket)
Inference engine: ort-tract (pure Rust, no native ORT binary required).
ort::set_api(ort_tract::api()) must be called before any Session is created.
Session is wrapped in Mutex<Session> because Session::run requires &mut self.
Config
Runtime config: config.toml
ML artifacts: mantis/static/artifacts/
deep_autoencoder.onnx- LSTM autoencoder modelinference_config.json- window size, feature names, scaler params, threshold
Implementation Priority
See TODO for the full backlog.