Flows and nodes
A flow is an execution graph. It is made of typed nodes connected by outcome-routed edges. A Flow Node is the unit of work on the canvas. Every node has a single node type drawn from one shared vocabulary. The catalog, the DSL parser and serializer, the executor, the validator, and the canvas all agree on that vocabulary, so there is no separate “DSL kind” concept.
The five node types
Section titled “The five node types”| Node type | Purpose | Executes as |
|---|---|---|
action | Tool-based work via a registered adapter (shell, filesystem, or a CLI tool). The adapter is chosen by data.adapter, its arm by data.actionId. | the adapter |
ai | Generative model invocation. A provider field selects the backend: local runs an on-device LLM (zero egress); a cloud provider invokes a frontier model, gated by settings and an API key. | the executor’s AI path |
agentic | Design-time flow generator: from a natural-language request it proposes a whole flow for review and apply. | nothing. The executor skips it, and Run routes it to the review flow |
utility | Built-in primitives that need no external adapter: sleep, log, set-variable, download, merge (fan-in), cron (schedule marker). | the utility adapter |
service | API-based integration: connect to an external service through its API. The concrete service is catalog data. | the generic service adapter |
The FlowNode shape
Section titled “The FlowNode shape”The canonical type, mirrored between the Rust core and the TypeScript contracts:
export type FlowNodeType = | "action" | "ai" | "agentic" | "utility" | "service";
export interface FlowNode<TData = Record<string, unknown>> { id: string; // unique within the flow type: FlowNodeType; label: string; // display name on the card data: TData; // defaults from the catalog entry, then user edits}The wire shape keeps type open-ended so a loaded graph that contains a canvas
preset or a not-yet-installed type still round-trips. The canvas renders an
“unsupported” stub rather than dropping the node.
Catalog-driven by design
Section titled “Catalog-driven by design”Flow Studio is catalog-driven. Every non-built-in node type on the canvas is one JSON catalog entry that is authored as data, not code. An entry carries everything needed to drop the node onto a canvas:
schemais a JSON Schema subset that drives the generic inspector’s form and both the frontend and backend validators.defaultsare the values stamped ontonode.datawhen a fresh node is dropped from the palette.commandTreeis embedded reference data, for example a parsed CLI command catalog, that the inspector reads to render cascading command pickers. There is no per-kind code path.settingsare the declared global-settings dependencies. The inspector surfaces a “Requires” hint when something is missing.loweringis the adapter and actionId used when serializing to DSL.
Adding a new catalog-driven node type is purely a JSON authoring exercise. You author the entry and install it from the Node Hub, and it appears on the palette with the right inspector form. There is no per-type React component to write. See the Node Hub.
Built-in carve-out: the ai and agentic types ship as first-class
built-ins. They are merged into the installed-nodes store so the palette,
factory, and validator treat them like any other type, but they never appear
in the Node Hub and cannot be uninstalled.
Service nodes
Section titled “Service nodes”Service nodes connect to external services through their APIs. The code is
vendor-neutral. One generic service adapter executes the call, and everything
service-specific lives in the catalog entry’s serviceIntegration descriptor.
That includes the vendor name, base URL, auth scheme, and operations.
"serviceIntegration": { "baseUrl": "https://api.example/v1", "auth": { "scheme": "oauth2" }, // oauth2 | bearer | basic | api_key "operations": [ { "id": "list-items", "label": "List items", "method": "GET", "path": "/items/{itemId}", "query": ["limit"] } ]}At run time the adapter resolves the node’s catalog slug to its integration,
fills {param} holes from node.data, attaches the connection credential from
the OS keyring, and sends the request. OAuth2 connections run authorization-code
with refresh through the system browser with loopback capture, so there is no
manual code paste. Service egress crosses the isolation boundary, so it is
audited and gated by connection presence.
Extending the node surface
Section titled “Extending the node surface”A Flow Node is extended along four axes. Each one is data or registration, with no per-type code:
- New catalog types. Author a JSON entry.
- New adapters. This is a contained, code-level change, and
actionnodes target the new adapter by name. - New service integrations. Add a
serviceentry with aserviceIntegrationdescriptor. - New AI providers. Register a cloud provider, and the unified
ainode picks it viaprovider.
The two code-level seams are documented in Extension APIs.
Related
Section titled “Related”- Adapters covers how action nodes reach the world.
- Execution model covers how nodes are scheduled.
- DSL node types covers the per-type data shapes.