API Reference
This section contains comprehensive API documentation for DaemonEye, covering all public interfaces, data structures, and usage examples.
Core API
The core API provides the fundamental interfaces for process monitoring, alerting, and data management.
API Overview
Component APIs
DaemonEye provides APIs for each component:
- ProcMonD API: Process collection and system monitoring
- daemoneye-agent API: Alerting and orchestration
- daemoneye-cli API: Command-line interface and management
- daemoneye-lib API: Shared library interfaces
API Design Principles
- Async-First: All APIs use async/await patterns
- Error Handling: Structured error types with
thiserror
- Type Safety: Strong typing with Rust's type system
- Documentation: Comprehensive rustdoc comments
- Examples: Code examples for all public APIs
Quick Reference
Process Collection
use daemoneye_lib::collector::ProcessCollector;
let collector = ProcessCollector::new();
let processes = collector.collect_processes().await?;
Database Operations
use daemoneye_lib::storage::Database;
let db = Database::new("processes.db").await?;
let processes = db.query_processes("SELECT * FROM processes WHERE pid = ?", &[1234]).await?;
Alert Management
use daemoneye_lib::alerting::AlertManager;
let mut alert_manager = AlertManager::new();
alert_manager.add_sink(Box::new(SyslogSink::new("daemon")?));
alert_manager.send_alert(alert).await?;
Configuration Management
use daemoneye_lib::config::Config;
let config = Config::load_from_file("config.yaml").await?;
let scan_interval = config.get::<u64>("app.scan_interval_ms")?;
Data Structures
ProcessInfo
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ProcessInfo {
pub pid: u32,
pub name: String,
pub executable_path: Option<String>,
pub command_line: Option<String>,
pub start_time: Option<DateTime<Utc>>,
pub cpu_usage: Option<f64>,
pub memory_usage: Option<u64>,
pub status: ProcessStatus,
pub executable_hash: Option<String>,
pub collection_time: DateTime<Utc>,
}
Alert
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Alert {
pub id: Uuid,
pub rule_name: String,
pub severity: AlertSeverity,
pub message: String,
pub process: ProcessInfo,
pub timestamp: DateTime<Utc>,
pub metadata: HashMap<String, String>,
}
DetectionRule
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DetectionRule {
pub name: String,
pub description: String,
pub sql_query: String,
pub priority: u32,
pub enabled: bool,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
Error Types
CollectionError
#[derive(Debug, Error)]
pub enum CollectionError {
#[error("Permission denied accessing process {pid}")]
PermissionDenied { pid: u32 },
#[error("Process {pid} no longer exists")]
ProcessNotFound { pid: u32 },
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("System error: {0}")]
SystemError(String),
}
DatabaseError
#[derive(Debug, Error)]
pub enum DatabaseError {
#[error("Database connection failed: {0}")]
ConnectionFailed(String),
#[error("Query execution failed: {0}")]
QueryFailed(String),
#[error("Transaction failed: {0}")]
TransactionFailed(String),
#[error("SQLite error: {0}")]
SqliteError(#[from] rusqlite::Error),
}
AlertError
#[derive(Debug, Error)]
pub enum AlertError {
#[error("Alert delivery failed: {0}")]
DeliveryFailed(String),
#[error("Invalid alert format: {0}")]
InvalidFormat(String),
#[error("Alert sink error: {0}")]
SinkError(String),
#[error("Alert queue full")]
QueueFull,
}
Service Traits
ProcessCollectionService
#[async_trait]
pub trait ProcessCollectionService: Send + Sync {
async fn collect_processes(&self) -> Result<CollectionResult, CollectionError>;
async fn get_system_info(&self) -> Result<SystemInfo, CollectionError>;
async fn get_process_by_pid(&self, pid: u32) -> Result<Option<ProcessInfo>, CollectionError>;
}
DetectionService
#[async_trait]
pub trait DetectionService: Send + Sync {
async fn execute_rules(&self, scan_context: &ScanContext) -> Result<Vec<Alert>, DetectionError>;
async fn load_rules(&self) -> Result<Vec<DetectionRule>, DetectionError>;
async fn add_rule(&self, rule: DetectionRule) -> Result<(), DetectionError>;
async fn remove_rule(&self, rule_name: &str) -> Result<(), DetectionError>;
}
AlertSink
#[async_trait]
pub trait AlertSink: Send + Sync {
async fn send(&self, alert: &Alert) -> Result<DeliveryResult, DeliveryError>;
async fn health_check(&self) -> HealthStatus;
fn name(&self) -> &str;
}
Configuration API
Config
pub struct Config {
app: AppConfig,
database: DatabaseConfig,
alerting: AlertingConfig,
security: SecurityConfig,
}
impl Config {
pub async fn load_from_file(path: &str) -> Result<Self, ConfigError>;
pub async fn load_from_env() -> Result<Self, ConfigError>;
pub fn get<T>(&self, key: &str) -> Result<T, ConfigError> where T: DeserializeOwned;
pub fn set<T>(&mut self, key: &str, value: T) -> Result<(), ConfigError> where T: Serialize;
pub fn validate(&self) -> Result<(), ConfigError>;
}
AppConfig
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AppConfig {
pub scan_interval_ms: u64,
pub batch_size: usize,
pub log_level: String,
pub data_dir: PathBuf,
pub log_dir: PathBuf,
pub pid_file: Option<PathBuf>,
pub user: Option<String>,
pub group: Option<String>,
pub max_memory_mb: Option<u64>,
pub max_cpu_percent: Option<f64>,
}
Database API
Database
pub struct Database {
conn: Connection,
}
impl Database {
pub async fn new(path: &str) -> Result<Self, DatabaseError>;
pub async fn create_schema(&self) -> Result<(), DatabaseError>;
pub async fn insert_process(&self, process: &ProcessInfo) -> Result<(), DatabaseError>;
pub async fn get_process(&self, pid: u32) -> Result<Option<ProcessInfo>, DatabaseError>;
pub async fn query_processes(&self, query: &str, params: &[&dyn ToSql]) -> Result<Vec<ProcessInfo>, DatabaseError>;
pub async fn insert_alert(&self, alert: &Alert) -> Result<(), DatabaseError>;
pub async fn get_alerts(&self, limit: Option<usize>) -> Result<Vec<Alert>, DatabaseError>;
pub async fn cleanup_old_data(&self, retention_days: u32) -> Result<(), DatabaseError>;
}
Alerting API
AlertManager
pub struct AlertManager {
sinks: Vec<Box<dyn AlertSink>>,
queue: Arc<Mutex<VecDeque<Alert>>>,
max_queue_size: usize,
}
impl AlertManager {
pub fn new() -> Self;
pub fn add_sink(&mut self, sink: Box<dyn AlertSink>);
pub async fn send_alert(&self, alert: Alert) -> Result<(), AlertError>;
pub async fn health_check(&self) -> HealthStatus;
pub fn queue_size(&self) -> usize;
pub fn queue_capacity(&self) -> usize;
}
Alert Sinks
SyslogSink
pub struct SyslogSink {
facility: String,
priority: String,
tag: String,
}
impl SyslogSink {
pub fn new(facility: &str) -> Result<Self, SinkError>;
pub fn with_priority(self, priority: &str) -> Self;
pub fn with_tag(self, tag: &str) -> Self;
}
WebhookSink
pub struct WebhookSink {
url: String,
method: String,
timeout: Duration,
retry_attempts: u32,
headers: HashMap<String, String>,
}
impl WebhookSink {
pub fn new(url: &str) -> Self;
pub fn with_method(self, method: &str) -> Self;
pub fn with_timeout(self, timeout: Duration) -> Self;
pub fn with_retry_attempts(self, attempts: u32) -> Self;
pub fn with_header(self, key: &str, value: &str) -> Self;
}
FileSink
pub struct FileSink {
path: PathBuf,
format: OutputFormat,
rotation: RotationPolicy,
max_files: usize,
}
impl FileSink {
pub fn new(path: &str) -> Self;
pub fn with_format(self, format: OutputFormat) -> Self;
pub fn with_rotation(self, rotation: RotationPolicy) -> Self;
pub fn with_max_files(self, max_files: usize) -> Self;
}
CLI API
Cli
pub struct Cli {
pub command: Commands,
pub config: Option<PathBuf>,
pub log_level: String,
}
#[derive(Subcommand)]
pub enum Commands {
Run(RunCommand),
Config(ConfigCommand),
Rules(RulesCommand),
Alerts(AlertsCommand),
Health(HealthCommand),
Query(QueryCommand),
Logs(LogsCommand),
}
impl Cli {
pub async fn execute(self) -> Result<(), CliError>;
}
Commands
RunCommand
#[derive(Args)]
pub struct RunCommand {
#[arg(short, long)]
pub daemon: bool,
#[arg(short, long)]
pub foreground: bool,
}
ConfigCommand
#[derive(Subcommand)]
pub enum ConfigCommand {
Show(ConfigShowCommand),
Set(ConfigSetCommand),
Get(ConfigGetCommand),
Validate(ConfigValidateCommand),
Load(ConfigLoadCommand),
}
#[derive(Args)]
pub struct ConfigShowCommand {
#[arg(long)]
pub include_defaults: bool,
#[arg(long)]
pub format: Option<String>,
}
RulesCommand
#[derive(Subcommand)]
pub enum RulesCommand {
List(RulesListCommand),
Add(RulesAddCommand),
Remove(RulesRemoveCommand),
Enable(RulesEnableCommand),
Disable(RulesDisableCommand),
Validate(RulesValidateCommand),
Test(RulesTestCommand),
Reload(RulesReloadCommand),
}
IPC API
IpcServer
pub struct IpcServer {
socket_path: PathBuf,
handlers: HashMap<String, Box<dyn IpcHandler>>,
}
impl IpcServer {
pub fn new(socket_path: &str) -> Self;
pub fn add_handler(&mut self, name: &str, handler: Box<dyn IpcHandler>);
pub async fn run(self) -> Result<(), IpcError>;
}
IpcClient
pub struct IpcClient {
socket_path: PathBuf,
connection: Option<Connection>,
}
impl IpcClient {
pub async fn new(socket_path: &str) -> Result<Self, IpcError>;
pub async fn send_request(&self, request: IpcRequest) -> Result<IpcResponse, IpcError>;
pub async fn close(self) -> Result<(), IpcError>;
}
IPC Messages
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum IpcRequest {
CollectProcesses,
GetProcess { pid: u32 },
QueryProcesses { query: String, params: Vec<Value> },
GetAlerts { limit: Option<usize> },
SendAlert { alert: Alert },
HealthCheck,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum IpcResponse {
Processes(Vec<ProcessInfo>),
Process(Option<ProcessInfo>),
QueryResult(Vec<ProcessInfo>),
Alerts(Vec<Alert>),
AlertSent,
Health(HealthStatus),
Error(String),
}
Utility APIs
Logger
pub struct Logger {
level: Level,
format: LogFormat,
output: LogOutput,
}
impl Logger {
pub fn new() -> Self;
pub fn with_level(self, level: Level) -> Self;
pub fn with_format(self, format: LogFormat) -> Self;
pub fn with_output(self, output: LogOutput) -> Self;
pub fn init(self) -> Result<(), LogError>;
}
Metrics
pub struct Metrics {
registry: Registry,
}
impl Metrics {
pub fn new() -> Self;
pub fn counter(&self, name: &str) -> Counter;
pub fn gauge(&self, name: &str) -> Gauge;
pub fn histogram(&self, name: &str) -> Histogram;
pub fn register(&self, metric: Box<dyn Metric>) -> Result<(), MetricsError>;
}
Error Handling
Error Types
All APIs use structured error types:
#[derive(Debug, Error)]
pub enum DaemonEyeError {
#[error("Collection error: {0}")]
Collection(#[from] CollectionError),
#[error("Database error: {0}")]
Database(#[from] DatabaseError),
#[error("Alert error: {0}")]
Alert(#[from] AlertError),
#[error("Configuration error: {0}")]
Config(#[from] ConfigError),
#[error("IPC error: {0}")]
Ipc(#[from] IpcError),
#[error("CLI error: {0}")]
Cli(#[from] CliError),
}
Error Context
Use anyhow
for error context:
use anyhow::{Context, Result};
pub async fn collect_processes() -> Result<Vec<ProcessInfo>> {
let processes = sysinfo::System::new_all()
.processes()
.values()
.map(|p| ProcessInfo::from(p))
.collect::<Vec<_>>();
Ok(processes)
.context("Failed to collect process information")
}
Examples
Basic Process Monitoring
use daemoneye_lib::collector::ProcessCollector;
use daemoneye_lib::storage::Database;
use daemoneye_lib::alerting::AlertManager;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize components
let collector = ProcessCollector::new();
let db = Database::new("processes.db").await?;
let mut alert_manager = AlertManager::new();
// Add alert sink
alert_manager.add_sink(Box::new(SyslogSink::new("daemon")?));
// Collect processes
let processes = collector.collect_processes().await?;
// Store in database
for process in &processes {
db.insert_process(process).await?;
}
// Check for suspicious processes
let suspicious = db.query_processes(
"SELECT * FROM processes WHERE name LIKE '%suspicious%'",
&[]
).await?;
// Send alerts
for process in suspicious {
let alert = Alert::new("suspicious_process", process);
alert_manager.send_alert(alert).await?;
}
Ok(())
}
Custom Alert Sink
use daemoneye_lib::alerting::{AlertSink, Alert, DeliveryResult, DeliveryError};
use async_trait::async_trait;
pub struct CustomSink {
endpoint: String,
client: reqwest::Client,
}
impl CustomSink {
pub fn new(endpoint: &str) -> Self {
Self {
endpoint: endpoint.to_string(),
client: reqwest::Client::new(),
}
}
}
#[async_trait]
impl AlertSink for CustomSink {
async fn send(&self, alert: &Alert) -> Result<DeliveryResult, DeliveryError> {
let response = self.client
.post(&self.endpoint)
.json(alert)
.send()
.await
.map_err(|e| DeliveryError::Network(e.to_string()))?;
if response.status().is_success() {
Ok(DeliveryResult::Success)
} else {
Err(DeliveryError::Http(response.status().as_u16()))
}
}
async fn health_check(&self) -> HealthStatus {
match self.client.get(&self.endpoint).send().await {
Ok(response) if response.status().is_success() => HealthStatus::Healthy,
_ => HealthStatus::Unhealthy,
}
}
fn name(&self) -> &str {
"custom_sink"
}
}
This API reference provides comprehensive documentation for all DaemonEye APIs. For additional examples and usage patterns, consult the specific API documentation.