12 — Telemetry
Status at launch: off
Telemetry ingestion is off at launch (GL-36). Nothing is collected or sent:
- The CLI and the Mac app send no usage events.
picasso telemetry onprintsTelemetry is off in this release. Nothing is collected or sent.and writes no preference file. The Mac app has no telemetry toggles. - The API has no ingestion endpoint.
GET /v1/client-configreportstelemetry: {ingestion_enabled: false, label: "off-at-launch"}, and the CLI and Mac app read that state. - The admin telemetry pages and the
/accountdashboard show "Telemetry off at launch" and configuration only. - Mac crash reports (MetricKit) are opt-in, redacted and saved locally only (the last 20 are kept). They are never uploaded.
The rest of this document is the design for a later, opt-in release. None of it ships at launch, and turning it on needs a new release with its own consent flow.
Planned design (post-launch)
Opt-in, fully anonymized, schema-documented. Telemetry is how we would know which features work, which providers are reliable, which ad surfaces convert, and which sessions are failing — without ever knowing what any individual user is doing. The schema would be public, the events inspectable, and the opt-out one command.
Consent
The planned first-run flow asks the user and defaults to off:
Picasso can send anonymized usage data to help us improve the product.
We never send your code, your prompts, your file paths, or any identifying
information. The full event schema is published at picassohq.com.
Send anonymized usage data?
[Y] Yes, help improve Picasso
[N] No (default)The choice is stored locally; the user changes it any time with picasso telemetry on|off.
What we collect
When opted in (planned):
- Session-shape events — session started, session ended, duration, model, provider, total tokens, total tool calls, total subagents, total cost.
- Loop events — turn count, plan-mode invoked, goal-mode invoked, error counts by class.
- Tool events — per-tool invocation count, per-tool average duration, per-tool error rate. Tool arguments and outputs are never sent.
- Performance events — TUI render lag, streaming latency, sandbox startup time.
- Ad events — sponsor slot impressions, dismissals (count, not user id). See
13-ads-monetization.mdfor the ad-attribution flow which is separate. - Crash and exception reports — stack traces with all paths and identifiers redacted.
- Cache harness events — token counts, cached-token counts, cache hit rates, provider family, cache policy ids, prompt/context hashes, omitted-context counts, latency buckets, and cache miss categories.
What we never collect
Hard rules, enforced by the SDK before any event leaves the user's machine:
- File contents.
- File paths (only the
<workspace>template token). - Prompts (only token counts).
- Model output (only token counts).
- Tool arguments or outputs.
- Raw cache prompt sections, raw cache keys, raw cache salts, explicit cached-content handles, source code, memory bodies, tool arguments, tool results, provider credentials, OAuth tokens, account ids, secrets, or ad metadata.
- Environment variable values.
- IP addresses (events ship over HTTPS without geo capture).
- Account identifiers in events (events are signed with a rotating anonymous device fingerprint, not the user id).
- Provider API keys, subscription OAuth tokens, or any other credential the user has authorized. Credentials never leave OS keychain. See
11-auth-and-accounts.md. - Subscription credit balances, plan tier, billing-cycle position, or any per-user financial number. Subscription metadata is read locally from provider response headers and rendered locally in the TUI / dashboard. It is never copied into telemetry. See
17-subscription-passthrough.md.
Schema
Every event conforms to the published schema. During local development the schema is served from the local docs/API process; after picassohq.com is registered it will live at https://picassohq.com/telemetry/schema/v1.json. New event types require a schema update with versioning.
type TelemetryEvent = {
schemaVersion: 1;
eventType: string;
eventTime: ISO8601;
deviceFingerprint: string; // rotating per-month, anonymous
appVersion: string;
osPlatform: "darwin" | "linux" | "windows";
// Event-specific payload
payload: Record<string, number | string | boolean>;
};No nested objects. No free-form strings. All payload fields are enumerated in the schema.
PII scrubbing
Before any event is queued:
- Run a redaction pass on all string fields.
- Strip any field matching the secret-detector patterns from
08-sandbox.md. - Replace any path string with
<path>. - Replace any URL with its domain only (
https://api.github.comnothttps://api.github.com/repos/me/secret). - Drop the event if any field fails redaction validation.
PII scrubbing is implemented in packages/telemetry and is the same code path on every platform. Audited annually.
Local inspection
Users can see exactly what was sent:
picasso telemetry log # show events from this session
picasso telemetry log --limit 50 # show the last 50 events
picasso telemetry export > events.json # full exportThe log is the same JSON sent to the server. Nothing additional is sent that the user cannot see locally.
Storage and retention
- Events ship to ClickHouse via a privacy-respecting pipeline (no IP capture on the ingestion endpoint).
- Raw events retained 90 days, aggregated metrics retained indefinitely.
- Users can request deletion of their device fingerprint's events via
picasso telemetry deleteor via the dashboard. Deletion processed within 7 days.
Public dashboard
A subset of aggregated telemetry is published as a transparency dashboard locally during development and at https://picassohq.com/stats after domain registration:
- Total active users.
- Average session duration.
- Most-used tools.
- Most-used models.
- Error rates by provider.
- TUI performance percentiles.
Per-user data is never published. Aggregated metrics only.
Crash reporting
Crashes ship through a separate channel with stricter rules:
- Stack traces only — no local variables, no source paths beyond the
<workspace>template token. - Crash reports include schema-versioned environment info (OS, app version, terminal type).
- Crash reports are retained 30 days, then deleted.
- Users can disable crash reporting independently of usage telemetry.
Opt-out semantics
When opted out:
- No telemetry events ship. None.
- Crash reports do not ship.
- The dashboard still works (it makes its own analytics requests when the user visits, which are first-party and subject to the same scrubbing rules).
- Ad telemetry still works for the dashboard ads only — see
13-ads-monetization.md. This is a clear, explicit exception with its own opt-out.
Acceptance criteria
- PII scrubber catches 100% of generated PII in a 10K-event fuzz test.
- Opt-out fully disables telemetry within 1 second of toggle.
- Public schema endpoint serves a valid JSON Schema document; every event conforms.
- Telemetry export produces the same bytes that were sent to the server.
- Crash report path is independent of usage telemetry path.
Dependencies
packages/telemetry— SDK.apps/api— ingestion endpoint.- ClickHouse + Tinybird for storage and dashboards.
- Sentry (or equivalent) for crash channel only.
Open questions
- Should we publish the telemetry pipeline code? (Current: yes, the SDK and ingestion code are open source.)
- Should there be a "diagnostics" mode that ships richer events temporarily for debugging? (Current: not in this release; no diagnostics command ships.)