Skip to main content

hessra_cap_policy/
config.rs

1//! TOML configuration parsing for the CList policy backend.
2
3use serde::Deserialize;
4use std::collections::HashMap;
5use thiserror::Error;
6
7/// Errors from policy configuration parsing.
8#[derive(Error, Debug)]
9pub enum PolicyConfigError {
10    #[error("failed to read policy file: {0}")]
11    Io(#[from] std::io::Error),
12    #[error("failed to parse policy TOML: {0}")]
13    Parse(#[from] toml::de::Error),
14}
15
16/// Top-level policy configuration.
17#[derive(Debug, Clone, Deserialize)]
18pub struct PolicyConfig {
19    /// Objects and their capability spaces.
20    #[serde(default)]
21    pub objects: Vec<ObjectConfig>,
22
23    /// Data classifications: maps target object IDs to exposure labels.
24    #[serde(default)]
25    pub classifications: HashMap<String, Vec<String>>,
26
27    /// Exposure restriction rules.
28    #[serde(default)]
29    pub exposure_rules: Vec<ExposureRuleConfig>,
30}
31
32/// Configuration for a single object and its capability space.
33#[derive(Debug, Clone, Deserialize)]
34pub struct ObjectConfig {
35    /// The object ID (e.g., "service:api-gateway", "agent:openclaw").
36    pub id: String,
37
38    /// Whether this object can delegate capabilities.
39    #[serde(default)]
40    pub can_delegate: bool,
41
42    /// Optional identity token configuration.
43    #[serde(default)]
44    pub identity: Option<IdentityConfigEntry>,
45
46    /// The object's capability grants.
47    #[serde(default)]
48    pub capabilities: Vec<CapabilityConfig>,
49}
50
51/// Identity token configuration for an object.
52#[derive(Debug, Clone, Deserialize)]
53pub struct IdentityConfigEntry {
54    /// Token TTL in seconds.
55    #[serde(default = "default_ttl")]
56    pub ttl: i64,
57    /// Whether the identity is delegatable.
58    #[serde(default)]
59    pub delegatable: bool,
60}
61
62fn default_ttl() -> i64 {
63    3600
64}
65
66/// A single capability grant configuration.
67#[derive(Debug, Clone, Deserialize)]
68pub struct CapabilityConfig {
69    /// The target object ID (e.g., "service:user-service", "tool:web-search").
70    pub target: String,
71    /// Allowed operations on the target (e.g., ["read", "write"]).
72    pub operations: Vec<String>,
73}
74
75/// An exposure restriction rule.
76#[derive(Debug, Clone, Deserialize)]
77pub struct ExposureRuleConfig {
78    /// Exposure labels that trigger this rule. Supports glob patterns (e.g., "PII:*").
79    pub labels: Vec<String>,
80
81    /// Match mode: "any" (default) or "all".
82    /// - "any": rule triggers if any label matches
83    /// - "all": rule triggers only if all labels match
84    #[serde(default = "default_match_mode")]
85    pub r#match: String,
86
87    /// Target object IDs that are blocked when this rule triggers.
88    /// Supports glob patterns (e.g., "tool:*").
89    pub blocks: Vec<String>,
90}
91
92fn default_match_mode() -> String {
93    "any".to_string()
94}
95
96impl PolicyConfig {
97    /// Load policy from a TOML file path.
98    pub fn from_file(path: &std::path::Path) -> Result<Self, PolicyConfigError> {
99        let content = std::fs::read_to_string(path)?;
100        Self::parse(&content)
101    }
102
103    /// Parse policy from a TOML string.
104    pub fn parse(content: &str) -> Result<Self, PolicyConfigError> {
105        let config: PolicyConfig = toml::from_str(content)?;
106        Ok(config)
107    }
108}