oxios_kernel/capability/
template.rs1use crate::types::AgentId;
28use uuid::Uuid;
29
30use super::types::{CSpace, Capability, CapabilityId, Issuer, ResourceRef, Rights};
31
32#[derive(Debug, Clone)]
38pub struct CapabilityTemplate {
39 caps: Vec<(ResourceRef, Rights)>,
40}
41
42impl CapabilityTemplate {
43 pub fn worker() -> Self {
49 let mut t = Self { caps: Vec::new() };
50 t.caps.push((
51 ResourceRef::Exec {
52 mode: "shell".into(),
53 },
54 Rights::EXECUTE | Rights::READ,
55 ));
56 t.caps
57 .push((ResourceRef::Browser, Rights::READ | Rights::EXECUTE));
58 t
59 }
60
61 pub fn standard() -> Self {
66 let mut t = Self::worker();
67 t.caps.push((
68 ResourceRef::KernelDomain {
69 domain: "memory".into(),
70 },
71 Rights::READ,
72 ));
73 t
74 }
75
76 pub fn operator() -> Self {
82 let mut t = Self::standard();
83 let extra = vec![
84 (
85 ResourceRef::Space { id: Uuid::nil() },
86 Rights::READ | Rights::WRITE,
87 ),
88 (
89 ResourceRef::Agent { id: AgentId::nil() },
90 Rights::READ | Rights::WRITE,
91 ),
92 (
93 ResourceRef::A2a,
94 Rights::READ | Rights::WRITE | Rights::EXECUTE,
95 ),
96 (
97 ResourceRef::KernelDomain {
98 domain: "persona".into(),
99 },
100 Rights::READ | Rights::WRITE,
101 ),
102 (
103 ResourceRef::KernelDomain {
104 domain: "program".into(),
105 },
106 Rights::READ | Rights::WRITE | Rights::EXECUTE,
107 ),
108 (
109 ResourceRef::Mcp { server: "*".into() },
110 Rights::READ | Rights::EXECUTE,
111 ),
112 (
114 ResourceRef::KernelDomain {
115 domain: "memory".into(),
116 },
117 Rights::READ | Rights::WRITE,
118 ),
119 ];
120 t.caps.extend(extra);
121 t
122 }
123
124 pub fn supervisor() -> Self {
129 let mut t = Self::operator();
130 let admin = vec![
131 (
132 ResourceRef::KernelDomain {
133 domain: "security".into(),
134 },
135 Rights::ALL,
136 ),
137 (
138 ResourceRef::KernelDomain {
139 domain: "budget".into(),
140 },
141 Rights::READ | Rights::WRITE,
142 ),
143 (
144 ResourceRef::KernelDomain {
145 domain: "resource".into(),
146 },
147 Rights::READ | Rights::WRITE,
148 ),
149 (
150 ResourceRef::KernelDomain {
151 domain: "cron".into(),
152 },
153 Rights::READ | Rights::WRITE | Rights::EXECUTE,
154 ),
155 ];
156 t.caps.extend(admin);
157 t
158 }
159
160 pub fn with_skills(names: &[&str]) -> Self {
166 let mut t = Self::worker();
167 for name in names {
168 t.caps.push((
169 ResourceRef::Skill {
170 name: (*name).into(),
171 },
172 Rights::EXECUTE | Rights::READ,
173 ));
174 }
175 t
176 }
177
178 pub fn with(mut self, resource: ResourceRef, rights: Rights) -> Self {
182 self.caps.push((resource, rights));
183 self
184 }
185
186 pub fn build(&self) -> CSpace {
188 self.build_for(AgentId::new_v4())
189 }
190
191 pub fn build_for(&self, agent_id: AgentId) -> CSpace {
193 let mut cspace = CSpace::new(agent_id);
194 for (resource, rights) in &self.caps {
195 let cap = Capability {
196 id: CapabilityId::new(),
197 resource: resource.clone(),
198 rights: *rights,
199 issuer: Issuer::Kernel,
200 };
201 cspace.insert(cap);
202 }
203 cspace
204 }
205
206 pub fn len(&self) -> usize {
208 self.caps.len()
209 }
210
211 pub fn is_empty(&self) -> bool {
213 self.caps.is_empty()
214 }
215}
216
217impl Default for CapabilityTemplate {
218 fn default() -> Self {
219 Self::worker()
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn worker_has_exec_and_browser() {
229 let cs = CapabilityTemplate::worker().build();
230 assert!(cs.can(
231 &ResourceRef::Exec {
232 mode: "shell".into()
233 },
234 Rights::EXECUTE
235 ));
236 assert!(cs.can(&ResourceRef::Browser, Rights::READ));
237 assert_eq!(cs.len(), 2);
238 }
239
240 #[test]
241 fn standard_adds_memory_read() {
242 let cs = CapabilityTemplate::standard().build();
243 assert!(cs.can(
244 &ResourceRef::KernelDomain {
245 domain: "memory".into()
246 },
247 Rights::READ
248 ));
249 assert!(!cs.can(
250 &ResourceRef::KernelDomain {
251 domain: "memory".into()
252 },
253 Rights::WRITE
254 ));
255 }
256
257 #[test]
258 fn operator_has_a2a_and_mcp() {
259 let cs = CapabilityTemplate::operator().build();
260 assert!(cs.can(&ResourceRef::A2a, Rights::EXECUTE));
261 assert!(cs.can(&ResourceRef::Mcp { server: "*".into() }, Rights::EXECUTE));
262 }
263
264 #[test]
265 fn supervisor_has_security_all() {
266 let cs = CapabilityTemplate::supervisor().build();
267 assert!(cs.can(
268 &ResourceRef::KernelDomain {
269 domain: "security".into()
270 },
271 Rights::ALL
272 ));
273 }
274
275 #[test]
276 fn with_skills_scoped() {
277 let cs = CapabilityTemplate::with_skills(&["git", "gh"]).build();
278 assert!(cs.can(&ResourceRef::Skill { name: "git".into() }, Rights::EXECUTE));
279 assert!(cs.can(&ResourceRef::Skill { name: "gh".into() }, Rights::EXECUTE));
280 assert!(!cs.can(
281 &ResourceRef::Skill {
282 name: "curl".into()
283 },
284 Rights::EXECUTE
285 ));
286 }
287
288 #[test]
289 fn builder_chaining() {
290 let cs = CapabilityTemplate::worker()
291 .with(
292 ResourceRef::KernelDomain {
293 domain: "custom".into(),
294 },
295 Rights::READ,
296 )
297 .build();
298 assert!(cs.can(
299 &ResourceRef::KernelDomain {
300 domain: "custom".into()
301 },
302 Rights::READ
303 ));
304 }
305
306 #[test]
307 fn build_for_specific_agent() {
308 let id = AgentId::new_v4();
309 let cs = CapabilityTemplate::worker().build_for(id);
310 assert_eq!(cs.agent_id, id);
311 }
312}