Skip to main content

systemprompt_security/authz/audit/
repository.rs

1//! `governance_decisions` insert primitive.
2//!
3//! Single canonical writer for the table. Both the extension's
4//! `POST /govern/authz` handler (for resolved decisions) and core's
5//! [`DbAuditSink`](super::DbAuditSink) (for webhook-fault, default-deny, and
6//! unrestricted-allow decisions) call this repository so there is exactly one
7//! SQL statement that knows the column layout.
8
9use sqlx::PgPool;
10
11#[derive(Debug)]
12pub struct GovernanceDecisionRecord<'a> {
13    pub id: &'a str,
14    pub user_id: &'a str,
15    pub session_id: &'a str,
16    pub tool_name: &'a str,
17    pub agent_id: Option<&'a str>,
18    pub agent_scope: &'a str,
19    pub decision: &'a str,
20    pub policy: &'a str,
21    pub reason: &'a str,
22    pub evaluated_rules: &'a serde_json::Value,
23    pub plugin_id: Option<&'a str>,
24}
25
26#[derive(Debug, Clone)]
27pub struct GovernanceDecisionRepository {
28    pool: std::sync::Arc<PgPool>,
29}
30
31impl GovernanceDecisionRepository {
32    pub const fn from_pool(pool: std::sync::Arc<PgPool>) -> Self {
33        Self { pool }
34    }
35
36    pub fn pool(&self) -> &PgPool {
37        &self.pool
38    }
39
40    pub async fn insert(&self, record: &GovernanceDecisionRecord<'_>) -> Result<(), sqlx::Error> {
41        insert_governance_decision(&self.pool, record).await
42    }
43}
44
45pub async fn insert_governance_decision(
46    pool: &PgPool,
47    record: &GovernanceDecisionRecord<'_>,
48) -> Result<(), sqlx::Error> {
49    sqlx::query!(
50        "INSERT INTO governance_decisions (id, user_id, session_id, tool_name, agent_id, \
51         agent_scope, decision, policy, reason, evaluated_rules, plugin_id) VALUES ($1, $2, $3, \
52         $4, $5, $6, $7, $8, $9, $10, $11)",
53        record.id,
54        record.user_id,
55        record.session_id,
56        record.tool_name,
57        record.agent_id,
58        record.agent_scope,
59        record.decision,
60        record.policy,
61        record.reason,
62        record.evaluated_rules,
63        record.plugin_id,
64    )
65    .execute(pool)
66    .await?;
67    Ok(())
68}