Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions crates/icp-cli/src/telemetry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,9 @@ const SEND_GUARD_SECS: u64 = 30 * 60;

/// Telemetry ingestion endpoint.
///
/// Deliberately set to a non-resolvable placeholder until the real endpoint is
/// confirmed. The `.invalid` TLD is reserved by RFC 2606 and will never
/// resolve, so any send attempt fails silently without reaching a third-party
/// server.
///
/// Override at runtime with the `ICP_TELEMETRY_ENDPOINT` environment variable
/// (intended for integration tests only).
const TELEMETRY_ENDPOINT: &str = "https://telemetry.invalid/v1/events";
const TELEMETRY_ENDPOINT: &str = "https://icp-cli.dfinity.network/telemetry";

/// How an argument was supplied.
#[derive(Clone, Debug, Serialize, Deserialize)]
Expand Down
19 changes: 11 additions & 8 deletions crates/icp-cli/tests/telemetry_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,17 @@ macro_rules! icp_with_telemetry {
.env_remove("ICP_TELEMETRY_DISABLED");
(icp_home, cmd)
}};
($ctx:expr, allow_upload) => {{
($ctx:expr, enable_send) => {{
let icp_home = $ctx.home_path().join("icp-home");
let mut cmd = $ctx.icp();
cmd.env("ICP_HOME", icp_home.as_str())
.env_remove("CI")
.env_remove("DO_NOT_TRACK")
.env_remove("ICP_TELEMETRY_DISABLED")
// Allow should_send() to return true so the send trigger fires.
.env_remove("ICP_CLI_TEST_NO_TELEMETRY_UPLOAD")
// Override the endpoint to a non-resolvable address so the
// background sender silently fails instead of hitting production.
.env(
"ICP_TELEMETRY_ENDPOINT",
"https://telemetry.invalid/v1/events",
Expand Down Expand Up @@ -287,7 +290,7 @@ fn telemetry_notice_suppressed_when_marker_exists() {
#[test]
fn telemetry_time_trigger_rotates_events() {
let ctx = TestContext::new();
let (icp_home, mut cmd) = icp_with_telemetry!(ctx, allow_upload);
let (icp_home, mut cmd) = icp_with_telemetry!(ctx, enable_send);
let telemetry_dir = icp_home.join("telemetry");

// Set next-send-time to Unix epoch (far in the past).
Expand All @@ -311,7 +314,7 @@ fn telemetry_time_trigger_rotates_events() {
#[test]
fn telemetry_size_trigger_rotates_events() {
let ctx = TestContext::new();
let (icp_home, mut cmd) = icp_with_telemetry!(ctx, allow_upload);
let (icp_home, mut cmd) = icp_with_telemetry!(ctx, enable_send);
let telemetry_dir = icp_home.join("telemetry");

// next-send-time far in the future → only the size trigger can fire.
Expand Down Expand Up @@ -352,7 +355,7 @@ fn telemetry_no_rotation_when_send_not_due() {
);
}

/// A failed batch send (default `.invalid` endpoint) must exit 0, produce no
/// A failed batch send (non-resolvable `.invalid` endpoint) must exit 0, produce no
/// output, and leave the batch file in place for retry.
#[test]
fn telemetry_failed_send_is_silent() {
Expand All @@ -364,8 +367,8 @@ fn telemetry_failed_send_is_silent() {
.join("batch-1700000000-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee.jsonl");
std::fs::write(&batch_path, FAKE_RECORD).expect("write batch file");

ctx.icp()
.args(["__telemetry-send-batch", batch_path.as_str()])
let (_icp_home, mut cmd) = icp_with_telemetry!(ctx, enable_send);
cmd.args(["__telemetry-send-batch", batch_path.as_str()])
.timeout(Duration::from_secs(15))
.assert()
.success()
Expand Down Expand Up @@ -429,7 +432,7 @@ fn telemetry_send_batch_delivers_data() {
#[test]
fn telemetry_stale_batches_deleted_on_trigger() {
let ctx = TestContext::new();
let (icp_home, mut cmd) = icp_with_telemetry!(ctx, allow_upload);
let (icp_home, mut cmd) = icp_with_telemetry!(ctx, enable_send);
let telemetry_dir = icp_home.join("telemetry");

// Time trigger will fire.
Expand Down Expand Up @@ -463,7 +466,7 @@ fn telemetry_stale_batches_deleted_on_trigger() {
#[test]
fn telemetry_excess_batches_pruned_on_trigger() {
let ctx = TestContext::new();
let (icp_home, mut cmd) = icp_with_telemetry!(ctx, allow_upload);
let (icp_home, mut cmd) = icp_with_telemetry!(ctx, enable_send);
let telemetry_dir = icp_home.join("telemetry");

// Time trigger will fire.
Expand Down
4 changes: 4 additions & 0 deletions docs/telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Each command invocation produces a single telemetry record with the following fi

| Field | Example | Purpose |
|---|---|---|
| `batch` | `a1b2c3d4-...` | Group records from the same transmission; server-side deduplication |
| `sequence` | `0`, `1`, `2` | Ordering of records within a batch |
| `machine_id` | `a1b2c3d4-...` | Count unique installations |
| `platform` | `macos`, `linux`, `windows`, `wsl` | Platform distribution |
| `arch` | `aarch64`, `x86_64` | Architecture distribution |
Expand Down Expand Up @@ -40,6 +42,8 @@ For example, `icp deploy --mode install --environment production` records:
]
```

The `batch` UUID is generated fresh each time records are transmitted and is not persisted across sends. Records within the same batch can be grouped for server-side deduplication.

The `machine_id` is a random UUID generated on first run and stored locally. It is used solely to count unique installations and is not linked to any user identity.

Additional fields may be introduced in future versions. This page will be updated accordingly. The same privacy principles apply: no personally identifiable information, no project data.
Expand Down
Loading