Crux
GuidesSafety

Safety

Boundary-targeted guardrails, constraints, tool policies, validation retry, and prompt-injection-resistant patterns.

Four incidents, four different fixes:

  • The model echoed a customer's email address into your logs. That is a guardrail: filter, transform, redact, warn, or block on typed boundaries.
  • The output parsed cleanly but cited no sources. That is a constraint: validate output semantics and retry the model with feedback on business-rule violations.
  • A tool call tried to delete a production record. That is a tool policy: block, request approval, or screen tool calls/results through the Safety decision model.
  • The model returned broken JSON. That is validation retry: Crux text-repairs first, then re-prompts with the Zod error.

Separate concern: Security, patterns to make your prompts injection-resistant.

Boundary targets

Guardrails and constraints bind to boundary.* descriptors instead of a broad "input/output" phase:

import { boundary, constraint, guardrail } from "@use-crux/core/safety";

export const redactPii = guardrail({
  id: "pii",
  on: [boundary.input.text(), boundary.output.text()],
  run: guardrail.pii({ strategy: "mask" }),
});

export const grounded = constraint({
  id: "grounded-answer",
  on: boundary.output.object<{ answer: string; citations: string[] }>(),
  run: constraint.judge({ judge, minScore: 0.8 }),
});

Useful targets include boundary.input.text(), boundary.input.media(), boundary.input.instructions(), boundary.output.text(), boundary.output.media(), boundary.output.object<T>(), boundary.memory.write<T>(), and boundary.validation.feedback(). The optionless input helpers cover all supported sources. Advanced policies can filter with boundary.input.text({ from: "retrieval" }) or boundary.input.media({ from: "tool" }). Use toolPolicy.args() and toolPolicy.result() for raw tool lifecycle decisions.

See Safety for content primitives for the exact language, structured, stream, image, speech, and transcription coverage matrix.

Report mode and streaming

Use safety.tune at a call site to shadow a policy, disable it, or override stream behavior:

await adapter.generate(prompt, {
  input,
  safety: {
    tune: {
      pii: { mode: "report" },
      "grounded-answer": { enabled: true },
    },
  },
});

Built-in text guardrails such as guardrail.pii(), guardrail.secrets(), and guardrail.injection() default to sentence-level streaming checks. Classifier guardrails and object/path guardrails run at final output unless you tune them.

The boundary, plainly

A recap of the four primitives side by side:

GuardrailConstraintTool policyValidation retry
Runs onSemantic model input/output, memory, and validation feedbackOne output boundaryRaw tool call, result, or approval lifecycleStructured output parse/validation
What it catchesForbidden content, PII, secrets, injection, formattingSemantic / business-rule violationsUnsafe tool execution or unsafe tool I/OSchema-parse failures, wrong types
What happens on a hitBlock, rewrite, warn, or reportRe-call the model with custom feedbackBlock, request approval, report, or rewrite tool I/OText-repair first, then re-call with the Zod error
Knows about retriesNo: it is a policy passYes: retry is the purposeNo, except approval suspensionYes: bounded by maxRetries and maxSteps
You writeguardrail({ id, on, run })constraint({ id, on, run })toolPolicy({ id, match, action }) or toolPolicy.args/resultvalidationRetry: { maxRetries }

Rules of thumb:

  • Schema parse failed? That's validation retry: built-in, just enable it.
  • Output parsed but business rule failed? That's a constraint: write a check.
  • Need to filter, redact, or transform content? That's a guardrail.
  • Need to gate tool execution? That's a tool policy. Use tool middleware for execution plumbing.

When should I use a guardrail?

  • You need to redact PII before logging, before sending to a tool, or before showing output
  • You want to block certain inputs entirely (profanity, prompt-injection markers)
  • You want to transform outputs (strip leading code-fence markers, normalize whitespace, decode entities)
  • You want to warn without blocking (attach metadata for downstream code)

When should I use a constraint?

  • The model occasionally returns output that fails a business rule (off-topic, wrong language, missing field)
  • You're willing to spend an extra round-trip to get correct output
  • Your output schema validates most outputs but a small percentage need a re-prompt with the validation error

When should I use neither?

  • The output is structurally invalid (won't parse): Zod schema validation already handles that
  • The check needs to read external state (database, RAG): use a tool call, not a guardrail
  • The check is about who is asking, not what: that's auth, not safety

Inspect and test safety

Safety decisions emit observability artifacts, Turn Decision Report rows, and Project Index definitions. Devtools opens runtime interventions in Runs Explain with model, action, semantic target, source (User, Tool, or Retrieval), privacy-safe origin coordinates, and strip escalation. Catalog shows authored canonical boundary tuples, strategy helper configuration and action, links from policies to completed operations, operation-side Safety attachments, and duplicate policy-id lints. Source filters affect runtime matching but are not stored as Project Index facts.

In Evals, use safety signal assertions for guardrail/constraint audits or the decision report matcher for the canonical safety read model:

ctx.expect.safety.toHaveBlocked("pii");
ctx.expect.decisionReport.safety.toHaveOutcome("pii", "rewrite", {
  reasonCode: "guardrail.redacted",
});

Pick a topic

How this fits with the rest of Crux

  • Eval validates output quality during testing. Guardrails and constraints validate it during execution. constraint.judge(...) bridges a scoring judge into runtime Safety enforcement; Eval safety and decision-report matchers regression-test the resulting production behavior.
  • Tools can implement domain-specific safety checks that need external data.
  • Devtools trace every guardrail hit and constraint retry.

On this page