Telemetry

The telemetry: block controls observability — what Coulisse logs to stderr, what it persists to SQLite for the studio UI, and whether it ships traces to your own OpenTelemetry backend.

Every field has a sensible default. Omit the block and you get stderr logs at info plus the studio's per-turn event tree, with no external traces.

Shape

telemetry:
  fmt:
    enabled: true        # stderr logs; default on
  sqlite:
    enabled: true        # mirrors spans into the studio's tables; default on
  otlp:                  # absent = disabled (default)
    endpoint: "http://localhost:4317"
    protocol: grpc       # or http_binary
    service_name: coulisse
    headers:
      authorization: "Bearer ${OTEL_API_KEY}"

All three layers compose. Turn sqlite off if you don't need the studio. Add otlp to ship the same traces to Grafana, SigNoz, Jaeger, Honeycomb, or any OTLP-compatible backend.

telemetry.fmt

FieldTypeRequiredNotes
enabledboolnoDefault true.

Writes structured logs to stderr. The level is controlled by the RUST_LOG environment variable; without it, the default is info,sqlx=warn (info from Coulisse, warnings only from the SQL driver). To see internal SQL traffic, run with RUST_LOG=debug. To silence everything, set RUST_LOG=error.

telemetry.sqlite

FieldTypeRequiredNotes
enabledboolnoDefault true.

Mirrors turn and tool_call tracing spans into the events and tool_calls tables that the studio UI reads. Without this layer, the studio loses its per-turn event tree and tool-call panel.

The schema is part of the same SQLite file the rest of Coulisse persists into (.coulisse/coulisse-memory.db).

telemetry.otlp

Absent (the default) means Coulisse does not export traces externally. To plug Coulisse into your own observability stack, set the block:

FieldTypeRequiredNotes
endpointstringyesCollector URL.
protocolenumnogrpc (default) or http_binary.
service_namestringnoOpenTelemetry resource attribute service.name. Default coulisse.
headersmapnoStatic HTTP/gRPC headers attached to every export.

Endpoint defaults

  • gRPC (the default): port 4317, e.g. http://localhost:4317.
  • HTTP/protobuf: port 4318, e.g. http://localhost:4318/v1/traces.

The collector you point at decides the rest — Coulisse ships traces with service.name = coulisse and span names turn, tool_call, and llm_call. Span fields carry user_id, turn_id, agent, tool_name, kind, and the rest documented in the features chapter.

Headers

Useful for managed backends:

telemetry:
  otlp:
    endpoint: "https://ingest.us.signoz.cloud:443"
    protocol: grpc
    headers:
      "signoz-access-token": "${SIGNOZ_TOKEN}"

YAML doesn't expand ${...} itself; substitute at deploy time (helm, envsubst, sops, etc.).

How the layers compose

The cli installs a single tracing_subscriber registry with the layers your config asked for, in order:

  1. RUST_LOG env filter
  2. fmt → stderr (when fmt.enabled)
  3. sqliteevents + tool_calls tables (when sqlite.enabled)
  4. otlp → external collector (when otlp is set)

Every span emitted by the running server fans out to all enabled layers. There is no priority or fallback — the SQLite layer keeps full payloads (full prompts, args, results), the OTLP layer ships the same span attributes to your collector. If your backend chokes on multi-megabyte attributes, drop those fields in your collector pipeline rather than at the source.