Skip to content

Commit da6e7d5

Browse files
authored
Introduce fully Anonymous hash (#369)
1 parent 1457247 commit da6e7d5

2 files changed

Lines changed: 88 additions & 6 deletions

File tree

README.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,24 @@ If you're using DeepFabric in production or research, we'd love to hear from you
373373

374374
### Analytics
375375

376-
We use fully anonymised analytics, to help us improve application performance and stability. We never send Personal identifiable information and we do not capture prompts, generated content, API keys, file names etc.
376+
We use privacy-respecting analytics to help us improve application performance and stability. We never send Personal identifiable information and we do not capture prompts, generated content, API keys, file names etc.
377377

378-
We capture model names, numeric parameters (temperature, depth, degree, batch_size), timing and success/failure rates - this then helps us find optimizations or bottlenecks.
378+
#### What We Collect
379+
- **Anonymous User ID**: A stable, one-way hash based on your machine characteristics (hostname + MAC address). This helps us understand unique user counts without identifying you. Its impossible to reverse this hash to get your actual machine details and one-way only.
380+
- **Usage Metrics**: Model names, numeric parameters (temperature, depth, degree, batch_size), timing and success/failure rates
381+
- **Developer Flag**: If you set `DEEPFABRIC_DEVELOPER=True`, events are marked to help us filter developer testing from real usage
379382

380-
You can fully disable all analytics by setting the environment variable `ANONYMIZED_TELEMETRY=False`.
383+
#### Privacy Guarantees
384+
- No usernames, emails, IP addresses, or personal information
385+
- User ID is cryptographically hashed and cannot be reversed and contains no Personal Identifiable Information
386+
- No prompts, generated datasets, or sensitive data is collected
387+
- All data is used solely for application improvement in regards to performance, stability, and feature usage
388+
389+
#### Control Your Participation
390+
```bash
391+
# Disable all analytics
392+
export ANONYMIZED_TELEMETRY=False
393+
394+
# Mark yourself as a developer (for filtering)
395+
export DEEPFABRIC_DEVELOPER=True
396+
```

deepfabric/metrics.py

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
1+
"""
2+
Simple analytics for DeepFabric using PostHog.
3+
4+
Provides a single trace() function for anonymous usage analytics.
5+
All analytics can be disabled by setting ANONYMIZED_TELEMETRY=False.
6+
7+
Privacy-respecting identity:
8+
- Generates a stable, anonymous user ID based on machine characteristics
9+
- Uses DEEPFABRIC_DEVELOPER=True to mark developer sessions for filtering
10+
- Never collects PII (names, emails, IP addresses, etc.)
11+
"""
12+
113
import contextlib
14+
import hashlib
215
import os
16+
import platform
17+
import uuid
318

419
try:
520
import importlib.metadata
@@ -9,7 +24,7 @@
924
VERSION = "development"
1025

1126
try:
12-
from posthog import Posthog
27+
from posthog import Posthog, identify_context, new_context
1328

1429
POSTHOG_AVAILABLE = True
1530
except ImportError:
@@ -24,11 +39,55 @@
2439
else:
2540
posthog = None
2641

42+
# Cache for the generated user ID using a dict to avoid global statement
43+
_user_id_cache: dict[str, str | None] = {"id": None}
44+
45+
46+
def _get_user_id() -> str:
47+
"""
48+
Generate a stable, anonymous user ID based on machine characteristics.
49+
50+
Creates a UUID from a hash of platform.node() (hostname) and uuid.getnode()
51+
(MAC address). This provides a persistent identifier across sessions without
52+
collecting PII.
53+
54+
Returns:
55+
str: A stable UUID string unique to this machine
56+
"""
57+
if _user_id_cache["id"] is not None:
58+
return _user_id_cache["id"]
59+
60+
# Combine hostname and MAC address for machine-specific identifier
61+
machine_info = f"{platform.node()}-{uuid.getnode()}"
62+
63+
# Create SHA256 hash and convert to UUID format
64+
hash_digest = hashlib.sha256(machine_info.encode()).hexdigest()
65+
66+
# Use first 32 hex chars to create a valid UUID
67+
user_uuid = str(uuid.UUID(hash_digest[:32]))
68+
69+
_user_id_cache["id"] = user_uuid
70+
return user_uuid
71+
72+
73+
def _is_developer() -> bool:
74+
"""
75+
Check if this session is marked as a developer session.
76+
77+
Returns:
78+
bool: True if DEEPFABRIC_DEVELOPER environment variable is set to 'True'
79+
"""
80+
return os.environ.get("DEEPFABRIC_DEVELOPER") == "True"
81+
2782

2883
def trace(event_name, event_properties=None):
2984
"""
3085
Send an analytics event if telemetry is enabled.
3186
87+
Uses privacy-respecting identity tracking with a stable, anonymous user ID
88+
generated from machine characteristics. Developer sessions are marked with
89+
the is_developer flag when DEEPFABRIC_DEVELOPER=True.
90+
3291
Args:
3392
event_name: Name of the event to track
3493
event_properties: Optional dictionary of event properties
@@ -46,11 +105,18 @@ def trace(event_name, event_properties=None):
46105
return
47106

48107
with contextlib.suppress(Exception):
49-
# Add version to all events
108+
# Generate stable user ID
109+
user_id = _get_user_id()
110+
111+
# Add version and developer flag to all events
50112
properties = event_properties or {}
51113
properties["version"] = VERSION
114+
properties["is_developer"] = _is_developer()
52115

53-
posthog.capture(distinct_id="deepfabric", event=event_name, properties=properties)
116+
# Use identity context to associate events with the user
117+
with new_context():
118+
identify_context(user_id)
119+
posthog.capture(event=event_name, properties=properties)
54120

55121

56122
def is_enabled():

0 commit comments

Comments
 (0)