Portal Community

Where Policies Sit in the Pipeline

Node Execution Request
    │
    ▼
NodePolicyEvaluator  ◄─── BEFORE DataStateMachineFactory is called
    │
    ├── Is this node allowed to use handler "blockchain-state"?
    ├── Is this node allowed to write at "App" scope?
    ├── Does the requesting tenant have access to this handler?
    │
    ├── [All checks pass] → continue to DataStateMachineFactory
    │
    └── [Any check fails] → execution blocked, audit log entry created

What NodePolicies Control

Policy Dimension What It Controls Example
Handler Access Which HandlerKey values the node may use "built-in" only; or "blockchain-state" allowed
Scope Ceiling Maximum scope level the node may declare Cannot exceed "Element" scope
Tenant Isolation Which tenant IDs the node can act on behalf of Node can only write for its own tenant
Capability Permissions Which NodeCapabilities the node can expose Can expose "entity" but not "datasource"
Audit Requirements Whether all state writes must be audit-logged Mandatory for financial nodes

Policy Configuration

Policies are declared in the NodeCapabilityPolicy entity (managed by the ProcessSecurity module) and evaluated by the NodePolicyEvaluator before the DataStateMachineFactory creates the handler instance.

{
  "nodeType": "InvoiceDispatchExecutor",
  "dataStateMachine": {
    "allowedHandlers": ["built-in"],
    "maxScope":        "Element",
    "requireAuditLog": true,
    "tenantIsolation": "strict"
  },
  "capabilities": {
    "allowed": ["entity", "businessService"],
    "denied":  ["datasource", "rules"]
  }
}

Privilege Keys

Individual operations can require privilege keys. A node must be granted the key before it can perform the operation. This allows fine-grained control within a single handler.

// NodePolicyEvaluator — privilege key enforcement
if (!await _policyService.HasPrivilegeAsync(
    nodeType:     "InvoiceDispatchExecutor",
    privilegeKey: "state:write:blockchain",
    tenantId:     ctx.TenantID,
    ct))
{
    _auditLog.Write(AuditLevel.Warning,
        $"Node {nodeType} attempted blocked privilege: state:write:blockchain");
    throw new NodePolicyViolationException("Privilege 'state:write:blockchain' not granted");
}

Audit Trail

Every state write by a node that has requireAuditLog: true generates a SecurityAuditLog entry. The entry records the node type, tenant, handler used, scope, item keys, status transition, and the calling user/service identity.

Field Description
NodeType The executor class name
HandlerKey Which handler was invoked
Scope The scope level used
ItemKey / ItemEntityKey What was processed
StatusFrom / StatusTo The state transition
TenantID Tenant context
ActorID User or service identity
Timestamp When the transition occurred
PolicyDecision Allowed / Denied

Tenant Isolation

Strict tenant isolation means a node can only read and write state records for the tenant it is executing under. The DataStateMachineFactory always binds TenantID from the workflow thread context. NodePolicies add a second enforcement layer: if the node config specifies a different tenantId, strict isolation blocks it before the factory is called.

Never configure a node with tenantIsolation: 'relaxed' in production without explicit approval from the security team. Relaxed mode allows cross-tenant state reads, which can expose data across organisational boundaries.

Use requireAuditLog: true for all nodes that process financial transactions, compliance records, healthcare data, or personal information. The audit trail is your evidence in regulatory inquiries.