diff --git a/crates/goose-cli/src/commands/configure.rs b/crates/goose-cli/src/commands/configure.rs index 122b143eb6..a5e5cc4402 100644 --- a/crates/goose-cli/src/commands/configure.rs +++ b/crates/goose-cli/src/commands/configure.rs @@ -94,6 +94,7 @@ pub fn configure_telemetry_consent_dialog() -> anyhow::Result { .interact()?; config.set_param(TELEMETRY_ENABLED_KEY, enabled)?; + goose::posthog::set_telemetry_enabled(enabled); if enabled { let _ = cliclack::log::success("Thank you for helping improve goose!"); @@ -1383,6 +1384,7 @@ pub fn configure_telemetry_dialog() -> anyhow::Result<()> { .interact()?; config.set_param(TELEMETRY_ENABLED_KEY, enabled)?; + goose::posthog::set_telemetry_enabled(enabled); if enabled { cliclack::outro("Telemetry enabled - thank you for helping improve goose!")?; diff --git a/crates/goose-server/src/routes/config_management.rs b/crates/goose-server/src/routes/config_management.rs index 60cdff93d9..fbae3f96a4 100644 --- a/crates/goose-server/src/routes/config_management.rs +++ b/crates/goose-server/src/routes/config_management.rs @@ -173,6 +173,12 @@ pub async fn upsert_config( ) -> Result, ErrorResponse> { let config = Config::global(); config.set(&query.key, &query.value, query.is_secret)?; + + if query.key == goose::posthog::TELEMETRY_ENABLED_KEY { + let enabled = query.value.as_bool().unwrap_or(false); + goose::posthog::set_telemetry_enabled(enabled); + } + Ok(Json(Value::String(format!("Upserted key {}", query.key)))) } @@ -197,6 +203,10 @@ pub async fn remove_config( config.delete(&query.key)?; } + if query.key == goose::posthog::TELEMETRY_ENABLED_KEY { + goose::posthog::set_telemetry_enabled(false); + } + Ok(Json(format!("Removed key {}", query.key))) } diff --git a/crates/goose/src/posthog.rs b/crates/goose/src/posthog.rs index b6dfd38f0c..061eb2d810 100644 --- a/crates/goose/src/posthog.rs +++ b/crates/goose/src/posthog.rs @@ -10,9 +10,8 @@ use chrono::{DateTime, Utc}; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use std::fs; -use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Mutex; -use std::time::{SystemTime, UNIX_EPOCH}; use uuid::Uuid; const POSTHOG_API_KEY: &str = "phc_RyX5CaY01VtZJCQyhSR5KFh6qimUy81YwxsEpotAftT"; @@ -41,38 +40,27 @@ pub fn get_telemetry_choice() -> Option { config.get_param::(TELEMETRY_ENABLED_KEY).ok() } -// Cached result of the telemetry consent check so that hot paths -// (e.g. Sentry traces_sampler) avoid disk I/O on every call. -static TELEMETRY_CACHE: AtomicBool = AtomicBool::new(false); -static TELEMETRY_CACHE_TIME: AtomicU64 = AtomicU64::new(0); -const TELEMETRY_CACHE_TTL_SECS: u64 = 5; +// In-memory telemetry consent flag. Initialised from config on first +// access and updated immediately via `set_telemetry_enabled()` when the +// config changes, so hot paths (Sentry callbacks) never hit disk. +static TELEMETRY_ENABLED: Lazy = Lazy::new(|| { + AtomicBool::new(get_telemetry_choice().unwrap_or(false)) +}); -/// Check if telemetry is enabled. -/// -/// Returns false if: -/// - GOOSE_TELEMETRY_OFF environment variable is set to "1" or "true" -/// - GOOSE_TELEMETRY_ENABLED config value is set to false -/// - User has not made a telemetry choice yet (opt-in required) +/// Check if telemetry is enabled (fast, no disk I/O). /// /// Returns true only if the user has explicitly opted in. -/// -/// The result is cached for up to 5 seconds to avoid repeated disk I/O -/// when called from high-frequency paths like Sentry's traces_sampler. pub fn is_telemetry_enabled() -> bool { - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_default() - .as_secs(); - let last = TELEMETRY_CACHE_TIME.load(Ordering::Relaxed); + TELEMETRY_ENABLED.load(Ordering::Relaxed) +} - if now.saturating_sub(last) < TELEMETRY_CACHE_TTL_SECS { - return TELEMETRY_CACHE.load(Ordering::Relaxed); - } - - let enabled = get_telemetry_choice().unwrap_or(false); - TELEMETRY_CACHE.store(enabled, Ordering::Relaxed); - TELEMETRY_CACHE_TIME.store(now, Ordering::Relaxed); - enabled +/// Update the in-memory telemetry consent flag. +/// +/// Call this whenever the `GOOSE_TELEMETRY_ENABLED` config value is +/// changed so that Sentry callbacks and other hot paths see the new +/// state immediately. +pub fn set_telemetry_enabled(enabled: bool) { + TELEMETRY_ENABLED.store(enabled, Ordering::Relaxed); } // ============================================================================