Rail Engine Support

View as Markdown

The NVIDIA NeMo Guardrails library ships 33 built-in rails under nemoguardrails/library. Each rail declares its behavior in a rail manifest, and each manifest exposes one or more surfaces. A surface is the flow name you list under rails.input.flows, rails.output.flows, or rails.retrieval.flows in config.yml.

The 33 manifests declare 78 surfaces in total. LLMRails runs all 78 through its Colang runtime, in both Colang 1.0 and Colang 2.x. IORails runs 59 of them by compiling the manifest directly, without Colang.

This page lists every surface and the engine that can run it. For capability areas that are not tied to a specific rail, such as streaming, observability, and the generation API, refer to Engine Feature Support.

How to Read This Page

Legend: ✓ supported · ✗ not supported.

The Flow column is the exact string you put in config.yml. The Notes column records either the conversation value the rail rewrites or the reason IORails cannot run it.

A ✓ in the IORails column means the surface is servable: nothing in its manifest contract puts it out of reach of the engine. Whether a particular configuration actually runs it also depends on that configuration, because IORails validates models and optional dependencies at startup. Refer to Configuration-Dependent Refusals.

Support Summary

The following table summarizes surface support by direction:

DirectionSurfacesLLMRailsIORails
Input323231
Output353528
Retrieval11110
Total787859

IORails does not accept a rails.retrieval section at all, so every retrieval surface is an LLMRails capability. A configuration that declares one routes to LLMRails by default. Refer to What Happens to an Unsupported Configuration.

Support Matrix

Input Surfaces

The following table lists the input surfaces:

FlowRailLLMRailsIORailsNotes
activefence moderation on inputActiveFence
activefence moderation on input detailedActiveFence
autoalign check inputAutoAlignRewrites user_message
ai defense inspect promptCisco AI Defense
clavata check inputClavata
content safety check inputContent Safety
context bloat detection on inputContext Bloat DetectionRewrites user_message
crowdstrike aidr guard inputCrowdStrike AIDRRewrites user_message
f5 guardrails scan inputF5 AI Guardrails
fiddler user safetyFiddler Guardrails
gcpnlp moderationGCP Text Moderation
gcpnlp moderation detailedGCP Text Moderation
gliner detect pii on inputGLiNER
gliner mask pii on inputGLiNERRewrites user_message
guardrailsai check inputGuardrails AI
hf classifier check inputHugging Face Classifier
jailbreak detection heuristicsJailbreak DetectionBackend ambiguity, issue #2285
jailbreak detection modelJailbreak Detection
llama guard check inputLlama Guard
pangea ai guard inputPangea AI GuardRewrites user_message. Deprecated rail
policyai moderation on inputPolicyAI
polygraf detect pii on inputPolygraf PII Detection
polygraf mask pii on inputPolygraf PII DetectionRewrites user_message
detect pii on inputPrivate AI
mask pii on inputPrivate AIRewrites user_message
protect promptPrompt SecurityRewrites user_message
regex check inputRegex Detection
self check inputSelf-Check Input
detect sensitive data on inputSensitive Data Detection
mask sensitive data on inputSensitive Data DetectionRewrites user_message
topic safety check inputTopic Safety
trend ai guard inputTrend Micro Vision One AI Guard

Output Surfaces

The following table lists the output surfaces:

FlowRailLLMRailsIORailsNotes
activefence moderation on outputActiveFence
alignscore check factsAlignScore Fact CheckingReads relevant_chunks
autoalign check outputAutoAlignRewrites bot_message
autoalign factcheck outputAutoAlign
autoalign groundedness outputAutoAlignReads relevant_chunks_sep
ai defense inspect responseCisco AI Defense
clavata check outputClavata
cleanlab trustworthinessCleanlab
content safety check outputContent Safety
crowdstrike aidr guard outputCrowdStrike AIDRRewrites bot_message
f5 guardrails scan outputF5 AI Guardrails
fiddler bot faithfulnessFiddler GuardrailsReads relevant_chunks
fiddler bot safetyFiddler Guardrails
gliner detect pii on outputGLiNER
gliner mask pii on outputGLiNERRewrites bot_message
guardrailsai check outputGuardrails AI
self check hallucinationHallucination DetectionReads _last_bot_prompt
hf classifier check outputHugging Face Classifier
injection detectionInjection DetectionRewrites bot_message
llama guard check outputLlama Guard
pangea ai guard outputPangea AI GuardRewrites bot_message. Deprecated rail
patronus api check outputPatronus AIReads relevant_chunks
patronus lynx check output hallucinationPatronus AIReads relevant_chunks
policyai moderation on outputPolicyAI
polygraf detect pii on outputPolygraf PII Detection
polygraf mask pii on outputPolygraf PII DetectionRewrites bot_message
detect pii on outputPrivate AI
mask pii on outputPrivate AIRewrites bot_message
protect responsePrompt SecurityRewrites bot_message
regex check outputRegex Detection
self check factsSelf-Check FactsReads relevant_chunks
self check outputSelf-Check Output
detect sensitive data on outputSensitive Data Detection
mask sensitive data on outputSensitive Data DetectionRewrites bot_message
trend ai guard outputTrend Micro Vision One AI Guard

Retrieval Surfaces

Retrieval surfaces run against the knowledge-base chunks retrieved for a turn. IORails has no retrieval pipeline, so all of them are LLMRails only:

FlowRailLLMRailsIORailsNotes
context bloat detection on retrievalContext Bloat DetectionRewrites relevant_chunks
gliner detect pii on retrievalGLiNERReads relevant_chunks
gliner mask pii on retrievalGLiNERRewrites relevant_chunks
hf classifier check retrievalHugging Face ClassifierRewrites relevant_chunks
polygraf detect pii on retrievalPolygraf PII DetectionReads relevant_chunks
polygraf mask pii on retrievalPolygraf PII DetectionRewrites relevant_chunks
detect pii on retrievalPrivate AIReads relevant_chunks
mask pii on retrievalPrivate AIRewrites relevant_chunks
regex check retrievalRegex DetectionRewrites relevant_chunks
detect sensitive data on retrievalSensitive Data DetectionReads relevant_chunks
mask sensitive data on retrievalSensitive Data DetectionRewrites relevant_chunks

Tool Surfaces

Tool rails are not manifest surfaces. IORails registers them separately as local structural and schema validators that reach no model:

FlowSectionLLMRailsIORailsNotes
tool call validationrails.tool_output.flowsValidates model-emitted tool calls
tool result validationrails.tool_input.flowsValidates application-returned tool results

For configuration and behavior, refer to Tool Calling.

Why IORails Refuses a Surface

IORails derives its scope from the manifest rather than from a hardcoded list of rail names. A surface is unsupported when the engine cannot satisfy its declared contract. Adding a new rail to the catalog therefore needs no change in the engine.

Two of the per-surface reasons are structural and one is temporary:

ReasonSurfacesExplanation
Rewrites a value the direction cannot apply7IORails can apply a rewrite to user_message on input and bot_message on output. A retrieval surface that rewrites relevant_chunks has nowhere to put the result.
Reads a conversation value IORails does not supply11IORails supplies only user_message and bot_message to a manifest Binding.context entry. A surface that binds relevant_chunks, relevant_chunks_sep, or _last_bot_prompt needs Colang runtime state that the engine does not build.
Explicit blocklist1jailbreak detection heuristics shares a manifest with jailbreak detection model, so the engine cannot tell whether the configuration needs torch and transformers installed. Tracked in issue #2285.

The checks run in the order the table lists them and report the first reason that applies. As a result, a retrieval surface that both rewrites and reads relevant_chunks is reported under the rewrite reason.

A third structural constraint applies to the configuration as a whole rather than to any one surface. IORails accepts only the input, output, config, tool_input, and tool_output rail sections, so a configuration that declares rails.retrieval, rails.dialog, or any other section routes to LLMRails by default, regardless of which flows it names.

The engine checks that constraint first, so it does not report a per-surface reason for a retrieval flow. IORails.unsupported_reason() reports the unsupported rail section and stops, so a configuration with a rails.retrieval section reports config has rails outside the IORails-supported set: ['retrieval'] rather than the rewrite or read reason its surfaces would give. The reasons in the table above describe what each surface’s manifest declares, which is what the retrieval rows of the matrix record.

What Happens to an Unsupported Configuration

The Guardrails facade decides what an unsupported rail means for your application:

ConstructorUnsupported-Configuration Behavior
Guardrails(config)Routes the configuration to LLMRails and logs the reason. This is the default
Guardrails(config, require_iorails=True)Raises ValueError naming the reason, instead of falling back
Guardrails(config, use_iorails=False)Uses LLMRails without consulting the rail catalog at all

Every “routes to LLMRails” statement on this page and in the guardrail catalog describes the default. Use require_iorails=True when you depend on an IORails-only capability such as OpenTelemetry metrics and would rather fail at startup than silently lose it.

Configuration-Dependent Refusals

Beyond the fixed matrix above, IORails validates two things per configuration when it compiles a rail at startup. Where LLMRails does not, it reports the same problem at request time instead:

CheckIORailsLLMRails
Optional dependency installedRefuses to compile the rail, naming the missing distribution and the extra to installImports lazily inside the action, so the failure surfaces on the first request that runs the rail
Model type named by $model= is declared in modelsRejected before either engine is constructedRejected before either engine is constructed
Model type fixed in the manifest is declared in modelsRefuses to compile the rail, naming the missing model type and the types the configuration does declareFails when the flow executes and the action asks for the model

A flow that carries a $model= parameter is validated by RailsConfig rather than by an engine, so loading the configuration raises InvalidRailsConfigurationError on both engines. The other two refusals come from the IORails compiler and raise RailCompilationError. Through the Guardrails facade those two are fallback conditions: by default the configuration routes to LLMRails and the reason is logged.

The rails that declare an optional dependency are the following:

RailDistributionsExtra
Cleanlabcleanlab-studio
GCP Text Moderationgoogle-cloud-language
Guardrails AIguardrails-ai
Hugging Face Classifiertransformers
Injection Detectionyara-python
Jailbreak Detectiontorch, transformers
Sensitive Data Detectionpresidio-analyzer, presidio-anonymizer, spacysdd

Hugging Face Classifier and Jailbreak Detection each offer an in-process backend and a remote endpoint. IORails enforces the dependency only when the configuration selects the in-process backend.

The surfaces that bind a model type are the following:

FlowModel TypeSourceValidated by
content safety check inputSelected by $model=Surface parameterRailsConfig, on both engines
content safety check outputSelected by $model=Surface parameterRailsConfig, on both engines
topic safety check inputSelected by $model=Surface parameterRailsConfig, on both engines
llama guard check inputllama_guardFixed in the manifestThe IORails compiler only
llama guard check outputllama_guardFixed in the manifestThe IORails compiler only
patronus lynx check output hallucinationpatronus_lynxFixed in the manifestThe IORails compiler only

RailsConfig reads the $model= parameter off the flow name, so it catches only the first three. A manifest-fixed model type is invisible to that check, which is why the last three are an IORails-only refusal.

IORails never reaches the model check for patronus lynx check output hallucination, because the surface check refuses it first.

A manifest can also declare a model under requirements.models, as Self-Check and Hallucination Detection do for llm. That declaration is descriptive metadata for the catalog. Neither engine enforces it at startup, so a missing model surfaces when the rail runs.

Rail Behavior Differences

The manifest and the action are shared, so both engines reach the same allow, block, or transform decision. The following table shows how each engine acts on that decision:

BehaviorLLMRailsIORails
Block presentationThe Colang flow decides: emit a refusal intent, or raise a configured exception when enable_rails_exceptions is setReturns a fixed refusal message for a non-streaming request, or a guardrails_violation payload for a streaming request
Rail action raises an exceptionThe Colang runtime converts ordinary action failures to an internal-error response. An LLMCallException propagates insteadThe engine’s fail-closed envelope converts it to a block, marks the outcome as failed, and returns an internal-error message. An error carrying an upstream HTTP status propagates instead
Transform (mask or rewrite) railsApplied through the Colang contextApplied by rerunning the remaining rails against the rewritten text. Rewriting rails are ordered ahead of judging rails so a mask reaches the checks behind it
Transform rails with parallel: trueHonored independentlyNot honored. Configuring any rewriting rail forces both directions sequential and emits a warning, because concurrent rails read the arriving text and a rewrite cannot compose
Transform rails with speculative_generation: trueNot applicable, LLMRails has no speculative generationNot honored. A rewriting input rail disables speculative generation and emits a warning, because the model would read the text before the rewrite lands
Retrieval-direction railsRun against retrieved chunksNot available

One transform rule is shared rather than per-engine. RailsConfig rejects a configuration that combines a rewriting output rail with output-rail streaming unless rails.output.streaming.stream_first is false and context_size is 0, because a rewrite cannot be applied to chunks that have already been sent. That validation runs for both engines.

For the shared decision object that both engines read, refer to Rail Outcomes.

Checking Support Programmatically

Ask the engine whether it can serve a whole configuration:

1from nemoguardrails import RailsConfig
2from nemoguardrails.guardrails.iorails import IORails
3
4config = RailsConfig.from_path("path/to/config")
5
6reason = IORails.unsupported_reason(config)
7if reason is None:
8 print("IORails can serve this configuration")
9else:
10 print(f"Falls back to LLMRails: {reason}")

Enumerate the surfaces the installed version declares:

1from nemoguardrails.manifests import RailDirection, default_rail_catalog
2
3catalog = default_rail_catalog()
4
5for (direction, name), surface in sorted(catalog.surfaces().items()):
6 print(f"{direction.value:10} {name}")
7
8input_surfaces = catalog.surfaces(direction=RailDirection.INPUT)

Use the following references for related concepts and configuration:

  • Engine Feature Support compares the two engines on capabilities that are not rail-specific.
  • Rail Manifests documents the manifest schema that this matrix is derived from.
  • Guardrail Catalog documents each rail’s configuration and behavior.
  • Rail Outcomes documents the allow, block, and transform decision that rail actions return.