GuardRail Atlas Forms
Guard rail configuration is authored in Atlas Forms, not in code. Each built-in guard has a dedicated Atlas Form (IDs 13000–13007). When a guard is added to a node type in the Flow Studio designer, the Atlas Form for that guard is embedded in the node's configuration panel, and its saved values are passed to SetConfiguration() at runtime.
How Atlas Forms Connect to Guards
The flow from designer to runtime has four steps:
- A designer opens a node's configuration panel and navigates to the Guard Rails tab.
- They select a guard (e.g. Rate Limiting) from the available guard list. The corresponding Atlas Form (e.g. Form 13000) is rendered inline.
- The designer fills in the guard's fields and saves. The values are stored as a JSON dictionary alongside the node's ProcessElement record.
- At execution time,
BaseNodeExecutorloads the stored configuration dictionary and callsguard.SetConfiguration(config)before the pipeline runs.
IGuardRailDescriptor.ConfigurationSchema property (a JSON Schema string). When you implement a custom guard and register it with DI, the Atlas Form system reads its ConfigurationSchema and automatically generates the form fields — no manual Atlas Form creation is needed for custom guards.
Atlas Form Registry (IDs 13000–13007)
| Form ID | Guard | Phase | Key Fields |
|---|---|---|---|
13000 |
RateLimitingGuard | Pre | rps, window, scope |
13001 |
InputValidationGuard | Pre | requiredFields, strictMode |
13002 |
TimeoutGuard | Pre + Post | maxExecutionMs |
13003 |
PiiDetectionGuard | Post | mode, piiPatterns, redactionValue |
13004 |
CircuitBreakerGuard | Pre | failureThreshold, cooldownSeconds, scope |
13005–13007 |
Reserved | — | Reserved for future built-in guards |
Form 13000 — RateLimitingGuard
Configures the rate limit applied in the Pre phase. All three fields are required.
// Stored configuration (from Atlas Form 13000)
{
"rps": 10, // requests per second — minimum 0.1, maximum 10000
"window": 60, // measurement window in seconds — minimum 1, maximum 3600
"scope": "tenant" // "global" | "tenant" | "user"
}
Validation rules enforced by RateLimitingGuardConfigValidator:
rpsmust be a number; minimum 0.1; values above 10000 produce a warningwindowmust be an integer; minimum 1; values above 3600 produce a warningscopemust be exactly"global","tenant", or"user"
Form 13001 — InputValidationGuard
Declares which input fields must be present before the executor runs.
// Stored configuration (from Atlas Form 13001)
{
"requiredFields": ["orderId", "customerId", "amount"],
"strictMode": false // true: any field not in requiredFields causes Blocked
}
Form 13002 — TimeoutGuard
Sets the maximum wall-clock execution time for this node. The Pre phase records a start timestamp; the Post phase measures elapsed time and blocks if exceeded.
// Stored configuration (from Atlas Form 13002)
{
"maxExecutionMs": 30000 // 30 000 ms = 30 seconds
}
Form 13003 — PiiDetectionGuard
Configures post-execution scanning of the executor's output data for PII patterns.
// Stored configuration (from Atlas Form 13003)
{
"mode": "redact", // "warn" | "block" | "redact"
"piiPatterns": ["email", "ccn", "ssn", "phone"],
"redactionValue": "[REDACTED]" // replacement text used in "redact" mode
}
Mode behaviours:
warn— PII found; returnsWarning(execution continues, warning logged)block— PII found; returnsBlocked(output is suppressed)redact— PII found; replaces values in-place and returnsSuccess(outputModified: true)
Form 13004 — CircuitBreakerGuard
Opens the circuit and blocks execution after repeated consecutive failures for the same node type and scope.
// Stored configuration (from Atlas Form 13004)
{
"failureThreshold": 5, // consecutive failures before circuit opens
"cooldownSeconds": 60, // how long the circuit stays open
"scope": "tenant" // "global" | "tenant"
}
Configuration Validation at Design Time
When a designer saves guard configuration in the Atlas Form, the system calls IGuardRail.ValidateWithDetails(config) before persisting. If validation fails, the form displays the returned errors and refuses to save.
// ValidateWithDetails is called on form save (design time)
public GuardRailConfigValidationResult ValidateWithDetails(IDictionary<string, object?> configuration)
{
var errors = new List<string>();
if (!configuration.ContainsKey("rps"))
errors.Add("rps is required");
if (!configuration.ContainsKey("window"))
errors.Add("window is required");
if (!configuration.ContainsKey("scope"))
errors.Add("scope is required");
else if (configuration["scope"]?.ToString() is not ("global" or "tenant" or "user"))
errors.Add("scope must be 'global', 'tenant', or 'user'");
return errors.Count > 0
? GuardRailConfigValidationResult.Invalid(errors)
: GuardRailConfigValidationResult.Valid();
}
Runtime Configuration Loading
SetConfiguration() is called once before the pipeline runs, not on every execution. Store parsed values in instance fields. Do not re-read the configuration dictionary inside ExecuteAsync.
// Called once per guard instance lifetime, before any ExecuteAsync call
public void SetConfiguration(IDictionary<string, object?> configuration)
{
if (configuration.TryGetValue("rps", out var rpsObj)
&& double.TryParse(rpsObj?.ToString(), out var rps))
{
_rps = rps;
}
if (configuration.TryGetValue("window", out var windowObj)
&& int.TryParse(windowObj?.ToString(), out var window))
{
_windowSeconds = window;
}
_scope = configuration.TryGetValue("scope", out var scopeObj)
? scopeObj?.ToString() ?? "tenant"
: "tenant";
}
Custom Guard — No Atlas Form Required
Custom guards registered via DI do not need a manually authored Atlas Form. The Atlas Form system reads the guard's ConfigurationSchema JSON Schema property and auto-generates form fields. The schema should describe every configuration key your SetConfiguration method reads:
// ConfigurationSchema drives auto-generated Atlas Form fields
public string ConfigurationSchema => @"{
""type"": ""object"",
""properties"": {
""allowedDomains"": {
""type"": ""array"",
""items"": { ""type"": ""string"" },
""description"": ""Approved email domains, e.g. ['example.com']"",
""minItems"": 1
}
},
""required"": [""allowedDomains""]
}";
ConfigurationSchema is what the Atlas Form system uses to generate fields. If you add a new configuration key to SetConfiguration but do not add it to ConfigurationSchema, designers will have no way to set that key in the UI. Keep the schema and the implementation in sync.
Guard Configuration Lifecycle Summary
| Stage | Who | What Happens |
|---|---|---|
| Design time | Atlas Form system | Reads ConfigurationSchema; renders form fields in the Guard Rails tab |
| Design time (save) | Atlas Form system | Calls ValidateWithDetails(config); stores JSON on success, shows errors on failure |
| Startup / first execution | BaseNodeExecutor |
Loads stored JSON; calls SetConfiguration(config) on each guard instance |
| Pre phase | Guard infrastructure | Calls ExecuteAsync(context, GuardRailPhase.Pre, ct) on all guards with Pre in SupportedPhases |
| Post phase | Guard infrastructure | Calls ExecuteAsync(context, GuardRailPhase.Post, ct) on all guards with Post in SupportedPhases |