From 4c1cbbd763e54f61dd6176ff78284abc925b5bc1 Mon Sep 17 00:00:00 2001 From: Russell Bryant Date: Thu, 20 Aug 2026 10:17:13 -0400 Subject: [PATCH 1/2] feat(build): add defaults-without-telemetry feature alias Cargo cannot subtract a single default feature, so compiling telemetry out meant `--no-default-features` plus a hand-maintained keep-list of the crate's other defaults. That keep-list was already wrong for operators: telemetry is the only default on openshell-server and openshell-driver-vm, but openshell-sandbox also defaults to `bundled-ca-roots`, so a bare `--no-default-features` silently swapped the supervisor onto the platform trust store. Add a `defaults-without-telemetry` alias to each of the three telemetry- carrying binary crates, enumerating every default except `telemetry`. Telemetry-free builds become `--no-default-features --features defaults-without-telemetry` and stay correct as the default set grows. The alias is a keep-list, not a switch. Enabling it on top of the defaults would otherwise produce a telemetry-on binary that reads as telemetry-free, so each crate root carries a `compile_error!` for the `telemetry` + `defaults-without-telemetry` combination. Add `rust:verify:defaults-without-telemetry` to guard both properties: each alias still equals its crate's defaults minus `telemetry`, and the mutual-exclusion error is wired up. The additive-misuse check matches on the `compile_error!` text rather than a nonzero exit code so it cannot pass vacuously on hosts where openshell-driver-vm fails to build for unrelated reasons. `rust:verify:telemetry-off` now builds through the alias. Signed-off-by: Russell Bryant --- .github/workflows/branch-checks.yml | 9 ++ README.md | 10 ++- architecture/build.md | 27 ++++-- crates/openshell-driver-vm/Cargo.toml | 9 ++ crates/openshell-driver-vm/src/lib.rs | 10 +++ crates/openshell-sandbox/Cargo.toml | 11 +++ crates/openshell-sandbox/src/lib.rs | 10 +++ crates/openshell-server/Cargo.toml | 9 ++ crates/openshell-server/src/lib.rs | 10 +++ tasks/rust.toml | 12 ++- .../verify-defaults-without-telemetry.sh | 88 +++++++++++++++++++ 11 files changed, 195 insertions(+), 10 deletions(-) create mode 100755 tasks/scripts/verify-defaults-without-telemetry.sh diff --git a/.github/workflows/branch-checks.yml b/.github/workflows/branch-checks.yml index 2d9dc9914f..c86409a0da 100644 --- a/.github/workflows/branch-checks.yml +++ b/.github/workflows/branch-checks.yml @@ -167,6 +167,15 @@ jobs: run: | cargo nextest run --profile ci --workspace --features openshell-server/test-support + - name: Verify telemetry can be compiled out + run: mise run rust:verify:telemetry-off + + - name: Verify the defaults-without-telemetry feature alias tracks the default feature set + run: mise run rust:verify:defaults-without-telemetry + + - name: Verify system CA roots build mode compiles and excludes bundled Mozilla roots + run: mise run rust:verify:system-ca-roots + python: name: Python (${{ matrix.runner }}) needs: pr_metadata diff --git a/README.md b/README.md index ba1ccf9859..28674cbdbf 100644 --- a/README.md +++ b/README.md @@ -258,7 +258,15 @@ 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. For Helm installs, set `server.telemetryEnabled=false`. 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, and each crate that carries it also defines a `defaults-without-telemetry` alias covering every other default feature. Build telemetry-free artifacts with `--no-default-features --features defaults-without-telemetry`: + +```shell +cargo build --release -p openshell-server --no-default-features --features defaults-without-telemetry +cargo build --release -p openshell-sandbox --no-default-features --features defaults-without-telemetry +cargo build --release -p openshell-driver-vm --no-default-features --features defaults-without-telemetry +``` + +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. 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. diff --git a/architecture/build.md b/architecture/build.md index c6810feece..6b3036c3b7 100644 --- a/architecture/build.md +++ b/architecture/build.md @@ -34,12 +34,27 @@ is the single switch that enables `openshell-core/telemetry` for its build graph. In-process drivers (`docker`, `kubernetes`, `podman`) inherit the gateway's setting through feature unification and carry no passthrough. -Building a binary with `--no-default-features` compiles out telemetry entirely: -no endpoint, no telemetry HTTP client, and no emission code. With telemetry -compiled out, `telemetry::enabled()` is always `false` and the `emit_*` helpers -are no-ops, so the data-model types stay available and dependent crates compile -unchanged. The runtime `OPENSHELL_TELEMETRY_ENABLED` switch remains the way to -disable telemetry in a default (telemetry-enabled) build. +Building a binary without the `telemetry` feature compiles out telemetry +entirely: no endpoint, no telemetry HTTP client, and no emission code. With +telemetry compiled out, `telemetry::enabled()` is always `false` and the +`emit_*` helpers are no-ops, so the data-model types stay available and +dependent crates compile unchanged. The runtime `OPENSHELL_TELEMETRY_ENABLED` +switch remains the way to disable telemetry in a default (telemetry-enabled) +build. + +Cargo cannot subtract a single default feature, so each of the three binary +crates also defines a `defaults-without-telemetry` alias listing every default +except `telemetry`. Telemetry-free builds use +`--no-default-features --features defaults-without-telemetry` and stay correct +as the default set grows, instead of dropping unrelated defaults the way a bare +`--no-default-features` does on `openshell-sandbox`. The alias is a keep-list, +not a switch: enabling it on top of the defaults would otherwise yield a +telemetry-on binary that reads as telemetry-free, so each crate root carries a +`compile_error!` for the `telemetry` + `defaults-without-telemetry` combination. +`rust:verify:defaults-without-telemetry` guards both properties — that each +alias still equals its crate's defaults minus `telemetry`, and that the +mutual-exclusion error is wired up — and `rust:verify:telemetry-off` builds +through the alias and inspects the resulting binaries for telemetry markers. Supervisor upstream TLS root-store selection is controlled by the `bundled-ca-roots` Cargo feature (on by default). Default builds use Mozilla diff --git a/crates/openshell-driver-vm/Cargo.toml b/crates/openshell-driver-vm/Cargo.toml index ebcb9d2bc2..30a0f2a970 100644 --- a/crates/openshell-driver-vm/Cargo.toml +++ b/crates/openshell-driver-vm/Cargo.toml @@ -59,6 +59,15 @@ default = ["telemetry"] ## default; build with `--no-default-features` for a telemetry-free VM driver ## that reports telemetry disabled to the sandboxes it launches. telemetry = ["openshell-core/telemetry"] +## Convenience alias: every default feature except `telemetry`. Build a +## telemetry-free VM driver with +## `--no-default-features --features defaults-without-telemetry` and stay +## correct as new default features are added. Cargo cannot subtract a single +## default feature, so this alias must be paired with `--no-default-features`; +## enabling it alongside `telemetry` is a compile error rather than a silent +## telemetry-on build. Kept in sync with `default` by +## `rust:verify:defaults-without-telemetry`. +defaults-without-telemetry = [] [dev-dependencies] openshell-otel-test-support = { path = "../openshell-otel-test-support" } diff --git a/crates/openshell-driver-vm/src/lib.rs b/crates/openshell-driver-vm/src/lib.rs index 98ba6b0c9a..f34c7dda8d 100644 --- a/crates/openshell-driver-vm/src/lib.rs +++ b/crates/openshell-driver-vm/src/lib.rs @@ -1,6 +1,16 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +// `defaults-without-telemetry` is an alias for the default feature set minus +// `telemetry`, not a switch that turns telemetry off. Cargo cannot subtract a +// default feature, so adding it on top of the defaults would otherwise produce +// a telemetry-on build that reads as telemetry-free. Fail the build instead. +#[cfg(all(feature = "telemetry", feature = "defaults-without-telemetry"))] +compile_error!( + "features `telemetry` and `defaults-without-telemetry` are mutually exclusive; \ + build a telemetry-free VM driver with `--no-default-features --features defaults-without-telemetry`" +); + pub mod driver; mod embedded_runtime; mod ffi; diff --git a/crates/openshell-sandbox/Cargo.toml b/crates/openshell-sandbox/Cargo.toml index c653db84dd..3463f03767 100644 --- a/crates/openshell-sandbox/Cargo.toml +++ b/crates/openshell-sandbox/Cargo.toml @@ -60,6 +60,17 @@ default = ["telemetry", "bundled-ca-roots"] ## `--no-default-features --features system-ca-roots` to build a supervisor ## that uses the platform trust store with telemetry intact. system-ca-roots = ["telemetry"] +## Convenience alias: every default feature except `telemetry`. Build a +## telemetry-free supervisor with +## `--no-default-features --features defaults-without-telemetry` and stay +## correct as new default features are added. Cargo cannot subtract a single +## default feature, so this alias must be paired with `--no-default-features`; +## enabling it alongside `telemetry` is a compile error rather than a silent +## telemetry-on build. Kept in sync with `default` by +## `rust:verify:defaults-without-telemetry`. Do not pair it with +## `system-ca-roots`, which re-enables `telemetry`; a build with neither +## telemetry nor bundled CA roots is plain `--no-default-features`. +defaults-without-telemetry = ["bundled-ca-roots"] telemetry = ["openshell-core/telemetry"] bundled-ca-roots = ["openshell-supervisor-network/bundled-ca-roots"] diff --git a/crates/openshell-sandbox/src/lib.rs b/crates/openshell-sandbox/src/lib.rs index b1c226cebd..2eae821fb8 100644 --- a/crates/openshell-sandbox/src/lib.rs +++ b/crates/openshell-sandbox/src/lib.rs @@ -5,6 +5,16 @@ //! //! This crate provides process sandboxing and monitoring capabilities. +// `defaults-without-telemetry` is an alias for the default feature set minus +// `telemetry`, not a switch that turns telemetry off. Cargo cannot subtract a +// default feature, so adding it on top of the defaults would otherwise produce +// a telemetry-on build that reads as telemetry-free. Fail the build instead. +#[cfg(all(feature = "telemetry", feature = "defaults-without-telemetry"))] +compile_error!( + "features `telemetry` and `defaults-without-telemetry` are mutually exclusive; \ + build a telemetry-free supervisor with `--no-default-features --features defaults-without-telemetry`" +); + mod activity_aggregator; mod denial_aggregator; #[cfg_attr(not(target_os = "linux"), allow(dead_code))] diff --git a/crates/openshell-server/Cargo.toml b/crates/openshell-server/Cargo.toml index ae35fc0fbf..adea697502 100644 --- a/crates/openshell-server/Cargo.toml +++ b/crates/openshell-server/Cargo.toml @@ -138,6 +138,15 @@ in-tree-compute-drivers = [ ## 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"] +## Convenience alias: every default feature except `telemetry`. Build a +## telemetry-free gateway with +## `--no-default-features --features defaults-without-telemetry` and stay +## correct as new default features are added. Cargo cannot subtract a single +## default feature, so this alias must be paired with `--no-default-features`; +## enabling it alongside `telemetry` is a compile error rather than a silent +## telemetry-on build. Kept in sync with `default` by +## `rust:verify:defaults-without-telemetry`. +defaults-without-telemetry = [] bundled-z3 = ["openshell-prover/bundled-z3"] test-support = [] diff --git a/crates/openshell-server/src/lib.rs b/crates/openshell-server/src/lib.rs index 3dc2acec06..1b9dcf6709 100644 --- a/crates/openshell-server/src/lib.rs +++ b/crates/openshell-server/src/lib.rs @@ -13,6 +13,16 @@ //! startup. Runtime selection only consults that registry or a configured //! external endpoint; it does not switch on driver names. +// `defaults-without-telemetry` is an alias for the default feature set minus +// `telemetry`, not a switch that turns telemetry off. Cargo cannot subtract a +// default feature, so adding it on top of the defaults would otherwise produce +// a telemetry-on build that reads as telemetry-free. Fail the build instead. +#[cfg(all(feature = "telemetry", feature = "defaults-without-telemetry"))] +compile_error!( + "features `telemetry` and `defaults-without-telemetry` are mutually exclusive; \ + build a telemetry-free gateway with `--no-default-features --features defaults-without-telemetry`" +); + mod auth; pub mod certgen; pub mod cli; diff --git a/tasks/rust.toml b/tasks/rust.toml index 854c2ac939..6ef34f60df 100644 --- a/tasks/rust.toml +++ b/tasks/rust.toml @@ -53,13 +53,19 @@ run = [ # markers, so the absent checks below can never become silently vacuous. "cargo build -p openshell-server --bin openshell-gateway", "tasks/scripts/verify-telemetry-compiled-out.sh present target/debug/openshell-gateway", - # Guard: telemetry-free builds must contain no telemetry markers. - "cargo build -p openshell-server --bin openshell-gateway --no-default-features", + # Guard: telemetry-free builds must contain no telemetry markers. Built + # through the `defaults-without-telemetry` alias, which is how the docs tell + # operators to produce these artifacts. + "cargo build -p openshell-server --bin openshell-gateway --no-default-features --features defaults-without-telemetry", "tasks/scripts/verify-telemetry-compiled-out.sh absent target/debug/openshell-gateway", - "cargo build -p openshell-sandbox --bin openshell-sandbox --no-default-features --features bundled-ca-roots", + "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", ] +["rust:verify:defaults-without-telemetry"] +description = "Verify the defaults-without-telemetry feature alias matches default minus telemetry and cannot be used additively" +run = "tasks/scripts/verify-defaults-without-telemetry.sh" + ["rust:verify:system-ca-roots"] description = "Verify system CA roots build mode compiles and excludes bundled Mozilla root crates" run = [ diff --git a/tasks/scripts/verify-defaults-without-telemetry.sh b/tasks/scripts/verify-defaults-without-telemetry.sh new file mode 100755 index 0000000000..4d78cbc5b4 --- /dev/null +++ b/tasks/scripts/verify-defaults-without-telemetry.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# Verify the `defaults-without-telemetry` alias still means "every default +# feature except telemetry", and that it cannot be used additively. +# +# Cargo cannot subtract a single default feature, so telemetry-free builds use +# `--no-default-features --features defaults-without-telemetry`. Two failure +# modes follow from that, and this guard covers both: +# +# 1. Drift. The alias enumerates the keep-list by hand, so it silently rots +# the moment a crate gains a new default feature. Telemetry-free builds +# would then quietly lose an unrelated default. +# 2. Additive misuse. `--features defaults-without-telemetry` without +# `--no-default-features` would otherwise compile a telemetry-on binary +# that reads as telemetry-free. Each crate root carries a `compile_error!` +# for that combination; this asserts the error is actually wired up. + +set -euo pipefail + +# Crates that define the alias. Each must forward `telemetry` and define +# `defaults-without-telemetry`. +CRATES=( + openshell-server + openshell-sandbox + openshell-driver-vm +) + +if ! command -v jq >/dev/null 2>&1; then + echo "error: 'jq' is required to inspect cargo metadata" >&2 + exit 2 +fi + +metadata=$(cargo metadata --no-deps --format-version 1) + +failed=0 +for crate in "${CRATES[@]}"; do + features=$(jq -c --arg crate "$crate" \ + '.packages[] | select(.name == $crate) | .features' <<<"$metadata") + + if [[ -z $features || $features == "null" ]]; then + echo "FAIL: crate '$crate' not found in workspace metadata" >&2 + failed=1 + continue + fi + + if ! jq -e 'has("defaults-without-telemetry")' <<<"$features" >/dev/null; then + echo "FAIL: $crate defines no 'defaults-without-telemetry' feature" >&2 + failed=1 + continue + fi + + expected=$(jq -r '(.default // []) - ["telemetry"] | sort | join(",")' <<<"$features") + actual=$(jq -r '(."defaults-without-telemetry" // []) | sort | join(",")' <<<"$features") + + if [[ $expected != "$actual" ]]; then + echo "FAIL: $crate 'defaults-without-telemetry' is out of sync with 'default'" >&2 + echo " default minus telemetry: [${expected}]" >&2 + echo " defaults-without-telemetry: [${actual}]" >&2 + echo " Update 'defaults-without-telemetry' in crates/$crate/Cargo.toml to match." >&2 + failed=1 + continue + fi + + echo "OK: $crate 'defaults-without-telemetry' == default minus telemetry [${expected}]" +done + +# Additive misuse must be a hard error. Match on the `compile_error!` text +# rather than a nonzero exit code: openshell-driver-vm does not build on every +# host, and a check that failed for an unrelated reason would make this guard +# silently vacuous. +for crate in "${CRATES[@]}"; do + output=$(cargo check -p "$crate" --features defaults-without-telemetry 2>&1 || true) + + if grep -qF "features \`telemetry\` and \`defaults-without-telemetry\` are mutually exclusive" <<<"$output"; then + echo "OK: $crate rejects 'telemetry' + 'defaults-without-telemetry'" + continue + fi + + echo "FAIL: $crate did not reject 'telemetry' + 'defaults-without-telemetry'" >&2 + echo " Expected the mutual-exclusion compile_error! in crates/$crate/src/lib.rs." >&2 + echo " Got:" >&2 + sed 's/^/ /' <<<"$output" | tail -20 >&2 + failed=1 +done + +exit "$failed" From cbd4a7108144476803ba642d73bf6caf9dc23047 Mon Sep 17 00:00:00 2001 From: Russell Bryant Date: Tue, 25 Aug 2026 16:44:05 -0400 Subject: [PATCH 2/2] fix feature alias for openshell-server Signed-off-by: Russell Bryant --- crates/openshell-server/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/openshell-server/Cargo.toml b/crates/openshell-server/Cargo.toml index adea697502..1cfd214f20 100644 --- a/crates/openshell-server/Cargo.toml +++ b/crates/openshell-server/Cargo.toml @@ -146,7 +146,7 @@ telemetry = ["openshell-core/telemetry"] ## enabling it alongside `telemetry` is a compile error rather than a silent ## telemetry-on build. Kept in sync with `default` by ## `rust:verify:defaults-without-telemetry`. -defaults-without-telemetry = [] +defaults-without-telemetry = ["in-tree-compute-drivers"] bundled-z3 = ["openshell-prover/bundled-z3"] test-support = []