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
3 changes: 3 additions & 0 deletions .github/workflows/branch-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ jobs:
- name: Verify telemetry can be compiled out
run: mise run rust:verify:telemetry-off

- name: Verify each compute driver can be compiled out
run: mise run rust:verify:drivers-off

- name: sccache stats
if: always()
run: |
Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,23 @@ OpenShell collects anonymous telemetry to help improve the project for developer

Disable telemetry at runtime by setting `OPENSHELL_TELEMETRY_ENABLED=false` on the gateway deployment. OpenShell propagates this deployment setting into sandbox supervisor environments so sandbox-side telemetry collection is disabled as well.

You can also compile telemetry out entirely. Telemetry support is a default-on `telemetry` Cargo feature; building with `--no-default-features` produces binaries that contain no telemetry endpoint, no telemetry HTTP client, and no emission code. Build telemetry-free artifacts with, for example, `cargo build --release -p openshell-server --no-default-features` (gateway) and the equivalent for `openshell-sandbox` and `openshell-driver-vm`. With telemetry compiled out, the gateway emits nothing and reports telemetry disabled to the sandboxes it launches.
You can also compile telemetry out entirely. Telemetry support is a default-on `telemetry` Cargo feature; building `openshell-server` with `--no-default-features` and selecting only the driver features you need produces a gateway that contains no telemetry endpoint, no telemetry HTTP client, and no emission code. The gateway requires at least one compute driver to link; for example, `cargo build --release -p openshell-server --bin openshell-gateway --no-default-features --features driver-kubernetes,driver-docker,driver-podman,driver-vm` builds a telemetry-free gateway with every driver. Build telemetry-free `openshell-sandbox` and `openshell-driver-vm` binaries with a plain `--no-default-features`. With telemetry compiled out, the gateway emits nothing and reports telemetry disabled to the sandboxes it launches.

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.

## Choosing Which Compute Drivers Are Compiled In

Each built-in compute driver — Kubernetes, Docker, Podman, VM — is gated by a Cargo feature on `openshell-server` (`driver-kubernetes`, `driver-docker`, `driver-podman`, `driver-vm`), all enabled by default. Operators who only need one driver can produce a slimmer gateway with a reduced supply-chain surface by building with `--no-default-features` and enabling the drivers they want:

```shell
cargo build --release -p openshell-server --bin openshell-gateway \
--no-default-features --features driver-kubernetes,telemetry
```

Any non-empty driver subset is a valid build, including zero built-in drivers (an extension-only gateway that reaches an out-of-tree driver over `[openshell.drivers.<name>].socket_path`). What the gateway needs at *runtime* is a `compute_drivers` entry it can serve: either a built-in this binary was built with, or an extension driver name with a matching socket_path entry. Naming a built-in that this gateway was not built with fails at startup with a message naming both the driver and the Cargo flag that re-enables it. Extension drivers reached via `--compute-driver-socket` work regardless of the feature set. See the [gateway configuration reference](docs/reference/gateway-config.mdx) for details.

## Notice and Disclaimer

This software automatically retrieves, accesses or interacts with external materials. Those retrieved materials are not distributed with this software and are governed solely by separate terms, conditions and licenses. You are solely responsible for finding, reviewing and complying with all applicable terms, conditions, and licenses, and for verifying the security, integrity and suitability of any retrieved materials for your specific use case. This software is provided "AS IS", without warranty of any kind. The author makes no representations or warranties regarding any retrieved materials, and assumes no liability for any losses, damages, liabilities or legal consequences from your use or inability to use this software or any retrieved materials. Use this software and the retrieved materials at your own risk.
Expand Down
24 changes: 24 additions & 0 deletions architecture/compute-runtimes.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,27 @@ them without changing the sandbox architecture.

When runtime infrastructure changes, validate the relevant sandbox e2e path and
update the matching driver README if a maintainer-facing constraint changes.

## Compile-Time Driver Selection

Each built-in runtime is gated by a Cargo feature on `openshell-server`:
`driver-kubernetes`, `driver-docker`, `driver-podman`, `driver-vm`. All
four are on by default, so the default gateway is byte-equivalent to
before the feature split. Building `--no-default-features` and selecting a
subset produces a gateway that carries only the requested drivers, along
with their transitive dependencies and driver-specific plumbing (Docker's
supervisor readiness impl, the K8s ServiceAccount authenticator, the VM
subprocess launcher, and so on).

A zero-driver build is a supported extension-only gateway shape. It
carries no in-tree driver code and speaks `compute_driver.proto` only to
out-of-tree drivers named in `compute_drivers` with a matching
`[openshell.drivers.<name>].socket_path`. A driver named in
`gateway.toml` that this binary was not built with fails at startup with
a message naming both the driver and the Cargo flag that re-enables it;
auto-detection skips drivers that were compiled out and falls through to
a "no suitable driver" error rather than silently picking a
different backend. Extension drivers reached via
`--compute-driver-socket` are always available regardless of driver
features — they run out-of-process and the gateway only needs to speak
`compute_driver.proto`.
9 changes: 9 additions & 0 deletions crates/openshell-driver-docker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@

#![allow(clippy::result_large_err)]

/// Compile-out guard: greppable proof this crate is linked into a binary.
///
/// `tasks/scripts/verify-drivers-compiled-out.sh` asserts this string is
/// present when `--features driver-docker` is on and absent when it is
/// off. `#[used]` prevents dead-code elimination; the linker cannot drop it.
/// Do not remove without updating the verify script.
#[used]
static COMPILE_MARKER: &str = "OPENSHELL_DRIVER_MARKER:docker";

use bollard::Docker;
use bollard::errors::Error as BollardError;
use bollard::models::{
Expand Down
9 changes: 9 additions & 0 deletions crates/openshell-driver-kubernetes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,12 @@ pub use config::{
};
pub use driver::{KubernetesComputeDriver, KubernetesDriverError};
pub use grpc::ComputeDriverService;

/// Compile-out guard: greppable proof this crate is linked into a binary.
///
/// `tasks/scripts/verify-drivers-compiled-out.sh` asserts this string is
/// present when `--features driver-kubernetes` is on and absent when it is
/// off. `#[used]` prevents dead-code elimination; the linker cannot drop it.
/// Do not remove without updating the verify script.
#[used]
static COMPILE_MARKER: &str = "OPENSHELL_DRIVER_MARKER:kubernetes";
9 changes: 9 additions & 0 deletions crates/openshell-driver-podman/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,12 @@ pub(crate) mod watcher;
pub use config::PodmanComputeConfig;
pub use driver::PodmanComputeDriver;
pub use grpc::ComputeDriverService;

/// Compile-out guard: greppable proof this crate is linked into a binary.
///
/// `tasks/scripts/verify-drivers-compiled-out.sh` asserts this string is
/// present when `--features driver-podman` is on and absent when it is
/// off. `#[used]` prevents dead-code elimination; the linker cannot drop it.
/// Do not remove without updating the verify script.
#[used]
static COMPILE_MARKER: &str = "OPENSHELL_DRIVER_MARKER:podman";
22 changes: 18 additions & 4 deletions crates/openshell-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ path = "src/main.rs"
[dependencies]
openshell-bootstrap = { path = "../openshell-bootstrap" }
openshell-core = { path = "../openshell-core", default-features = false }
openshell-driver-docker = { path = "../openshell-driver-docker" }
openshell-driver-kubernetes = { path = "../openshell-driver-kubernetes" }
openshell-driver-podman = { path = "../openshell-driver-podman" }
# Compute driver crates are gated by the `driver-*` features below.
openshell-driver-docker = { path = "../openshell-driver-docker", optional = true }
openshell-driver-kubernetes = { path = "../openshell-driver-kubernetes", optional = true }
openshell-driver-podman = { path = "../openshell-driver-podman", optional = true }
Comment thread
elezar marked this conversation as resolved.
openshell-ocsf = { path = "../openshell-ocsf" }
openshell-policy = { path = "../openshell-policy" }
openshell-prover = { path = "../openshell-prover" }
Expand Down Expand Up @@ -99,11 +100,24 @@ arc-swap = "1"
notify = "8"

[features]
default = ["telemetry"]
default = [
"telemetry",
"driver-kubernetes",
"driver-docker",
"driver-podman",
"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.
telemetry = ["openshell-core/telemetry"]
## Compile in the in-tree compute drivers.
driver-kubernetes = ["dep:openshell-driver-kubernetes"]
driver-docker = ["dep:openshell-driver-docker"]
driver-podman = ["dep:openshell-driver-podman"]
## The VM driver itself runs out-of-process;
## this feature gates the in-server code that spawns and speaks gRPC to it.
driver-vm = []
bundled-z3 = ["openshell-prover/bundled-z3"]
test-support = []

Expand Down
1 change: 1 addition & 0 deletions crates/openshell-server/src/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub mod authz;
pub mod guard;
mod http;
pub mod identity;
#[cfg(feature = "driver-kubernetes")]
pub mod k8s_sa;
pub mod method_authz;
pub mod oidc;
Expand Down
2 changes: 2 additions & 0 deletions crates/openshell-server/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1764,6 +1764,7 @@ mem_mib = "not-a-number"
}

#[test]
#[cfg(feature = "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 @@ -1789,6 +1790,7 @@ namespace = "agents"
}

#[test]
#[cfg(feature = "driver-kubernetes")]
fn driver_specific_value_overrides_gateway_inheritance() {
let file = config_file_from_toml(
r#"
Expand Down
48 changes: 47 additions & 1 deletion crates/openshell-server/src/compute/driver_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,25 @@

use crate::config_file;
use crate::defaults::LocalTlsPaths;
use openshell_core::{ComputeDriverKind, Error, Result};
#[cfg(any(
feature = "driver-kubernetes",
feature = "driver-docker",
feature = "driver-podman",
feature = "driver-vm",
))]
use openshell_core::ComputeDriverKind;
use openshell_core::{Error, Result};
#[cfg(feature = "driver-docker")]
use openshell_driver_docker::DockerComputeConfig;
#[cfg(feature = "driver-kubernetes")]
use openshell_driver_kubernetes::KubernetesComputeConfig;
#[cfg(feature = "driver-podman")]
use openshell_driver_podman::PodmanComputeConfig;
use serde::Deserialize;
use std::collections::BTreeMap;
use std::path::PathBuf;

#[cfg(feature = "driver-vm")]
use super::VmComputeConfig;

#[derive(Debug, Clone, PartialEq, Eq)]
Expand All @@ -39,13 +50,28 @@ impl From<&LocalTlsPaths> for GuestTlsPaths {
#[derive(Clone, Copy)]
pub struct DriverStartupContext<'a> {
pub file: Option<&'a config_file::ConfigFile>,
// Consumed only by the docker/podman/vm runtime-defaults helpers below.
#[cfg_attr(
not(any(
feature = "driver-docker",
feature = "driver-podman",
feature = "driver-vm"
)),
allow(dead_code)
)]
pub guest_tls: Option<&'a GuestTlsPaths>,
#[cfg_attr(
not(any(feature = "driver-podman", feature = "driver-vm")),
allow(dead_code)
)]
pub gateway_port: u16,
#[cfg_attr(not(feature = "driver-vm"), allow(dead_code))]
pub gateway_tls_enabled: bool,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer #[cfg] directly on the fields over #[cfg_attr(..., allow(dead_code))]. The cfg_attr form uses a double-negated predicate (not(any(...))) that is harder to read and fragile — if a new driver reads one of these fields, the suppression predicate needs updating in sync or the warning silently returns.

With #[cfg] the struct accurately reflects what exists per build:

#[derive(Clone, Copy)]
pub struct DriverStartupContext<'a> {
    pub file: Option<&'a config_file::ConfigFile>,
    #[cfg(any(feature = "driver-docker", feature = "driver-podman", feature = "driver-vm"))]
    pub guest_tls: Option<&'a GuestTlsPaths>,
    #[cfg(any(feature = "driver-podman", feature = "driver-vm"))]
    pub gateway_port: u16,
    #[cfg(feature = "driver-vm")]
    pub gateway_tls_enabled: bool,
    pub endpoint_overrides: &'a BTreeMap<String, PathBuf>,
}

The three construction sites (run_server, test_context_with_endpoint_overrides, test_driver_startup) each need matching #[cfg] on the three field assignments — the same pattern used throughout the rest of this PR. Non-blocking; the current approach compiles and is tested.

pub endpoint_overrides: &'a BTreeMap<String, PathBuf>,
}

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

#[cfg(feature = "driver-kubernetes")]
pub fn kubernetes_config_for_k8s_sa_bootstrap(
file: Option<&config_file::ConfigFile>,
) -> Result<KubernetesComputeConfig> {
Expand All @@ -71,6 +98,7 @@ pub fn kubernetes_config_for_k8s_sa_bootstrap(
}

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

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

/// Build the selected VM config from TOML plus runtime defaults.
#[cfg(feature = "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);
Expand Down Expand Up @@ -141,12 +171,14 @@ where
})
}

#[cfg(feature = "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;
}
}

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

#[cfg(feature = "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 @@ -170,6 +203,7 @@ fn apply_docker_runtime_defaults(cfg: &mut DockerComputeConfig, context: DriverS
);
}

#[cfg(feature = "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 @@ -193,6 +227,7 @@ fn apply_vm_runtime_defaults(cfg: &mut VmComputeConfig, context: DriverStartupCo
);
}

#[cfg(feature = "driver-podman")]
fn apply_podman_env_overrides(podman: &mut PodmanComputeConfig) {
if let Ok(p) = std::env::var("OPENSHELL_PODMAN_SOCKET") {
podman.socket_path = PathBuf::from(p);
Expand Down Expand Up @@ -221,6 +256,11 @@ fn validate_remote_driver_config(cfg: &RemoteDriverConfig, name: &str) -> Result
)))
}

#[cfg(any(
feature = "driver-docker",
feature = "driver-podman",
feature = "driver-vm"
))]
fn apply_guest_tls_defaults_to_split_fields(
ca: &mut Option<PathBuf>,
cert: &mut Option<PathBuf>,
Expand Down Expand Up @@ -263,6 +303,7 @@ mod tests {
}

#[test]
#[cfg(feature = "driver-kubernetes")]
fn k8s_sa_bootstrap_rejects_missing_kubernetes_driver_config() {
let err = kubernetes_config_for_k8s_sa_bootstrap(None).unwrap_err();
assert!(err.to_string().contains("[openshell.drivers.kubernetes]"));
Expand All @@ -274,6 +315,7 @@ mod tests {
}

#[test]
#[cfg(feature = "driver-kubernetes")]
fn k8s_sa_bootstrap_uses_configured_namespace_and_service_account() {
let file: config_file::ConfigFile = toml::from_str(
r#"
Expand All @@ -292,6 +334,7 @@ service_account_name = "sandbox-sa"
}

#[test]
#[cfg(feature = "driver-podman")]
fn podman_config_reads_bind_mount_opt_in_from_driver_table() {
let file: config_file::ConfigFile = toml::from_str(
r"
Expand All @@ -307,6 +350,7 @@ enable_bind_mounts = true
}

#[test]
#[cfg(feature = "driver-docker")]
fn docker_config_reads_bind_mount_opt_in_from_driver_table() {
let file: config_file::ConfigFile = toml::from_str(
r"
Expand Down Expand Up @@ -383,6 +427,7 @@ socket_path = "/run/openshell/kyma.sock"
}

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

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