Approval
Policy-driven approval system for agent-initiated actions including browser navigation, desktop clicks, and shell commands.
The approval module provides a policy-driven approval system that decides whether agent-initiated actions should be allowed, denied, or escalated for user confirmation. This applies to browser navigation, desktop UI interactions, shell commands, and other potentially sensitive operations.
Design Philosophy
The approval system follows three principles:
- Default deny — Actions are blocked unless explicitly permitted by policy
- Tiered decision — Blocklist → Allowlist → Defaults → Ask, in order of priority
- Configuration-driven — Policies are defined in config files, not hardcoded
Architecture
ActionRequest ──▶ ApprovalPolicy::check() ──▶ ApprovalDecision
│
┌────┴────┐
│ Config │ (blocklist → allowlist → defaults → ask)
└─────────┘Core Types
ActionRequest
Represents an action the agent wants to perform:
pub struct ActionRequest {
pub action_type: ActionType,
pub target: String,
pub agent_id: String,
pub context: String,
pub timestamp: DateTime<Utc>,
}ActionType
Categories of actions that can be requested:
pub enum ActionType {
BrowserNavigate,
DesktopClick,
ShellCommand,
FileOperation,
// ... etc
}ApprovalDecision
The outcome of a policy check:
pub enum ApprovalDecision {
Allow,
Deny { reason: String },
Ask { prompt: String },
}Policy Types
ConfigApprovalPolicy
The main policy implementation that loads rules from configuration:
pub struct ConfigApprovalPolicy {
config: PolicyConfig,
}
impl ConfigApprovalPolicy {
pub fn load() -> Self { /* ... */ }
pub async fn check(
&self,
request: &ActionRequest,
) -> ApprovalDecision { /* ... */ }
}Decision flow:
- Check blocklist — if target matches,
Deny - Check allowlist — if target matches,
Allow - Check default rules by action type
- If no rule matches,
Askthe user
PolicyRule
Individual rule definition:
pub struct PolicyRule {
pub pattern: String, // Glob pattern
pub decision: DefaultDecision,
pub description: String,
}Glob Matching
Patterns use glob syntax with path-safe semantics:
pub fn matches_glob(pattern: &str, target: &str) -> bool*matches any characters except/?matches any single character except/**matches any path segment
The ? pattern uses [^/] regex internally for consistent path-safe matching.
Configuration Format
Policies are defined in the main configuration file:
[approval]
[[approval.blocklist]]
pattern = "**/secrets/*"
decision = "deny"
description = "Never access secret files"
[[approval.allowlist]]
pattern = "https://github.com/**"
decision = "allow"
description = "Allow GitHub navigation"
[approval.defaults]
browser_navigate = "ask"
shell_command = "deny"Safety Properties
- No SQL injection — All queries use parameterized bindings
- No lock poisoning — Uses
tokio::synclocks (no poisoning) - Safe arithmetic — Risk scores use
saturating_add/saturating_mul - No UTF-8 slicing — No
&s[..n]patterns
Code Location
src/approval/mod.rs— Module entry point and public APIsrc/approval/config.rs— Configuration loading and glob matchingsrc/approval/policy.rs— Policy evaluation logicsrc/approval/types.rs— Core typessrc/approval/adapters.rs— Integration adapters
See Also
- Configuration — How approval policies are configured
- Security: Overview — Security architecture