Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/branch-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,9 @@ jobs:
cargo test -p openshell-gateway --all-targets --no-default-features --features compute-driver-docker
cargo test -p openshell-gateway --all-targets --no-default-features --features compute-driver-kubernetes
cargo test -p openshell-gateway --all-targets --no-default-features --features compute-driver-podman
cargo test -p openshell-gateway --all-targets --no-default-features --features compute-driver-vm
cargo test -p openshell-gateway --all-targets --no-default-features --features compute-driver-managed
cargo test -p openshell-gateway --all-targets --no-default-features --features compute-driver-mxc
cargo test -p openshell-gateway --all-targets --no-default-features --features compute-driver-docker,compute-driver-vm
cargo test -p openshell-gateway --all-targets --no-default-features --features compute-driver-docker,compute-driver-managed

- name: Verify the defaults-without-telemetry feature alias tracks the default feature set
run: tasks/scripts/verify-defaults-without-telemetry.sh
Expand Down
13 changes: 4 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ license = "Apache-2.0"
repository = "https://github.com/NVIDIA/OpenShell"

[workspace.dependencies]
openshell-managed-compute-driver = { package = "openshell-driver-vm", path = "crates/openshell-driver-vm", default-features = false }

# Async runtime
tokio = { version = "1.43", features = ["full"] }

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -349,20 +349,20 @@ cargo build --release -p openshell-driver-vm --no-default-features --features de

The resulting binaries contain no telemetry endpoint, no telemetry HTTP client, and no emission code. With telemetry compiled out, the gateway emits nothing and reports telemetry disabled to the sandboxes it launches. Cargo has no way to subtract a single default feature, so `defaults-without-telemetry` must be paired with `--no-default-features`; passing it on its own leaves the defaults in place and fails the build rather than producing a binary that still emits.

The gateway also exposes separate Cargo features for its built-in compute drivers: `compute-driver-kubernetes`, `compute-driver-docker`, `compute-driver-podman`, `compute-driver-vm`, and `compute-driver-mxc`. Disable the default feature set, then enable only the drivers and telemetry mode required by the target binary. For example:
The gateway also exposes separate Cargo features for its built-in compute drivers: `compute-driver-kubernetes`, `compute-driver-docker`, `compute-driver-podman`, `compute-driver-managed`, and `compute-driver-mxc`. The driver-owned managed adapter currently registers the standalone VM driver without putting VM-specific code in the gateway crate. Disable the default feature set, then enable only the drivers and telemetry mode required by the target binary. For example:

```shell
# Docker only, with telemetry support.
cargo build --release -p openshell-gateway --no-default-features --features telemetry,compute-driver-docker

# Docker and VM only, with telemetry compiled out.
cargo build --release -p openshell-gateway --no-default-features --features compute-driver-docker,compute-driver-vm
cargo build --release -p openshell-gateway --no-default-features --features compute-driver-docker,compute-driver-managed

# Windows MXC only, with telemetry support and bundled Z3.
cargo build --release -p openshell-gateway --no-default-features --features telemetry,compute-driver-mxc,bundled-z3
```

Regular builds retain their platform driver set through the default `in-tree-compute-drivers` compatibility feature. On Windows, `compute-driver-mxc` selects MXC; the other four features install unsupported-driver stubs. On other platforms, MXC is excluded.
Regular builds retain their platform driver set through the default `in-tree-compute-drivers` compatibility feature. On Windows, `compute-driver-mxc` selects MXC; Docker, Kubernetes, and Podman install unsupported-driver stubs, while the managed standalone adapter is excluded. On other platforms, MXC is excluded.

Telemetry events are limited to anonymous operational categories and counts, such as sandbox lifecycle outcomes, provider profile buckets, policy decision counts, and aggregate network activity denial categories. OpenShell telemetry does not collect sandbox names or IDs, hostnames, file paths, binary paths, prompts, credentials, provider names, model names, or user content.

Expand Down
16 changes: 12 additions & 4 deletions architecture/compute-runtimes.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ compose that gateway with Docker, Podman, Kubernetes, and VM driver executables
over the public UDS gRPC contract so an in-tree driver cannot silently depend
on a server-only API.

Driver crates own their configuration defaults and backend-specific startup
contract. The standalone VM driver exposes a lightweight `managed` feature for
its configuration and subprocess arguments; this does not link libkrun or the
VM runtime into the gateway. Its optional `gateway-integration` feature owns the
`ComputeDriverFactory` adapter and exports an opaque registration; the gateway
only installs that provider. The server owns the generic managed-child readiness
probe, UDS connection, supervision, and socket cleanup.

## Stop and Start Lifecycle

The gateway persists lifecycle intent before mutating compute:
Expand Down Expand Up @@ -328,10 +336,10 @@ can request a specific number of GPUs or the driver-specific default behaviour.
For all in-tree drivers, this is equivalent to selecting a single GPU.

VM runtime state paths are derived only from driver-validated sandbox IDs
matching `[A-Za-z0-9._-]{1,128}`. The gateway-owned VM driver socket uses a
private `run/` directory plus Unix peer UID/PID checks. Standalone
unauthenticated TCP mode is disabled unless explicitly enabled for local
development.
matching `[A-Za-z0-9._-]{1,128}`. The gateway-managed VM driver socket uses a
driver-configured private `run/` directory plus Unix peer UID/PID checks.
Standalone unauthenticated TCP mode is disabled unless explicitly enabled for
local development.

Runtime-specific implementation notes belong in the driver crate README:

Expand Down
12 changes: 10 additions & 2 deletions crates/openshell-driver-vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ openshell-otel = { path = "../openshell-otel", optional = true }
openshell-policy = { path = "../openshell-policy", optional = true }
openshell-driver-podman = { path = "../openshell-driver-podman", optional = true }
openshell-vfio = { path = "../openshell-vfio", optional = true }
openshell-server = { path = "../openshell-server", default-features = false, optional = true }

async-trait = { version = "0.1", optional = true }
base64 = { workspace = true, optional = true }
bollard = { version = "0.20", features = ["ssh"], optional = true }
tokio = { workspace = true }
Expand Down Expand Up @@ -65,8 +67,15 @@ zstd = { version = "0.13", optional = true }

[features]
default = ["compute-driver", "telemetry"]
## Expose VM-specific managed launch configuration without linking the VM
## runtime implementation into the gateway process.
managed = ["dep:openshell-policy", "dep:rustix"]
## Adapt the standalone driver to the standard gateway registry. The adapter
## remains driver-owned and does not pull the VM runtime into the gateway.
gateway-integration = ["managed", "dep:async-trait", "dep:openshell-server"]
## Build the standalone compute driver and its host runtime implementation.
compute-driver = [
"managed",
"dep:base64",
"dep:bollard",
"dep:clap",
Expand All @@ -78,7 +87,6 @@ compute-driver = [
"dep:nix",
"dep:oci-client",
"dep:openshell-otel",
"dep:openshell-policy",
"dep:openshell-driver-podman",
"dep:openshell-vfio",
"dep:opentelemetry",
Expand All @@ -87,7 +95,6 @@ compute-driver = [
"dep:prost",
"dep:prost-types",
"dep:rand",
"dep:rustix",
"dep:sha2",
"dep:tar",
"dep:tokio-stream",
Expand Down Expand Up @@ -117,6 +124,7 @@ defaults-without-telemetry = ["compute-driver"]
openshell-otel-test-support = { path = "../openshell-otel-test-support" }
temp-env = "0.3"
tempfile = "3"
toml = { workspace = true }
opentelemetry_sdk = { workspace = true, features = ["testing"] }

# smol-rs/polling drives the BSD/macOS parent-death detection in
Expand Down
7 changes: 4 additions & 3 deletions crates/openshell-driver-vm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ The driver embeds libkrun, libkrunfw, the guest OCI unpacker, the portable guest
```mermaid
flowchart LR
subgraph host["Host"]
gateway["openshell-gateway<br/>(vm::spawn)"]
driver["openshell-driver-vm<br/>libkrun"]
gateway["openshell-gateway<br/>(generic registry consumer)"]
driver["openshell-driver-vm<br/>driver-owned registry adapter + libkrun"]
supervisor["openshell-supervisor<br/>host policy supervisor"]
gateway <-->|"gRPC over UDS<br/>compute-driver.sock"| driver
supervisor <-->|"authenticated gRPC<br/>policy + relay"| gateway
Expand Down Expand Up @@ -305,5 +305,6 @@ so driver choice remains automatic unless the user explicitly overrides it.

## TODOs

- The gateway still configures the driver via CLI args; this will move to a gRPC bootstrap call so the driver interface is uniform across backends. See the `TODO(driver-abstraction)` note in `crates/openshell-gateway/src/vm.rs`.
- Managed launch still configures the driver via CLI args; a future gRPC
bootstrap call can make configuration uniform across standalone backends.
- macOS local builds are codesigned by `tasks/scripts/gateway-vm.sh`; the generated Homebrew formula signs the release tarball driver for local installs.
141 changes: 141 additions & 0 deletions crates/openshell-driver-vm/src/gateway.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

//! Optional adapter from the standalone VM driver to the gateway registry.

use crate::{VmComputeConfig, spawn_managed_vm_driver};
use openshell_core::telemetry::TelemetryComputeDriver;
use openshell_core::{Error, Result};
use openshell_server::{
ComputeDriverBuildContext, ComputeDriverConfigContext, ComputeDriverFactory,
ComputeDriverInstance, ComputeDriverRegistration, connect_managed_compute_driver,
};
use std::path::{Path, PathBuf};

const DRIVER_NAME: &str = "vm";

/// Build the VM driver's self-contained gateway registration.
pub fn gateway_registration() -> Result<ComputeDriverRegistration> {
ComputeDriverRegistration::new(DRIVER_NAME, u16::MAX, None, VmFactory).map(|registration| {
registration
.with_telemetry_category(TelemetryComputeDriver::anonymous_category(DRIVER_NAME))
.with_local_singleplayer()
})
}

#[derive(Clone, Copy)]
struct VmFactory;

#[async_trait::async_trait]
impl ComputeDriverFactory for VmFactory {
fn supports_config_preflight(&self) -> bool {
true
}

fn validate_config(&self, context: ComputeDriverConfigContext<'_>) -> Result<()> {
let mut config = vm_config(context)?;
apply_default_grpc_endpoint(
&mut config,
context.gateway_tls_enabled(),
context.gateway_port(),
);
config.validate_configuration()
}

async fn build(&self, context: ComputeDriverBuildContext<'_>) -> Result<ComputeDriverInstance> {
let mut config = vm_config(context.config_context())?;
require_guest_tls(&context)?;
if !context.gateway_tls_enabled() || context.guest_tls_paths().is_some() {
apply_default_grpc_endpoint(
&mut config,
context.gateway_tls_enabled(),
context.gateway_port(),
);
}
apply_guest_tls(
&mut config.guest_tls_ca,
&mut config.guest_tls_cert,
&mut config.guest_tls_key,
context.guest_tls_paths(),
);
let launch = spawn_managed_vm_driver(
context.gateway_log_level(),
context.gateway_name(),
&config,
context.otlp_config().map(|config| config.endpoint.as_str()),
)?;
let (child, socket_path) = launch.into_parts();
let endpoint = connect_managed_compute_driver(DRIVER_NAME, socket_path, child)
.await
.map_err(|error| Error::execution(error.to_string()))?;
Ok(ComputeDriverInstance::ManagedRemote(endpoint))
}
}

fn vm_config(context: ComputeDriverConfigContext<'_>) -> Result<VmComputeConfig> {
let mut config: VmComputeConfig = context.driver_config()?;
if config.state_dir.as_os_str().is_empty() {
config.state_dir = VmComputeConfig::default_state_dir();
}
Ok(config)
}

fn apply_default_grpc_endpoint(config: &mut VmComputeConfig, tls_enabled: bool, port: u16) {
if config.grpc_endpoint.trim().is_empty() {
let scheme = if tls_enabled { "https" } else { "http" };
config.grpc_endpoint = format!("{scheme}://127.0.0.1:{port}");
}
}

fn require_guest_tls(context: &ComputeDriverBuildContext<'_>) -> Result<()> {
if context.gateway_tls_enabled() && context.guest_tls_paths().is_none() {
return Err(Error::config(format!(
"gateway TLS requires guest_tls_ca, guest_tls_cert, and guest_tls_key in [openshell.gateway] when using the {DRIVER_NAME} compute driver"
)));
}
Ok(())
}

fn apply_guest_tls(
ca: &mut Option<PathBuf>,
cert: &mut Option<PathBuf>,
key: &mut Option<PathBuf>,
defaults: Option<(&Path, &Path, &Path)>,
) {
if ca.is_none()
&& cert.is_none()
&& key.is_none()
&& let Some((default_ca, default_cert, default_key)) = defaults
{
*ca = Some(default_ca.to_owned());
*cert = Some(default_cert.to_owned());
*key = Some(default_key.to_owned());
}
}

#[cfg(test)]
mod tests {
use super::apply_guest_tls;
use std::path::{Path, PathBuf};

#[test]
fn package_managed_guest_bundle_is_injected_when_driver_paths_are_absent() {
let mut ca = None;
let mut cert = None;
let mut key = None;
apply_guest_tls(
&mut ca,
&mut cert,
&mut key,
Some((
Path::new("ca.pem"),
Path::new("client.pem"),
Path::new("client-key.pem"),
)),
);

assert_eq!(ca, Some(PathBuf::from("ca.pem")));
assert_eq!(cert, Some(PathBuf::from("client.pem")));
assert_eq!(key, Some(PathBuf::from("client-key.pem")));
}
}
8 changes: 8 additions & 0 deletions crates/openshell-driver-vm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ pub mod driver;
mod embedded_runtime;
#[cfg(feature = "compute-driver")]
mod ffi;
#[cfg(all(not(target_os = "windows"), feature = "gateway-integration"))]
mod gateway;
#[cfg(feature = "compute-driver")]
pub mod gpu;
#[cfg(feature = "compute-driver")]
mod isolation;
#[cfg(feature = "compute-driver")]
pub mod lifecycle;
#[cfg(feature = "managed")]
mod managed;
#[cfg(feature = "compute-driver")]
pub mod otel_tracing;
#[cfg(feature = "compute-driver")]
Expand All @@ -34,12 +38,16 @@ mod runtime;

#[cfg(feature = "compute-driver")]
pub use driver::{VmDriver, VmDriverConfig};
#[cfg(all(not(target_os = "windows"), feature = "gateway-integration"))]
pub use gateway::gateway_registration;
#[cfg(feature = "compute-driver")]
pub use lifecycle::{
BackendFeature, ExtensionCapabilities, ExtensionDescriptor, GuestInitDropin, LaunchAbortReason,
LaunchPlan, LifecycleError, LifecycleExtension, LifecycleExtensionRegistry, LifecycleResult,
RestoreContext,
};
#[cfg(feature = "managed")]
pub use managed::{ManagedVmDriverProcess, VmComputeConfig, spawn_managed_vm_driver};
#[cfg(feature = "compute-driver")]
pub use runtime::{
VM_RUNTIME_DIR_ENV, VmBackend, VmLaunchConfig, VsockPortMap, configured_runtime_dir, run_vm,
Expand Down
Loading
Loading