diff --git a/.agents/skills/debug-openshell-cluster/SKILL.md b/.agents/skills/debug-openshell-cluster/SKILL.md index 319031b1d1..f90c3c81f8 100644 --- a/.agents/skills/debug-openshell-cluster/SKILL.md +++ b/.agents/skills/debug-openshell-cluster/SKILL.md @@ -233,6 +233,9 @@ release. Look for failed installs, unexpected values, missing namespace, wrong image tag, TLS settings that do not match the registered endpoint, and scheduling failures. +`server.telemetryEnabled` renders `OPENSHELL_TELEMETRY_ENABLED` on the gateway +pod, and the gateway propagates the effective value to sandbox supervisors. + When no external credential driver is enabled, the Helm chart uses the gateway's default encrypted database credential storage. The chart creates a retained Kubernetes Secret for the shared KEK, injects it into gateway pods, and diff --git a/.agents/skills/test-release-canary/SKILL.md b/.agents/skills/test-release-canary/SKILL.md index 4bf7d38ae3..dd2de33e57 100644 --- a/.agents/skills/test-release-canary/SKILL.md +++ b/.agents/skills/test-release-canary/SKILL.md @@ -16,6 +16,11 @@ The Release Canary (`.github/workflows/release-canary.yml`) smoke-tests the arti | `fedora` | `fedora:latest` container | `install.sh` installs the RPM packages, the local gateway starts under Podman, and `openshell status` succeeds. | | `kubernetes` | `ubuntu-latest` + kind | `helm install oci://ghcr.io/nvidia/openshell/helm-chart --version 0.0.0-dev` succeeds in a kind cluster, the gateway pod becomes Ready, port-forward exposes 8080, and the released CLI registers the in-cluster gateway and runs `openshell status` against it. | +All canary jobs disable anonymous OpenShell telemetry. Host package jobs inject +`OPENSHELL_TELEMETRY_ENABLED=false` through the service environment, and the +Kubernetes job installs with `server.telemetryEnabled=false`, so smoke traffic +does not contribute to product usage metrics. + `install.sh` defaults to the *latest tagged* release — the canary is therefore checking that the most recent public release still installs, not the just-published `dev` build. The `kubernetes` job is the exception: it pins to `0.0.0-dev` chart + `:dev` images. ## Trigger paths @@ -83,6 +88,7 @@ helm install openshell oci://ghcr.io/nvidia/openshell/helm-chart \ --version 0.0.0-dev \ --namespace openshell --create-namespace \ --set server.disableTls=true \ + --set server.telemetryEnabled=false \ --wait --timeout 5m kubectl wait --namespace openshell \ diff --git a/.github/workflows/release-canary.yml b/.github/workflows/release-canary.yml index 896d12d190..799e5e971e 100644 --- a/.github/workflows/release-canary.yml +++ b/.github/workflows/release-canary.yml @@ -14,6 +14,9 @@ defaults: run: shell: bash +env: + OPENSHELL_TELEMETRY_ENABLED: "false" + jobs: macos: name: macOS Homebrew @@ -24,6 +27,7 @@ jobs: - name: Ensure VM driver run: | launchctl setenv OPENSHELL_DRIVERS vm + launchctl setenv OPENSHELL_TELEMETRY_ENABLED "$OPENSHELL_TELEMETRY_ENABLED" - name: Install and check status run: | @@ -44,7 +48,8 @@ jobs: fi sudo systemctl start docker || sudo service docker start mkdir -p "${HOME}/.config/openshell" - printf 'OPENSHELL_DRIVERS=docker\n' > "${HOME}/.config/openshell/gateway.env" + printf 'OPENSHELL_DRIVERS=docker\nOPENSHELL_TELEMETRY_ENABLED=%s\n' \ + "$OPENSHELL_TELEMETRY_ENABLED" > "${HOME}/.config/openshell/gateway.env" docker info - name: Install and check status @@ -130,11 +135,13 @@ jobs: HOME=/root \ XDG_RUNTIME_DIR=/run/user/0 \ DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/0/bus \ + OPENSHELL_TELEMETRY_ENABLED="$OPENSHELL_TELEMETRY_ENABLED" \ INSTALL_SH_URL="https://raw.githubusercontent.com/NVIDIA/OpenShell/${{ github.event.workflow_run.head_sha || github.sha }}/install.sh" \ bash -s <<'EOF' set -euo pipefail mkdir -p "${HOME}/.config/openshell" - printf 'OPENSHELL_DRIVERS=podman\n' > "${HOME}/.config/openshell/gateway.env" + printf 'OPENSHELL_DRIVERS=podman\nOPENSHELL_TELEMETRY_ENABLED=%s\n' \ + "$OPENSHELL_TELEMETRY_ENABLED" > "${HOME}/.config/openshell/gateway.env" podman info curl -LsSf "${INSTALL_SH_URL}" | sh openshell status @@ -177,6 +184,8 @@ jobs: - name: Install snap (dangerous — from release, not store) run: | set -euo pipefail + sudo systemctl set-environment \ + "OPENSHELL_TELEMETRY_ENABLED=${OPENSHELL_TELEMETRY_ENABLED}" sudo snap install ./release/*.snap --dangerous - name: Connect interfaces @@ -222,6 +231,7 @@ jobs: --version 0.0.0-dev \ --namespace "$RELEASE_NAMESPACE" --create-namespace \ --set server.disableTls=true \ + --set "server.telemetryEnabled=${OPENSHELL_TELEMETRY_ENABLED}" \ --wait --timeout 5m - name: Verify gateway pod is Ready diff --git a/README.md b/README.md index 4b8c37015b..0f64359c67 100644 --- a/README.md +++ b/README.md @@ -258,7 +258,7 @@ OpenShell is built agent-first — your agent is your first collaborator. Before OpenShell collects anonymous telemetry to help improve the project for developers. This data is not used to track individual user behavior. It helps us understand aggregate usage of sandbox, provider, and policy workflows so we can prioritize product improvements and share usage trends with the community. -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. +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. diff --git a/architecture/build.md b/architecture/build.md index 47fb4a668d..d5b9f1a759 100644 --- a/architecture/build.md +++ b/architecture/build.md @@ -196,6 +196,10 @@ The high-level CI model: 5. Gate jobs verify that the mirror branch matches the PR head, or that the merge-group workflow ran for the queued SHA, and that the expected non-gate workflow actually ran. 6. Release workflows rebuild and publish binaries, wheels, images, and docs. +Repository CI keeps telemetry compiled into release-parity artifacts but +disables emission for Rust tests, E2E runs, and release canaries. This prevents +synthetic activity from contributing to product usage metrics. + See `CI.md` for the contributor workflow, labels, and maintainer merge-queue workflow. ## Docs Site diff --git a/deploy/helm/openshell/README.md b/deploy/helm/openshell/README.md index 7096a8ca74..13d821213b 100644 --- a/deploy/helm/openshell/README.md +++ b/deploy/helm/openshell/README.md @@ -253,6 +253,7 @@ add `ci/values-spire.yaml` to the OpenShell release values files. | server.sandboxJwt.signingSecretName | string | `""` | Name of the Opaque Secret holding the signing key material. Empty falls back to the chart fullname with "-jwt-keys" appended. | | server.sandboxJwt.ttlSecs | int | `3600` | Token TTL in seconds. Defaults to 3600 (1h). | | server.sandboxNamespace | string | `""` | Namespace where sandbox pods are created. Defaults to the Helm release namespace (.Release.Namespace) when left empty. | +| server.telemetryEnabled | bool | `true` | Enable anonymous OpenShell telemetry from the gateway and the sandbox supervisors it launches. | | server.tls.certSecretName | string | `"openshell-server-tls"` | K8s secret (type kubernetes.io/tls) with tls.crt and tls.key for the server. | | server.tls.clientCaSecretName | string | `"openshell-server-client-ca"` | K8s secret with ca.crt for client certificate verification (mTLS). Set to "" to disable mTLS and run HTTPS-only (use OIDC for auth instead). | | server.tls.clientTlsSecretName | string | `"openshell-client-tls"` | K8s secret mounted into sandbox pods for mTLS to the server. | diff --git a/deploy/helm/openshell/templates/_gateway-workload.tpl b/deploy/helm/openshell/templates/_gateway-workload.tpl index 1af71bfb05..3db50a5ee6 100644 --- a/deploy/helm/openshell/templates/_gateway-workload.tpl +++ b/deploy/helm/openshell/templates/_gateway-workload.tpl @@ -75,6 +75,8 @@ spec: - name: SSL_CERT_FILE value: /etc/openshell-tls/oidc-ca/ca.crt {{- end }} + - name: OPENSHELL_TELEMETRY_ENABLED + value: {{ .Values.server.telemetryEnabled | quote }} volumeMounts: {{- if eq (include "openshell.workloadKind" .) "statefulset" }} - name: openshell-data diff --git a/deploy/helm/openshell/tests/gateway_config_test.yaml b/deploy/helm/openshell/tests/gateway_config_test.yaml index f98c321fee..8125559e71 100644 --- a/deploy/helm/openshell/tests/gateway_config_test.yaml +++ b/deploy/helm/openshell/tests/gateway_config_test.yaml @@ -42,6 +42,26 @@ tests: path: spec.template.spec.containers[0].name value: openshell-gateway + - it: enables anonymous telemetry by default + template: templates/statefulset.yaml + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: OPENSHELL_TELEMETRY_ENABLED + value: "true" + + - it: disables anonymous telemetry when configured + template: templates/statefulset.yaml + set: + server.telemetryEnabled: false + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: OPENSHELL_TELEMETRY_ENABLED + value: "false" + - it: mounts the OIDC CA bundle when TLS is disabled template: templates/statefulset.yaml set: diff --git a/deploy/helm/openshell/values.yaml b/deploy/helm/openshell/values.yaml index 39205df1bf..3b9ba3f96a 100644 --- a/deploy/helm/openshell/values.yaml +++ b/deploy/helm/openshell/values.yaml @@ -164,6 +164,9 @@ affinity: {} server: # -- Gateway log level. logLevel: info + # -- Enable anonymous OpenShell telemetry from the gateway and the sandbox + # supervisors it launches. + telemetryEnabled: true # -- Namespace where sandbox pods are created. Defaults to the Helm release # namespace (.Release.Namespace) when left empty. sandboxNamespace: "" diff --git a/docs/kubernetes/setup.mdx b/docs/kubernetes/setup.mdx index c2fca827f1..e342c9875c 100644 --- a/docs/kubernetes/setup.mdx +++ b/docs/kubernetes/setup.mdx @@ -151,6 +151,7 @@ The most commonly changed values are: | `workload.allowMultiReplicaStatefulSet` | Allow `replicaCount > 1` with `workload.kind=statefulset`. Prefer Deployment for external database-backed multi-replica gateways. | | `server.sandboxNamespace` | Namespace where sandbox pods are created. Defaults to the Helm release namespace when left empty. | | `server.externalDbSecret` | Secret containing a PostgreSQL connection URI in the `uri` key. Use when the database is managed outside the chart. | +| `server.telemetryEnabled` | Enable anonymous OpenShell telemetry from the gateway and its sandbox supervisors. Set to `false` to opt out. | | `server.sandboxImage` | Default sandbox image used when a sandbox does not specify one. | | `server.sandboxImagePullSecrets` | Image pull secrets attached to sandbox pods. Referenced Secrets must exist in the sandbox namespace. | | `server.grpcEndpoint` | Endpoint that sandbox supervisors use to call back to the gateway. Must be reachable from inside the cluster. | diff --git a/e2e/support/gateway-common.sh b/e2e/support/gateway-common.sh index 6e25b30e0b..d9f336b411 100644 --- a/e2e/support/gateway-common.sh +++ b/e2e/support/gateway-common.sh @@ -5,6 +5,10 @@ # Shared helpers for local gateway-backed e2e wrappers. Driver-specific setup, # cleanup, and runtime behavior stay in the Docker/Podman wrapper scripts. +# E2E traffic is synthetic and must not contribute to product usage metrics. +# Keep an explicit override so telemetry-specific tests can opt back in. +export OPENSHELL_TELEMETRY_ENABLED="${OPENSHELL_TELEMETRY_ENABLED:-false}" + e2e_cargo_target_dir() { local root=$1 shift diff --git a/e2e/with-kube-gateway.sh b/e2e/with-kube-gateway.sh index cde230daaf..08a19a1ce9 100755 --- a/e2e/with-kube-gateway.sh +++ b/e2e/with-kube-gateway.sh @@ -666,6 +666,7 @@ if [ "${OPENSHELL_E2E_CREDENTIAL_DRIVERS:-0}" = "1" ] \ fi helm_extra_args=() +helm_extra_args+=(--set "server.telemetryEnabled=${OPENSHELL_TELEMETRY_ENABLED}") if [ -n "${HOST_GATEWAY_IP}" ]; then helm_extra_args+=(--set "server.hostGatewayIP=${HOST_GATEWAY_IP}") fi diff --git a/tasks/test.toml b/tasks/test.toml index ed0d17d7af..dda89f9bf6 100644 --- a/tasks/test.toml +++ b/tasks/test.toml @@ -51,6 +51,7 @@ run = "bash tasks/scripts/e2e-gpu-build-images.sh" ["test:rust"] description = "Run Rust tests" +env = { OPENSHELL_TELEMETRY_ENABLED = "false" } run = [ # Run the workspace once without openshell-server so we can run that crate # with test-only helpers enabled.