Skip to main content

agent_guard/
lib.rs

1//! AgentGuard - Linux control plane for AI agent security.
2//!
3//! # Scaffold notice
4//!
5//! This crate is a **scaffold**: it defines the security trait surface and
6//! receipt types, but does not implement any OS-level enforcement (BPF LSM,
7//! cgroup v2, Landlock, seccomp, or eBPF). The `initialize()` method only
8//! sets an internal boolean. No actual sandboxing is applied.
9//!
10//! Do not rely on this crate for production agent containment. Until real
11//! enforcement is implemented, treat all security decisions as advisory.
12//!
13//! # Linux Only
14//!
15//! This crate is only available on Linux. It will not compile on other systems.
16//!
17//! # Example
18//!
19//! ```ignore
20//! use agent_guard::{AgentGuard, Subject, Action, ActionType};
21//!
22//! let mut guard = AgentGuard::new();
23//! guard.initialize()?;
24//! ```
25
26#![cfg_attr(not(target_os = "linux"), allow(dead_code))]
27
28mod control_plane;
29mod error;
30mod receipt;
31
32pub use control_plane::ControlPlane;
33pub use error::{Error, Result};
34pub use receipt::{Action, ActionType, SecurityDecision, SecurityMechanism, Subject};
35
36#[cfg(test)]
37use chrono::Utc;
38use std::sync::atomic::{AtomicBool, Ordering};
39
40/// AgentGuard security manager.
41///
42/// This is the main entry point for the agent-guard crate. It provides
43/// a unified interface to Linux security mechanisms.
44///
45/// # Platform Support
46///
47/// This struct is only available on Linux. On other platforms, all methods
48/// will return errors indicating the platform is unsupported.
49#[derive(Debug)]
50pub struct AgentGuard {
51    initialized: AtomicBool,
52}
53
54impl AgentGuard {
55    /// Create a new AgentGuard instance.
56    ///
57    /// # Deprecation notice
58    ///
59    /// agent-guard is a scaffold — no OS enforcement is implemented.
60    /// This constructor is deprecated to prevent false confidence in
61    /// security guarantees. See the crate-level scaffold notice.
62    #[deprecated(note = "agent-guard is a scaffold — no OS enforcement is implemented")]
63    #[cfg(target_os = "linux")]
64    pub fn new() -> Self {
65        Self {
66            initialized: AtomicBool::new(false),
67        }
68    }
69
70    /// Create a new AgentGuard instance (non-Linux stub).
71    #[deprecated(note = "agent-guard is a scaffold — no OS enforcement is implemented")]
72    #[cfg(not(target_os = "linux"))]
73    pub fn new() -> Self {
74        Self {
75            initialized: AtomicBool::new(false),
76        }
77    }
78
79    /// Initialize the security control plane.
80    ///
81    /// # Deprecation notice
82    ///
83    /// This method is a no-op scaffold: it only sets an internal boolean.
84    /// No OS-level enforcement (BPF LSM, cgroup, Landlock, seccomp, eBPF)
85    /// is applied. Deprecated to prevent false security confidence.
86    ///
87    /// On Linux, this sets up the security mechanisms.
88    /// On other platforms, this returns an error.
89    #[deprecated(note = "agent-guard is a scaffold — no OS enforcement is implemented")]
90    pub fn initialize(&mut self) -> Result<()> {
91        #[cfg(target_os = "linux")]
92        {
93            // Linux-specific initialization would go here
94            // For now, mark as initialized
95            self.initialized.store(true, Ordering::SeqCst);
96            Ok(())
97        }
98
99        #[cfg(not(target_os = "linux"))]
100        {
101            Err(Error::InvalidConfig(
102                "AgentGuard is only available on Linux".to_string(),
103            ))
104        }
105    }
106
107    /// Check if the guard is initialized.
108    pub fn is_initialized(&self) -> bool {
109        self.initialized.load(Ordering::SeqCst)
110    }
111
112    /// Create a security decision for a subject and action.
113    #[cfg(test)]
114    fn make_decision(&self, subject: &Subject, action: &Action) -> SecurityDecision {
115        let decision_id = format!("guard-{}-{}", subject.name, Utc::now().timestamp());
116        SecurityDecision {
117            decision_id,
118            subject: subject.clone(),
119            action: action.clone(),
120            allowed: true,
121            reason: "Allowed by AgentGuard control plane".to_string(),
122            timestamp: Utc::now(),
123            mechanisms: vec![],
124        }
125    }
126}
127
128impl Default for AgentGuard {
129    fn default() -> Self {
130        Self::new()
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    #[test]
139    #[allow(deprecated)]
140    fn test_agent_guard_new() {
141        let guard = AgentGuard::new();
142        assert!(!guard.is_initialized());
143    }
144
145    #[test]
146    #[allow(deprecated)]
147    #[cfg(target_os = "linux")]
148    fn test_agent_guard_initialize() {
149        let mut guard = AgentGuard::new();
150        assert!(guard.initialize().is_ok());
151        assert!(guard.is_initialized());
152    }
153
154    #[test]
155    #[allow(deprecated)]
156    fn test_make_decision() {
157        let guard = AgentGuard::new();
158        let subject = Subject {
159            pid: Some(1234),
160            name: "test-agent".to_string(),
161            cgroup_path: None,
162        };
163        let action = Action {
164            action_type: ActionType::FileRead,
165            resource: "/etc/passwd".to_string(),
166            metadata: None,
167        };
168        let decision = guard.make_decision(&subject, &action);
169        assert!(decision.allowed);
170        assert_eq!(decision.subject.name, "test-agent");
171    }
172
173    #[test]
174    #[allow(deprecated)]
175    #[cfg(not(target_os = "linux"))]
176    fn test_agent_guard_not_available() {
177        let mut guard = AgentGuard::new();
178        let result = guard.initialize();
179        assert!(result.is_err());
180    }
181}