Skip to main content

aura_core/effects/
runtime_capability.rs

1//! Runtime capability admission effects.
2//!
3//! This trait exposes runtime capability inventory and admission checks used by
4//! theorem-pack style protocol gating.
5
6use async_trait::async_trait;
7use serde::{Deserialize, Serialize};
8
9/// Stable key for a runtime capability contract.
10#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, Default)]
11pub struct CapabilityKey(String);
12
13impl CapabilityKey {
14    /// Create a capability key from a stable identifier.
15    pub fn new(key: impl Into<String>) -> Self {
16        Self(key.into())
17    }
18
19    /// Borrow the underlying key string.
20    pub fn as_str(&self) -> &str {
21        &self.0
22    }
23}
24
25impl std::fmt::Display for CapabilityKey {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        f.write_str(&self.0)
28    }
29}
30
31impl From<String> for CapabilityKey {
32    fn from(value: String) -> Self {
33        Self::new(value)
34    }
35}
36
37impl From<&str> for CapabilityKey {
38    fn from(value: &str) -> Self {
39        Self::new(value)
40    }
41}
42
43/// Admission errors for theorem-pack/runtime capability checks.
44#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)]
45pub enum AdmissionError {
46    /// A required runtime capability is missing or disabled.
47    #[error("missing runtime capability: {capability}")]
48    MissingCapability { capability: CapabilityKey },
49    /// A required theorem pack is not declared or not admitted by Aura.
50    #[error("missing required theorem pack: {theorem_pack}")]
51    MissingTheoremPack { theorem_pack: String },
52    /// One required theorem-pack capability is missing or disabled.
53    #[error("missing theorem-pack capability `{capability}` for `{theorem_pack}`")]
54    MissingTheoremPackCapability {
55        theorem_pack: String,
56        capability: CapabilityKey,
57    },
58    /// Capability inventory could not be loaded.
59    #[error("runtime capability inventory unavailable: {reason}")]
60    InventoryUnavailable { reason: String },
61    /// Runtime contracts were required but unavailable.
62    #[error("missing runtime contracts for capability admission")]
63    MissingRuntimeContracts,
64    /// Internal admission failure.
65    #[error("runtime capability admission internal error: {reason}")]
66    Internal { reason: String },
67}
68
69/// Runtime capability query/admission interface.
70#[async_trait]
71pub trait RuntimeCapabilityEffects: Send + Sync {
72    /// Fetch the currently admitted runtime capability inventory.
73    async fn capability_inventory(&self) -> Result<Vec<(CapabilityKey, bool)>, AdmissionError>;
74
75    /// Require all listed capabilities to be present and enabled.
76    async fn require_capabilities(&self, required: &[CapabilityKey]) -> Result<(), AdmissionError> {
77        let inventory = self.capability_inventory().await?;
78        for required_key in required {
79            let admitted = inventory
80                .iter()
81                .find(|(present_key, _)| present_key == required_key)
82                .is_some_and(|(_, admitted)| *admitted);
83            if !admitted {
84                return Err(AdmissionError::MissingCapability {
85                    capability: required_key.clone(),
86                });
87            }
88        }
89        Ok(())
90    }
91}
92
93#[async_trait]
94impl<T: RuntimeCapabilityEffects + ?Sized> RuntimeCapabilityEffects for std::sync::Arc<T> {
95    async fn capability_inventory(&self) -> Result<Vec<(CapabilityKey, bool)>, AdmissionError> {
96        (**self).capability_inventory().await
97    }
98
99    async fn require_capabilities(&self, required: &[CapabilityKey]) -> Result<(), AdmissionError> {
100        (**self).require_capabilities(required).await
101    }
102}