deepstrike_core/governance/
audit.rs1use compact_str::CompactString;
2
3use crate::types::message::ToolCall;
4use crate::types::policy::GovernanceVerdict;
5
6#[derive(Debug, Clone)]
8pub struct AuditEntry {
9 pub tool_name: CompactString,
10 pub call_id: CompactString,
11 pub verdict: &'static str,
12 pub stage: Option<&'static str>,
13 pub reason: Option<String>,
14 pub timestamp_ms: u64,
15}
16
17pub struct AuditLog {
19 entries: Vec<AuditEntry>,
20 current_time_ms: u64,
21}
22
23impl AuditLog {
24 pub fn new() -> Self {
25 Self {
26 entries: Vec::new(),
27 current_time_ms: 0,
28 }
29 }
30
31 pub fn set_time(&mut self, now_ms: u64) {
32 self.current_time_ms = now_ms;
33 }
34
35 pub fn record_allow(&mut self, call: &ToolCall) {
36 self.entries.push(AuditEntry {
37 tool_name: call.name.clone(),
38 call_id: call.id.clone(),
39 verdict: "allow",
40 stage: None,
41 reason: None,
42 timestamp_ms: self.current_time_ms,
43 });
44 }
45
46 pub fn record_deny(&mut self, call: &ToolCall, verdict: &GovernanceVerdict) {
47 let (stage, reason) = match verdict {
48 GovernanceVerdict::Deny { stage, reason } => (Some(*stage), Some(reason.clone())),
49 GovernanceVerdict::RateLimited { retry_after_ms } => (
50 Some("rate_limit"),
51 Some(format!("retry after {}ms", retry_after_ms)),
52 ),
53 GovernanceVerdict::AskUser { reason } => (Some("permission"), Some(reason.clone())),
54 GovernanceVerdict::Allow => (None, None),
55 };
56 self.entries.push(AuditEntry {
57 tool_name: call.name.clone(),
58 call_id: call.id.clone(),
59 verdict: "deny",
60 stage,
61 reason,
62 timestamp_ms: self.current_time_ms,
63 });
64 }
65
66 pub fn entries(&self) -> &[AuditEntry] {
67 &self.entries
68 }
69
70 pub fn len(&self) -> usize {
71 self.entries.len()
72 }
73
74 pub fn is_empty(&self) -> bool {
75 self.entries.is_empty()
76 }
77
78 pub fn clear(&mut self) {
79 self.entries.clear();
80 }
81}
82
83impl Default for AuditLog {
84 fn default() -> Self {
85 Self::new()
86 }
87}