diff --git a/.env.example b/.env.example index 9ee9335..c9f9b3f 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,13 @@ ##### Change the name of this file to .env after updating it! +############ +# [optional] +# Anonymous telemetry - helps improve the project +# Set to false to disable (default: true) +# INSTALLATION_ID is auto-generated, do not modify +############ +# SCARF_ANALYTICS=false + ############ # [required] # n8n credentials - you set this to whatever you want, just make it a long and secure string for both! diff --git a/CHANGELOG.md b/CHANGELOG.md index 5418d47..ee89e32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project are documented in this file. ## [December 2025] ### Added +- **Anonymous Telemetry** - Optional usage analytics via Scarf (opt-out with `SCARF_ANALYTICS=false`) - **Gost Proxy** - HTTP/HTTPS proxy for AI services outbound traffic (geo-bypass) - **Welcome Page** - Post-install dashboard with service credentials and quick start - **Makefile** - Common project commands (`make install`, `make update`, `make logs`, `make status`, etc.) diff --git a/README.md b/README.md index d93af8b..5eb5c6a 100644 --- a/README.md +++ b/README.md @@ -363,6 +363,23 @@ When you build automations in n8n that need to read or write files on your serve Want to see who has contributed to this project? Check out the [**GitHub Contributors Page**](https://github.com/kossakovsky/n8n-install/graphs/contributors)! -## 📜 License +## Telemetry + +This installer collects anonymous usage statistics via [Scarf](https://scarf.sh) to help improve the project. **No personal data is collected.** + +Data collected: +- Event type (install/update start/complete) +- Installer version +- Selected services +- OS type (e.g., ubuntu-24.04) +- Random installation ID (to correlate start/complete events) +- Country (determined by Scarf from IP, not stored by us) + +To opt out, add to your `.env` file after installation: +``` +SCARF_ANALYTICS=false +``` + +## License This project (originally created by the n8n team, with further development by contributors - see "Important Links") is licensed under the Apache License 2.0. See the [LICENSE](LICENSE) file for details. diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.0 diff --git a/scripts/03_generate_secrets.sh b/scripts/03_generate_secrets.sh index 08068b0..4b9c425 100755 --- a/scripts/03_generate_secrets.sh +++ b/scripts/03_generate_secrets.sh @@ -548,6 +548,11 @@ done log_success ".env file generated successfully in the project root ($OUTPUT_FILE)." +# Save INSTALLATION_ID if passed from install.sh (for telemetry correlation) +if [[ -n "${INSTALLATION_ID:-}" ]]; then + write_env_var "INSTALLATION_ID" "$INSTALLATION_ID" "$OUTPUT_FILE" +fi + # Uninstall caddy apt remove -y caddy diff --git a/scripts/apply_update.sh b/scripts/apply_update.sh index 1867468..7b47da3 100755 --- a/scripts/apply_update.sh +++ b/scripts/apply_update.sh @@ -32,6 +32,9 @@ require_file "$RUN_SERVICES_SCRIPT" "$RUN_SERVICES_SCRIPT not found." cd "$PROJECT_ROOT" +# Send telemetry: update started +send_telemetry "update_start" + # --- Call 03_generate_secrets.sh in update mode --- log_info "Ensuring .env file is up-to-date with all variables..." bash "$SCRIPT_DIR/03_generate_secrets.sh" --update || { @@ -120,4 +123,7 @@ bash "$SCRIPT_DIR/07_final_report.sh" || { } # --- End of Final Report --- -exit 0 \ No newline at end of file +# Send telemetry: update completed with current services +send_telemetry "update_complete" "$(read_env_var COMPOSE_PROFILES)" + +exit 0 diff --git a/scripts/install.sh b/scripts/install.sh index 8a01809..88d76d6 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -47,6 +47,14 @@ fi # Initialize paths using utils.sh helper init_paths +# Generate installation ID for telemetry correlation (before .env exists) +# This ID will be saved to .env by 03_generate_secrets.sh +INSTALLATION_ID=$(get_installation_id) +export INSTALLATION_ID + +# Send telemetry: installation started +send_telemetry "install_start" + # Check if all required scripts exist and are executable in the current directory required_scripts=( "01_system_preparation.sh" @@ -152,4 +160,7 @@ log_success "File permissions fixed!" log_success "Installation complete!" -exit 0 \ No newline at end of file +# Send telemetry: installation completed with selected services +send_telemetry "install_complete" "$(read_env_var COMPOSE_PROFILES)" + +exit 0 diff --git a/scripts/utils.sh b/scripts/utils.sh index 310f455..34494cd 100755 --- a/scripts/utils.sh +++ b/scripts/utils.sh @@ -806,3 +806,110 @@ restore_preserved_dirs() { rm -rf "$backup_base" return 0 } + +#============================================================================= +# ANONYMOUS TELEMETRY (Scarf) +#============================================================================= +# Sends anonymous usage statistics to help improve the project. +# No personal data is collected. Users can opt-out by setting SCARF_ANALYTICS=false +# or DO_NOT_TRACK=1 + +# Scarf endpoint for anonymous telemetry +SCARF_ENDPOINT="https://kossakovsky.gateway.scarf.sh/n8n-install" + +# Get or generate installation ID (persistent across sessions) +# Usage: id=$(get_installation_id) +# Priority: 1) exported env var, 2) .env file, 3) generate new +get_installation_id() { + # First check if ID is already in environment (exported from parent process) + # This ensures consistency when called multiple times before .env exists + if [[ -n "${INSTALLATION_ID:-}" ]]; then + echo "$INSTALLATION_ID" + return 0 + fi + + # Then check .env file for existing ID + if [[ -f "$ENV_FILE" ]]; then + local existing_id + existing_id=$(grep "^INSTALLATION_ID=" "$ENV_FILE" 2>/dev/null | cut -d'=' -f2- | tr -d '"' | tr -d "'") + if [[ -n "$existing_id" ]]; then + echo "$existing_id" + return 0 + fi + fi + + # Generate new ID (12 char hex = 6 bytes) + local new_id + new_id=$(od -An -tx1 -N6 /dev/urandom | tr -d ' \n') + + # Save to .env if file exists + if [[ -f "$ENV_FILE" ]]; then + if grep -q "^INSTALLATION_ID=" "$ENV_FILE" 2>/dev/null; then + sed -i.bak "s/^INSTALLATION_ID=.*/INSTALLATION_ID=\"${new_id}\"/" "$ENV_FILE" + rm -f "${ENV_FILE}.bak" + else + echo "INSTALLATION_ID=\"${new_id}\"" >> "$ENV_FILE" + fi + fi + + echo "$new_id" +} + +# Detect OS type +# Usage: os=$(get_os_type) +get_os_type() { + if [[ -f /etc/os-release ]]; then + . /etc/os-release + echo "${ID:-linux}-${VERSION_ID:-unknown}" + elif [[ "$(uname)" == "Darwin" ]]; then + echo "macos-$(sw_vers -productVersion 2>/dev/null || echo 'unknown')" + else + echo "$(uname -s | tr '[:upper:]' '[:lower:]')-unknown" + fi +} + +# Send anonymous telemetry event +# Usage: send_telemetry "install_start" +# send_telemetry "install_complete" "$COMPOSE_PROFILES" +# send_telemetry "update_start" +# send_telemetry "update_complete" "$COMPOSE_PROFILES" +# Arguments: +# $1 - event_type: "install_start", "install_complete", "update_start", "update_complete" +# $2 - services (optional): comma-separated list of selected profiles (for *_complete events) +# Note: To get installation ID, call get_installation_id directly before this function +send_telemetry() { + local event_type="$1" + local services="${2:-}" + + # Load environment (for SCARF_ANALYTICS check and INSTALLATION_ID) + [[ -f "$ENV_FILE" ]] && source "$ENV_FILE" 2>/dev/null + + # Get installation ID and OS + local install_id + install_id=$(get_installation_id) + local os_type + os_type=$(get_os_type) + + # Opt-out check: respect SCARF_ANALYTICS and DO_NOT_TRACK + if [[ "${SCARF_ANALYTICS:-true}" == "false" ]] || [[ "${DO_NOT_TRACK:-0}" == "1" ]]; then + return 0 + fi + + # Get version from VERSION file + local version="unknown" + if [[ -f "$PROJECT_ROOT/VERSION" ]]; then + version=$(cat "$PROJECT_ROOT/VERSION" | tr -d '\n\r') + fi + + # Build URL with query parameters (URL-encode services to handle special chars) + local url="${SCARF_ENDPOINT}?event=${event_type}&version=${version}&id=${install_id}&os=${os_type}" + + if [[ -n "$services" ]]; then + # URL encode spaces (commas are allowed in query strings per RFC 3986) + local encoded_services="${services// /%20}" + url="${url}&services=${encoded_services}" + fi + + # Send telemetry in background with short timeout (non-blocking, silent) + curl -sf --connect-timeout 2 --max-time 2 "$url" >/dev/null 2>&1 & +}