Data loss prevention (DLP)
Organization-owned PII detection plus hallucination, regex, and JSON validation as workflow guardrails.
The DLP layer is a configurable workflow gate. It runs whenever a workflow wants to validate input or output content against a defined policy — organization-owned PII detection, hallucination scoring, JSON validation, or regex match.
This page documents the Guardrails block and the four validation types it supports.
When to use it
Typical placements:
- Input gate — scan a user message for PII before it reaches an LLM.
- Output gate — scan an LLM response for PII before returning it to the user.
- Hallucination gate — score an answer against a knowledge base before exposing it.
- Format gate — assert that an LLM produced valid JSON / matches a regex before piping it downstream.
The Guardrails block is part of the scrydon:guardrails product — see Vendors → Scrydon.
Four validation types
| Type | Speed | Cost | Best for |
|---|---|---|---|
| PII detection | Depends on organization rules | Depends on enabled detectors | Validate content against the organization DLP policy |
| Hallucination | LLM cost | Per-call | Grounding outputs against a knowledge base |
| JSON validation | ~1ms | None | Strict schema compliance |
| Regex match | ~1ms | None | Custom pattern enforcement |
PII detection
The governed Guardrails PII operation uses the rule set maintained by organization administrators under Settings → Governance → DLP. A workflow cannot choose a separate entity list, detector method, or enforcement action. This keeps capability scanning and workflow validation on one policy.
When a rule matches, the Guardrails validation fails and the workflow follows its failure branch. The result contains rule metadata and character spans for routing and diagnostics, but never the matched raw text:
{
"passed": false,
"evaluated": true,
"validationType": "pii",
"policyMode": "enforce",
"findings": [
{
"ruleId": "email-address",
"entityType": "EMAIL",
"detector": "builtin",
"confidence": "HIGH",
"channel": "tool_call",
"start": 8,
"end": 21
}
]
}The policy must be available and enabled for the tool_call channel. Direct execution outside Scrydon's governed host is refused because it cannot load the trusted organization policy.
Hallucination detection
Validates an LLM output against a knowledge base using RAG plus LLM scoring:
Confidence scale:
| Score | Meaning |
|---|---|
| 0–2 | Full hallucination — contradicts or is unsupported by context |
| 3–4 | Low confidence — significant claims not in context |
| 5–6 | Medium confidence — partially supported |
| 7–8 | High confidence — mostly supported, minor gaps |
| 9–10 | Fully grounded — all claims verified against context |
Example output
{
"passed": false,
"score": 2,
"reasoning": "The claim about Q4 revenue is not mentioned in any knowledge base document",
"error": "Low confidence: score 2/10 is below threshold 3"
}Configuration
The Guardrails block exposes these fields in the workflow editor:
| Field | Type | Options |
|---|---|---|
| Content to validate | Long text input | Free text or wired from upstream block |
| Validation type | Dropdown | Valid JSON, Regex match, PII detection |
| Regex pattern | Text input | Required for Regex match |
PII rule selection, detector configuration, and policy mode are organization settings, not Guardrails block inputs.
The underlying Guardrails integration also exposes the hallucination-check tool with model and knowledge-base parameters. It is not currently listed in the workflow block's validation-type dropdown.
Pairing with the Evaluator block
The Guardrails block is fail-closed — if it fails, the workflow takes the failure branch. The Evaluator block is scoring — it returns a score the workflow can branch on however it wants. Use Guardrails for hard gates, Evaluator for quality checks that drive retries or fallback paths.
Guardrails can run on any text input or output — it's not LLM-specific. Validate a user-uploaded document for PII before ingesting it into a knowledge base, for instance.
How we benchmark the platform DLP engine
Beyond the Guardrails block, Scrydon runs the organization DLP policy on covered platform capability calls (including LLM completions, transcription, and embeddings). Governed tool execution separately applies trusted endpoint classification and blocks no-write-down violations when classification mode is Enforce. Detection-quality claims are easy to make and hard to verify, so we measure the shared engine the same way we measure retrieval quality: against a public, third-party benchmark, with the harness in CI as a release gate — a release that regresses below the floors does not ship.
Measured on a 2,661-prompt English slice of ai4privacy/pii-masking-300k (pinned dataset revision, evaluated at build time — the dataset itself is never redistributed) plus an in-repo clearance-escalation bank:
| Check | Result | Release floor |
|---|---|---|
| PII recall (graded entity types) | 95.7% | ≥ 90% |
| PII precision | 79.8% | ≥ 75% |
| Clearance egress — confidential tier recall | 100% | ≥ 95% |
| Clearance egress — secret tier recall | 100% | ≥ 99% |
Per graded entity type: email 99.6%, IP address 99.5%, phone number 86.1% recall.
What we don't claim. The report carries a mandatory out-of-scope section listing
detection categories the engine does not cover, with measured evidence rather than
silence: free-text person names (NER territory — the regex heuristic measured 14%
recall, so we do not grade it), free-form date-of-birth formats (3% — the engine
claims ISO 8601 only), and format-generic national identifiers (27% — the engine
claims specific jurisdictional formats such as US_SSN or BE_NRN, not arbitrary
digit runs). Each is documented in the signed report artifact so auditors see the
boundary, not a marketing number.
Organization DLP policy and blocked LLM calls
The Guardrails workflow operation and platform LLM calls use the same organization DLP policy (Settings → Governance → DLP). When that policy is in Enforce mode and an LLM input scan produces a finding — a PII/secret pattern match or a prompt-injection classification — the call is blocked before anything reaches the model vendor:
HTTP 422 Unprocessable Entity
{
"error": "llm_blocked_by_dlp",
"detail": "Your input contained content that could not be processed.",
"dlpReason": "input_blocked"
}detailis the admin-configured Blocked input message from the DLP policy editor.dlpReasondistinguishes an input block (input_blocked) from a fail-closed classifier or policy-load denial (classifier_unavailable,policy_not_loaded).- Workflow agent steps fail with
Blocked by DLP policy: <message>; SDK and API callers receive the typed policy denial on unary and streaming Base Platform operations. - A 422 means the block is deterministic — retrying the same input re-blocks. Review the matching rule under Settings → Governance → DLP → Hits or adjust the policy mode.
- The Triggered by column on Hits names who a hit is attributed to: the signed-in user; the person who delegated a workflow run, even when it ran as a service account; or the service account itself when it called the platform directly. A hit raised by a platform service acting for no one shows no one.
The semantic prompt-injection classifier is enabled when an organization has not explicitly opted out. Under Advanced Settings, administrators can also configure the canonical pre-action policy: an external destination allow/deny list and maximum data-classification ranks per tool category.
Custom patterns and long inputs
Every rule is scanned over the whole field, however long the prompt, response, file or outbound request is.
- Your own patterns and exceptions run on a linear-time engine. Its running time grows only in proportion to the text, whatever the pattern, so a custom rule can neither slow the platform down nor give up without reporting a match. Write patterns in ordinary JavaScript regular-expression syntax.
\w+,.*and other unbounded repetitions are fine. - Some syntax is not supported, and such a pattern cannot be saved. The rule editor names the reason:
- lookarounds:
(?=…),(?!…),(?<=…),(?<!…). Match the surrounding text instead, or use a rule exception; - backreferences:
\1,\k<name>; - legacy JavaScript escapes, such as
\cwithout a letter, or octal escapes; - a repetition count above 1,000, or a pattern too complex for the engine (large counted repetitions are the usual cause: give a repetition a minimum and no maximum instead of a wide range);
- a repeated group whose content can be empty, such as
(a|)*or(,?\s*)*(make the content required, for example(?:,\s+)*); - with both the
ianduflags: a negated Unicode property (\P{…}) or\B; - a character class that matches nothing, such as
[]or[^\W\w], when it is repeated or optional; - without the
uflag, an emoji written as escapes (\uD83D\uDE00) followed by a repetition or placed inside a character class (add theuflag, or write it outside the class as a group); - with the
uflag, half of an emoji on its own (a lone surrogate), typed or escaped; - with the
iflag, character classes that list too many characters, such as two[\S]or several ranges as wide as[\u0100-\uffff](use\Soutside brackets, narrow the ranges, or dropi); - a pattern longer than 512 characters.
- lookarounds:
- A few patterns would match slightly differently from JavaScript on some characters, so on a field that contains one of them the rule fails closed (see below) and runs exactly as JavaScript on every other field. The editor shows a note: without the
uflag,., negated classes and wide character ranges could match half of an emoji, so a field that contains an emoji, or half of one, fails closed (add theuflag to avoid this); and with case-insensitive matching withoutu, a few characters such as the Kelvin sign (K), the long s (ſ) or ẞ fold differently, so a field that contains one of them fails closed. - Every custom rule has a field limit, shown in the rule editor: larger patterns cost more per character, so a rule runs on fields up to a limit (about 8.9 million characters for a short pattern such as
secret-[0-9]{6}, about 330,000 forpassword\s*[:=]\s*\S{8,200}). A longer field fails closed for that rule. A rule's exceptions count toward the same limit: when the rule's pattern and its exceptions together do not fit a field, the exceptions do not run on it and every match stands. - Custom rules and all exceptions share a time budget per field: 100 ms, or 1 second per MB of text. Exceptions run only when their rule has matched. Built-in detectors always run. Once the budget is used up, each remaining custom rule fails closed for that field, and a rule's exceptions stop excusing its matches, built-in rules included: the matches stand. The scan records which rules were skipped. Compiling a policy's patterns and building its dictionaries is not charged to this budget: it happens at most once per request, and a request may spend at most 1 second on it (a policy at every limit takes about 0.15 s). Past that, the rules not yet compiled fail closed for the rest of the request, so in Enforce mode the request is blocked; this only happens on a server several times slower than usual, or one holding compiled rules for a very large number of requests at once.
- A policy holds at most 100 rules, and a rule at most 16 exceptions. The editor refuses more. A policy saved earlier with more keeps running every rule, but only the first 16 exceptions of a rule apply.
- Dictionaries (a dictionary rule, or a dictionary exception) match their terms as whole words, ignoring case (Greek final ς and σ match each other, and Turkish ı and İ match i and I, so
kırmızımatchesKIRMIZI), and hold at most 1,000 terms and 32,768 characters in total. Every occurrence is found, overlapping ones included. All terms are matched in one pass, so a longer list does not slow a scan down per term. A dictionary counts toward its rule's field limit like a pattern does. The editor refuses a larger one. A dictionary saved earlier that is larger fails closed as a rule, and is ignored as an exception, so its rule matches more, not less. - A policy as a whole may hold at most 262,144 characters of dictionary terms, and its custom patterns together must stay small enough for the engine to keep compiled; the editor names which limit a save exceeded. A policy saved earlier over these limits keeps loading: exceptions past them are ignored (their rules match more, not less) and reported.
- What the audit log records when the budget or a cap stops an exception. The scan's row names the rules whose exceptions did not all run (up to 20 rule ids, plus the total count), and each of those rules gets an Exception skipped row with the reason
budget_exhausted. An exception that is over a cap gets an Exception skipped row with the reasonover_capwhen the policy loads. These rows name the rule and the exception's position, never the pattern or the matched text; repeats within one request are written once, with a count. - Audit rows per request. One request writes one row per distinct fact: a scan row per channel with totals, a violation row per rule, finding type and channel, an action row per rule, action and direction, each with the number of occurrences. So the number of rows grows with the rules that matched, not with the length of the answer: a streamed answer with a finding in every sentence writes a handful of rows, not one per sentence; the positions of individual hits are not kept. If the client cancels a streamed answer part way, the rows for what it had already received are still written.
- Failing closed means the whole field is treated as that rule's match, without the pattern running. In Enforce mode the call is blocked. With a mask, redact or hash action, the whole field is rewritten. With an audit action, the hit is only reported. The hit is recorded without a content snippet.
- Rules saved earlier. A rule saved before these checks that uses unsupported syntax fails closed on every field, and the policy's hits show a warning naming the rule. A rule that may match differently on some characters keeps running, fails closed only on fields that contain them, and shows a warning asking you to review and save it. An exception saved earlier with unsupported syntax is ignored, so its rule matches more, not less. To fix any of them, edit the rule and save.
- Built-in detectors have a maximum match length. For example, an email address matches at most 64 characters before the
@, and a JWT at most about 16,000 characters. An API key or JWT longer than its limit is still redacted whole. If you use the@scrydon/sdk-authoringcatalog to redact text yourself, pass each API key and JWT match throughextendPastBoundto get the same result. - Rules and presets that Scrydon supplies (attack signatures, the policy editor's presets) are measured and bounded, and copies saved earlier have been updated. Padding an attack with extra spaces makes those rules fire, not miss.
- Exceptions excuse exactly the matches they contain in the whole field.
No write-down: a workflow that reads classified data
Separately from DLP, a workflow run carries a classification high-water mark — the highest clearance of anything it has read. When your organization's classification mode is Enforce (Settings → Governance → Classification), a tool call that would send that data to a lower-rated endpoint is refused:
HTTP 422 Unprocessable Entity
{
"code": "POLICY_BLOCKED",
"details": { "policyCode": "EGRESS_BLOCKED_BY_CLEARANCE" }
}A workflow block shows the refusal as CLEARANCE_EGRESS_VIOLATION.
The mark is a property of the run, not of one connection between two blocks: once the run has read CONFIDENTIAL content, anything it sends afterwards is treated as carrying it. It rises when a knowledge-base search or document read returns classified content, it is inherited by a child workflow, and it applies to the tools an Agent block chooses to call.
Three things decide whether you ever see this:
- Content nobody classified does not raise the mark. A knowledge base whose documents carry no clearance label leaves the mark unset and nothing is refused.
- An endpoint nobody rated counts as the level your organization chose. That is the Unrated endpoints count as setting on the Clearance tab: INTERNAL for an organization created after the setting was added, PUBLIC for an older one unless an administrator changed it (see Classification and clearance). It covers unrated accounts, MCP servers, and connections that have no account (API-key, basic-auth, bot-token and no-auth connections, which cannot be rated). Rate the accounts and MCP servers your workflows send to under Settings → Governance → Identity → Endpoints, or a run that read content classified above that level cannot send to them. Rating attaches to the organization account an OAuth-family connection resolves; a connection with no account cannot be rated individually and always counts as the chosen level. HTTP-request and webhook destinations do not follow this setting: they cannot be rated, keep rank 0 (the bottom of every scheme) and are refused any data classified above rank 0.
- MCP servers are rated like extension accounts. The same no-write-down check applies to every MCP tool call, from an Agent block or a standalone MCP block. Rate each MCP server in the MCP servers group of the same Endpoints page. An MCP server nobody rated counts as that same organization-chosen level, and so does one rated under a classification scheme your organization no longer uses: a run that read content classified above that level cannot call its tools. Earlier releases did not check MCP calls, so if your organization runs in Enforce, rate your MCP servers before you upgrade, or switch to Audit until they are rated.
Set the classification mode to Audit to record every would-be refusal as evidence without blocking, while you rate your endpoints.
Related
- Architecture → Cortex — where Cortex can apply guardrails on every LLM call as a workflow-independent layer.
- Knowledge base clearance — clearance is a separate axis from DLP, not a replacement.
- Compliance — how DLP maps to framework controls (GDPR, EU AI Act, ISO 42001).