Skip to main content

arbiter_storage/
traits.rs

1//! Async storage traits for Arbiter state persistence.
2//!
3//! These traits define the contract for storing agents, sessions, and
4//! delegation links. Implementations can back these with in-memory
5//! storage, SQLite, PostgreSQL, or any other backend.
6//!
7//! REQ-007: Storage behind async trait; swappable backends.
8
9use async_trait::async_trait;
10use chrono::{DateTime, Utc};
11use uuid::Uuid;
12
13use crate::error::StorageError;
14
15// ── Agent storage types ─────────────────────────────────────────────
16
17/// Trust level assigned to an agent, determining its permissions.
18/// Mirrors arbiter_identity::TrustLevel but lives in the storage layer
19/// to avoid circular dependencies.
20#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
21#[serde(rename_all = "lowercase")]
22pub enum StoredTrustLevel {
23    Untrusted,
24    Basic,
25    Verified,
26    Trusted,
27}
28
29impl std::fmt::Display for StoredTrustLevel {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        match self {
32            StoredTrustLevel::Untrusted => write!(f, "untrusted"),
33            StoredTrustLevel::Basic => write!(f, "basic"),
34            StoredTrustLevel::Verified => write!(f, "verified"),
35            StoredTrustLevel::Trusted => write!(f, "trusted"),
36        }
37    }
38}
39
40impl std::str::FromStr for StoredTrustLevel {
41    type Err = StorageError;
42
43    fn from_str(s: &str) -> Result<Self, Self::Err> {
44        match s.to_lowercase().as_str() {
45            "untrusted" => Ok(StoredTrustLevel::Untrusted),
46            "basic" => Ok(StoredTrustLevel::Basic),
47            "verified" => Ok(StoredTrustLevel::Verified),
48            "trusted" => Ok(StoredTrustLevel::Trusted),
49            _ => Err(StorageError::Serialization(format!(
50                "unknown trust level: {s}"
51            ))),
52        }
53    }
54}
55
56/// A stored agent record.
57#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
58pub struct StoredAgent {
59    pub id: Uuid,
60    pub owner: String,
61    pub model: String,
62    pub capabilities: Vec<String>,
63    pub trust_level: StoredTrustLevel,
64    pub created_at: DateTime<Utc>,
65    pub expires_at: Option<DateTime<Utc>>,
66    pub active: bool,
67}
68
69/// A stored delegation link.
70#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
71pub struct StoredDelegationLink {
72    pub id: i64,
73    pub from_agent: Uuid,
74    pub to_agent: Uuid,
75    pub scope_narrowing: Vec<String>,
76    pub created_at: DateTime<Utc>,
77    pub expires_at: Option<DateTime<Utc>>,
78}
79
80// ── Session storage types ───────────────────────────────────────────
81
82/// Stored session status.
83#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
84#[serde(rename_all = "lowercase")]
85pub enum StoredSessionStatus {
86    Active,
87    Closed,
88    Expired,
89}
90
91impl std::fmt::Display for StoredSessionStatus {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        match self {
94            StoredSessionStatus::Active => write!(f, "active"),
95            StoredSessionStatus::Closed => write!(f, "closed"),
96            StoredSessionStatus::Expired => write!(f, "expired"),
97        }
98    }
99}
100
101impl std::str::FromStr for StoredSessionStatus {
102    type Err = StorageError;
103
104    fn from_str(s: &str) -> Result<Self, Self::Err> {
105        match s.to_lowercase().as_str() {
106            "active" => Ok(StoredSessionStatus::Active),
107            "closed" => Ok(StoredSessionStatus::Closed),
108            "expired" => Ok(StoredSessionStatus::Expired),
109            _ => Err(StorageError::Serialization(format!(
110                "unknown session status: {s}"
111            ))),
112        }
113    }
114}
115
116/// Stored data sensitivity level.
117#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
118#[serde(rename_all = "lowercase")]
119pub enum StoredDataSensitivity {
120    Public,
121    Internal,
122    Confidential,
123    Restricted,
124}
125
126impl std::fmt::Display for StoredDataSensitivity {
127    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128        match self {
129            StoredDataSensitivity::Public => write!(f, "public"),
130            StoredDataSensitivity::Internal => write!(f, "internal"),
131            StoredDataSensitivity::Confidential => write!(f, "confidential"),
132            StoredDataSensitivity::Restricted => write!(f, "restricted"),
133        }
134    }
135}
136
137impl std::str::FromStr for StoredDataSensitivity {
138    type Err = StorageError;
139
140    fn from_str(s: &str) -> Result<Self, Self::Err> {
141        match s.to_lowercase().as_str() {
142            "public" => Ok(StoredDataSensitivity::Public),
143            "internal" => Ok(StoredDataSensitivity::Internal),
144            "confidential" => Ok(StoredDataSensitivity::Confidential),
145            "restricted" => Ok(StoredDataSensitivity::Restricted),
146            _ => Err(StorageError::Serialization(format!(
147                "unknown data sensitivity: {s}"
148            ))),
149        }
150    }
151}
152
153/// A stored task session record.
154#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
155pub struct StoredSession {
156    pub session_id: Uuid,
157    pub agent_id: Uuid,
158    pub delegation_chain_snapshot: Vec<String>,
159    pub declared_intent: String,
160    pub authorized_tools: Vec<String>,
161    pub time_limit_secs: i64,
162    pub call_budget: u64,
163    pub calls_made: u64,
164    pub rate_limit_per_minute: Option<u64>,
165    pub rate_window_start: DateTime<Utc>,
166    pub rate_window_calls: u64,
167    pub rate_limit_window_secs: u64,
168    pub data_sensitivity_ceiling: StoredDataSensitivity,
169    pub created_at: DateTime<Utc>,
170    pub status: StoredSessionStatus,
171}
172
173// ── Async storage traits ────────────────────────────────────────────
174
175/// Async storage trait for agent records.
176#[async_trait]
177pub trait AgentStore: Send + Sync {
178    /// Insert a new agent. Returns the stored agent.
179    async fn insert_agent(&self, agent: &StoredAgent) -> Result<(), StorageError>;
180
181    /// Get an agent by ID.
182    async fn get_agent(&self, id: Uuid) -> Result<StoredAgent, StorageError>;
183
184    /// Update an agent's trust level.
185    async fn update_trust_level(
186        &self,
187        id: Uuid,
188        level: StoredTrustLevel,
189    ) -> Result<(), StorageError>;
190
191    /// Mark an agent as inactive.
192    async fn deactivate_agent(&self, id: Uuid) -> Result<(), StorageError>;
193
194    /// List all agents.
195    async fn list_agents(&self) -> Result<Vec<StoredAgent>, StorageError>;
196}
197
198/// Async storage trait for task session records.
199#[async_trait]
200pub trait SessionStore: Send + Sync {
201    /// Insert a new session.
202    async fn insert_session(&self, session: &StoredSession) -> Result<(), StorageError>;
203
204    /// Get a session by ID.
205    async fn get_session(&self, session_id: Uuid) -> Result<StoredSession, StorageError>;
206
207    /// Update a session (full replacement).
208    async fn update_session(&self, session: &StoredSession) -> Result<(), StorageError>;
209
210    /// Delete expired sessions. Returns the number removed.
211    async fn delete_expired_sessions(&self) -> Result<usize, StorageError>;
212
213    /// List all sessions.
214    async fn list_sessions(&self) -> Result<Vec<StoredSession>, StorageError>;
215}
216
217/// Async storage trait for delegation links.
218#[async_trait]
219pub trait DelegationStore: Send + Sync {
220    /// Insert a new delegation link. Returns the auto-generated ID.
221    async fn insert_delegation(&self, link: &StoredDelegationLink) -> Result<i64, StorageError>;
222
223    /// Get all delegation links where `from_agent` matches.
224    async fn get_delegations_from(
225        &self,
226        agent_id: Uuid,
227    ) -> Result<Vec<StoredDelegationLink>, StorageError>;
228
229    /// Get all delegation links where `to_agent` matches.
230    async fn get_delegations_to(
231        &self,
232        agent_id: Uuid,
233    ) -> Result<Vec<StoredDelegationLink>, StorageError>;
234
235    /// Get all delegation links.
236    async fn list_delegations(&self) -> Result<Vec<StoredDelegationLink>, StorageError>;
237}