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:
| Guardrail | Constraint | Tool policy | Validation retry | |
|---|---|---|---|---|
| Runs on | Semantic model input/output, memory, and validation feedback | One output boundary | Raw tool call, result, or approval lifecycle | Structured output parse/validation |
| What it catches | Forbidden content, PII, secrets, injection, formatting | Semantic / business-rule violations | Unsafe tool execution or unsafe tool I/O | Schema-parse failures, wrong types |
| What happens on a hit | Block, rewrite, warn, or report | Re-call the model with custom feedback | Block, request approval, report, or rewrite tool I/O | Text-repair first, then re-call with the Zod error |
| Knows about retries | No: it is a policy pass | Yes: retry is the purpose | No, except approval suspension | Yes: bounded by maxRetries and maxSteps |
| You write | guardrail({ id, on, run }) | constraint({ id, on, run }) | toolPolicy({ id, match, action }) or toolPolicy.args/result | validationRetry: { 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
Guardrails
Filter, transform, redact, or warn on inputs and outputs.
Constraints
Semantic output validation with model retry on business-rule violations.
Validation retry
Auto-recover from schema-parse failures and wrong types.
Tool middleware
Execution plumbing and where toolPolicy fits.
Security
Injection-resistant prompt patterns.
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.