Node Configuration & Schema-Driven Forms
Applies to: ChengOS v0.1.0+ | Last verified: 2026-08-12 | Source:
chengflow-ui/src/features/editor/utils/controlUtils.tsx,chengflow-ui/src/features/editor/components/DynamicNodeInspector.tsx,chengflow/docs/i18n-node-convention-guide.md
Select a node and the right-hand property panel shows its configuration. Those forms are
generated, not hand-written: the backend publishes each node's inputs as a JSON Schema
carrying x-* UI extensions, and the frontend's field control registry turns each field into a
control. Add an input to a node in Rust and its form field appears with no frontend change.
Knowing how that mapping works is what lets you predict the panel instead of being surprised by it.
How a field becomes a control
The registry resolves each field through an ordered chain and stops at the first match. The order is the important part:
- Connected state — highest priority. A field fed by an incoming edge renders as a read-only "connected" indicator, not an editable box. This is why a field you wired up stops being typeable: the edge is the value now.
- File path fields — render as a path picker.
max_tokens— gets a purpose-built control.- Sensitive fields — inferred from the field name. A name containing
password,api_key/apikey,token/access_token, orsecret/private_keybecomes a masked input automatically. Passwords and secrets mask fully; API keys show the last 4 characters and tokens the last 6, so you can tell two keys apart without exposing either. - Explicit
x-control— the node author's declared choice (see the table below). - Enum / options — a field with an enum becomes a select.
- Type match — the fallback:
string→ text,number→ number,boolean→ switch.
Step 4 is worth remembering when authoring nodes: naming a field api_key gets you masking
for free, and naming a non-secret field token gets you masking you did not want.
Available controls
x-control selects from a fixed set of renderers:
| Control | Renders as |
|---|---|
text, input, textarea, rich-text | Single-line or expandable multi-line text |
number, slider | Numeric entry or a slider |
switch, checkbox | Toggle |
select, radio, multi-select | Choice from options |
code, code-editor | Syntax-highlighted editor (language from x-language) |
json, json-editor | Structured JSON editor |
key-value | Editable key/value pairs |
date, time, datetime-local | Date and time pickers |
file, file-upload | Path picker or uploader (accepted types from x-accept) |
color, color-picker | Colour picker |
credential-select | Vault credential dropdown, filtered by x-credential-providers |
llm-model-select | Unified model dropdown across all connected providers |
workflow-select | Workflow picker (filtered by x-workflow-types) |
dynamic-select | Options fetched at runtime from x-api-endpoint |
mcp-server-manager, mcp-visibility-editor | MCP server configuration widgets |
document-extract-selector | Document field selection widget |
Extensions that change behaviour
Beyond picking a control, x-* extensions shape the panel itself:
Visibility and grouping
| Extension | Effect |
|---|---|
x-advanced | Hidden behind the advanced section |
x-conditional | Shown only when a condition holds |
x-depends-on | Options are recomputed from another field's value |
x-collapsed-visible | Stays visible on a minimized node |
x-debug-field | Diagnostic only |
x-deprecated | Marked as deprecated but still rendered |
x-readonly | Displayed, not editable |
Options sourcing
x-options supplies a static list. x-options-source / x-options-from supplies a dynamic
one — notably ports, which builds options from the node's own input port names, which is how
"pick one of my inputs" fields work. x-allow-custom lets you type a value that is not in the
list.
Model selection
x-model-type, x-model-options, x-model-name-includes, x-model-name-excludes, and
x-provider-filter narrow the model dropdown — so an embedding field offers embedding models
rather than every model the provider exposes.
Other
x-rows (textarea height), x-step (numeric step), x-dynamic-array (repeatable groups),
x-list-modal / x-compact-list / x-list-max-visible (list presentation),
x-auto-populate (fill from context), x-fallback-control (what to use if the preferred
control is unavailable), x-generic-kv and x-provider-presets (credential forms).
Referencing other nodes' values
Rather than hardcoding, reference an upstream value with {{variable_name}}. The variable
selector — the picker beside supported fields — lists what is actually reachable at that point
in the graph, which is more reliable than typing a name and hoping.
Two ways to get a value into a node, and the distinction matters:
- An edge into an input port passes data directly. The field becomes read-only (rule 1).
- A
{{...}}reference inside a text field interpolates into that text. Use this for prompts, where you want a sentence around the value.
Credentials
Fields with credential-select list matching vault entries by provider. Select an entry; the
node stores a reference. The secret is fetched server-side at execution time and never sent to
the browser or into a model prompt.
Translated labels
Labels, descriptions, placeholders, and option text come from i18n bundles keyed by
x-i18n-key, loaded from /api/v1/i18n. Untranslated entries fall back to en-US rather than
rendering blank, so a missing translation degrades instead of breaking.
Next steps
- Canvas basics — assembling the graph.
- Testing and debugging — verifying configuration.
- LLM and model configuration — populating the model dropdown.