Observability
ClawQL observability answers operational questions about agent deployments: is the system healthy, what is slow, what broke, and — for agent traffic — is something behaving in a way it should not be. This is runtime telemetry (logs, metrics, traces, profiles) collected through Grafana Alloy, governed by a multi-provider registry, and correlated in LGTM+ (Loki, Grafana, Tempo, Mimir, plus Pyroscope). It is not compliance-grade proof of what happened later — that is Audit Trail. It is not product analytics — that is clawql-analytics.
Package: clawql-observability · README · package spec · provider registry design
Related: Audit Trail (WORM proof, not live telemetry) · Audit tool & observability (in-process MCP audit ring buffer — different surface) · Authentication (Faro ingest JWTs and ATR scopes)
What this package is not
Three surfaces are often confused:
| Surface | Question it answers |
|---|---|
clawql-observability | What is the system doing right now? (LGTM+, Alloy, dashboards) |
clawql-audit | What can we prove happened later? (hash-chained WORM trail) |
MCP audit tool | What did this session record in-process? (ring buffer, not LGTM+) |
Product pageviews and funnels live in clawql-analytics, not here — engineers use Faro RUM for errors and performance; product teams use analytics for retention and funnels.
Ingest: Alloy owns the hot path
All OTLP-capable signals route through Grafana Alloy before landing in backends. Alloy handles batching, retries, backpressure, and per-exporter failure isolation. Fan-out to multiple backends per signal type (for example logs to two Loki endpoints) is configured in River output lists — not reimplemented in TypeScript.
The TypeScript provider registry is the authorized front door to that configuration: who may register backends, which ATR scopes apply, WORM audit of config changes, health checks, Alloy River generation, and (next) query federation.
Phase 3b ships generateAlloyRiverEffect / applyAlloyConfigEffect: a registry snapshot becomes a complete River file (exporters + batch fan-out + optional Faro), validated structurally, written to disk, and audited with OBSERVABILITY_ALLOY_CONFIG_APPLIED. Multi-provider log/metric/trace registration produces multiple entries in the batch output lists — Alloy owns the runtime fan-out.
Phase 3c ships Effect-native query federation (ObservabilityQueryService): governed LogQL / PromQL / TraceQL / profile reads with provider selection (one | all | primary), ATR scope checks, and OBSERVABILITY_RAW_DATA_ACCESSED WORM events. HTTP IO goes through TelemetryQueryTransport (Effect.tryPromise + fetch in Live; tests substitute a Layer).
Browser RUM uses a Cloudflare Worker proxy with ephemeral HS256 JWTs — no static public DSN. Invalid credentials return HTTP 204 with no body.
Provider registry (Phase 3)
Observability uses signal-typed provider interfaces — not one flat provider type:
| Signal | Interface | Built-in LGTM+ plugin |
|---|---|---|
| Logs | LogProvider | Loki |
| Metrics | MetricProvider | Mimir |
| Traces | TraceProvider | Tempo |
| Profiles | ProfileProvider | Pyroscope |
Unlike clawql-analytics (one active provider at a time), observability defaults to multiple providers per signal type for redundant fan-out — but that fan-out is expressed in Alloy config generated from the registry, not in application push loops.
Governance scopes include observability:configure, observability:query_*, and observability:export. Provider add/remove/config change emits WORM governance events (not individual telemetry samples).
Escape hatch: vendors that cannot speak OTLP may use a rare direct TypeScript push adapter. Exceptional means rare, not less audited — same scopes, WORM, health checks, and documented justification required at registration.
Phases
| Phase | Scope |
|---|---|
| 1 | LGTM+ docker-compose + Alloy OTLP pipeline |
| 2 | Faro + ephemeral-JWT Worker proxy |
| 3a | Signal-typed registries, LGTM+ built-ins, scopes, WORM hooks, health checks (shipped) |
| 3b | Registry → Alloy River config generator (shipped) |
| 3c | Query federation (LogQL / PromQL / TraceQL / profiles) (shipped) |
| 3d | Host MCP + HTTP + WORM bridge (shipped) |
| 4 | Langfuse dual-export + Panguard correlation + Falco/Tetragon/Wazuh → Loki (shipped) |
| 5 | Alerting catalog + Vault/env Faro JWT keys + Alloy reload (shipped) |
Still deferred: blocking N-of-M exporter quorum (provider-registry design §9 — Alloy-scoped only if ever needed).
Local quick start
npm run build -w clawql-observability
npm run compose:up -w clawql-observability
Open Grafana at http://localhost:3000 (local dev). Send OTLP to Alloy on 4317 (gRPC) or 4318 (HTTP). CI runs the LGTM+ stack smoke workflow on every change to the package.
Opt-in security sensor stubs (Falco / Tetragon / Wazuh → Loki):
npm run compose:security -w clawql-observability
Phase 4 / 5 enablement
Host flag CLAWQL_ENABLE_OBSERVABILITY=1 (or CRD observability.enabled) turns on MCP tools and HTTP routes. Layer flags on top:
| Area | Env / knobs |
|---|---|
| Langfuse dual-export | CLAWQL_ENABLE_LANGFUSE or LANGFUSE_ENABLED; LANGFUSE_OTLP_AUTH_HEADER (Alloy sys.env); Langfuse OTLP base URL as configured for the provider |
| Panguard → Loki | CLAWQL_PANGUARD_TELEMETRY_LOKI_URL (optional push; host wrap still emits deny attributes without it) |
| Faro JWT mint | TELEMETRY_JWT_SIGNING_KEY or CLAWQL_TELEMETRY_JWT_VAULT_* via TelemetrySigningKeyService; POST /observability/telemetry/token |
| Alerts | MCP observability_alerts · GET /observability/alerts · catalog alerts/default-alert-rules.yaml |
| Alloy reload after apply | CLAWQL_ALLOY_RELOAD_PID (SIGHUP) or CLAWQL_ALLOY_RELOAD_K8S_DEPLOYMENT (+ optional namespace) |
| Security sensors | Compose profile security; Helm helm/security-overlay.yaml; River alloy/security-sensors.river labels service_name=clawql-falco|clawql-tetragon|clawql-wazuh |
Grafana folder ClawQL provisions LGTM+ Overview and Langfuse ↔ Panguard correlation (uid: clawql-langfuse-panguard).
Summary
| Property | Mechanism |
|---|---|
| Runtime telemetry vs proof vs product | LGTM+ observability · WORM audit · analytics — separate packages |
| Hot-path fan-out | Grafana Alloy River output lists — not TypeScript loops |
| Registry job | Governed config, health, WORM, Alloy generation, query federation |
| Browser ingest | Ephemeral JWT → Worker → private Alloy faro.receiver |
| Multi-backend per signal | Signal-typed registries; LGTM+ as default built-in plugins |
| Agent / security correlation | Langfuse traces + Panguard denies + Falco/Tetragon/Wazuh logs |
| Escape hatch governance | Same scopes and audit as main path; justification required |
| Quorum | Deferred — not in TypeScript; Alloy-only if ever needed (§9) |