Skip to content
Open
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
17 changes: 11 additions & 6 deletions .agents/skills/debug-openshell-cluster/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@ The target deployment flow is:
4. The CLI registers a reachable gateway endpoint with `openshell gateway add`.
5. The gateway creates sandboxes through the selected compute driver.

The standard gateway binary explicitly installs its compiled Docker, Podman,
Kubernetes, and VM registrations at startup. With no configured driver, the
gateway probes only installed registrations in priority order (Kubernetes,
Podman, then Docker); VM has no probe and remains opt-in. A custom gateway
binary may install a different set, so confirm the binary's registered drivers
when auto-detection reports that no suitable driver is available.
The standard gateway binary's default feature set explicitly installs its
compiled Docker, Podman, Kubernetes, and VM registrations at startup. Source
builds can compile any subset with the `compute-driver-docker`,
`compute-driver-podman`, `compute-driver-kubernetes`, and `compute-driver-vm`
features. With no configured driver, the gateway probes only installed
registrations in priority order (Kubernetes, Podman, then Docker); VM has no
probe and remains opt-in. A subset or custom gateway build may install a
different set, so confirm the binary's registered drivers when auto-detection
reports that no suitable driver is available. If configuration selects a
driver that was not compiled in, the gateway treats the name as an external
driver and reports a missing `socket_path` unless an endpoint is configured.

For local evaluation only, TLS may be disabled and the gateway can be reached through `http://127.0.0.1:<port>`.

Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/branch-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,15 @@ jobs:
cargo build -p openshell-sandbox --bin openshell-sandbox --no-default-features --features defaults-without-telemetry
tasks/scripts/verify-telemetry-compiled-out.sh absent target/debug/openshell-sandbox

- name: Verify selective gateway compute-driver builds
run: |
cargo check -p openshell-server --all-targets --no-default-features
cargo check -p openshell-server --all-targets --no-default-features --features compute-driver-docker
cargo check -p openshell-server --all-targets --no-default-features --features compute-driver-kubernetes
cargo check -p openshell-server --all-targets --no-default-features --features compute-driver-podman
cargo check -p openshell-server --all-targets --no-default-features --features compute-driver-vm
cargo check -p openshell-server --all-targets --no-default-features --features compute-driver-docker,compute-driver-vm

- name: Verify the defaults-without-telemetry feature alias tracks the default feature set
run: tasks/scripts/verify-defaults-without-telemetry.sh

Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,18 @@ 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`, and `compute-driver-vm`. 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-server --no-default-features --features telemetry,compute-driver-docker

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

Regular builds still include all four drivers through the default `in-tree-compute-drivers` compatibility feature.

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.

Opting out applies only to telemetry emitted by OpenShell. Third-party services, model providers, inference endpoints, agents, or tools that you configure and use with OpenShell may have their own terms and privacy practices.
Expand Down
14 changes: 9 additions & 5 deletions architecture/compute-runtimes.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,15 @@ to `run_cli_with_compute_drivers`; factories receive merged driver config and
finish through the same in-process runtime adapter. A configured UDS endpoint
still takes precedence over a compiled registration with the same name.

The standard server crate groups first-party registrations behind the
`in-tree-compute-drivers` feature. Protocol-only gateway builds disable that
feature and link no compute-driver crates. E2E lanes 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.
The standard server crate exposes one feature per first-party registration:
`compute-driver-kubernetes`, `compute-driver-docker`,
`compute-driver-podman`, and `compute-driver-vm`. Builds can enable any subset.
The default `in-tree-compute-drivers` feature remains an alias for all four, so
existing gateway builds retain the full driver set. Protocol-only gateway
builds disable the default features and link no compute-driver crates. E2E
lanes 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.
External Kubernetes drivers support shared and managed workspace modes.
Operator mode requires an in-process dynamic namespace allowlist and is
rejected when Kubernetes is configured through an external endpoint.
Expand Down
17 changes: 12 additions & 5 deletions crates/openshell-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,20 @@ openshell-driver-mxc = { path = "../openshell-driver-mxc" }

[features]
default = ["telemetry", "in-tree-compute-drivers"]
## Link the first-party compute drivers into the standard gateway binary.
## Disable this feature for a protocol-only gateway that uses external drivers.
## Link every first-party compute driver into the standard gateway binary.
## This compatibility alias preserves the historical all-drivers feature.
in-tree-compute-drivers = [
"dep:openshell-driver-docker",
"dep:openshell-driver-kubernetes",
"dep:openshell-driver-podman",
"compute-driver-docker",
"compute-driver-kubernetes",
"compute-driver-podman",
"compute-driver-vm",
]
## Link individual first-party compute drivers. Start with
## `--no-default-features` when building a gateway with a selected subset.
compute-driver-docker = ["dep:openshell-driver-docker"]
compute-driver-kubernetes = ["dep:openshell-driver-kubernetes"]
compute-driver-podman = ["dep:openshell-driver-podman"]
compute-driver-vm = []
## Compile in anonymous telemetry emission (forwards to openshell-core/telemetry).
## On by default; build with `--no-default-features` for a telemetry-free gateway
## that contains no telemetry endpoint, HTTP client, or emission code.
Expand Down
4 changes: 2 additions & 2 deletions crates/openshell-server/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1946,7 +1946,7 @@ mem_mib = "not-a-number"
}

#[test]
#[cfg(not(target_os = "windows"))]
#[cfg(all(not(target_os = "windows"), feature = "compute-driver-kubernetes"))]
fn driver_inherits_shared_image_from_gateway_section() {
// [openshell.gateway].default_image inherits into the K8s driver
// table when the driver-specific table does not set it.
Expand All @@ -1972,7 +1972,7 @@ namespace = "agents"
}

#[test]
#[cfg(not(target_os = "windows"))]
#[cfg(all(not(target_os = "windows"), feature = "compute-driver-kubernetes"))]
fn driver_specific_value_overrides_gateway_inheritance() {
let file = config_file_from_toml(
r#"
Expand Down
10 changes: 9 additions & 1 deletion crates/openshell-server/src/compute/driver_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@
//! driver-specific environment overrides, and applying gateway startup defaults.
//! It does not acquire, connect to, or start compute drivers.

#[cfg(all(not(target_os = "windows"), feature = "in-tree-compute-drivers"))]
#[cfg(all(
not(target_os = "windows"),
any(
feature = "compute-driver-docker",
feature = "compute-driver-kubernetes",
feature = "compute-driver-podman",
feature = "compute-driver-vm"
)
))]
pub mod builtin;

use crate::config_file;
Expand Down
55 changes: 52 additions & 3 deletions crates/openshell-server/src/compute/driver_config/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,40 @@

//! Configuration construction for built-in compute drivers.

use super::{DriverStartupContext, GuestTlsPaths, driver_config_from_context};
#[cfg(any(
feature = "compute-driver-docker",
feature = "compute-driver-podman",
feature = "compute-driver-vm"
))]
use super::GuestTlsPaths;
use super::{DriverStartupContext, driver_config_from_context};
#[cfg(feature = "compute-driver-vm")]
use crate::compute::VmComputeConfig;
#[cfg(test)]
#[cfg(all(
test,
any(
feature = "compute-driver-docker",
feature = "compute-driver-podman",
feature = "compute-driver-vm"
)
))]
use crate::config_file;
use openshell_core::{ComputeDriverKind, Result};
#[cfg(feature = "compute-driver-docker")]
use openshell_driver_docker::DockerComputeConfig;
#[cfg(feature = "compute-driver-kubernetes")]
use openshell_driver_kubernetes::KubernetesComputeConfig;
#[cfg(feature = "compute-driver-podman")]
use openshell_driver_podman::PodmanComputeConfig;
#[cfg(any(
feature = "compute-driver-docker",
feature = "compute-driver-podman",
feature = "compute-driver-vm"
))]
use std::path::PathBuf;

/// Build the selected Kubernetes config from TOML plus runtime defaults.
#[cfg(feature = "compute-driver-kubernetes")]
pub fn kubernetes_config_from_context(
context: DriverStartupContext<'_>,
) -> Result<KubernetesComputeConfig> {
Expand All @@ -23,6 +46,7 @@ pub fn kubernetes_config_from_context(
}

/// Build the selected Podman config from TOML plus runtime defaults.
#[cfg(feature = "compute-driver-podman")]
pub fn podman_config_from_context(
context: DriverStartupContext<'_>,
) -> Result<PodmanComputeConfig> {
Expand All @@ -32,6 +56,7 @@ pub fn podman_config_from_context(
}

/// Build the selected Docker config from TOML plus runtime defaults.
#[cfg(feature = "compute-driver-docker")]
pub fn docker_config_from_context(
context: DriverStartupContext<'_>,
) -> Result<DockerComputeConfig> {
Expand All @@ -41,12 +66,14 @@ pub fn docker_config_from_context(
}

/// Build the selected VM config from TOML plus runtime defaults.
#[cfg(feature = "compute-driver-vm")]
pub fn vm_config_from_context(context: DriverStartupContext<'_>) -> Result<VmComputeConfig> {
let mut cfg = driver_config_from_context(context, ComputeDriverKind::Vm.as_str())?;
apply_vm_runtime_defaults(&mut cfg, context);
Ok(cfg)
}

#[cfg(feature = "compute-driver-kubernetes")]
fn apply_kubernetes_runtime_defaults(k8s: &mut KubernetesComputeConfig) {
if let Ok(size) = std::env::var("OPENSHELL_K8S_WORKSPACE_DEFAULT_STORAGE_SIZE") {
k8s.workspace_default_storage_size = size;
Expand All @@ -56,6 +83,7 @@ fn apply_kubernetes_runtime_defaults(k8s: &mut KubernetesComputeConfig) {
}
}

#[cfg(feature = "compute-driver-podman")]
fn apply_podman_runtime_defaults(
podman: &mut PodmanComputeConfig,
context: DriverStartupContext<'_>,
Expand All @@ -70,6 +98,7 @@ fn apply_podman_runtime_defaults(
);
}

#[cfg(feature = "compute-driver-docker")]
fn apply_docker_runtime_defaults(cfg: &mut DockerComputeConfig, context: DriverStartupContext<'_>) {
apply_guest_tls_defaults_to_split_fields(
&mut cfg.guest_tls_ca,
Expand All @@ -79,6 +108,7 @@ fn apply_docker_runtime_defaults(cfg: &mut DockerComputeConfig, context: DriverS
);
}

#[cfg(feature = "compute-driver-vm")]
fn apply_vm_runtime_defaults(cfg: &mut VmComputeConfig, context: DriverStartupContext<'_>) {
if cfg.state_dir.as_os_str().is_empty() {
cfg.state_dir = VmComputeConfig::default_state_dir();
Expand All @@ -102,6 +132,11 @@ fn apply_vm_runtime_defaults(cfg: &mut VmComputeConfig, context: DriverStartupCo
);
}

#[cfg(any(
feature = "compute-driver-docker",
feature = "compute-driver-podman",
feature = "compute-driver-vm"
))]
fn apply_guest_tls_defaults_to_split_fields(
ca: &mut Option<PathBuf>,
cert: &mut Option<PathBuf>,
Expand All @@ -119,6 +154,7 @@ fn apply_guest_tls_defaults_to_split_fields(
}
}

#[cfg(feature = "compute-driver-podman")]
fn apply_podman_env_overrides(podman: &mut PodmanComputeConfig) {
if let Ok(p) = std::env::var("OPENSHELL_PODMAN_SOCKET") {
podman.socket_path = Some(PathBuf::from(p));
Expand All @@ -131,10 +167,18 @@ fn apply_podman_env_overrides(podman: &mut PodmanComputeConfig) {
}
}

#[cfg(test)]
#[cfg(all(
test,
any(
feature = "compute-driver-docker",
feature = "compute-driver-podman",
feature = "compute-driver-vm"
)
))]
mod tests {
use super::*;
use std::collections::BTreeMap;
use std::path::PathBuf;

fn test_context(file: Option<&config_file::ConfigFile>) -> DriverStartupContext<'_> {
static EMPTY_ENDPOINT_OVERRIDES: std::sync::LazyLock<BTreeMap<String, PathBuf>> =
Expand All @@ -148,6 +192,7 @@ mod tests {
}
}

#[cfg(feature = "compute-driver-podman")]
#[test]
fn podman_config_reads_bind_mount_opt_in_from_driver_table() {
let file: config_file::ConfigFile = toml::from_str(
Expand All @@ -163,6 +208,7 @@ enable_bind_mounts = true
assert!(cfg.enable_bind_mounts);
}

#[cfg(feature = "compute-driver-docker")]
#[test]
fn docker_config_reads_bind_mount_opt_in_from_driver_table() {
let file: config_file::ConfigFile = toml::from_str(
Expand All @@ -178,6 +224,7 @@ enable_bind_mounts = true
assert!(cfg.enable_bind_mounts);
}

#[cfg(feature = "compute-driver-docker")]
#[test]
fn docker_config_reads_socket_path_from_driver_table() {
let file: config_file::ConfigFile = toml::from_str(
Expand All @@ -193,6 +240,7 @@ socket_path = "/tmp/docker.sock"
assert_eq!(cfg.socket_path, Some(PathBuf::from("/tmp/docker.sock")));
}

#[cfg(feature = "compute-driver-docker")]
#[test]
fn docker_config_reports_selected_invalid_driver_table() {
let file: config_file::ConfigFile = toml::from_str(
Expand All @@ -211,6 +259,7 @@ unknown_docker_key = true
);
}

#[cfg(feature = "compute-driver-vm")]
#[test]
fn vm_config_reports_selected_invalid_driver_table() {
let file: config_file::ConfigFile = toml::from_str(
Expand Down
Loading
Loading