Blocked before the call,
for $0.00.
Define a deterministic check and attach it to a stage — a denied keyword, a regex, a URL allowlist, a length bound, a JSON-schema. Block, redact, or flag before the model or a tool ever runs. Microseconds, offline, no model call — and every decision lands in the audit chain.
from cendor.core import instrument from cendor.guardrails import install, rules client = instrument(OpenAI()) install([ rules.keyword_deny(["ignore previous instructions"], action="block"), rules.regex_rule(r"\bsk-[A-Za-z0-9]{20,}\b", action="redact", stage="input"), rules.url_allowlist(["docs.cendor.ai"], stage="input"), ]) client.chat.completions.create(model="gpt-4o", messages=msgs) # blocked prompt → GuardrailTripped BEFORE the request is sent ($0.00 spent) # leaked key → the provider receives "[redacted]" instead of the secret
Gate the loop where it matters — not just the answer.
A guardrail is attached to one or more intervention points, matching Azure Foundry's intervention points and OpenAI's four decorator types. Output-only checks can't stop a tool's side effects — so gate the tool_call to stop a dangerous action before it runs.
A check returns a Verdict to trip, or nothing to pass. block fails closed · redact replaces the payload and continues · flag records and continues. Evaluation is blocking (v0): the built-ins are µs-scale, so parallel-with-the-model buys nothing.
Regex and arithmetic. No model, no network, no ML.
This is the local-first claim: every built-in is deterministic — microseconds, offline, $0. AWS itself prices this tier (regex + word filters) at zero; a local library owns it.
trip when any denied word appears (substring, case-insensitive by default)
trip when a pattern matches; action="redact" substitutes each match
a URL's host is not allowlisted / is denied (subdomains match)
the payload exceeds a char and/or exact token bound (counts via core.tokens)
the output isn't valid JSON, or violates a minimal type/required/properties/items schema
your own check(payload, ctx) — sync or async
Deliberately not built in: PII/secret detection lives in acttrace's validator-gated catalogue (guard(Policy…)) — one detection engine, not two. llm_judge(judge) is a bring-your-own-model adapter contract, not a bundled classifier: you supply the call, and its latency/cost is stated honestly.
Pure, at the seam, or in the loop.
"We blocked it" — in the hash chain, not a log line.
Every trip or flag emits a GuardrailDecision on core's bus. If an AuditLog is attached, it chains that decision as a tamper-evident guardrail_decision entry — the guardrail's name, stage, action, and reason (never the raw payload). No import between the two libraries: acttrace duck-types the decision.
No competitor pairs a provider-agnostic, local-first gate with audit-grade evidence in Python and TypeScript. That's the lane.
Where the edges are — by design.
The built-ins match exactly what you configure. A jailbreak they were never told about will pass. Pair them with a llm_judge adapter for open-ended risk — the deterministic rules are the free floor, not a ceiling.
Where the built-ins are microseconds and $0, an extra model call is typically seconds and billed. llm_judge is a contract precisely so that cost is yours to measure — not assumed.
Via install(), output guardrails inspect the completed call and raise after it ran. Streamed deltas already shown can't be unshown.
Use acttrace's guard(Policy…) for validator-gated detectors — one detection engine, kept in one place.
Gated in one line.
guardrails docs → · guardrails cookbook → · works alone, composes with the Cendor stack