Skip to main content

aios_protocol/
policy.rs

1//! Policy types: capabilities, policy sets, and evaluation results.
2
3use serde::{Deserialize, Serialize};
4
5/// A capability token representing a specific permission.
6///
7/// Capabilities are pattern-based strings like `"fs:read:/session/**"`.
8/// They support glob matching for flexible policy evaluation.
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
10pub struct Capability(pub String);
11
12impl Capability {
13    pub fn new(value: impl Into<String>) -> Self {
14        Self(value.into())
15    }
16
17    pub fn fs_read(glob: &str) -> Self {
18        Self(format!("fs:read:{glob}"))
19    }
20
21    pub fn fs_write(glob: &str) -> Self {
22        Self(format!("fs:write:{glob}"))
23    }
24
25    pub fn net_egress(host: &str) -> Self {
26        Self(format!("net:egress:{host}"))
27    }
28
29    pub fn exec(command: &str) -> Self {
30        Self(format!("exec:cmd:{command}"))
31    }
32
33    pub fn secrets(scope: &str) -> Self {
34        Self(format!("secrets:read:{scope}"))
35    }
36
37    pub fn as_str(&self) -> &str {
38        &self.0
39    }
40}
41
42/// A set of policy rules governing agent capabilities.
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct PolicySet {
45    pub allow_capabilities: Vec<Capability>,
46    pub gate_capabilities: Vec<Capability>,
47    pub max_tool_runtime_secs: u64,
48    pub max_events_per_turn: u64,
49}
50
51impl Default for PolicySet {
52    fn default() -> Self {
53        Self {
54            allow_capabilities: vec![
55                Capability::fs_read("/session/**"),
56                Capability::fs_write("/session/artifacts/**"),
57                Capability::exec("git"),
58            ],
59            gate_capabilities: vec![Capability::new("payments:initiate")],
60            max_tool_runtime_secs: 30,
61            max_events_per_turn: 256,
62        }
63    }
64}
65
66/// Result of evaluating capabilities against a policy set.
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct PolicyEvaluation {
69    pub allowed: Vec<Capability>,
70    pub requires_approval: Vec<Capability>,
71    pub denied: Vec<Capability>,
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn capability_factory_methods() {
80        assert_eq!(Capability::fs_read("/tmp").as_str(), "fs:read:/tmp");
81        assert_eq!(Capability::fs_write("/out").as_str(), "fs:write:/out");
82        assert_eq!(
83            Capability::net_egress("api.com").as_str(),
84            "net:egress:api.com"
85        );
86        assert_eq!(Capability::exec("git").as_str(), "exec:cmd:git");
87        assert_eq!(Capability::secrets("prod").as_str(), "secrets:read:prod");
88    }
89
90    #[test]
91    fn policy_set_default() {
92        let ps = PolicySet::default();
93        assert_eq!(ps.allow_capabilities.len(), 3);
94        assert_eq!(ps.gate_capabilities.len(), 1);
95        assert_eq!(ps.max_tool_runtime_secs, 30);
96    }
97
98    #[test]
99    fn capability_serde_roundtrip() {
100        let cap = Capability::fs_read("/session/**");
101        let json = serde_json::to_string(&cap).unwrap();
102        let back: Capability = serde_json::from_str(&json).unwrap();
103        assert_eq!(cap, back);
104    }
105}