Skip to main content

oxios_kernel/access_manager/
context.rs

1//! Agent security context — unforgeable identity token.
2//!
3//! `AgentContext` is the proof that the kernel has authenticated an agent.
4//! It cannot be constructed without going through the kernel's agent lifecycle,
5//! making it impossible to bypass security checks at the type level.
6
7use std::sync::Arc;
8
9use crate::capability::CSpace;
10use crate::types::AgentId;
11
12/// Agent security context — unforgeable proof of agent identity.
13///
14/// This type can only be created by:
15/// - `KernelHandle` during agent lifecycle (production)
16/// - `AgentContext::test_fixture()` in `#[cfg(test)]` only
17///
18/// Tools that require access control accept `AgentContext` instead of
19/// a raw `Option<String>`. The type's existence is itself proof that
20/// the kernel has authenticated the agent.
21#[derive(Debug, Clone)]
22pub struct AgentContext {
23    /// Unique agent identifier.
24    pub agent_id: AgentId,
25    /// Human-readable agent name for permission lookups.
26    pub agent_name: String,
27    /// Agent's capability space — determines which tools the agent can access.
28    pub cspace: Arc<CSpace>,
29}
30
31impl AgentContext {
32    /// Create a test fixture with the given name.
33    ///
34    /// Only available in test builds. Generates a random agent ID and a
35    /// permissive CSpace with standard capabilities.
36    #[cfg(test)]
37    pub fn test_fixture(name: &str) -> Self {
38        let agent_id = AgentId::new_v4();
39        let mut cspace = CSpace::new(agent_id);
40
41        // Grant standard capabilities for testing
42        use crate::capability::{Capability, ResourceRef, Rights};
43        cspace.insert(Capability::kernel(
44            ResourceRef::Exec {
45                mode: "shell".into(),
46            },
47            Rights::ALL,
48        ));
49        cspace.insert(Capability::kernel(
50            ResourceRef::Exec {
51                mode: "structured".into(),
52            },
53            Rights::ALL,
54        ));
55        cspace.insert(Capability::kernel(
56            ResourceRef::KernelDomain {
57                domain: "fs".into(),
58            },
59            Rights::ALL,
60        ));
61        cspace.insert(Capability::kernel(
62            ResourceRef::KernelDomain {
63                domain: "agent".into(),
64            },
65            Rights::ALL,
66        ));
67        // Grant all common tools
68        for tool in [
69            "bash", "read", "write", "edit", "grep", "find", "ls", "exec",
70        ] {
71            cspace.insert(Capability::kernel(
72                ResourceRef::KernelDomain {
73                    domain: tool.into(),
74                },
75                Rights::EXECUTE,
76            ));
77        }
78
79        Self {
80            agent_id,
81            agent_name: name.to_string(),
82            cspace: Arc::new(cspace),
83        }
84    }
85
86    /// Create a test fixture with specific capabilities.
87    #[cfg(test)]
88    pub fn test_fixture_with_cspace(name: &str, cspace: CSpace) -> Self {
89        Self {
90            agent_id: AgentId::new_v4(),
91            agent_name: name.to_string(),
92            cspace: Arc::new(cspace),
93        }
94    }
95}
96
97impl std::fmt::Display for AgentContext {
98    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99        write!(f, "agent:{}:{}", self.agent_name, self.agent_id)
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_fixture_has_name() {
109        let ctx = AgentContext::test_fixture("test-agent");
110        assert_eq!(ctx.agent_name, "test-agent");
111        assert!(!ctx.agent_id.is_nil());
112    }
113
114    #[test]
115    fn test_display() {
116        let ctx = AgentContext::test_fixture("my-agent");
117        let s = format!("{}", ctx);
118        assert!(s.starts_with("agent:my-agent:"));
119    }
120}