Portal Community

How Atlas Forms Connect to Guards

The flow from designer to runtime has four steps:

  1. A designer opens a node's configuration panel and navigates to the Guard Rails tab.
  2. They select a guard (e.g. Rate Limiting) from the available guard list. The corresponding Atlas Form (e.g. Form 13000) is rendered inline.
  3. The designer fills in the guard's fields and saves. The values are stored as a JSON dictionary alongside the node's ProcessElement record.
  4. At execution time, BaseNodeExecutor loads the stored configuration dictionary and calls guard.SetConfiguration(config) before the pipeline runs.
ConfigurationSchema Drives Field Generation The fields rendered in the Atlas Form are generated from the guard's 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:

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:

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 Authoritative The JSON Schema in 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