Documentation

The Policy Architecture

flowchart TD NFS["INodeFieldManifestSource\nImplemented by executor settings class"] NFM["NodeFieldManifest\nCollection of NodeFieldDescriptors\none per field"] NFD["NodeFieldDescriptor\nField name + 4 policy groups"] subgraph P["Policy Groups"] direction LR EP["ExpressionPolicy\nWhen + how the field\nis evaluated"] DP["DataFlowPolicy\nHow the field\nparticipates in\ndata flow"] HP["HilPolicy\nHow the field\nappears in\nHIL forms"] SP["SecurityPolicy\nField-level\nsecurity rules"] end NFS --> NFM --> NFD --> P style NFS fill:#1e2640,stroke:#6c8cff,color:#e2e8f0 style NFM fill:#221c3e,stroke:#a78bfa,color:#e2e8f0 style NFD fill:#162d22,stroke:#34d399,color:#e2e8f0 style P fill:#111425,stroke:#2a3060 style EP fill:#1e2640,stroke:#6c8cff,color:#e2e8f0 style DP fill:#1e2640,stroke:#6c8cff,color:#e2e8f0 style HP fill:#2d2616,stroke:#fbbf24,color:#e2e8f0 style SP fill:#2d1616,stroke:#f87171,color:#e2e8f0

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.

PropertyTypeOptionsDefault
EvaluationStageEvaluationStageAtConfigLoad, AtInputReady, NeverAtConfigLoad
EvaluatorKindEvaluatorKindTemplate, JavaScript, JSONPathTemplate

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.

DataFlowRoleMeaning
ConfigPure configuration — does not participate in data wiring in the editor
InputReceives data from an upstream node's output via an input handle
OutputProduces data that downstream nodes can consume via an output handle
InternalUsed 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.

PropertyTypePurpose
SendToHilboolWhether this field is included in the HIL payload at all
DisplayModeDisplayModeHow the value is shown to the human (see below)
InputModeInputModeWhether the human can edit the value (see below)
LabelstringHuman-readable field label. Can be an expression string (evaluated by IHilLabelResolver)
DescriptionstringLonger description shown to the human. Also supports expressions

DisplayMode Values

ValueBehaviour
ReadableContextField and its value are shown to the human as informational context
ReadableMaskedField label is shown but the value is hidden (masked with asterisks)
ConcealedField is not shown to the human at all, even though SendToHil = true (used for audit-only tracking)

InputMode Values

ValueBehaviour
LockedRead-only — the human can see the value but cannot change it
EditableOptionalThe human can edit the value, but it is not required
RequiredFromHumanThe human must provide a value — the HIL form cannot be submitted without it
PrefilledEditableThe 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.

PropertyPurpose
IsSensitiveMarks the field as sensitive — it will not appear in execution logs or traces
AllowedDirectivesRestricts which directive types are valid for this field. If set, only listed directives are permitted; others cause a validation error
RequireEncryptedRequires 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:

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.

Manifest is per settings class, not per executor 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.