Portal Community

Top-Level Structure

The root key is "guardRails" within a node's configuration JSON:

{
  "guardRails": {
    "groups": [
      "default-guard-rail-group",
      "standard-security-group"
    ],
    "mandatoryGroups": [
      "compliance-audit-group"
    ],
    "individual": [
      { "name": "TimeoutGuard", ... },
      { "name": "RateLimitingGuard", ... }
    ]
  }
}
KeyTypeDescription
groupsstring[]Named groups of guards. Each group is a pre-registered list of guard configs resolved by GuardConfigResolver.
mandatoryGroupsstring[]Groups that always run and cannot be disabled by node-level overrides.
individualobject[]Specific guard instances with per-node config. Merged with group configs.

Individual Guard Entry Fields

FieldTypeRequiredDescription
namestringyesGuard name — must match IGuardRailExecutor.Name exactly (case-sensitive)
enabledboolyesfalse skips this guard entirely
orderint?noExecution order within the phase. null = default sequential order
enableDelayedEnforcementboolnoGuard runs but does not immediately block — violations are checked after execution completes
failModestringno"block" (default) or "allow" — override the guard's blocking behavior at node level
configobjectnoGuard-specific settings. Schema depends on the guard (see below).

Complete Example: Full Node Config

{
  "guardRails": {
    "groups": ["default-guard-rail-group"],
    "mandatoryGroups": ["compliance-audit-group"],
    "individual": [

      // 1. Timeout: block if node takes more than 5 seconds
      {
        "name": "TimeoutGuard",
        "enabled": true,
        "order": 1,
        "config": { "timeoutMs": 5000, "action": "block" }
      },

      // 2. Input validation: require userId and amount fields
      {
        "name": "InputValidationGuard",
        "enabled": true,
        "order": 2,
        "config": {
          "schema": {
            "type": "object",
            "required": ["userId", "amount"],
            "properties": {
              "userId": { "type": "string" },
              "amount": { "type": "number" }
            }
          },
          "strictMode": true
        }
      },

      // 3. Rate limiting: 50 requests/second per tenant
      {
        "name": "RateLimitingGuard",
        "enabled": true,
        "order": 3,
        "config": { "rps": 50, "window": 60, "scope": "tenant" }
      },

      // 4. Circuit breaker: open after 5 failures, recover after 60s
      {
        "name": "CircuitBreakerGuard",
        "enabled": true,
        "order": 4,
        "config": { "threshold": 5, "timeout": 60000 }
      },

      // 5. PII detection (Pre): block if SSN/Email/Phone in input
      {
        "name": "PiiDetectionGuard",
        "enabled": true,
        "order": 5,
        "config": { "sensitivityLevel": "medium" }
      },

      // 6. PII redaction (Post): mask PII in output
      {
        "name": "PiiRedactionGuard",
        "enabled": true,
        "config": { "redaction_method": "mask" }
      }
    ]
  }
}

Config Resolution Chain

When the runtime needs a guard config for a given node, GuardConfigResolver resolves it in this order:

1

Check cache

Cache key: guard-config:{tenantId}:{nodeKey}. 15-minute TTL. If hit, return immediately.

2

Check templates

In-memory dictionary of registered templates (case-insensitive). If found, validates TimeoutMs via IGuardTimeoutValidator, caches result, returns.

3

Not found

Logs a warning and returns null. The guard is skipped for this execution.

Group Resolution

Groups are registered at startup:

await resolver.RegisterGroupAsync("default-guard-rail-group", new[] {
    "timeout-config",
    "rate-limit-tenant-config",
    "circuit-breaker-config"
});

// When a node references "default-guard-rail-group", it expands to all three configs

Mandatory groups run even if the individual guard entry has "enabled": false. They cannot be bypassed at the node level.

Timeout Validation (Design A.10)

Guard timeout must not exceed node timeout When a guard config contains TimeoutMs, GuardConfigResolver calls IGuardTimeoutValidator.ValidateBounds(guardId, timeoutMs) at resolution time. If the guard timeout exceeds the node's configured timeout, an InvalidOperationException is thrown during startup — not at runtime. This catches misconfiguration before any request is processed.

Configuration Cache

PropertyValue
TTL15 minutes
Key formatguard-config:{tenantId}:{nodeKey}
InvalidationTTL expiry only — no explicit invalidation
ImplementationGuardConfigCache — in-memory Singleton
Multi-tenancyTenantId in key ensures per-tenant isolation

SetConfiguration Pattern

Every guard receives its resolved config before execution via:

// Called by GuardConfigResolver / GuardFactory at resolution time
public void SetConfiguration(IDictionary<string, object?> configuration)
{
    _configuration = configuration ?? new Dictionary<string, object?>();
}

// Guards read settings during ExecuteAsync (never during construction)
private int TimeoutMs => _configuration.TryGetValue("timeoutMs", out var v) ? (int)v : 5000;