use crate::contract::budget::{
AdmittedBudgets, BudgetDimension, BudgetFailure, BudgetRequirements,
};
use crate::contract::capability::{Capability, Enforcement, EvidenceClaim, EvidenceSet};
use crate::contract::host_control::HostControl;
use crate::contract::ids::{BackendId, BoundaryPlanHash};
use crate::contract::support::BackendProfileSnapshot;
use serde::{Deserialize, Serialize};
pub const BOUNDARY_PLAN_SCHEMA_VERSION: u16 = 1;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum BoundaryRequirement {
Capability(Capability),
HostControl(HostControl),
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Workload {
Process {
exe: String,
args: Vec<String>,
},
Wasm {
module_ref: String,
},
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct EvidenceRequirements {
pub require_captured_streams: bool,
pub require_exit_status: bool,
}
impl EvidenceRequirements {
#[must_use]
pub fn required_claims(&self) -> EvidenceSet {
let mut set = EvidenceSet::new();
if self.require_captured_streams {
set.insert(EvidenceClaim::CapturedStreams);
}
if self.require_exit_status {
set.insert(EvidenceClaim::TerminalOutcome);
}
set
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct BoundarySpec {
pub workload: Workload,
pub capabilities: Vec<Capability>,
pub controls: Vec<HostControl>,
pub budgets: BudgetRequirements,
pub evidence: EvidenceRequirements,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct AdmittedRequirement {
pub requirement: BoundaryRequirement,
pub enforcement: Enforcement,
pub mechanism: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct BoundaryPlan {
pub schema_version: u16,
pub plan_id: BoundaryPlanHash,
pub backend: BackendId,
pub profile: BackendProfileSnapshot,
pub admitted: Vec<AdmittedRequirement>,
pub workload: Workload,
pub budgets: AdmittedBudgets,
pub evidence: EvidenceRequirements,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum PlanError {
Unsupported {
requirement: BoundaryRequirement,
backend: BackendId,
},
WorkloadIncompatible {
backend: BackendId,
workload: Workload,
},
ProfileInsufficient {
backend: BackendId,
detail: String,
},
BudgetInvalid {
detail: String,
},
EvidenceUnsatisfiable {
backend: BackendId,
detail: String,
},
BudgetRefused {
backend: BackendId,
dimension: BudgetDimension,
failure: BudgetFailure,
},
UnknownBackend {
backend: BackendId,
},
InvalidPolicy {
detail: String,
},
ShadowDivergence {
detail: String,
},
}
impl std::fmt::Display for PlanError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Unsupported {
requirement,
backend,
} => write!(
f,
"backend {backend} cannot enforce required requirement {requirement:?}"
),
Self::WorkloadIncompatible { backend, workload } => {
write!(f, "backend {backend} cannot run workload {workload:?}")
}
Self::ProfileInsufficient { backend, detail } => {
write!(
f,
"backend {backend} machine profile insufficient: {detail}"
)
}
Self::BudgetInvalid { detail } => write!(f, "invalid budget: {detail}"),
Self::EvidenceUnsatisfiable { backend, detail } => write!(
f,
"backend {backend} cannot satisfy required evidence: {detail}"
),
Self::BudgetRefused {
backend,
dimension,
failure,
} => write!(
f,
"backend {backend} budget membrane refused dimension {dimension:?}: {failure:?}"
),
Self::UnknownBackend { backend } => write!(f, "unknown backend {backend}"),
Self::InvalidPolicy { detail } => write!(f, "invalid capability policy: {detail}"),
Self::ShadowDivergence { detail } => {
write!(f, "admission shadow divergence (fail-closed): {detail}")
}
}
}
}
impl std::error::Error for PlanError {}