use std::sync::Arc;
#[cfg(target_os = "linux")]
struct PostureKernelVerifier {
strict: bool,
signing_keys: Vec<String>,
allowed_hashes: Vec<String>,
daemon: Option<Arc<boatramp_server::DaemonRuntime>>,
}
#[cfg(target_os = "linux")]
impl std::fmt::Debug for PostureKernelVerifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PostureKernelVerifier")
.field("strict", &self.strict)
.field("signing_keys", &self.signing_keys.len())
.field("allowed_hashes", &self.allowed_hashes.len())
.field("has_daemon", &self.daemon.is_some())
.finish()
}
}
#[cfg(target_os = "linux")]
impl boatramp_firecracker::KernelVerifier for PostureKernelVerifier {
fn verify(&self, bytes: &[u8], expected_hash: &str) -> std::result::Result<(), String> {
let sig = self
.daemon
.as_ref()
.and_then(|d| d.effective().default_kernel.clone())
.filter(|dk| dk.sha256 == expected_hash)
.and_then(|dk| dk.sig);
let kref = boatramp_core::daemon_config::KernelRef {
source: expected_hash.to_string(),
sha256: expected_hash.to_string(),
sig,
};
boatramp_core::kernel_trust::verify_kernel(
bytes,
&kref,
self.strict,
&self.signing_keys,
&self.allowed_hashes,
)
.map_err(|e| e.to_string())
}
}
#[cfg(target_os = "macos")]
struct VzPostureKernelVerifier {
strict: bool,
signing_keys: Vec<String>,
allowed_hashes: Vec<String>,
daemon: Option<Arc<boatramp_server::DaemonRuntime>>,
}
#[cfg(target_os = "macos")]
impl std::fmt::Debug for VzPostureKernelVerifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VzPostureKernelVerifier")
.field("strict", &self.strict)
.field("signing_keys", &self.signing_keys.len())
.field("allowed_hashes", &self.allowed_hashes.len())
.field("has_daemon", &self.daemon.is_some())
.finish()
}
}
#[cfg(target_os = "macos")]
impl boatramp_vz::KernelVerifier for VzPostureKernelVerifier {
fn verify(&self, bytes: &[u8], expected_hash: &str) -> std::result::Result<(), String> {
let sig = self
.daemon
.as_ref()
.and_then(|d| d.effective().default_kernel.clone())
.filter(|dk| dk.sha256 == expected_hash)
.and_then(|dk| dk.sig);
let kref = boatramp_core::daemon_config::KernelRef {
source: expected_hash.to_string(),
sha256: expected_hash.to_string(),
sig,
};
boatramp_core::kernel_trust::verify_kernel(
bytes,
&kref,
self.strict,
&self.signing_keys,
&self.allowed_hashes,
)
.map_err(|e| e.to_string())
}
}
#[cfg(target_os = "macos")]
fn macos_supports_vz() -> bool {
if cfg!(not(target_arch = "aarch64")) {
return false;
}
let major = sysctl_string("kern.osproductversion")
.and_then(|v| v.split('.').next().and_then(|m| m.parse::<u32>().ok()));
matches!(major, Some(m) if m >= 15)
}
#[cfg(target_os = "macos")]
fn sysctl_string(name: &str) -> Option<String> {
let out = std::process::Command::new("sysctl")
.args(["-n", name])
.output()
.ok()?;
if !out.status.success() {
return None;
}
Some(String::from_utf8_lossy(&out.stdout).trim().to_string())
}
pub async fn build_compute(
cfg: Option<&crate::config::ComputeConfig>,
storage: std::sync::Arc<dyn boatramp_core::Storage>,
data_dir: &std::path::Path,
node_id: u64,
strict: bool,
daemon: Option<Arc<boatramp_server::DaemonRuntime>>,
worker_exe: Option<&std::path::Path>,
) -> (
boatramp_core::compute::BackendRegistry,
boatramp_core::compute::Node,
) {
use boatramp_core::compute::{BackendKind, BackendRegistry, Node};
let mut backends: BackendRegistry = std::collections::BTreeMap::new();
let empty_node = |id| Node {
id,
region: None,
labels: std::collections::BTreeMap::new(),
free_vcpus: 0,
free_mem_mib: 0,
backends: Vec::new(),
};
let Some(cfg) = cfg else {
return (backends, empty_node(node_id));
};
match boatramp_docker::DockerBackend::connect() {
Ok(docker) => {
let docker = docker
.with_endpoint(cfg.docker_endpoint)
.with_volume_mode(cfg.docker_volume_mode)
.with_data_dir(data_dir)
.with_writable_root_allowed(!strict)
.with_cap_add_allowed(!strict);
if docker.reachable().await {
backends.insert("docker".to_string(), std::sync::Arc::new(docker));
} else {
tracing::debug!("no reachable docker daemon; skipping docker backend");
}
}
Err(e) => tracing::debug!(%e, "docker backend unavailable"),
}
#[cfg(target_os = "linux")]
match worker_exe.map_or_else(std::env::current_exe, |p| Ok(p.to_path_buf())) {
Ok(self_exe) => match boatramp_container::ContainerBackend::new(
storage.clone(),
data_dir.to_path_buf(),
cfg.bridge.clone(),
&cfg.subnet,
self_exe,
) {
Ok(c) => {
let c = c.with_cap_add_allowed(!strict);
backends.insert("container".to_string(), std::sync::Arc::new(c));
}
Err(e) => tracing::warn!(%e, "container backend unavailable"),
},
Err(e) => tracing::warn!(%e, "current_exe for container backend"),
}
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
if std::path::Path::new("/dev/kvm").exists() {
match (
worker_exe.map_or_else(std::env::current_exe, |p| Ok(p.to_path_buf())),
boatramp_core::ipam::IpPool::new(&cfg.subnet),
) {
(Ok(self_exe), Ok(pool)) => {
let gateway = pool.gateway().to_string();
let verifier: Arc<dyn boatramp_firecracker::KernelVerifier> =
Arc::new(PostureKernelVerifier {
strict,
signing_keys: cfg.kernel_signing_pubkeys.clone(),
allowed_hashes: cfg.kernel_allowed_hashes.clone(),
daemon: daemon.clone(),
});
match boatramp_firecracker::EmbeddedVmmBackend::new(
storage.clone(),
self_exe, data_dir.to_path_buf(),
cfg.bridge.clone(),
gateway,
&cfg.subnet,
verifier,
) {
Ok(vmm) => {
backends.insert("vmm-embedded".to_string(), std::sync::Arc::new(vmm));
}
Err(e) => tracing::warn!(%e, "embedded VMM backend unavailable"),
}
}
(Err(e), _) => tracing::warn!(%e, "current_exe for VMM backend"),
(_, Err(e)) => tracing::warn!(%e, "bad compute subnet for VMM backend"),
}
} else {
tracing::debug!("no /dev/kvm; skipping embedded VMM backend");
}
#[cfg(target_os = "macos")]
if macos_supports_vz() {
match (
worker_exe.map_or_else(std::env::current_exe, |p| Ok(p.to_path_buf())),
boatramp_core::ipam::IpPool::new(&cfg.subnet),
) {
(Ok(self_exe), Ok(_pool)) => {
let verifier: Arc<dyn boatramp_vz::KernelVerifier> =
Arc::new(VzPostureKernelVerifier {
strict,
signing_keys: cfg.kernel_signing_pubkeys.clone(),
allowed_hashes: cfg.kernel_allowed_hashes.clone(),
daemon: daemon.clone(),
});
match boatramp_vz::VzBackend::new(
storage.clone(),
self_exe, data_dir.to_path_buf(),
&cfg.subnet, verifier,
) {
Ok(vz) => {
let vz = vz.with_writable_root_allowed(!strict);
backends.insert("vmm-vz".to_string(), std::sync::Arc::new(vz));
}
Err(e) => tracing::warn!(%e, "macOS VMM backend unavailable"),
}
}
(Err(e), _) => tracing::warn!(%e, "current_exe for macOS VMM backend"),
(_, Err(e)) => tracing::warn!(%e, "bad compute subnet for macOS VMM backend"),
}
} else {
tracing::debug!("not Apple silicon + macOS 15+; skipping macOS VMM backend");
}
let _ = (&storage, data_dir); #[cfg(not(any(all(target_os = "linux", target_arch = "x86_64"), target_os = "macos")))]
let _ = (strict, &daemon);
let free_vcpus = if cfg.vcpus > 0 {
cfg.vcpus
} else {
std::thread::available_parallelism()
.map(|n| n.get() as u32)
.unwrap_or(1)
};
let free_mem_mib = if cfg.mem_mib > 0 { cfg.mem_mib } else { 1024 };
let advertised: Vec<BackendKind> = backends
.iter()
.map(|(id, b)| {
let caps = b.capabilities();
BackendKind {
id: id.clone(),
isolation: caps.isolation,
persistent_volumes: caps.persistent_volumes,
scale_to_zero: caps.scale_to_zero,
}
})
.collect();
tracing::info!(backends = ?advertised, free_vcpus, free_mem_mib, "compute node inventory");
let node = Node {
id: node_id,
region: cfg.region.clone(),
labels: std::collections::BTreeMap::new(),
free_vcpus,
free_mem_mib,
backends: advertised,
};
(backends, node)
}