Crux
GuidesObservability

Privacy and redaction

What Crux telemetry keeps, redacts, or omits, including DefinitionRef.source and provider errors.

Crux observability is built to be safe by default, in plain terms: nothing leaves your process until you configure a transport, payload capture can be turned off centrally, a redaction failure drops the record instead of sending it unredacted, and join metadata never carries host filesystem roots or unbounded provider dumps.

Graph capture policy

Configure capture once; every consumer (devtools HTTP transport, subscribeObservability(), diagnostics channel, @use-crux/otel) sees the same sanitized records:

import { config } from "@use-crux/core";

export default config({
  observability: {
    recordInputs: "off", // 'inline' | 'reference' | 'off'
    recordOutputs: "reference",
    redactPatterns: [
      /\bACME-\d{6}\b/,
      {
        pattern: /\bCUSTOMER-\d+\b/,
        replacement: "[customer-id]",
      },
    ],
    redactPaths: ["customer.email", "credentials.session"],
    redactRecord: (record) => {
      // return a replacement record, or null to drop fail-closed
      return record;
    },
  },
});

redactPaths is the one data-only persistence policy shared by runtime feedback, Eval evidence and run files, Review context, Add-to-eval sidecars, and generated Runtime hosts. Paths are relative to the payload being stored. Arrays are transparent, so customers.email redacts email in every item of customers. Crux also always redacts authorization, API-key, token, and secret keys at any depth; those defaults cannot be disabled.

Run crux runtime generate after changing this policy. Generated registries contain the normalized paths for the selected deployment and expose only a secret-free policy fingerprint in the authenticated host manifest. The fingerprint is part of Eval evidence identity, so evidence written under a different policy is never reused. A remote Eval result that would need redaction is rejected before durable storage instead of silently changing its meaning.

Crux Local invalidates the previous generated policy before a refresh and finishes initial generation before exposing feedback and Review mutation routes. Those writes return an actionable 503 while no current, validated snapshot is available; they never fall back to an empty configured policy.

  • inline: embed bounded previews on the record
  • reference: keep identity/size pointers without full bodies where supported
  • off: omit payload-shaped content

redactPatterns is deployment-wide observability policy. It rewrites matching strings in captured artifact previews and URIs, nested attribute values, and run/span error messages. Bare expressions use [REDACTED]; object-form replacements are literal, so JavaScript replacement tokens such as $& and $1 are not expanded. This changes telemetry only—the application, model, provider, and tool still receive the original values.

Patterns are trusted application configuration. Avoid expressions susceptible to catastrophic backtracking, because JavaScript cannot place a general complexity bound on regular-expression matching.

Every consumer sees records only after the shared privacy and sanitization passes. A failure inside pattern redaction or your redactRecord hook drops the telemetry record entirely (fail-closed): broken redaction never leaks an unredacted payload or fails the application operation. The emit-path ordering and toSafeJsonValue() truncation defaults are documented in the Observability reference.

Use redactPatterns for deployment-wide observability string rewriting. Use redactRecord for advanced graph-record transformations or to drop a record. Use redactPaths for the stable, portable persisted-data policy shared by Eval, feedback, Review, sidecars, and generated Runtime hosts.

Visibility in Devtools

Devtools exposes privacy-safe status without exposing the policy or captured values. Catalog's project-level Observability privacy card says whether the effective config loaded by Local contains declarative patterns. Runs shows Redacted only when declarative patterns actually changed captured telemetry, and names only the affected broad surfaces.

Crux does not expose rules, matches, values, replacements, nested paths, or counts. A Runs badge proves a configured pattern changed the named telemetry surface; it is not proof that no sensitive data remains. A custom redactRecord change alone does not produce declarative-pattern evidence. Catalog policy is project-level and never creates Safety guardrail definitions or per-definition relations. Deployed environments may use different effective config from the Local project view.

DefinitionRef source

Runtime records may attach:

definitionRefs?: Array<{
  id: string
  kind: ProjectDefinitionKind
  role: DefinitionRefRole
  source?: { file: string; line: number; column?: number }
}>

source is a SanitizedSourceRef:

  • Repo-relative only: absolute host paths are never emitted on the wire
  • No .. traversal: paths that escape the project root are dropped
  • No function names: stack-derived function fields are intentionally never emitted
  • Omitted when unproven: if the runtime cannot prove a safe relative path (missing project root, path outside root, bad line), source is absent entirely

Built-in emitters usually omit source. Historical Run detail resolves location only from the exact named manifest; current-only Catalog views use the current compiler read model separately. When runtime code holds a genuine compiled location, sanitizeDefinitionSource(source, { projectRoot }) is the only supported conversion.

Provider errors

Normalized adapter failures use:

interface CruxProviderError {
  kind: CruxProviderErrorKind;
  code: string; // bounded, namespaced machine id
  retryable: boolean;
  message?: string; // optional, redacted human text
}
FieldRetention rule
kindClosed enum, classification only
codeShort namespaced string (openai.rate_limit, anthropic.stream_completion_failed, crux.timeout.generate, …), enough to debug without raw payloads
retryableBoolean classification only, does not trigger retries by itself
messageOptional; always passed through redactProviderMessage / toSafeJsonValue before storage. Empty after redaction → omitted

There is no providerEvidence bag and no raw response-body field on the public error shape. Prefer code + kind in logs; treat message as best-effort, already-redacted text.

OpenTelemetry

@use-crux/otel projects metadata from the same graph stream and drops known payload attribute keys (text, query, messages, output, …) by default.

  • GenAI message content attributes are off unless captureMessageContent: true or OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true (each content attribute capped at 32KB)
  • Tool spans record toModelOutput() shape/size only: not raw tool results
  • Corpus / ingest identifiers export as hashes, not raw paths or URLs
  • W3C baggage is untrusted input: nothing is copied onto spans unless listed in baggageAttributeAllowlist

Bounded media streams

streamImage() and streamSpeech() contribute operation/role, routing commitment, attempt and progressive event counts, byte totals, validated MIME facts, timing, terminal state, and canonical preview/final Safety coordinates. Those are descriptors, not media. The graph, Local read model, Runs UI, and OTel projection never receive prompts, chunks, assembled bytes, base64, URLs, filenames, hashes, refs, native provider events, or held Safety media.

An enforcing policy may be presented as held, then released or held, then discarded based on terminal and Safety facts. That label does not imply the held bytes were captured. Report mode may be shown as live.

Execution evidence

Qualified execution evidence uses an even narrower positive allowlist. crux.evidence contains only the validated evidence ID, role, canonical or normalized custom kind, optional role-valid conclusion, and canonical subject kind. Coverage contains only role and status. Evidence payloads, raw custom kinds, graph endpoints, raw resource IDs, paths, recovery envelopes, idempotency keys, content digests, producer identity, supersession, markers, and arbitrary attributes are always excluded.

Local payload expiry returns payloadState: "redacted" and may expose the bounded reason retention; access-controlled reads prefer the less revealing access. Explicit privacy deletion writes only private resurrection guards and permanently rejects delayed evidence retries. See Execution evidence.

Storage and Memory explorers

DevTools Storage and related resource views show wiring, capabilities, and operation summaries. They do not display record values, vector contents, blob bodies, or signed URLs unless the application explicitly enables raw debug inspection.

On this page