Skip to content

Telemetry and Activity

OpenTelemetry export and ingest, optional activity and analytics primitives for extensions, vendor organization activity imports, and recording timeline synchronization.

Telemetry export

Optional OpenTelemetry export (preloop.services.otel_export, disabled by default) emits GenAI spans for governed model calls and MCP tool calls to any OTLP endpoint, carrying gen_ai.conversation.id when a runtime session id is present. Token and cost attributes match the ApiUsage row for that request; prompts, completions and tool arguments are not attached. Exporter errors are logged and never fail the user-facing call. It supplements the ApiUsage ledger rather than replacing it. See OTLP export.

Telemetry ingest

The opposite direction: POST /api/v1/telemetry/otlp/v1/{metrics,logs,traces} (preloop.api.endpoints.telemetry_otlp, preloop.services.otlp_telemetry) receives Claude Code, Claude Desktop and Cowork OTLP/HTTP exports, directly or relayed by a Claude apps gateway's telemetry.forward_to. A key scoped only to telemetry:ingest reaches nothing else. An allowlist keeps api_request/api_error events and the cost, token and session metrics and drops everything that could carry prompts, commands or paths. Events enrich the matching gateway usage row (by x-client-request-id) or create one telemetry_estimate row, which a later gateway row for the same request replaces in its own transaction; metrics build hourly aggregates only for sessions with no log events and no gateway rows. Dedup keys in telemetry_ingest_dedup make exporter retries no-ops. See Claude client telemetry ingest.

Platform analytics extension primitives

models.crud.admin_analytics provides windowed model-ledger and report-event aggregations for optional platform administration extensions. Platform-funded usage and legacy built-in model usage use separate partial indexes; explicit account funding never falls through to legacy classification. Gateway requests snapshot funding and purpose in usage metadata. Weekly email preferences include an audited account tracking switch, and the console renders it only when an extension advertises report_engagement_tracking. Core does not serve public tracking or cross-account analytics endpoints.

Optional live activity foundations

Core accepts content-free prompt and turn timestamps through backend APIs and stores them alongside bounded activity rollups, presence transitions and worker watermarks through its CRUD layer. CLI and runtime-plugin hook producers are outside this foundation. Hook timestamps stay out of the session replay timeline and remain available to activity aggregation. Optional Cloud and Enterprise extensions own derivation and authenticated board APIs; core keeps the capability off by default. Presence realtime events require view_users for another person or device, with an own-subject exception. The Fleet Map remains the console visualization; this foundation adds no Activity page, navigation entry or frontend component.

Instance-provided built-in models are enabled, non-retired global model rows; selection and Fleet discovery use ownership/configuration rather than a fixed provider alias. Custom model discovery applies the operator endpoint CIDR policy and pins each request to a checked IP while preserving Host/TLS identity. Local inference pricing defaults to zero after explicit prices; unknown remote models remain unpriced. See local model operations.

Residual credential storage uses the existing SecretReference backend for model and webhook values, and hash-only storage for all Preloop API keys. A backfill revision clears the three legacy columns; a later release removes them once no pod of the previous release can still map them. See the credential storage upgrade guide for rolling-upgrade behaviour, encryption-key continuity and rollback limits.

Recording timeline synchronization

Session artifacts optionally store typed media_timing independently of labels. services/media_sync.py resolves exact step offsets, producer timestamps and median-corrected server timestamps using metadata-only CRUD reads. The same resolver feeds GET /runtime-sessions/{id}/media-sync and signed evidence exports. Browser-step receipt times are server-owned; the migration backfills existing rows from activity creation time. Timing PATCH writes and its audit record commit atomically under the artifact account/session authorization.

The Lit session-recording-player lives in the session observer, outside Talk routing and transcript construction. It uses the existing authorized artifact cache, independent mappings per recording, and a default-on syncedRecordingPlayer host option. Unmapped moments never clamp; GIFs use a plain-image fallback. Browser Use and Skyvern producers emit timing through the existing deposit API. See the recording contract.

Optional vendor organization activity

The Copilot daily importer exposes the copilot_organization_metrics service hook after reading seats. An installed extension may import daily aggregate reports through the existing read-only client; its service and endpoints live outside core. Account-scoped snapshots and governed-owner lookups remain in preloop.models.crud. The OSS Lit copilot-activity-panel is mounted in Cost and Agents only when copilot_seat_activity is enabled, and hides data when the extension denies permissions or entitlement. Vendor counters never enter gateway token totals. Nullable premium fields preserve unknown values through parsing, aggregation, API schemas and display. Durable per-day billing status snapshots retain retry work independently of the adoption cursor; unavailable days replace stale spend and make incomplete range totals unknown. Optional extension failures are isolated from core imports with a sanitized warning.

Upstream MCP OAuth discovers protected resources and their selected authorization server, stores encrypted single-use authorization transactions, and coordinates token refresh across workers. The Lit tools setup exposes connection state and local disconnect; transport recovery never replays a dispatched tool call.

Every upstream MCP session built by MCPClient (services/mcp_client_pool.py) carries the identity headers defined in services/upstream_identity.py: User-Agent: Preloop/<version> (shared with the model path), X-Preloop-Correlation-Id equal to the audit correlation_id on proxied tool calls, and an opt-in X-Preloop-Instance. Per-server auth_config flags remove them or add the instance id; client headers are never forwarded.

Restricted CI's optional shared-flow selector resolves exactly one repository from the principal's immutable project binding before minting a read lease. Tool audit observations are stored as bounded canonical chunks in flow_execution_log, separate from clipped console rows and never broadcast on realtime channels. A new evidence action reads only an owned execution, with hashes and explicit coverage/redaction flags. STOPPED writes queue the existing principal-filtered completion outbox transactionally. Codex's opt-in native result schema uses CLI structured output plus the existing capture-time validation gate; protocol translation preserves the requested schema or fails explicitly.

Completed human-owned reviews can be exported through an explicit, expiring execution_result_grant, without adopting their execution into a CI principal. The CRUD layer binds current owner consent to original provider revisions and result/event digests; immutable grant fields and monotonic revocation are guarded in PostgreSQL. Export checks authority before and after a bounded provider read. The separate publisher verifies the public receipt, exact head/base and stable publication marker, then requires independent provider read-back. It neither launches a model nor receives publication authority from the export grant.