kumoba 0.3.0

Fast VM sandboxes for AI agents, as a Rust library
Documentation
use crate::{Error, Result, RuntimeConfig};
use std::path::{Path, PathBuf};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HostPlatform {
    MacOsArm64,
    LinuxArm64,
    LinuxX86_64,
}
impl HostPlatform {
    pub fn current() -> Result<Self> {
        match (std::env::consts::OS,std::env::consts::ARCH) {
            ("macos","aarch64") => Ok(Self::MacOsArm64),
            ("linux","aarch64") => Ok(Self::LinuxArm64),
            ("linux","x86_64") => Ok(Self::LinuxX86_64),
            _ => Err(Error::InvalidInput("supported hosts are macOS/ARM64 and Linux/ARM64 or x86_64; Windows requires the experimental WSL2 Linux route".into())),
        }
    }
    pub fn image_arch(self) -> &'static str {
        match self {
            Self::MacOsArm64 | Self::LinuxArm64 => "arm64",
            Self::LinuxX86_64 => "x86_64",
        }
    }
    pub fn guest_target(self) -> &'static str {
        match self {
            Self::MacOsArm64 | Self::LinuxArm64 => "aarch64-unknown-linux-musl",
            Self::LinuxX86_64 => "x86_64-unknown-linux-musl",
        }
    }
    fn libraries(self) -> (Vec<PathBuf>, Vec<PathBuf>) {
        match self {
            Self::MacOsArm64 => (
                vec![
                    "/opt/homebrew/opt/libkrun/lib/libkrun.1.dylib".into(),
                    "/opt/homebrew/opt/libkrun/lib/libkrun.dylib".into(),
                ],
                vec!["/opt/homebrew/opt/libkrunfw/lib/libkrunfw.5.dylib".into()],
            ),
            Self::LinuxArm64 | Self::LinuxX86_64 => {
                let arch = if self == Self::LinuxArm64 {
                    "aarch64"
                } else {
                    "x86_64"
                };
                let dirs = [
                    "/usr/local/lib64".to_owned(),
                    "/usr/local/lib".to_owned(),
                    "/usr/lib64".to_owned(),
                    format!("/usr/local/lib/{arch}-linux-gnu"),
                    format!("/usr/lib/{arch}-linux-gnu"),
                    "/usr/lib".to_owned(),
                ];
                (
                    dirs.iter()
                        .map(|d| Path::new(d).join("libkrun.so.1"))
                        .collect(),
                    dirs.iter()
                        .map(|d| Path::new(d).join("libkrunfw.so.5"))
                        .collect(),
                )
            }
        }
    }
}
fn find_library(variable: &str, candidates: Vec<PathBuf>) -> Result<PathBuf> {
    if let Some(path) = std::env::var_os(variable) {
        let path = PathBuf::from(path);
        if path.is_file() {
            return Ok(path);
        }
        return Err(Error::InvalidInput(format!(
            "{variable} must name an existing trusted library file"
        )));
    }
    candidates.into_iter().find(|p|p.is_file()).ok_or_else(||Error::InvalidInput(format!("required host library not found; install libkrun 1.19.6/libkrunfw 5.6.2 or set {variable}")))
}
fn beside_executable(mut candidates: Vec<PathBuf>, names: &[&str]) -> Vec<PathBuf> {
    let bundled = std::env::current_exe().ok().and_then(|exe| {
        exe.parent()
            .and_then(Path::parent)
            .map(|dir| dir.join("lib"))
    });
    if let Some(dir) = bundled {
        let mut first: Vec<PathBuf> = names.iter().map(|name| dir.join(name)).collect();
        first.append(&mut candidates);
        candidates = first;
    }
    candidates
}
impl RuntimeConfig {
    pub fn for_host(worker: impl Into<PathBuf>, state_dir: impl Into<PathBuf>) -> Result<Self> {
        let platform = HostPlatform::current()?;
        let (libraries, firmware) = platform.libraries();
        let (libraries, firmware) = match platform {
            HostPlatform::MacOsArm64 => (
                beside_executable(libraries, &["libkrun.1.dylib"]),
                beside_executable(firmware, &["libkrunfw.5.dylib"]),
            ),
            HostPlatform::LinuxArm64 | HostPlatform::LinuxX86_64 => (
                beside_executable(libraries, &["libkrun.so.1"]),
                beside_executable(firmware, &["libkrunfw.so.5"]),
            ),
        };
        Ok(Self::new(
            worker,
            find_library("KUMOBAD_LIBKRUN", libraries)?,
            find_library("KUMOBAD_LIBKRUNFW", firmware)?,
            state_dir,
        ))
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn guest_architecture_and_host_library_names() {
        assert_eq!(
            HostPlatform::LinuxX86_64.guest_target(),
            "x86_64-unknown-linux-musl"
        );
        assert_eq!(
            HostPlatform::LinuxArm64.image_arch(),
            HostPlatform::MacOsArm64.image_arch()
        );
        for p in [HostPlatform::LinuxArm64, HostPlatform::LinuxX86_64] {
            let (lib, fw) = p.libraries();
            assert!(lib.iter().all(|p| p.ends_with("libkrun.so.1")));
            assert!(fw.iter().all(|p| p.ends_with("libkrunfw.so.5")));
        }
    }
}