Product telemetry#
Lens Agents emits product telemetry as OpenTelemetry log records. Telemetry helps Lens improve the platform and helps operators understand usage patterns. No conversation content, prompts, responses, tool-call arguments, or file contents are ever included.
The full catalogue is 57 events, listed below. All of them are emitted by the platform server itself.
Emission semantics#
Who emits what#
Every event comes from the platform server, through one OpenTelemetry LoggerProvider carrying these resource attributes:
| Resource attribute | Value |
|---|---|
service.name |
lens-agents-backend |
service.namespace |
lens-agents |
service.version |
The platform version |
deployment.environment.name |
config.deploymentEnvironment |
Transport#
Events are sent as batched OTLP/HTTP log records to {OTEL_EXPORTER_OTLP_ENDPOINT}/v1/logs. A BatchLogRecordProcessor handles queuing and retry. When OTEL_EXPORTER_OTLP_ENDPOINT is unset or empty, the telemetry subsystem is a full no-op — no buffering, no network calls.
User attribution#
An actor is resolved to a user.id only when a person did the thing:
| Auth context type | Resolved user.id |
Rationale |
|---|---|---|
oidc |
OIDC subject ID | Human user actions are attributed |
api-token |
undefined (omitted) |
The event fires but is unattributed — the count is accurate, the actor is the token itself |
sandbox |
undefined (omitted) |
Agent-initiated actions are not attributed to a person |
cluster-jwt |
undefined (omitted) |
Cluster-initiated actions are not user-attributed |
When user.id is undefined the attribute is omitted entirely from the log record — downstream systems can filter on its presence to separate human-initiated from machine-initiated events.
What is NOT instrumented#
Ordinary read operations do not emit telemetry events. The instrumented set is state-mutating actions — create, update, delete, rotate, revoke, issue — plus MCP session start and end, aggregate tool-usage counts, and periodic snapshots of running sandboxes and connectors.
Configuration#
Two environment variables control telemetry for both the agent runtime and the control plane:
| Variable | Required | Description |
|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT |
No | Base URL of the OTLP/HTTP collector. Released images bake in a Lens-operated endpoint, so leaving this unset does not mean nothing is sent. |
OTEL_EXPORTER_OTLP_API_KEY |
No | Passed as the x-api-key header to the collector. Use when your collector requires authentication. |
Helm values#
| Value | Default | Purpose |
|---|---|---|
telemetry.enabled |
true |
Master switch. Always rendered into the ConfigMap. Set false to send nothing at all. |
telemetry.endpoint |
(empty) | OTLP/HTTP collector base URL. Empty falls through to the endpoint baked into the image, not to "off". |
telemetry.apiKey |
(empty) | Sent as x-api-key when your collector requires authentication. |
telemetry.existingSecret |
(empty) | Read the API key from an existing Secret instead. |
telemetry.existingSecretKey |
OTEL_EXPORTER_OTLP_API_KEY |
Key within that Secret. |
A default install does send telemetry
Released images carry a Lens-operated OTLP endpoint. The chart omits OTEL_EXPORTER_OTLP_ENDPOINT when telemetry.endpoint is empty, which lets that baked-in endpoint apply. Setting telemetry.endpoint redirects the events to a collector you run; only telemetry.enabled=false stops them.
Both choices are one Helm value. See Point at your own collector and Turning it off.
Base attributes#
Every event (runtime and control-plane) carries these attributes automatically:
| Attribute | Description |
|---|---|
event.id |
Auto-generated UUIDv7 per event |
event.name |
Event name (see the catalogue below) |
user.id |
OIDC subject of the actor. Omitted whenever the actor is not a signed-in person |
deployment.id |
A persistent identifier for this install, generated once and stored in your database |
service.instance.id |
IP address of the emitting instance |
deployment.environment.name |
Whatever you set config.deploymentEnvironment to |
cloud.*, host.* |
Provider, region, and host identifiers, when the standard OpenTelemetry detectors find them on AWS, GCP, or Azure. Absent on-premises |
deployment.id is what makes events from one install correlatable across time. If that, or the cloud metadata, is more than you want to share, point telemetry at your own collector or turn it off.
Event catalogue#
Fifty-seven events, all emitted by the platform server. Every one carries the base attributes above in addition to the keys listed here.
Organizations#
| Event | Attribute keys |
|---|---|
org created |
org.id, org.name |
org updated |
org.id |
org deleted |
org.id |
org member role updated |
org.id, member.user.id, member.role |
org member removed |
org.id, member.user.id |
Projects#
| Event | Attribute keys |
|---|---|
project created |
org.id, project.id, project.name |
project updated |
org.id, project.id |
project deleted |
org.id, project.id |
project keys rotated |
org.id, project.id |
Teams and invitations#
| Event | Attribute keys |
|---|---|
team created |
org.id, team.id, team.name |
team updated |
org.id, team.id |
team deleted |
org.id, team.id |
team member added |
org.id, team.id, member.user.id or member.agent_token.id |
team member removed |
org.id, team.id, member.id |
team project access granted |
org.id, team.id, project.id, access.role |
team project access revoked |
org.id, team.id, project.id |
org invitation sent |
org.id, invitation.id |
org invitation accepted |
org.id, invitation.id |
org invitation declined |
org.id, invitation.id |
org invitation revoked |
org.id, invitation.id |
Policies and policy bindings#
| Event | Attribute keys |
|---|---|
policy created |
org.id, policy.id, policy.scope, project.id (when project-scoped) |
policy updated |
org.id, policy.id, policy.scope |
policy deleted |
org.id, policy.id, policy.scope |
policy binding created |
org.id, binding.id, binding.scope, project.id (when project-scoped) |
policy binding updated |
org.id, binding.id |
policy binding deleted |
org.id, binding.id |
Policy contents are never sent — only the identifier and scope. What a policy allows stays in your database.
Sandboxes#
| Event | Attribute keys |
|---|---|
sandbox created |
org.id, project.id, sandbox.id, sandbox.name, sandbox.type, container.image |
sandbox started |
org.id, project.id, sandbox.id, sandbox.type |
sandbox stopped |
org.id, project.id, sandbox.id, sandbox.type |
sandbox deleted |
org.id, project.id, sandbox.id, sandbox.name, sandbox.type |
sandbox connected |
project.id, sandbox.id, sandbox.name, sandbox.type, container.image, org.id when known |
sandbox disconnected |
project.id, sandbox.id, sandbox.name, sandbox.type, container.image, disconnect.code, org.id when known |
sandbox token issued |
org.id, project.id, token.id, token.ttl_ms |
container.image is the image reference you supplied. If your image names are themselves sensitive, that is a reason to route telemetry to your own collector.
Clusters#
| Event | Attribute keys |
|---|---|
cluster created |
org.id, project.id, cluster.id |
cluster updated |
org.id, project.id, cluster.id |
cluster deleted |
org.id, project.id, cluster.id |
cluster tunnel token rotated |
org.id, project.id, cluster.id |
AWS connections and credentials#
| Event | Attribute keys |
|---|---|
aws connection created |
org.id, project.id, connection.id |
aws connection updated |
org.id, project.id, connection.id |
aws connection deleted |
org.id, project.id, connection.id |
credential created |
org.id, project.id, credential.id |
credential updated |
org.id, project.id, credential.id |
credential deleted |
org.id, project.id, credential.id |
Credential values, header names, and target domains are not sent. Only the row identifier.
Connectors and MCP servers#
| Event | Attribute keys |
|---|---|
mcp server created |
org.id, project.id, mcp_server.id |
mcp server updated |
org.id, project.id, mcp_server.id |
mcp server deleted |
org.id, project.id, mcp_server.id |
mcp server oauth completed |
org.id, project.id, mcp_server.id, credential.id |
connector created (http) |
org.id, project.id, connector.id, connector.kind, connector.entryCount |
connectors snapshot |
org.id, project.id, connector.id, connector.kind, connector.status, connector.enabled, connector.has_credential |
Upstream URLs are not sent, and the discovered tool list is not sent. connector.entryCount is a count, not a list. Names of tools an agent actually calls do appear, in the aggregate tool usage counts below.
MCP sessions and tool usage#
| Event | Attribute keys |
|---|---|
mcp session started |
org.id, project.id, sandbox.id, sandbox.type (each when known) |
mcp session ended |
session.end.reason, plus org.id, project.id, sandbox.id, sandbox.type when known |
tool usage |
org.id, project.id, tool.name, tool.kind, tool.outcome, tool.calls, plus sandbox.id, sandbox.type, connector.id when known |
runtime snapshot |
org.id, project.id, sandbox.id, sandbox.type. With no sandboxes running, a single event carrying sandboxes.connected: 0 |
tool usage records which tool ran, how it ended, and how many times, aggregated per flush. That includes the names of connector tools an agent called. Tool arguments and results are never included.
API tokens#
| Event | Attribute keys |
|---|---|
api token created |
org.id, token.id, token.expires_at (when set) |
api token revoked |
org.id, token.id |
Spending limits#
| Event | Attribute keys |
|---|---|
spending limit set |
org.id, limit.cents, actor.type, actor.id (when scoped to one actor) |
spending limit removed |
org.id, limit.id |
What is NOT sent#
Product telemetry is strictly limited to lifecycle and metadata events. The following are never included:
- Conversation content — prompts, responses, and message history
- Tool-call arguments or results — MCP tool inputs, Kubernetes API responses, shell output
- File and workspace contents — files read, written, or transferred by agents
- Credentials — API keys, tokens, or secrets, and the domains and headers they are injected into
- Policy contents — what a policy allows; only its identifier and scope are sent
- Connector targets — upstream URLs, and the discovered tool list. Names of tools an agent calls do appear in aggregate
tool usagecounts
All attribute values are identifiers (IDs, names, numeric thresholds). No free-text or user-generated content is captured.
Point at your own collector#
Send telemetry to any OTLP/HTTP-compatible collector you operate:
helm upgrade lens-agents oci://ghcr.io/lensapp/lens-agents --reuse-values \
--set telemetry.endpoint="https://otel.internal.example.com" \
--set telemetry.existingSecret=lens-agents-otel
This works with any OpenTelemetry-compatible backend — Grafana Alloy, Datadog, Honeycomb, a self-hosted OpenTelemetry Collector, or anything else that accepts OTLP/HTTP log records. The events described on this page then land in your own observability stack and go nowhere else.
Turning it off#
helm upgrade lens-agents oci://ghcr.io/lensapp/lens-agents --reuse-values \
--set telemetry.enabled=false
telemetry.enabled is the master switch and is always rendered into the ConfigMap, so false disables the pipeline whatever the image was built with. Leaving telemetry.endpoint empty is not enough — an empty endpoint falls through to the endpoint baked into released images.
Related#
- Audit trail — detailed action-level logging across all agent activity
- Install on Kubernetes — the Helm values that configure this
- Data sovereignty — data residency and GDPR
- Privacy and PII controls — what data flows to model providers