Privacy-first usage analytics for Rust open source projects - understand how your CLI tools and libraries are used in the wild
telemetry-kit helps open source maintainers understand how their tools are used in the wild. Add privacy-first telemetry in 3 lines of code. Self-host or use our managed service. Perfect for CLI tools, libraries, and Rust applications.
The following features are fully implemented and tested:
- β Interactive Consent Prompts: First-run consent dialogs for privacy compliance (NEW!)
- β Smart Instrumentation Recommendations: AI-powered code analysis suggesting where to add telemetry (NEW!)
- β Auto-Sync Background Task: Automatic event synchronization in the background
- β SQLite β Service Sync Protocol: Offline-first event buffering with HMAC-SHA256 authentication
- β
Privacy-First User IDs: Anonymous client identifiers with
client_prefix (SHA-256 hashed machine IDs) - β Event Tracking: Command and feature event builders with fluent API
- β Ingestion Server: Production-ready Rust server with PostgreSQL + Redis
- β Rate Limiting: Token-based rate limits (Free/Pro/Business/Enterprise tiers)
- β Replay Protection: Nonce-based duplicate request detection
- β Batch Ingestion: 1-1000 events per request with partial success handling
- β Docker Deployment: Complete docker-compose stack for local development
# 1. Start the server
cd server
docker compose up -d
# 2. Run the end-to-end test
cd ..
cargo run --example e2e_sync_testSee DEPLOYMENT_GUIDE.md for complete setup instructions.
Events are now automatically synced in the background - no manual .sync() calls required!
use telemetry_kit::prelude::*;
#[tokio::main]
async fn main() -> Result<()> {
// Initialize with auto-sync (enabled by default)
let telemetry = TelemetryKit::builder()
.service_name("my-app")?
.with_sync_credentials(org_id, app_id, token, secret)?
.auto_sync(true) // Enable auto-sync (default: true)
.sync_interval(60) // Sync every 60 seconds (default)
.sync_on_shutdown(true) // Sync before exit (default: true)
.build()?;
// Track events - they sync automatically in the background
telemetry.track_command("build", |event| {
event.success(true).duration_ms(1234)
}).await?;
// Graceful shutdown with final sync
telemetry.shutdown().await?;
Ok(())
}Features:
- Background tokio task syncs events at configurable intervals
- Graceful shutdown with optional final sync
- Respects DO_NOT_TRACK environment variable
- Exponential backoff on sync failures
- Thread-safe implementation
See examples/auto_sync.rs for a complete example.
Manage telemetry configuration and operations from the command line.
# Install CLI
cargo install telemetry-kit --features cli
# Create a new project with telemetry pre-configured
telemetry-kit new my-app # CLI application
telemetry-kit new my-lib -p lib # Library
telemetry-kit new my-api -p web --with-sync # Web service with sync
# Analyze code for instrumentation opportunities (NEW!)
telemetry-kit analyze # Analyze current directory
telemetry-kit analyze --detailed # Show detailed recommendations with code snippets
telemetry-kit analyze --path src/ # Analyze specific directory
telemetry-kit analyze --format json # Output as JSON
# Interactive setup (for existing projects)
telemetry-kit init
# View statistics
telemetry-kit stats
# Test sync credentials
telemetry-kit test
# Validate configuration
telemetry-kit validate
# Clean local events
telemetry-kit cleanAvailable Commands:
new- Create new projects with telemetry pre-configuredanalyze- Get smart recommendations on where to add instrumentation (NEW!)init- Interactive project setup with credential configurationtest- Validate sync credentialsstats- View event statistics (total, synced, unsynced)sync- Manually trigger synchronizationvalidate- Validate configurationclean- Clear local event database
See CLI.md for complete CLI documentation.
As an open source maintainer, you want to understand:
- Which features are actually used vs ignored
- Where users encounter errors or confusion
- How your CLI tool performs in real-world environments
- Whether your library is used correctly
But current telemetry solutions are:
- Too complex: Setting up OpenTelemetry requires understanding multiple crates and writing verbose boilerplate
- Not opinionated: You don't know what to track or where to add instrumentation
- Privacy-blind: Easy to accidentally log PII, risking your users' trust
- CLI-unfriendly: Most tools designed for long-running services, not CLI applications
- Hard to self-host: Commercial solutions or complex infrastructure required
- Trust issues: Users disable telemetry because they don't trust what's collected
use telemetry_kit::prelude::*;
#[tokio::main]
#[telemetry_kit::instrumented] // π That's it!
async fn main() {
telemetry_kit::init()
.service_name("my-awesome-cli")
.endpoint("https://telemetry.myapp.com")
.anonymous()
.init();
// Your code here - automatically instrumented!
}What you get as an OSS maintainer:
- β 3 lines instead of 50+: Sensible defaults, zero boilerplate
- π― Smart instrumentation: Auto-detect CLI commands, errors, and performance bottlenecks
- π Privacy-first: Built-in anonymization, GDPR-compliant, earns user trust
- π CLI-optimized: Works with short-lived processes, offline-capable
- π¦ Self-hostable: Simple Docker-based collection server included
- π€ AI-suggested: Get recommendations on what to instrument
- π GitHub Badges: Show usage metrics in your README
- π₯ Public Dashboards: Share anonymous analytics with your community
Understand Your Users Without Compromising Their Privacy:
// Add to your CLI tool or library
let telemetry = TelemetryKit::builder()
.service_name("my-awesome-cli")?
.service_version(env!("CARGO_PKG_VERSION"))
.prompt_for_consent()? // Ask users first
.build()?;
// Track what matters
telemetry.track_command("build", |event| {
event.success(true).duration_ms(1234)
}).await?;What You Can Learn:
- π Feature Usage: Which commands/features are popular vs unused
β οΈ Error Patterns: Where users struggle or encounter bugs- β‘ Performance: Real-world execution times and bottlenecks
- π Platform Distribution: OS/architecture breakdown
- π Adoption Trends: New vs returning users, version migration
Build Trust With Your Community:
- π Transparent: Users see exactly what you collect
- π Respectful: DO_NOT_TRACK honored automatically
- β Consent-First: Optional interactive prompts
- π Open Source: Inspect the code, self-host if preferred
- π― Anonymous: No PII, just anonymous usage patterns
Showcase Your Impact:
- π Add usage badges to your README:
- π Share public dashboards with your community
- π Show growth and adoption metrics to sponsors
// Literally one line
telemetry_kit::init().auto_configure()?;telemetry_kit::init()
.anonymous() // Anonymous user IDs
.sanitize_emails() // Hash email addresses
.exclude_env_vars() // Don't capture environment
.gdpr_compliant() // Full GDPR compliance
.consent_prompt() // Ask user on first run
.init();Enable automatic function instrumentation with the macros feature:
use telemetry_kit::prelude::*;
#[instrument] // Auto-track duration
async fn fetch_data(url: &str) -> Result<Data, Error> {
// Function execution is automatically timed
// Works with both sync and async functions
// Supports Result types for success/failure tracking
let response = reqwest::get(url).await?;
let data = response.json().await?;
Ok(data)
}Features:
- Automatic execution timing for all instrumented functions
- Works with async and sync functions
- Supports functions with or without Result return types
- Zero-overhead when macros feature is disabled
- Compile-time code generation
Enable with:
[dependencies]
telemetry-kit = { version = "0.2", features = ["macros"] }See examples/instrument_macro.rs for a complete example.
Note: Currently the macro measures timing but doesn't send telemetry yet. Full telemetry integration coming soon!
use telemetry_kit::cli::*;
#[derive(Parser)]
#[command(name = "my-cli")]
struct Cli {
#[command(subcommand)]
command: Commands,
}
// Automatically tracks:
// - Which commands are used
// - Command duration
// - Success/failure rates
// - Anonymous usage patterns- Core telemetry abstraction over OpenTelemetry
- Privacy-first defaults (anonymization, sanitization)
- Basic instrumentation macros
- Simple OTLP export
- Documentation and examples
- CLI scaffolding tool (
telemetry-kit init) - Auto-detection of instrumentation points
- Smart suggestions (analyze code, suggest where to add tracking)
- Pre-built configuration templates
- VS Code extension for inline suggestions
- Short-lived process handling
- Offline caching with automatic sync
- User consent flow (first-run prompts)
- Minimal overhead (<1ms impact)
- Graceful degradation when server unavailable
- Simple collection server (Docker one-liner)
- Built-in dashboard for basic analytics
- SQLite/PostgreSQL storage backends
- REST API for custom integrations
- Pre-built dashboards for common patterns
- AI-powered insights (usage patterns, anomaly detection)
- Anonymous user cohorts
- A/B testing support
- Feature flag integration
- Custom metric definitions
- Multi-project dashboards
| Feature | telemetry-kit | OpenTelemetry | tracing | sentry-rust |
|---|---|---|---|---|
| Setup Complexity | β Low (3 lines) | β Low | ||
| Privacy Built-in | β Yes | β No | β No | |
| CLI Optimized | β Yes | β No | β No | |
| Auto-instrumentation | β Yes | β No | β No | |
| Self-hostable | β Included | N/A | β Commercial | |
| Smart Suggestions | β Yes | β No | β No | β No |
| Offline Support | β Yes | β No | N/A | |
| GDPR Compliant | β Built-in |
[dependencies]
telemetry-kit = "0.0.1"use telemetry_kit::prelude::*;
#[tokio::main]
async fn main() -> Result<()> {
// Initialize with defaults
let _guard = telemetry_kit::init()
.service_name("my-app")
.init()?;
// Your application code
do_work().await?;
Ok(())
}
#[instrument]
async fn do_work() -> Result<()> {
// Automatically tracked!
Ok(())
}use clap::Parser;
use telemetry_kit::cli::*;
#[derive(Parser)]
#[telemetry_kit::track_commands] // Auto-track all commands!
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[tokio::main]
async fn main() -> Result<()> {
let cli = Cli::parse();
telemetry_kit::init()
.for_cli() // CLI-specific optimizations
.consent_on_first_run() // Ask user permission
.init()?;
match cli.command {
Commands::Build => build().await?,
Commands::Deploy => deploy().await?,
}
Ok(())
}βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Your Application β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β Macros β β Middleware β β Manual β β
β β #[instrument]β β Tracking β β Tracking β β
β ββββββββ¬ββββββββ ββββββββ¬ββββββββ ββββββββ¬ββββββββ β
β ββββββββββββββββββββ΄βββββββββββββββββββ β
βββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
ββββββββββββΌβββββββββββ
β telemetry-kit Core β
β β
β β’ Privacy filters β
β β’ Anonymization β
β β’ Batching β
β β’ Sampling β
ββββββββββββ¬βββββββββββ
β
βββββββββββββββββββββββΌββββββββββββββββββββββ
β β β
βββββββββΌβββββββββ βββββββββββΌβββββββββ ββββββββββΌβββββββββ
β OpenTelemetry β β Simple Server β β Custom β
β (OTLP) β β (Self-hosted) β β Backends β
ββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
telemetry-kit is privacy-first by design:
- Anonymous by default: No PII collected unless explicitly enabled
- User consent: Built-in consent flow for CLI applications
- Data sanitization: Automatic scrubbing of sensitive data
- GDPR compliant: Right to erasure, data portability, consent management
- Transparent: Users can see exactly what data is collected
- Opt-out friendly: Easy to disable, respects DO_NOT_TRACK
Interactive Consent Prompts (NEW!):
use telemetry_kit::prelude::*;
#[tokio::main]
async fn main() -> Result<()> {
// Show interactive consent dialog on first run
let telemetry = TelemetryKit::builder()
.service_name("my-app")?
.service_version("1.0.0")
.prompt_for_consent()? // Full prompt with privacy details
.build()?;
// Or use minimal one-liner prompt
let telemetry = TelemetryKit::builder()
.service_name("my-app")?
.prompt_minimal()? // Shorter prompt
.build()?;
Ok(())
}Privacy Configuration:
use telemetry_kit::prelude::*;
use telemetry_kit::privacy::PrivacyConfig;
// Strict privacy mode (GDPR-compliant)
let telemetry = TelemetryKit::builder()
.service_name("my-app")?
.strict_privacy() // Requires consent, sanitizes data, 30-day retention
.build()?;
// Or configure individually
let telemetry = TelemetryKit::builder()
.service_name("my-app")?
.consent_required(true)
.sanitize_paths(true)
.sanitize_emails(true)
.data_retention(90) // Days
.build()?;Manage Consent via CLI:
telemetry-kit consent grant # Enable telemetry
telemetry-kit consent deny # Disable telemetry
telemetry-kit consent status # Check current statusSee examples/interactive_consent.rs and examples/privacy.rs for complete examples.
We welcome contributions! This is a new project and we're building it in the open.
- Check out our Roadmap for planned features
- Look at GitHub Issues for tasks
- Read CONTRIBUTING.md for guidelines
- Join discussions in GitHub Discussions
- π¨ Design: API design, ergonomics, developer experience
- π Documentation: Examples, tutorials, guides
- π§ Implementation: Core features, integrations, tools
- π§ͺ Testing: Unit tests, integration tests, real-world usage
- π Community: Blog posts, talks, spreading the word
- Quick Start Guide (coming soon)
- API Reference
- Privacy Guide (coming soon)
- CLI Best Practices (coming soon)
- Self-Hosting Guide (coming soon)
- Roadmap - Feature roadmap and release plan
- Security Policy - Vulnerability disclosure and security practices
- SLSA Compliance - Supply chain security documentation
- Contributing Guide - How to contribute to the project
- Project Docs - Internal development documentation
This project is inspired by:
- The simplicity of sentry-rust
- The power of OpenTelemetry
- The ergonomics of tracing
- The privacy-focus of Plausible Analytics
- The developer experience of Next.js Analytics
This project is dual-licensed under your choice of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
You may choose either license at your option.
We follow the Rust community standard of dual licensing to give you flexibility:
- Choose MIT if you prefer simplicity and maximum compatibility (including GPL2)
- Choose Apache 2.0 if you want explicit patent protection and contributor agreements
Both licenses are permissive and allow commercial use, modification, distribution, and private use.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Built with β€οΈ by Ibrahim Cesar and contributors.
Special thanks to:
- The OpenTelemetry project
- The Rust tracing ecosystem
- Everyone who provided feedback and ideas
This project includes the X-Clacks-Overhead: GNU Terry Pratchett header in all HTTP requests to keep the memory of Sir Terry Pratchett alive in the overhead of the internet. Learn more at gnuterrypratchett.com.
"A man is not dead while his name is still spoken." - Going Postal, Terry Pratchett
Status: π§ Early Development - API is unstable and will change
Current Version: 0.0.1 - Placeholder release for crate reservation
First Usable Release: v0.1.0 (Target: Q1 2025)
β Star us on GitHub | π Read the Docs | π¬ Discussions