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=trueand 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
| Command | What it does |
|---|---|
agentjail telemetry status | Show whether it’s on/off and why (env var, CI, config, or default), plus your anonymous ID |
agentjail telemetry disable | Turn it off (persisted) |
agentjail telemetry enable | Turn it back on |
agentjail telemetry view | Print the exact JSON currently queued to be sent |
agentjail telemetry reset | Generate 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.