Live telemetry — goal, why it wasn't automatic, and the fix
The goal
An org admin opens Admin → Observability and sees their developers' AI-agent
security activity stream in live, automatically — no per-developer manual
steps, no refreshing. When a developer's Claude/Cursor/etc. session triggers a
Warden finding (a blocked rm -rf, a secret-exfil attempt, a risky MCP call),
it should appear in the dashboard within seconds, redacted (metadata +
hashes, never raw commands or secrets).
How it's supposed to work (the pipeline)
Developer's AI agent (Claude Code)
│ every tool call fires a hook
▼
prismor hook-dispatch ← the installed `prismor` runtime
│ PolicyEngine evaluates the event → findings
▼
prismor telemetry sink ← warden/sinks.py _dispatch_prismor
│ builds a REDACTED record (warden/enterprise/telemetry.py), uses the enrolled
│ device key (~/.prismor/identity.json) → POST
▼
POST /api/telemetry/ingest ← prismor-web, device-auth
│ writes TelemetryEvent scoped to {org, user, device}
▼
Dashboard (AgentMonitoringView) ← polls /api/telemetry/stats every 15s
│ "Waiting for telemetry…" → live data, no refresh
For this to be automatic, the code the hook runs must contain the cloud
sink, and the machine must be enrolled (prismor enroll <token>).
Why it wasn't happening automatically
Diagnosed on 2026-06-10. The dashboard was frozen at ~3h old because:
- The user's Claude Code hooks invoke the pipx-installed runtime:
~/.local/pipx/venvs/immunity-agent/.../warden/cli.py hook-dispatch … - That install is v1.5.8 — predating the cloud sink. It's missing
identity.py,telemetry.py,remote_policy.py, and itssinks.pyhas noprismordispatcher. - So every live tool call wrote findings to the local
prismor.db, but nothing uploaded. The dashboard only had data from a one-time manual backfill of 3,067 historical local findings — hence "last activity 3h ago."
In short: enrolled ✓, but the live hook runs old code with no uploader.
The solution
Production (the real "automatic")
Publish immunity-agent ≥ 1.6.x (this branch — identity/telemetry/
remote_policy + the prismor sink) to PyPI. Developers pipx upgrade immunity-agent. Their existing hooks already call the installed
warden/cli.py, so once it's the new code, every finding uploads live with
zero further steps. Enrollment is one-time (prismor enroll <token>).
Local / pre-release (stopgap for testing)
Point the Claude Code hooks at the dev checkout so live sessions run the new code now, and keep the local control-plane server running:
// ~/.claude/settings.json — hook command
"PYTHONPATH=<repo> <repo>/venv/bin/python -m warden.cli \
hook-dispatch --agent claude --workspace \"$HOME\" --mode observe"
(Requires the local prismor-web server up at the api_base in
~/.prismor/identity.json. A new Claude session must be started to pick up the
hook change.)
Verification (tested 2026-06-10)
- Ran a finding through the dev
hook-dispatchwith the real enrolled identity → uploaded asecret_exfiltrationevent live to the org, redacted, within ~1s. Dashboard (15s poll) reflects it with no refresh. - Backfill of 3,067 historical local findings → all ingested redacted.
- Full control loop, a real remote Linux box, and a tamper test all pass.
Known limitation (follow-up)
Telemetry currently uploads only when there is a finding — the sink fires on
current_findings. Benign tool calls (git status, a normal file read) produce
no event. So the "Tool Calls Inspected (24h)" KPI counts flagged calls, not
total tool calls — the label oversells.
Planned fix: emit a lightweight redacted event for every tool call (type +
verdict=allowed), so the dashboard reflects true volume and the KPI is honest.
Until then, the dashboard is an accurate view of security-relevant activity
(blocks/warns), which for typical usage is still substantial.
Operational notes
- Redaction: records carry severity/category/verdict/tool/title +
evidenceHash— never raw commands, paths, prompts, or secrets (unless an org admin opts into full capture). - Heartbeat:
Device.lastSeenAtupdates on every upload; a device that stops reporting is the detectable signal (see the tamper analysis). - Local server dependency: the stopgap points live telemetry at the local dev server — if it stops, uploads fail silently (best-effort, never blocks the agent). Production removes this dependency.