Skip to content

Ingest Claude Code OpenTelemetry as a third capture tier #56

Description

@blitzcrieg1

Claude Code emits a well-specified OpenTelemetry stream that overlaps the hook tier and, on two fields, beats it. Rather than treat it as a competitor, ingest it as a third capture tier alongside IDE hooks and tools/mcp_audit_proxy.py.

Why

Two fields in the OTel stream are strictly better than what the hook tier produces today:

  • claude_code.tool_decision.source carries the observed provenance of a permission decision: config, hook, user_permanent, user_temporary, user_reject, user_abort. Agentmetry reconstructs this from timing and marks it inferred:tool_ran_after_ask, which the README correctly says must never be cited as a human decision. The telemetry observes what we guess.
  • claude_code.tool_result.tool_result_size_bytes is the how-much-data-moved number tracked in Tool responses are not recorded, so the trail cannot see how much data moved #45.

Everything else in the stream (mcp_server_connection, permission_mode_changed, api_request, auth, internal_error) is additive rather than competing.

Spec: https://code.claude.com/docs/en/monitoring-usage

Shape

An OTLP receiver that normalises into the existing canonical schema, so nothing downstream changes:

  • Hash chain, MITRE/ATLAS mapping, sequence rules and SIEM forwarders all operate on canonical events and should need no modification. Normalisation happens before detection, not inside it.
  • session.id maps to session_id; prompt.id is a correlation key within one agent and does not correlate across two, so correlation_id still has to be derived the way it is today.
  • Claude Code events land in the same local trail as Cursor and Codex events, which is the cross-agent correlation OTel cannot do by itself.
  • Where OTel and a hook both describe the same tool call, the observed tool_decision should win over the inferred approval and the inferred: marker should drop for that half of the trail. Dedup strategy needs deciding: see the batch dedup already in core/audit/ingest.py.

Open questions

  • Do the existing 15 rules actually fire unmodified on normalised OTel events? This is the assumption the whole design rests on and it is untested.
  • OTEL_LOG_TOOL_DETAILS is off by default. Without it there is no command string, so most command-shaped traits and therefore most sequence rules cannot fire. Is a tier that degrades to tool_name only worth shipping, and how should agentmetry doctor report that state?
  • Receiver transport: OTLP gRPC on loopback, or tail a file the collector writes? Loopback receiver is more work and avoids depending on a collector being configured.
  • Managed settings lock the OTLP destination. If an admin has pointed it at a central collector, can Agentmetry also receive locally, or does this only work where we own the endpoint config?

Not in scope

Emitting OTLP out of Agentmetry. That is the separate OTLP export item already on the roadmap.

Context

Reasoning written up in ai-audit-watch post why-not-just-opentelemetry, which links here for status and explicitly says this is a plan and not a release.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions