From b8e25acff8794f77f9348b3b5a149384674bf28b Mon Sep 17 00:00:00 2001 From: fathiaoyinloye Date: Mon, 1 Jun 2026 20:11:43 +0100 Subject: [PATCH] feat: add configuration commands for telemetry and network settings --- README.md | 45 +++++++++++- TELEMETRY_PRIVACY.md | 153 +++++++++++++++++++++++++++++++++++++++++ src/commands/config.rs | 120 ++++++++++++++++++++++++++++++++ src/commands/mod.rs | 1 + src/main.rs | 5 ++ src/utils/config.rs | 4 ++ src/utils/telemetry.rs | 11 +++ 7 files changed, 337 insertions(+), 2 deletions(-) create mode 100644 TELEMETRY_PRIVACY.md create mode 100644 src/commands/config.rs diff --git a/README.md b/README.md index 128e34e7..c767b5d2 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,27 @@ starforge network test starforge network test mainnet ``` +### Configuration commands + +```bash +# Show all configuration settings +starforge config show + +# Get a specific setting +starforge config get telemetry +starforge config get network + +# Set a configuration value +starforge config set telemetry false +starforge config set network mainnet +``` + +Common settings: +- **telemetry**: Enable/disable anonymous usage telemetry (`true` or `false`) +- **network**: Set the default network (`testnet`, `mainnet`, or custom network name) + +For privacy information, see [Telemetry & Privacy](#telemetry--privacy). + ### Scaffold commands ```bash @@ -335,9 +356,29 @@ cargo test Generate this value outside the codebase using your preferred secure workflow, such as a local Stellar key generation command or an existing throwaway test wallet. The key should live only in your shell environment or secret manager, not in source control. ---- +### Telemetry & Privacy + +starforge collects **anonymous telemetry** to help us improve the CLI. **No personal data is collected** — only command names, success/failure status, and execution time. -## Contract Templates +#### Disable Telemetry + +If you prefer not to participate: + +```bash +# Permanently disable telemetry +starforge config set telemetry false + +# Or use an environment variable (useful for CI/CD) +export STARFORGE_TELEMETRY=0 +``` + +**What's collected**: Command name, timestamp, success status, duration (milliseconds), and a random anonymous ID. + +**What's NOT collected**: Wallet addresses, secret keys, contract code, configuration values, error messages, or personal information. + +For detailed information, see [TELEMETRY_PRIVACY.md](./TELEMETRY_PRIVACY.md). + +--- | Template | Description | |----------|-------------| diff --git a/TELEMETRY_PRIVACY.md b/TELEMETRY_PRIVACY.md new file mode 100644 index 00000000..51b5f749 --- /dev/null +++ b/TELEMETRY_PRIVACY.md @@ -0,0 +1,153 @@ +# Telemetry & Privacy + +StarForge collects telemetry data to help us understand usage patterns and improve the CLI. This document explains exactly what is collected, how to disable it, and what we do with the data. + +## Privacy-First Design + +- **Opt-out by default**: Telemetry is enabled by default but can be easily disabled +- **No personal data**: We never collect personal information, credentials, or sensitive data +- **Anonymous ID**: Usage is tracked with a random UUID, not tied to your identity +- **Local-first**: Telemetry is stored locally in your machine and only sent with explicit consent (future versions) +- **Transparent**: Full source code is available for audit + +## What Data is Collected + +For each CLI command executed, StarForge collects: + +- **Command name**: Which command was run (e.g., `wallet`, `deploy`, `new`) +- **Timestamp**: When the command was executed +- **Success/Failure**: Whether the command completed successfully +- **Duration**: How long the command took in milliseconds +- **Anonymous ID**: A random UUID generated once per machine + +### Example Telemetry Event + +```json +{ + "timestamp": "2025-01-15T10:30:45Z", + "event": "deploy", + "properties": { + "success": true, + "duration_ms": 2500 + }, + "anonymous_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +### What We Do NOT Collect + +- ❌ Wallet addresses or secret keys +- ❌ Contract code or source files +- ❌ Configuration values (network URLs, custom networks) +- ❌ Error messages or stack traces +- ❌ User identity, email, or personal information +- ❌ File paths or local system information + +## How to Disable Telemetry + +### Option 1: Configuration Command (Recommended) + +Disable telemetry permanently using the `config` command: + +```bash +starforge config set telemetry false +``` + +View your current telemetry setting: + +```bash +starforge config show +# or +starforge config get telemetry +``` + +Re-enable telemetry: + +```bash +starforge config set telemetry true +``` + +### Option 2: Environment Variable + +Disable telemetry for a single command or session: + +```bash +# Disable for a single command +STARFORGE_TELEMETRY=0 starforge deploy --wasm my_contract.wasm + +# Disable for the entire shell session +export STARFORGE_TELEMETRY=0 +starforge wallet list +starforge deploy --wasm my_contract.wasm +``` + +Accepted values to disable telemetry: +- `0`, `false`, `off`, `disabled`, `no` + +### Option 3: CI/CD Pipelines + +For automated environments, set the environment variable: + +```bash +# In GitHub Actions +env: + STARFORGE_TELEMETRY: "0" + +# In GitLab CI +script: + - export STARFORGE_TELEMETRY=0 + - starforge deploy --wasm my_contract.wasm +``` + +## Configuration Storage + +Your telemetry preference is stored in: + +``` +~/.starforge/config.toml +``` + +Example: + +```toml +network = "testnet" +telemetry_enabled = false + +[[wallets]] +name = "deployer" +public_key = "GABC...XYZ" +# ... +``` + +Telemetry logs are stored in: + +``` +~/.starforge/data/telemetry.log +~/.starforge/data/anonymous_id +``` + +These files are created only if telemetry is enabled. + +## Future: Remote Telemetry + +In future versions, StarForge may offer opt-in remote telemetry (sending anonymous data to a analytics service). This will be: + +- **Completely optional**: Requires explicit opt-in, never enabled by default +- **Aggregated**: Only high-level statistics are sent (e.g., "10 deployments per day", not individual events) +- **Auditable**: Full details published on what is collected and where it goes +- **Respectable**: Always respects `STARFORGE_TELEMETRY=0` and config settings + +## Questions or Concerns? + +If you have privacy concerns or questions about telemetry: + +1. **Review the code**: Full source available at https://github.com/Josetic224/StarForge +2. **Check the logs**: Inspect `~/.starforge/data/telemetry.log` to see what was collected +3. **Disable it**: Use `starforge config set telemetry false` if you prefer not to participate +4. **Report issues**: Open an issue on GitHub with any privacy concerns + +## Additional Privacy Resources + +- [PRIVACY_POLICY.md](./PRIVACY_POLICY.md) - Full privacy policy +- [SECURITY_LOGGING_AUDIT.md](./SECURITY_LOGGING_AUDIT.md) - Security logging details +- [GitHub Repository](https://github.com/Josetic224/StarForge) - Open source, fully auditable diff --git a/src/commands/config.rs b/src/commands/config.rs new file mode 100644 index 00000000..3de289ca --- /dev/null +++ b/src/commands/config.rs @@ -0,0 +1,120 @@ +use anyhow::{bail, Result}; +use clap::Subcommand; +use colored::*; + +#[derive(Subcommand)] +pub enum ConfigCommands { + /// Get a configuration value + Get { + /// Configuration key to retrieve (e.g., telemetry, network, wallet) + key: String, + }, + /// Set a configuration value + Set { + /// Configuration key to set (e.g., telemetry) + key: String, + /// Configuration value (e.g., true, false) + value: String, + }, + /// Show all configuration settings + Show, +} + +pub fn handle_config(cmd: ConfigCommands) -> Result<()> { + match cmd { + ConfigCommands::Get { key } => handle_get(&key), + ConfigCommands::Set { key, value } => handle_set(&key, &value), + ConfigCommands::Show => handle_show(), + } +} + +fn handle_get(key: &str) -> Result<()> { + let cfg = crate::utils::config::load()?; + + match key.to_lowercase().as_str() { + "telemetry" => { + let enabled = cfg.telemetry_enabled.unwrap_or(true); + println!("{}: {}", key.cyan(), if enabled { "enabled" } else { "disabled" }); + Ok(()) + } + "network" => { + println!("{}: {}", key.cyan(), cfg.network); + Ok(()) + } + _ => { + bail!( + "Unknown configuration key: '{}'\n\nAvailable keys:\n - telemetry\n - network", + key + ); + } + } +} + +fn handle_set(key: &str, value: &str) -> Result<()> { + let mut cfg = crate::utils::config::load()?; + + match key.to_lowercase().as_str() { + "telemetry" => { + let enabled = match value.to_lowercase().as_str() { + "true" | "on" | "enabled" | "yes" => true, + "false" | "off" | "disabled" | "no" => false, + _ => { + bail!( + "Invalid value for telemetry: '{}'\n\nUse 'true'/'enabled'/'on'/'yes' or 'false'/'disabled'/'off'/'no'.", + value + ); + } + }; + cfg.telemetry_enabled = Some(enabled); + crate::utils::config::save(&cfg)?; + println!( + "✓ {} set to {}", + "telemetry".green(), + if enabled { "enabled" } else { "disabled" } + ); + Ok(()) + } + "network" => { + crate::utils::config::validate_network(value)?; + cfg.network = value.to_string(); + crate::utils::config::save(&cfg)?; + println!("✓ {} set to {}", "network".green(), value); + Ok(()) + } + _ => { + bail!( + "Unknown configuration key: '{}'\n\nAvailable keys:\n - telemetry\n - network", + key + ); + } + } +} + +fn handle_show() -> Result<()> { + let cfg = crate::utils::config::load()?; + + println!("\n{}", "=== StarForge Configuration ===".bold()); + println!(); + println!(" {}: {}", "Network".cyan(), cfg.network); + + let telemetry_status = cfg.telemetry_enabled.unwrap_or(true); + println!( + " {}: {}", + "Telemetry".cyan(), + if telemetry_status { "enabled" } else { "disabled" } + ); + + println!("\n{}", "Configuration file:".cyan()); + if let Ok(cfg_path) = crate::utils::config::get_config_path() { + println!(" {}", cfg_path.display()); + } + + println!("\n{}", "Data directory:".cyan()); + if let Ok(data_dir) = crate::utils::config::get_data_dir() { + println!(" {}", data_dir.display()); + } + + println!(); + + Ok(()) +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index ad090895..d11c76f6 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,5 +1,6 @@ pub mod benchmark; pub mod completions; +pub mod config; pub mod contract; pub mod deploy; pub mod gas; diff --git a/src/main.rs b/src/main.rs index 490c5680..19d6d4d8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,6 +56,9 @@ enum Commands { Deploy(commands::deploy::DeployArgs), /// Show starforge config and environment info Info, + /// Manage starforge configuration (telemetry, network) + #[command(subcommand)] + Config(commands::config::ConfigCommands), Tx(commands::tx::TxArgs), // fetch transaction for the account @@ -129,6 +132,7 @@ fn main() { Commands::Inspect(_) => "inspect", Commands::Deploy(_) => "deploy", Commands::Info => "info", + Commands::Config(_) => "config", Commands::Tx(_) => "tx", Commands::Network(_) => "network", Commands::Node(_) => "node", @@ -155,6 +159,7 @@ fn main() { Commands::Inspect(cmd) => commands::inspect::handle(cmd), Commands::Deploy(args) => commands::deploy::handle(args), Commands::Info => commands::info::handle(), + Commands::Config(cmd) => commands::config::handle_config(cmd), Commands::Tx(args) => commands::tx::handle(args), Commands::Network(cmd) => commands::network::handle(cmd), Commands::Node(cmd) => commands::node::handle(cmd), diff --git a/src/utils/config.rs b/src/utils/config.rs index 2cbff17e..8d850f05 100644 --- a/src/utils/config.rs +++ b/src/utils/config.rs @@ -354,6 +354,10 @@ pub fn get_data_dir() -> Result { Ok(dir) } +pub fn get_config_path() -> Result { + Ok(config_path()) +} + pub fn config_path() -> PathBuf { config_dir().join("config.toml") } diff --git a/src/utils/telemetry.rs b/src/utils/telemetry.rs index f3f68def..c887d1d6 100644 --- a/src/utils/telemetry.rs +++ b/src/utils/telemetry.rs @@ -14,6 +14,17 @@ pub struct TelemetryData { } pub fn track_event(event: &str, properties: serde_json::Value) -> Result<()> { + // Check environment variable first (for CI/automation that cannot modify config) + if let Ok(env_val) = std::env::var("STARFORGE_TELEMETRY") { + let disabled = matches!( + env_val.to_lowercase().as_str(), + "0" | "false" | "off" | "disabled" | "no" + ); + if disabled { + return Ok(()); + } + } + let cfg = config::load()?; // Check if telemetry is enabled (default to true, but respect opt-out)