# How to Configure OpenTelemetry and PostHog Telemetry for Strix

> Learn how to configure OpenTelemetry and PostHog telemetry for Strix. Control span export and usage analytics using environment variables or the CLI config file.

- Repository: [Strix/strix](https://github.com/usestrix/strix)
- Tags: how-to-guide
- Published: 2026-03-26

---

**Strix supports both OpenTelemetry (OTEL) span export to Traceloop and anonymized PostHog usage analytics, with each subsystem independently toggleable via environment variables or the `~/.strix/cli-config.json` file.**

The `usestrix/strix` repository ships with a built-in telemetry stack that captures execution traces and high-level usage metrics. You can configure OpenTelemetry for distributed tracing and PostHog for product analytics without modifying source code, using only runtime configuration flags.

## Understanding Strix Telemetry Flags

Strix evaluates three hierarchical flags defined in [`strix/config/config.py`](https://github.com/usestrix/strix/blob/main/strix/config/config.py) to determine whether telemetry flows are active. According to the source code, these boolean-ish values are parsed from environment variables or the CLI config file:

- **Master switch**: `STRIX_TELEMETRY` (config key: `strix_telemetry`) defaults to `"1"` (enabled)
- **OpenTelemetry**: `STRIX_OTEL_TELEMETRY` (config key: `strix_otel_telemetry`) defaults to `None`, falling back to the master switch
- **PostHog**: `STRIX_POSTHOG_TELEMETRY` (config key: `strix_posthog_telemetry`) defaults to `None`, falling back to the master switch

In [`strix/telemetry/flags.py`](https://github.com/usestrix/strix/blob/main/strix/telemetry/flags.py), the helper functions `is_otel_enabled()` and `is_posthog_enabled()` check these variables. If any flag resolves to a disabled value (`0`, `false`, `no`, or `off`), that telemetry path is silently skipped.

## Configuring OpenTelemetry (Traceloop)

When the `Tracer` class initializes in [`strix/telemetry/tracer.py`](https://github.com/usestrix/strix/blob/main/strix/telemetry/tracer.py) (lines 48-82), it inspects the OTEL flag and conditionally invokes `_setup_telemetry()` (lines 114-121). This method performs two distinct export configurations based on your environment.

### Local-Only JSONL Export

By default, Strix writes spans to a local JSON Lines file. The `bootstrap_otel()` function in [`strix/telemetry/utils.py`](https://github.com/usestrix/strix/blob/main/strix/telemetry/utils.py) (lines 337-445) creates a simple exporter that appends each span to `events.jsonl` inside the current run directory.

### Remote Export via Traceloop

To forward spans to a remote collector, provide both a base URL and API key. The tracer reads `traceloop_base_url` and `traceloop_api_key` from the global `Config` object (lines 124-126 in [`tracer.py`](https://github.com/usestrix/strix/blob/main/tracer.py)). If present, `bootstrap_otel()` adds an OTLP HTTP exporter and sets the internal `_remote_export_enabled` flag to `True`.

Enable remote OpenTelemetry export with these variables:

```bash
export STRIX_OTEL_TELEMETRY=1
export STRIX_TRACELOOP_BASE_URL="https://otlp.traceloop.com"
export STRIX_TRACELOOP_API_KEY="your-api-key-here"

```

If either URL or key is missing, Strix falls back to local-only JSONL output.

## Configuring PostHog Analytics

PostHog telemetry lives in [`strix/telemetry/posthog.py`](https://github.com/usestrix/strix/blob/main/strix/telemetry/posthog.py) (lines 9-31 and 78-107). When enabled, the library emits anonymized usage events containing no target URLs, source code, or vulnerability details.

**Event lifecycle:**
- **Start**: `posthog.start()` fires a `scan_started` event capturing model type, scan mode (white-box vs. black-box), interactivity flags, and first-run detection
- **End**: At the conclusion of `main()` in [`strix/interface/main.py`](https://github.com/usestrix/strix/blob/main/strix/interface/main.py) (lines 47-53), a `scan_ended` event transmits aggregated counts, token usage, and total duration

Disable PostHog while keeping OTEL active:

```bash
export STRIX_POSTHOG_TELEMETRY=0

```

## Complete Configuration Example

The following shell script demonstrates a fully enabled telemetry stack with remote OTEL export:

```bash
#!/usr/bin/env bash

# Enable all telemetry channels

export STRIX_TELEMETRY=1
export STRIX_OTEL_TELEMETRY=1
export STRIX_POSTHOG_TELEMETRY=1

# Configure remote Traceloop endpoint (omit for local-only)

export STRIX_TRACELOOP_BASE_URL="https://otlp.traceloop.com"
export STRIX_TRACELOOP_API_KEY="my-secret-key"

# Execute scan

strix --target https://example.com --scan-mode quick

```

After execution, verify that `./strix_runs/<run-name>/events.jsonl` contains OTEL spans, and check your Traceloop dashboard for remote traces.

## Verifying Your Telemetry Configuration

You can inspect the active configuration at runtime using the public API from [`strix/telemetry/flags.py`](https://github.com/usestrix/strix/blob/main/strix/telemetry/flags.py):

```python
from strix.telemetry.flags import is_otel_enabled, is_posthog_enabled

print("OTEL enabled:", is_otel_enabled())       # Checks STRIX_OTEL_TELEMETRY

print("PostHog enabled:", is_posthog_enabled()) # Checks STRIX_POSTHOG_TELEMETRY

```

To confirm whether remote export is active for the current tracer instance:

```python
from strix.telemetry.tracer import get_global_tracer

tracer = get_global_tracer()
if tracer:
    print("Remote OTEL export:", tracer._remote_export_enabled)

```

## Summary

- **Three-tier controls**: The master `STRIX_TELEMETRY` flag governs both subsystems, while `STRIX_OTEL_TELEMETRY` and `STRIX_POSTHOG_TELEMETRY` allow fine-grained overrides.
- **Dual OTEL modes**: OpenTelemetry spans write locally to `events.jsonl` by default; supply `STRIX_TRACELOOP_BASE_URL` and `STRIX_TRACELOOP_API_KEY` to enable remote OTLP export.
- **Privacy-first analytics**: PostHog captures only high-level metadata (`scan_started`, `scan_ended`) with zero code or vulnerability payload transmission.
- **Runtime verification**: Use `is_otel_enabled()`, `is_posthog_enabled()`, and the tracer's `_remote_export_enabled` attribute to audit active exporters programmatically.

## Frequently Asked Questions

### How do I completely disable all telemetry in Strix?

Set the master switch to `0`, `false`, `no`, or `off`:

```bash
export STRIX_TELEMETRY=0

```

This disables both OpenTelemetry and PostHog regardless of their individual flag values, as implemented in [`strix/telemetry/flags.py`](https://github.com/usestrix/strix/blob/main/strix/telemetry/flags.py).

### Can I use OpenTelemetry without sending data to a remote collector?

Yes. Simply omit `STRIX_TRACELOOP_BASE_URL` and `STRIX_TRACELOOP_API_KEY`. The `bootstrap_otel()` function in [`strix/telemetry/utils.py`](https://github.com/usestrix/strix/blob/main/strix/telemetry/utils.py) will initialize only the local JSONL exporter, writing spans to `events.jsonl` in your run directory without network transmission.

### What data does PostHog receive during a scan?

PostHog receives anonymized aggregate metrics only. According to [`strix/telemetry/posthog.py`](https://github.com/usestrix/strix/blob/main/strix/telemetry/posthog.py) and [`strix/interface/main.py`](https://github.com/usestrix/strix/blob/main/strix/interface/main.py), events include the LLM model name, scan mode classification, token counts, and duration—but never the target URL, source code under review, or identified vulnerabilities.

### Where does Strix store the local OTEL span data?

Spans are written to `events.jsonl` inside the specific run directory (typically `./strix_runs/<run-name>/`). This path is determined during `Tracer` initialization in [`strix/telemetry/tracer.py`](https://github.com/usestrix/strix/blob/main/strix/telemetry/tracer.py), where the JSONL exporter is configured alongside any remote OTLP endpoints.