use std::sync::Arc;
use batpak::coordinate::Coordinate;
use batpak::store::{Open, Store};
use hostbat::{
GoldenVector, GuardDescriptor, HostError, HostModule, SchemaDescriptor, SchemaId, SchemaRole,
SchemaVersion,
};
use syncbat::{
AdmissionDecision, AdmissionGuard, Ctx, EffectClass, Handler, HandlerError, HandlerResult,
OperationDescriptor, OperationEffectRow,
};
use crate::contract::events::BoundaryReportEvent;
use crate::{
BackendId, BackendRegistry, BoundaryPlan, BoundaryPlanner, BoundaryReport, BoundaryRunner,
BoundarySpec, PlanError,
};
pub const BOUNDARY_MODULE_ID: &str = "bvisor.boundary";
pub const BOUNDARY_RUN_OP: &str = "bvisor.boundary.run";
pub const BOUNDARY_GUARD_CODE: &str = "bvisor.boundary.admission.v1";
pub const BOUNDARY_RECEIPT_NAMESPACE: &str = "bvisor";
const SPEC_SCHEMA_REF: &str = "bvisor.boundary.spec.v1";
const REPORT_SCHEMA_REF: &str = "bvisor.boundary.report.v1";
const REPORT_RECEIPT_KIND: &str = "bvisor.boundary.report.v1";
struct BoundaryContext {
registry: Arc<BackendRegistry>,
backend: BackendId,
store: Arc<Store<Open>>,
coordinate: Coordinate,
}
impl BoundaryContext {
fn plan(&self, spec: &BoundarySpec) -> Result<BoundaryPlan, PlanError> {
BoundaryPlanner::new(&self.registry).plan(spec, &self.backend)
}
}
fn decode_spec(input: &[u8]) -> Result<BoundarySpec, String> {
batpak::canonical::from_bytes(input).map_err(|error| error.to_string())
}
fn encode_report(report: &BoundaryReport) -> Result<Vec<u8>, String> {
batpak::canonical::to_bytes(report).map_err(|error| error.to_string())
}
fn boundary_schema(
id: &'static str,
role: SchemaRole,
golden_label: &'static str,
) -> Result<SchemaDescriptor, HostError> {
let golden_bytes =
batpak::canonical::to_bytes(&golden_label).map_err(|error| HostError::SchemaInvalid {
schema: id.to_owned(),
detail: format!("boundary golden fixture encoding failed: {error}"),
})?;
SchemaDescriptor::new(
SchemaId::new(id)?,
SchemaVersion(1),
role,
vec![GoldenVector::new("boundary", golden_bytes)],
)
}
fn plan_error_code(error: &PlanError) -> &'static str {
match error {
PlanError::Unsupported { .. } => "unsupported",
PlanError::WorkloadIncompatible { .. } => "workload-incompatible",
PlanError::ProfileInsufficient { .. } => "profile-insufficient",
PlanError::BudgetInvalid { .. } => "budget-invalid",
PlanError::EvidenceUnsatisfiable { .. } => "evidence-unsatisfiable",
PlanError::BudgetRefused { .. } => "budget-refused",
PlanError::UnknownBackend { .. } => "unknown-backend",
PlanError::ShadowDivergence { .. } => "shadow-divergence",
PlanError::InvalidPolicy { .. } => "invalid-policy",
}
}
struct BoundaryGuard {
cx: Arc<BoundaryContext>,
}
impl AdmissionGuard for BoundaryGuard {
fn admit(
&self,
_descriptor: &OperationDescriptor,
input: &[u8],
_cx: &mut Ctx<'_>,
) -> AdmissionDecision {
let spec = match decode_spec(input) {
Ok(spec) => spec,
Err(detail) => return AdmissionDecision::deny("decode-error", detail),
};
match self.cx.plan(&spec) {
Ok(_plan) => AdmissionDecision::Admit,
Err(error) => AdmissionDecision::deny(plan_error_code(&error), error.to_string()),
}
}
}
struct BoundaryRunHandler {
cx: Arc<BoundaryContext>,
}
impl Handler for BoundaryRunHandler {
fn handle(&mut self, input: &[u8], _cx: &mut Ctx<'_>) -> HandlerResult {
let spec = decode_spec(input).map_err(HandlerError::invalid_input)?;
let plan = self
.cx
.plan(&spec)
.map_err(|error| HandlerError::failed(error.to_string()))?;
let report = BoundaryRunner::new(&self.cx.registry)
.run(&plan)
.map_err(|error| HandlerError::failed(error.to_string()))?;
let event = BoundaryReportEvent {
report: report.clone(),
};
let _receipt = self
.cx
.store
.append_typed(&self.cx.coordinate, &event)
.map_err(|error| HandlerError::failed(format!("persist boundary report: {error}")))?;
encode_report(&report).map_err(HandlerError::failed)
}
}
pub struct BoundaryModuleConfig {
pub registry: Arc<BackendRegistry>,
pub backend: BackendId,
pub store: Arc<Store<Open>>,
pub coordinate: Coordinate,
}
pub fn boundary_module(config: BoundaryModuleConfig) -> Result<HostModule, HostError> {
let cx = Arc::new(BoundaryContext {
registry: config.registry,
backend: config.backend,
store: config.store,
coordinate: config.coordinate,
});
let descriptor = OperationDescriptor::new(
BOUNDARY_RUN_OP,
EffectClass::Emit,
SPEC_SCHEMA_REF,
REPORT_SCHEMA_REF,
REPORT_RECEIPT_KIND,
)
.with_effect_row(OperationEffectRow::new().emits_receipt(REPORT_RECEIPT_KIND));
HostModule::builder(BOUNDARY_MODULE_ID, 1)
.operation(
descriptor,
BoundaryRunHandler {
cx: Arc::clone(&cx),
},
)?
.schema(boundary_schema(
SPEC_SCHEMA_REF,
SchemaRole::OperationInput,
"bvisor-boundary-spec-v1",
)?)?
.schema(boundary_schema(
REPORT_SCHEMA_REF,
SchemaRole::OperationOutput,
"bvisor-boundary-report-v1",
)?)?
.schema(boundary_schema(
REPORT_RECEIPT_KIND,
SchemaRole::ReceiptPayload,
"bvisor-boundary-receipt-v1",
)?)?
.guard(
GuardDescriptor::new(BOUNDARY_GUARD_CODE),
BoundaryGuard { cx },
)?
.receipt_namespace(BOUNDARY_RECEIPT_NAMESPACE)?
.build()
}
#[cfg(all(test, feature = "dangerous-test-hooks"))]
mod e2e_tests {
use super::{boundary_module, BoundaryModuleConfig, BOUNDARY_RUN_OP};
use crate::contract::budget::{BudgetRequirements, MinGuarantee};
use crate::contract::host_control::HostControl;
use crate::contract::plan::{EvidenceRequirements, Workload};
use crate::sim::backend::{LieMode, OneShotLiar, SimBackend};
use crate::{BackendId, BackendRegistry, BoundaryReport, BoundarySpec, InertBackend};
use batpak::coordinate::Coordinate;
use batpak::store::{Open, Store, StoreConfig};
use hostbat::{Host, HostBuilder};
use std::sync::Arc;
use tempfile::TempDir;
fn valid_spec() -> BoundarySpec {
BoundarySpec {
workload: Workload::Process {
exe: "true".to_owned(),
args: Vec::new(),
},
capabilities: Vec::new(),
controls: vec![HostControl::LaunchWorkload],
budgets: BudgetRequirements::uniform(64, MinGuarantee::Mediated),
evidence: EvidenceRequirements::default(),
}
}
fn open_store(dir: &TempDir) -> Arc<Store<Open>> {
Arc::new(Store::open(StoreConfig::new(dir.path())).expect("open store"))
}
fn report_coordinate() -> Coordinate {
Coordinate::new("bvisor:boundary", "bvisor:report").expect("coordinate")
}
fn host_over(registry: BackendRegistry, backend: BackendId, store: Arc<Store<Open>>) -> Host {
let module = boundary_module(BoundaryModuleConfig {
registry: Arc::new(registry),
backend,
store,
coordinate: report_coordinate(),
})
.expect("boundary module builds");
HostBuilder::new()
.mount(module)
.expect("mount")
.build()
.expect("build host")
}
#[test]
fn honest_sim_admits_runs_seals_and_persists() {
let mut registry = BackendRegistry::new();
registry.register(Arc::new(SimBackend::new(Box::new(OneShotLiar::new(
LieMode::Honest,
)))));
let backend = BackendId::new(SimBackend::ID);
let dir = TempDir::new().expect("tempdir");
let store = open_store(&dir);
let before = store.stats().event_count;
let mut host = host_over(registry, backend, Arc::clone(&store));
let spec_bytes = batpak::canonical::to_bytes(&valid_spec()).expect("encode spec");
let result = host
.invoke(BOUNDARY_RUN_OP, spec_bytes)
.expect("an honest-sim spec is admitted, run, and sealed");
let report: BoundaryReport =
batpak::canonical::from_bytes(result.output()).expect("the output is a sealed report");
assert_ne!(
report.body_hash.0, [0u8; 32],
"the returned report is sealed (body_hash computed over the body)",
);
assert_eq!(
store.stats().event_count,
before + 1,
"the sealed report persisted as exactly one durable 0xE event",
);
}
#[test]
fn inert_backend_denies_at_admission_with_no_effect_and_no_persist() {
let mut registry = BackendRegistry::new();
registry.register(Arc::new(InertBackend::new()));
let backend = BackendId::new(InertBackend::ID);
let dir = TempDir::new().expect("tempdir");
let store = open_store(&dir);
let before = store.stats().event_count;
let mut host = host_over(registry, backend, Arc::clone(&store));
let spec_bytes = batpak::canonical::to_bytes(&valid_spec()).expect("encode spec");
let result = host.invoke(BOUNDARY_RUN_OP, spec_bytes);
assert!(
result.is_err(),
"the all-Unsupported Inert floor refuses every budgeted spec → the guard \
denies before the handler (and any backend effect) runs",
);
assert_eq!(
store.stats().event_count,
before,
"a denied boundary persists nothing — no effect without admission",
);
}
}