All docs
Docs · Reference
Telemetry
your application's metrics
Traces answer "what did this request do?". Telemetry answers "how often does the thing my application cares about happen?" — signups completed, books queued, checkouts started. A stack emits metric intents into the envelope; after the request completes, the chassis validates them and ships them to the metrics backend you configure. Delivery is best-effort and happens off the request path — a down backend never slows or fails a request.
Emitting a metric
Write intents to _txc.telemetry.metrics, composed with &array / &object:
WHEN @web.req.url.path == "/subscribe"
EMIT @telemetry.metrics = &array(
&object("name", "book.queued",
"kind", "counter",
"value", 1,
"attrs", &object("source", "search"))) A nano-op can contribute the same way — return {"_txc":{"telemetry":{"metrics":[…]}}}. Because arrays accumulate in the
merge, several operations can each add metrics to one request.
Each metric:
| field | required | notes |
|---|---|---|
name | yes | domain.action, e.g. book.queued |
kind | yes | counter or histogram (gauge not yet) |
value | yes | a JSON number; counters must be ≥ 0 |
unit | no | "1", "ms", "bytes", … |
attrs | no | scalar key/values, e.g. {"plan":"premium"} |
Keep names low-cardinality and put the varying detail in attrs — book.queued with source: "search", not book.dune.queued.
Turning it on
Two tenant secrets are the whole configuration:
txco secrets set TELEMETRY_ENDPOINT # your OTLP/HTTP endpoint, e.g. https://ingest.example.com:4318
txco secrets set TELEMETRY_HEADERS # optional auth headers: "x-api-key=…" (k1=v1,k2=v2) Setting TELEMETRY_ENDPOINT turns export on; deleting it turns it off
(within a few minutes — rotation likewise). Without it, intents are
simply dropped. Any OTLP-compatible backend works: SigNoz, Grafana
Cloud, Honeycomb, Datadog, a self-hosted collector. Endpoints must be https (http only toward localhost).
What arrives
Metrics are batched per tenant and exported with context attached: service.name is the tenant slug, plus txco.tenant, txco.node, txco.environment on the stream and txco.stack, txco.src on each
point. Nodes export independently — aggregate in the backend
(sum(book.queued) by txco.tenant, or by txco.node to inspect one
machine).
Guardrails
Validation drops a bad intent, never the request: unknown kinds,
non-numeric values, and malformed names are discarded and counted in the
chassis diagnostic chassis.telemetry.dropped, tagged with a reason.
Attributes are capped (16 per metric, 64 metrics per request, bounded
key/value lengths) and keys that look sensitive — email, token, password, and the like — are redacted before export.
The chassis’s own signals
There are two telemetry layers, configured independently:
- Your application’s metrics — this page. Emitted from stacks, per-tenant, configured with tenant secrets.
- The chassis’s own signals — runtime health for whoever operates
the node: request counts and timings, per-operation durations, and
diagnostics like
chassis.telemetry.dropped. Configured per node with the standard OpenTelemetry environment variables:
OTEL_EXPORTER_OTLP_ENDPOINT=https://collector.example.com:4318
OTEL_SERVICE_NAME=txco-chassis # the default
OTEL_RESOURCE_ATTRIBUTES=… # extra resource attributes The layers never mix: the node’s OTEL_* credentials are not sent to
tenant endpoints, and tenant metrics don’t ride the chassis stream.
Node knobs
--telemetry-enabled (default true) gates the subsystem; --telemetry-exporter picks the backend: otlp (default) or log,
which writes each metric as a chassis log line — handy in development,
no secrets needed.