1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
//! Cognee product-analytics client (`send_telemetry`).
//!
//! Mirrors Python's `cognee.shared.utils.send_telemetry`. Fires a
//! single fire-and-forget HTTP POST to `https://test.prometh.ai` for
//! every public API call so the cognee maintainers have an aggregate
//! view of how the SDK is exercised.
//!
//! Enabled by default (locked decision 1 — Python parity). The runtime
//! check happens **before** any identity derivation or HTTP code path,
//! so disabling at runtime costs zero.
//!
//! See [`docs/observability/send_telemetry.md`][user-doc] for the full
//! operator-facing reference (payload schema, salt rotation, privacy
//! notes, troubleshooting).
//!
//! [user-doc]: https://github.com/topoteretes/cognee-rs/blob/main/docs/observability/send_telemetry.md
//!
//! # Quick start
//!
//! ```no_run
//! # #[cfg(feature = "telemetry")] {
//! use cognee_telemetry::send_telemetry;
//! use serde_json::json;
//!
//! send_telemetry(
//! "cognee.forget",
//! "user-id-string",
//! Some(json!({ "endpoint": "POST /api/v1/forget" })),
//! );
//! # }
//! ```
//!
//! # Opt-out
//!
//! At runtime: `TELEMETRY_DISABLED=1` (any non-empty value) or
//! `ENV=test` / `ENV=dev`.
//!
//! At compile time: build `cognee-lib` (or any consumer) with
//! `--no-default-features`. [`send_telemetry`] and
//! [`try_send_telemetry`] still exist in the public surface but are
//! compiled to noop bodies — no `reqwest`, no `tokio` runtime
//! fallback, no PBKDF2 cost.
//!
//! # Environment variables
//!
//! | Var | Default | Effect |
//! |---|---|---|
//! | `TELEMETRY_DISABLED` | unset | Any non-empty value disables. Read on every call. |
//! | `ENV` | unset | If `test` or `dev`, disables. Read on every call. |
//! | `LLM_API_KEY` | unset | Source of `api_key_tracking_id` (locked decision 11 — read at every event-emission, never cached). |
//! | `TRACKING_ID` | unset | Override `anonymous_id`. |
//! | `TELEMETRY_API_KEY_TRACKING_SALT` | `cognee.telemetry.api-key-tracking.v1` | PBKDF2 salt override (locked decision 12). |
//! | `TELEMETRY_REQUEST_TIMEOUT` | `5` | HTTP timeout in seconds. Clamped to `[1, 60]`. Read once per process. |
//!
//! # Logging
//!
//! All diagnostics use the `cognee.telemetry` tracing target. Enable
//! with `RUST_LOG=cognee.telemetry=debug`.
use Error;
use Uuid;
/// Property value type for `additional_properties`. Resolves to
/// `serde_json::Value` when the `telemetry` feature is on, or `()`
/// when it is off. Keep all public signatures referring to this alias
/// rather than `serde_json::Value` directly so the surface compiles
/// under `--no-default-features`.
///
/// Callers should pass a `Value::Object` — non-object values are
/// silently dropped at sanitization time with a `cognee.telemetry`
/// debug log. Reserved keys (`time`, `user_id`, `anonymous_id`,
/// `persistent_id`, `api_key_tracking_id`, `api_key_hash`,
/// `sdk_runtime`, `cognee_version`) MUST NOT appear in the object.
pub use Value as PropertyValue;
/// Placeholder property type used when the `telemetry` feature is
/// disabled. Replaced by `serde_json::Value` once the feature is on.
///
/// Public-API callers should hold values as `Option<PropertyValue>`
/// so the same code compiles in both feature states.
pub type PropertyValue = ;
/// Errors returned by [`try_send_telemetry`].
///
/// In practice, [`try_send_telemetry`] always returns `Ok(())` today —
/// transport, serialization and proxy errors are swallowed at debug
/// level (`cognee.telemetry` target) to preserve fire-and-forget
/// semantics. The error variant exists so future failure modes
/// (e.g. backpressure rejection) can be surfaced without a breaking
/// change.
/// Reference type for the `user_id` field. Accepts a `Uuid`, a
/// `&str`, or `Option<Uuid>` via the [`From`] impls below; callers
/// rarely construct this directly.
///
/// The chosen variant is serialized to a string in the wire payload:
/// [`UserIdRef::Uuid`] becomes the canonical hyphenated UUID,
/// [`UserIdRef::Symbolic`] passes through as-is, and
/// [`UserIdRef::None`] becomes the empty string.
/// Format a `tenant_id` for the telemetry wire payload, mirroring
/// Python `str(user.tenant_id) if user.tenant_id else "Single User Tenant"`.
///
/// Lifecycle emitters (pipeline, task, search) thread an
/// `Option<Uuid>` through the runtime context and call this helper at
/// the emission site so the on-the-wire string is byte-for-byte
/// identical to the Python implementation when no tenant has been
/// configured.
/// Returns the cognee crate version string for use in analytics
/// payloads. Matches Python's `cognee.__version__`.
///
/// Equivalent to `env!("CARGO_PKG_VERSION")` evaluated inside the
/// `cognee-telemetry` crate. The workspace pins all cognee crates to
/// the same version via `version.workspace = true`, so the value is
/// the same as `cognee-lib`'s reported version. Lifecycle emitters in
/// `cognee-core` and elsewhere should call this accessor instead of
/// inlining `env!("CARGO_PKG_VERSION")`, which would otherwise expand
/// to the calling crate's version.
///
/// Always available — does not depend on the `telemetry` feature
/// flag.
/// Fire-and-forget product-analytics event.
///
/// Returns immediately; the HTTP POST is dispatched on a detached
/// tokio task with a 5-second (configurable via
/// `TELEMETRY_REQUEST_TIMEOUT`) total timeout. Errors are swallowed
/// at debug level on the `cognee.telemetry` tracing target. When
/// called outside a tokio runtime, falls back to a one-shot
/// single-thread runtime (locked decision 5) and logs a `warn`-level
/// notice — behaviour is correct (the event still fires) but
/// indicates a perf-improvement opportunity.
///
/// No-op when:
/// - the `telemetry` cargo feature is disabled at compile time
/// (function still exists but compiles to an empty body, so
/// consuming code stays binary-compatible across feature flips),
/// - `TELEMETRY_DISABLED` is set to a non-empty value at runtime,
/// - `ENV` is `"test"` or `"dev"`.
///
/// # Environment variables
///
/// | Var | Default | Effect |
/// |---|---|---|
/// | `TELEMETRY_DISABLED` | unset | Any non-empty value disables. |
/// | `ENV` | unset | If `test` or `dev`, disables. |
/// | `LLM_API_KEY` | unset | Hashed into `api_key_tracking_id` (read at every call). |
/// | `TRACKING_ID` | unset | Override `anonymous_id`. |
/// | `TELEMETRY_API_KEY_TRACKING_SALT` | (well-known default) | Override PBKDF2 salt. |
/// | `TELEMETRY_REQUEST_TIMEOUT` | `5` | Total HTTP timeout (seconds), clamped `[1, 60]`. |
///
/// See the [user-facing
/// guide](https://github.com/topoteretes/cognee-rs/blob/main/docs/observability/send_telemetry.md)
/// for the full reference.
/// Same as [`send_telemetry`] but returns
/// `Result<(), TelemetryError>` for callers that want to know whether
/// dispatch was attempted.
///
/// The `Ok(())` return does **not** mean the proxy received the
/// payload — it means the dispatch was scheduled. Transport failures
/// are still swallowed at debug level on the `cognee.telemetry`
/// target (mirrors Python's fire-and-forget semantics).
///
/// In current builds this function always returns `Ok(())`. The
/// [`TelemetryError`] variant exists so future failure modes
/// (e.g. backpressure rejection) can be surfaced without a breaking
/// change to the signature. Honours the same opt-out and runtime
/// fallback semantics as [`send_telemetry`]; see that function's
/// rustdoc for env-var details.