Node Field Policies
Every configuration field in a node executor can carry a set of policies that
control how the expression engine handles it, how it participates in data flow,
how it appears in HIL forms, and what security rules apply. These policies are
declared via NodeFieldDescriptor objects gathered into a
NodeFieldManifest.
The Policy Architecture
NodeFieldManifest and INodeFieldManifestSource
A NodeFieldManifest is a collection of NodeFieldDescriptor
objects — one per field — that declares the complete policy set for that field.
It is produced by implementing INodeFieldManifestSource on the
executor's settings class.
public class MyNodeSettings : BaseNodeExecutorSettings, INodeFieldManifestSource
{
public string ApiUrl { get; set; }
public string EmployeeId { get; set; }
public int MaxRetries { get; set; }
public NodeFieldManifest GetFieldManifest() => new NodeFieldManifest()
.Add(nameof(ApiUrl), f => f
.Expression(EvaluationStage.AtConfigLoad, EvaluatorKind.Template)
.DataFlow(DataFlowRole.Config))
.Add(nameof(EmployeeId), f => f
.Expression(EvaluationStage.AtInputReady, EvaluatorKind.Template)
.DataFlow(DataFlowRole.Input)
.Hil(h => h.SendToHil(DisplayMode.ReadableContext, InputMode.Locked,
label: "Employee", description: "The employee being processed")))
.Add(nameof(MaxRetries), f => f
.Expression(EvaluationStage.AtConfigLoad, EvaluatorKind.Template)
.DataFlow(DataFlowRole.Config));
}
ExpressionPolicy
Declares when and how the field's value is expression-evaluated. Both properties must be set; omitting either reverts to the default.
| Property | Type | Options | Default |
|---|---|---|---|
EvaluationStage | EvaluationStage | AtConfigLoad, AtInputReady, Never | AtConfigLoad |
EvaluatorKind | EvaluatorKind | Template, JavaScript, JSONPath | Template |
A field with EvaluationStage.Never is never sent through the expression
engine. Its raw string value is always used as-is. This is appropriate for fields
that contain regex patterns, SQL statements, or other text where @{
might appear as a literal character.
DataFlowPolicy
Declares how the field participates in the node's data flow — primarily used by the Studio UI to determine which fields appear as input handles, output handles, or pure configuration in the workflow editor.
| DataFlowRole | Meaning |
|---|---|
Config | Pure configuration — does not participate in data wiring in the editor |
Input | Receives data from an upstream node's output via an input handle |
Output | Produces data that downstream nodes can consume via an output handle |
Internal | Used internally by the node; not exposed in the editor at all |
The runtime does not enforce DataFlowPolicy — it is purely advisory for the Studio UI's drag-and-drop data wiring feature. Executors operate on the resolved config regardless of the role declared.
HilPolicy
Declares how the field participates in HIL (Human-in-the-Loop) suspension payloads.
When a node suspends for human review, the expression engine builds the HIL payload
by collecting all fields that have SendToHil = true.
| Property | Type | Purpose |
|---|---|---|
SendToHil | bool | Whether this field is included in the HIL payload at all |
DisplayMode | DisplayMode | How the value is shown to the human (see below) |
InputMode | InputMode | Whether the human can edit the value (see below) |
Label | string | Human-readable field label. Can be an expression string (evaluated by IHilLabelResolver) |
Description | string | Longer description shown to the human. Also supports expressions |
DisplayMode Values
| Value | Behaviour |
|---|---|
ReadableContext | Field and its value are shown to the human as informational context |
ReadableMasked | Field label is shown but the value is hidden (masked with asterisks) |
Concealed | Field is not shown to the human at all, even though SendToHil = true (used for audit-only tracking) |
InputMode Values
| Value | Behaviour |
|---|---|
Locked | Read-only — the human can see the value but cannot change it |
EditableOptional | The human can edit the value, but it is not required |
RequiredFromHuman | The human must provide a value — the HIL form cannot be submitted without it |
PrefilledEditable | The field is pre-filled from the current resolved value, but the human can change it |
SecurityPolicy
Field-level security controls applied by the expression pipeline before evaluation. This is independent of the broader process-security (RBAC/ABAC) layer.
| Property | Purpose |
|---|---|
IsSensitive | Marks the field as sensitive — it will not appear in execution logs or traces |
AllowedDirectives | Restricts which directive types are valid for this field. If set, only listed directives are permitted; others cause a validation error |
RequireEncrypted | Requires the field's value to be stored encrypted in the database configuration |
IsSensitive = true should be set on any field that holds credentials,
PII, or other confidential data. The execution trace will show the field name but
record [redacted] as the value.
Default Behaviour — No Manifest
If an executor's settings class does not implement INodeFieldManifestSource,
all fields implicitly use ExpressionPolicy.Default:
EvaluationStage = AtConfigLoadEvaluatorKind = Template- No DataFlowPolicy, HilPolicy, or SecurityPolicy
This means only @{env:}, @{secret:}, and
@{context:} directives will resolve. Runtime directives
(@{input:}, @{output:}, @{var:})
will remain as literal text. The node will never appear in HIL payloads.
Fields will not be redacted from execution traces.
For simple utility nodes, the default is often sufficient. For any node that needs runtime data in its config or participates in human review, a manifest is required.
INodeFieldManifestSource is implemented on the settings class
(the class you pass to LoadAndValidateConfigAsync), not on the executor
itself. A single executor with multiple config variants can declare different
manifests for each variant.