Skip to main content

icebox_client/
lib.rs

1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct GovernAction {
7    pub action: String,
8    pub target: String,
9    pub capability: String,
10    pub impact: String,
11    pub destructive: bool,
12    #[serde(default)]
13    pub metadata: HashMap<String, String>,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct GovernResult {
18    pub approved: bool,
19    pub decision: String,
20    pub reason: Option<String>,
21    pub decision_id: u64,
22    pub chain_tip: String,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct ActionOutcome {
27    pub success: bool,
28    pub evidence: Vec<String>,
29    pub data: serde_json::Value,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct RecordResult {
34    pub decision_id: u64,
35    pub chain_tip: String,
36}
37
38#[derive(Debug, thiserror::Error)]
39pub enum ClientError {
40    #[error("HTTP error: {0}")]
41    Http(#[from] reqwest::Error),
42    #[error("governance denied: {0}")]
43    Denied(String),
44    #[error("response parse error: {0}")]
45    Parse(String),
46}
47
48/// ICEBOX Governance SDK client. Wraps any action in a GEE with one call.
49pub struct GovernClient {
50    client: reqwest::Client,
51    base_url: String,
52}
53
54impl GovernClient {
55    pub fn new(base_url: &str) -> Result<Self, ClientError> {
56        let client = reqwest::Client::builder().build()?;
57        Ok(GovernClient {
58            client,
59            base_url: base_url.trim_end_matches('/').to_string(),
60        })
61    }
62
63    pub async fn govern(&self, action: GovernAction) -> Result<GovernResult, ClientError> {
64        let resp = self
65            .client
66            .post(format!("{}/api/v1/govern", self.base_url))
67            .json(&action)
68            .send()
69            .await?;
70        let result: GovernResult = resp.json().await?;
71        Ok(result)
72    }
73
74    pub async fn record(
75        &self,
76        action: GovernAction,
77        outcome: ActionOutcome,
78    ) -> Result<RecordResult, ClientError> {
79        let resp = self
80            .client
81            .post(format!("{}/api/v1/govern/record", self.base_url))
82            .json(&(action, outcome))
83            .send()
84            .await?;
85        let result: RecordResult = resp.json().await?;
86        Ok(result)
87    }
88}