aa_core/storage/policy_store.rs
1//! [`PolicyStore`] — read-side access to an agent's effective policy.
2
3use super::{AgentId, PolicyDocument, Result};
4use async_trait::async_trait;
5
6/// Fetches and invalidates the effective [`PolicyDocument`] for an agent.
7///
8/// The runtime calls [`get_policy`](PolicyStore::get_policy) on the hot path
9/// before evaluating an action, so backends are expected to serve from a fast
10/// store (or a cache wrapper layered on top — see Epic C). When a policy changes,
11/// [`invalidate`](PolicyStore::invalidate) drops any cached copy so the next read
12/// reloads from the source of truth.
13///
14/// # Example
15///
16/// ```
17/// use aa_core::storage::{AgentId, PolicyDocument, PolicyStore, Result, StorageError};
18/// use async_trait::async_trait;
19///
20/// /// A backend that has no policy for any agent.
21/// struct EmptyPolicyStore;
22///
23/// #[async_trait]
24/// impl PolicyStore for EmptyPolicyStore {
25/// async fn get_policy(&self, agent_id: &AgentId) -> Result<PolicyDocument> {
26/// Err(StorageError::NotFound(format!("{:?}", agent_id.as_bytes())))
27/// }
28///
29/// async fn invalidate(&self, _agent_id: &AgentId) -> Result<()> {
30/// Ok(())
31/// }
32/// }
33/// ```
34#[async_trait]
35pub trait PolicyStore: Send + Sync {
36 /// Return the effective policy for `agent_id`.
37 ///
38 /// Returns [`StorageError::NotFound`](super::StorageError::NotFound) when the
39 /// agent has no policy on record.
40 async fn get_policy(&self, agent_id: &AgentId) -> Result<PolicyDocument>;
41
42 /// Drop any cached policy for `agent_id` so the next read reloads it.
43 ///
44 /// Idempotent: invalidating an agent with no cached entry succeeds.
45 async fn invalidate(&self, agent_id: &AgentId) -> Result<()>;
46}