Telemetry

Fred ships behavioral telemetry — sessions, turns, tool calls, slash commands — to api.fredcode.net so we can recursively improve the agent loop. It is default-on for authenticated users, opt-out, and never includes prompt text, response text, tool args, or file contents.

Two telemetry stories — make sure you're on the right page

This page is about the central, default-on telemetry that goes to our Neon database — what we use to understand how Fred is being used and what's broken.

The /usage dashboard is a separate thing: it's fed by usage_events rows the proxy writes for billing. That's token counts and cost, not behavior. See /docs/billing.

There is also a third option — self-hosted telemetry to your own Postgres, used by Fred contributors and anyone who'd rather pipe behavior signals into their own DB. Set DSC_TELEMETRY_DB_URL; that env var takes precedence over the central path. Skip to Self-hosted if that's what you're after.

Default on, opt-out, key-scoped

Once you've run fred login, behavioral telemetry starts sending on the next session. We tie rows to your user_id (the same account that owns your fred_live_* key) so we can tell whose behavior shows what — that's what makes the data useful for product improvement, vs anonymous noise.

On the first session that successfully starts the central writer, you'll see a single dim-text notice:

Fred sends anonymized behavioral telemetry to improve the
product. Run /telemetry off to opt out.

It prints once and never again.

Opting out

Three ways, all equivalent in effect:

  • /telemetry off — in the REPL. Persistent across sessions, takes effect on the next launch. Current session keeps sending until you /exit.
  • fred telemetry off — same thing from a shell, no REPL needed. Useful in provisioning scripts.
  • --no-telemetry — one-shot flag. Disables for THIS session only; doesn't touch your persisted preference. Good for ad-hoc runs you want kept off the books.

Re-enable at any time with /telemetry on. Status check: /telemetry (no arg) prints the current routing.

What we collect

Four event types, written to four tables on our Neon DB:

TableGranularityWhat's in it
telemetry_sessionsOne row per CLI sessionstart/end time, model, fred version, python version, OS, shell, git_dirty (boolean only), AGENTS.md hash, tool catalog
telemetry_turnsOne row per agent loop iterationprompt/completion/cached token counts, finish_reason, duration_ms, num_tool_calls, mode (act/plan), api_error_type, prompt/response character counts (length, never the text)
telemetry_tool_callsOne row per tool invocationtool_name, args fingerprint (SHA-256 hash, not the args), permission outcome, duration_ms, outcome (ok/error), result size in bytes (length only)
telemetry_eventsNamed eventsslash command invocations, API retries, plan/act mode transitions, model swaps

What we never collect

  • The text of your prompts
  • The text of model responses
  • The contents of files you view, edit, or pipe
  • The arguments to tool calls (file paths, command strings, search queries)
  • Tool result text (only its length in bytes and a "starts with Error:" boolean)
  • Your hostname, current working directory, or git commit SHA
  • Your environment variables
  • The system prompt text (just its SHA-256)

This is enforced in two places: the CLI's RemoteWriter._to_remote() drops these fields before queueing, and the proxy worker's validateBatch() hard-rejects any payload that includes them. A misbehaving client can't poison the dataset.

Tip
If you're worried Fred 0.X.Y might have a bug that captures something it shouldn't — opt-out is one command and the data flow stops immediately. We can also issue a hotfix version and force everyone onto it via the auto-update notice.

How we use it

The central telemetry exists so Fred gets better — concretely:

  • Where flash gets stuck — finish_reason patterns, num_tool_calls per turn, duration tails. Tells us when to recommend pro automatically.
  • Which slash commands are dead — usage frequency by name. Drop the ones nobody uses; promote the ones people are reaching for.
  • Retry storms — api_error_type distributions, retry latency. Tunes our backoff base + max attempts.
  • Tool dispatch loops — args_fingerprint repeats within a session. Indicates the model is retrying the same tool with the same args, usually a hint to break the loop.
  • Plan-mode adoption — mode transitions, plan→act drop-off. Are people actually using plan mode? Are they switching back too early?
  • Permission patterns — which tools get auto-allowed, which get denied. Informs default permission rules.

Self-hosted (DSC_TELEMETRY_DB_URL)

If you'd rather pipe telemetry into your own Postgres — for compliance reasons, for team-internal dogfooding, or because you're a Fred contributor — set the env var:

export DSC_TELEMETRY_DB_URL="postgresql://user:pass@host:5432/dbname"

This takes precedence over the central path. The CLI uses the same envelope shape (richer than what we accept centrally — includes user_input/assistant_text if you don't set DSC_TELEMETRY_NO_TEXT=1) and writes to dsc_sessions / dsc_turns / dsc_tool_calls / dsc_events tables in your DB. The schema is created idempotently on first run; see Fred/src/dsc/telemetry/writer.py for the canonical DDL.

The self-hosted path is also how the Fred team debugs the agent loop. It's not going away.

Heads up

When DSC_TELEMETRY_DB_URL is set, NO data goes to api.fredcode.net. Your behavior signals only land in your own Postgres. That's the whole point of the override.

Schema

Server-side (central) tables live in FredWeb/src/db/migrations/0003_telemetry_schema.sql. Self-hosted DDL lives in Fred/src/dsc/telemetry/writer.py. They share the same shape minus the text-bearing columns the central path drops.

Why we do it this way

Recursive product improvement requires a real dataset. Fully anonymous telemetry is nice-sounding but not actionable — "some user got stuck on something" doesn't tell us anything we can fix. Tying rows to your account means we can answer questions like "the people running into retry storms — what models are they on, what tool calls do they make right before, what's their session duration look like." That's the bar.

The cost is honesty: we keep this page accurate (what's collected, what's not) and we make the opt-out one command. If we ever want to add a new field to telemetry, this page gets updated in the same PR.