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 recordreference: keep identity/size pointers without full bodies where supportedoff: 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),
sourceis 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
}| Field | Retention rule |
|---|---|
kind | Closed enum, classification only |
code | Short namespaced string (openai.rate_limit, anthropic.stream_completion_failed, crux.timeout.generate, …), enough to debug without raw payloads |
retryable | Boolean classification only, does not trigger retries by itself |
message | Optional; 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: trueorOTEL_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.