← All docs

Telemetry

What anonymous usage data agentjail collects, how it stays anonymous, and how to turn it off with one command or an environment variable.

agentjail collects anonymous, opt-out usage statistics to help us decide what to improve. This page documents exactly what is and isn’t sent, how to see it, and how to turn it off. The source of truth for the data contract is internal/telemetry/event.go in the repo: every field below is built there and nowhere else.

TL;DR

  • Anonymous. Tied to a random ID generated once on your machine, never your name, hostname, username, IP, or any hardware fingerprint.
  • No payloads, ever. We never send file paths, command text, repo names, environment contents, MCP server names, or policy contents.
  • Off automatically in CI (CI=true and common CI variables).
  • Opt out anytime:
agentjail telemetry disable
# or, per-shell / in CI / in a Dockerfile:
export AGENTJAIL_SEND_ANONYMOUS_USAGE_STATS=false

Controlling telemetry

CommandWhat it does
agentjail telemetry statusShow whether it’s on/off and why (env var, CI, config, or default), plus your anonymous ID
agentjail telemetry disableTurn it off (persisted)
agentjail telemetry enableTurn it back on
agentjail telemetry viewPrint the exact JSON currently queued to be sent
agentjail telemetry resetGenerate a new anonymous ID and clear the queue

Resolution order (first match wins): the AGENTJAIL_SEND_ANONYMOUS_USAGE_STATS environment variable → CI auto-detection → your ~/.agentjail/telemetry.json setting → default (on). The variable accepts false/0/no to disable and true/1/yes to enable.

Exactly what is sent

Every event carries a random distinct_id (your anonymous ID), a per-event $insert_id (used only to de-duplicate retried sends), and the agentjail_version.

Lifecycle events (install, uninstall, update) are sent immediately and synchronously when they happen, without waiting for the daemon’s batched flush, so they’re captured even if the daemon never runs. Everything else is spooled locally and delivered by the daemon.

install

Emitted once, immediately after a successful install: os, arch, optional install_method (curl/brew), optional agents (the agent enums whose hooks were wired), and agents_detected (how many supported agents were found).

uninstall

Emitted immediately on teardown (full or single-agent), before any state is removed: os, arch, and optional agents (which agents were unhooked; omitted for a full teardown).

update

Emitted when you run agentjail update: from_version, to_version, os, arch.

session_start

Emitted when the daemon starts: os, arch, and optional install_method.

feature_used

Emitted when you run a CLI command: a command enum (unknown commands collapse to other, so your arguments are never recorded) and an optional agents enum (claude/codex/cursor).

decision_rollup

Aggregated policy decisions for a window (not per decision): action_counts such as {"allow":120,"deny":3,"ask":1}, and rule_counts keyed by rule ID such as command_policy/rm_rf, never the matched path or command. Also includes optional rule_action_counts (keyed by "action|rule_id"), tool_counts (decisions per tool name), agent_counts (decisions per agent identity), and spool_dropped (events dropped if the local queue overflowed). Custom rule IDs appear verbatim; see “A note on custom rule names” below.

perf_rollup

Aggregated daemon performance: eval_p50_ms, eval_p95_ms, and restarts.

policy_config

Your configuration at daemon start and on reload: custom_rule_count (a count; rule contents are never sent) and disabled_rules (your disabled-rules list verbatim, which may include custom rule IDs).

fail_open

Emitted when the hook fails open (can’t reach or parse a daemon decision and allows the action rather than blocking): os and a reason enum (dial-daemon, read-response, parse-response, read-stdin, parse-input, other), never the tool input.

heartbeat

Best-effort, at most once per ~24h on a CLI run: os, latest_version (the latest release seen), and update_available.

feedback

Only when you run agentjail feedback (never automatic): the message you typed, an optional contact you typed, and os. Sent even if usage telemetry is disabled.

A real decision_rollup, the entirety of what leaves your machine for that event:

{
  "event": "decision_rollup",
  "properties": {
    "distinct_id": "3f9c2a7e-8b1d-4c5a-9e2f-1a2b3c4d5e6f",
    "$insert_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "agentjail_version": "1.1.0",
    "action_counts": { "allow": 120, "deny": 3, "ask": 1 },
    "rule_counts": { "command_policy/rm_rf": 3, "file_policy/ssh_key_read": 1 }
  }
}

What is never sent

File paths, command text, repository names or URLs, environment variable contents, MCP server names, policy file contents, IP addresses, hostnames, usernames, timestamps finer than the day, or any hardware identifier.

This isn’t enforced by review alone: telemetry events are built from fixed structs, never by serializing arbitrary data, so a payload cannot be attached by accident.

A note on custom rule names

We do send the IDs of your custom rules (e.g. custom/<name>/<rule>) in rule_counts and your disabled_rules list, so we can understand what kinds of custom rules people build. We do not send the contents of any rule, nor the paths or commands they match. If you’d rather not share custom rule IDs, name your rules generically, or turn telemetry off with agentjail telemetry disable. Run agentjail telemetry view to see exactly what’s queued before anything is sent.

Sending feedback

agentjail feedback "the publish guard fires too often"
# or run it with no message and it prompts you, plus an optional contact

agentjail feedback sends a one-off feedback event with your message, an optional contact you may provide, your OS, version, and the random anonymous ID. Because it’s an explicit action you took, it sends even if usage telemetry is disabled, and it never includes anything beyond what you typed plus version and OS. If the build has no backend configured, it prints a GitHub issue link instead.

How it’s delivered

Most events are aggregated locally and delivered by the daemon in a single batched HTTPS request. The daemon flushes shortly after startup (a couple of minutes) and then roughly every 6 hours, plus on graceful shutdown. CLI commands only write a local event to ~/.agentjail/telemetry-spool.jsonl, which the daemon picks up on its next flush.

The exception is lifecycle events (install, uninstall, update) and feedback: the CLI sends these immediately and synchronously rather than waiting for the daemon flush, since they may happen when no daemon is running. If you’re offline or a send fails, spooled events stay queued and are retried later; the local queue is capped so it can’t grow without bound.

Backend

We currently use PostHog (US cloud) as our analytics backend. This may change; the data we send, listed above, will not. The telemetry host is included in agentjail’s default network allowlist so agentjail doesn’t block its own telemetry; you’re free to remove it.