Skip to main content

AxonFlow v9.5.0 Release Notes

v9.5.0 is a minor release that completes AxonFlow's ingest of the native OpenTelemetry (OTLP) stream that Claude Code and Claude Cowork emit. Three additions work together: a new POST /v1/metrics endpoint lands the tools' usage counters — tokens, cost, sessions, lines of code, tool-permission decisions, active time — as canonical, governed usage records; the existing log-ingest plane now reads the acting developer's identity and session from each record, so governed activity attributes to a real person instead of an anonymous placeholder; and both ingest planes now make a rejected export diagnosable rather than silent. One idempotent database migration applies automatically on startup, so it is a drop-in upgrade from v9.4.x.

The OTLP-ingest capabilities are an Enterprise feature. In Community the new endpoint is present but returns 501.

Added

OTLP metrics ingest — POST /v1/metrics (Enterprise)

Claude Code and Cowork export two kinds of OpenTelemetry data: per-request events as OTLP logs, and aggregate usage as OTLP metrics (token, cost, session, lines-of-code, commit, pull-request, tool-permission-decision, and active-time counters). AxonFlow already ingested the logs; v9.5.0 adds ingest for the metrics stream.

Each accepted datapoint becomes a canonical usage_events row in the same store the portal's usage and reporting surfaces already read — so aggregate per-developer and per-session usage works with no extra wiring. The ingest:

  • Normalizes cumulative exports to deltas. Claude Code exports counters cumulatively; AxonFlow converts each export to the incremental usage it represents, so a value is never double-counted, and a duplicate or out-of-order datapoint within a batch cannot inflate the total.
  • Keys every row on session and developer, and tags org and tenant from the authenticated license — never from the telemetry, which a client controls.
  • Bounds every client-supplied value before it is stored, so a malformed or hostile datapoint cannot corrupt a shared total or abort a batch.

See Claude Cowork & Claude Code OTEL Ingest for the endpoint, configuration, and what lands from the metrics stream.

Record-level identity on the log-ingest plane (Enterprise)

Real Claude Code and Cowork exporters place the acting user's user.email, session.id, and Anthropic account identifiers on each individual log record, not on the stream's resource. v9.5.0 reads them there (with a resource-level fallback), so governed activity attributes to the developer who performed it rather than to an anonymous placeholder.

Two honest constraints, documented in full on Per-Developer Identity:

  • Per-member attribution on this plane requires the client to be signed in with an Anthropic account (OAuth / managed login). A client authenticating with an API key emits no developer email at all; those records are still stored, redacted, signed, and session-keyed — they simply attribute to a reserved fallback address instead of a named person.
  • The telemetry user.email is an attribution attribute, not an authenticated identity. The org and tenant boundary is always the license, never the telemetry.

Per-tenant ingest-reject visibility (Enterprise)

Previously, an export rejected before it reached the handler — a wrong or malformed credential, an unsupported payload — failed silently: the operator saw "zero rows" with no error, and could not tell a mis-configured exporter from an idle one. Both ingest planes now emit an axonflow_otel_ingest_rejected_total{route, tenant, reason} counter and a log line for every rejected request, so a mis-configured or mis-authenticated exporter is immediately diagnosable. Tenant labels are bounded, so an unauthenticated caller cannot inflate metric cardinality or another tenant's counter.

Fixed

A control byte in an OTLP field no longer loses an audit record or drops a batch (Enterprise)

A NUL or other control byte in a client-supplied OTLP field is valid UTF-8, so it survived the ingest's UTF-8 repair — but the database rejects it, and the insert would abort. On the log-ingest plane that lost the governed audit record for the affected event; on the metrics plane it rejected the entire export batch, including every well-formed sibling datapoint. v9.5.0 sanitizes every client-supplied string that reaches storage on either plane — control bytes are stripped, ordinary prose whitespace is preserved — before it is persisted, so a single hostile field can no longer erase an audit record or discard a batch.

Migration

core/140 adds nullable OTLP usage columns and three partial indexes to usage_events to back the metrics-ingest records above. It is additive — it does not rewrite existing rows, and the new columns stay empty until Enterprise OTLP metrics ingest writes them. It applies automatically on startup.

Upgrading

v9.5.0 is a drop-in upgrade from v9.4.x. Pull the new images and restart; the core/140 migration applies automatically. The OTLP-ingest capabilities are Enterprise-only; a Community deployment is unaffected apart from the new /v1/metrics endpoint returning 501.