Delivered Nodes
Operator-delivered workflow nodes: a signed plugin whose verbs bind to an in-platform Provider Family, installed from a source, refreshed by the reconciler, and gated by requiredProviders. Covers the shipped integration catalog and the default-off governance rule.
Most nodes in the catalog are compiled into the platform: a folder under src/lib/nodes/ ships with the release. Delivered nodes are different. They arrive inside a signed plugin that an operator installs from a source and refreshes on change - no platform redeploy needed to add or update a node's declarative surface. This is how an operator-installed integration like Typefully reaches your workflow palette.
This page covers delivered nodes at the workflow-author altitude: what they are, how they appear in your palette, and why a node you expect might be off by default. Operators manage delivery and availability from the gated console - see Plugin Sources and the Node Catalog.
The model
A delivered node is a plugin verb that carries a node block. Four pieces fit together:
| Piece | What it is |
|---|---|
| Plugin | A signed, versioned bundle (registry_plugins / registry_plugin_versions) with a manifest declaring provider families, verbs, and skills. |
| Verb | A namespaced operation (typefully.draft.create). A verb with a node block materializes as a visible canvas node; one without stays headless. |
| Provider Family | The in-platform executable that actually runs the verb. The verb's implementedBy field (e.g. typefully.api) binds it to a family in the static FAMILY_REGISTRY. |
| Delivered node row | The materialized projection (delivered_nodes) that the palette and executor read alongside the compiled manifest. |
flowchart LR
P["Signed plugin<br/>(manifest + verbs)"] -->|reconcile| DN["delivered_nodes<br/>(materialized rows)"]
DN -->|projected| PAL["Palette<br/>(builtin ∪ delivered)"]
V["verb.implementedBy<br/>'typefully.api'"] -->|resolves at run time| FAM["Provider Family<br/>(FAMILY_REGISTRY)"]
FAM -->|invoke| API["Provider API"]The key invariant: delivery is external, execution is in-platform. A refresh can add or change the declarative surface of a node - its display name, schema, ports, gating - but it cannot introduce new executable behavior. A verb whose implementedBy family id is not registered in-platform fails closed and never surfaces. (See the ADR for the boundary rationale.)
Verb to node
A delivered verb declares the same metadata a compiled node does, inlined in the plugin manifest. For example, the Typefully Create Draft node (src/lib/plugins/typefully/manifest.ts):
{
id: 'typefully.draft.create',
kind: 'action',
implementedBy: 'typefully.api', // → in-platform Provider Family
sideEffectClass: 'external-write',
inputSchema: { /* JSON Schema 7, inlined */ },
outputSchema: { /* … */ },
node: { // present ⇒ visible canvas node
category: 'action',
displayName: 'Create Draft',
providerId: 'typefully', // palette grouping
requiredProviders: ['typefully'], // integration gate
lifecycleTag: 'enabled',
ports: [ /* typed I/O ports */ ],
configSchema: { /* rich-control overlay */ },
},
}The reconciler projects each such verb into a DiscoveredNode matching the shape compiled nodes produce, so the palette, config panels, and port typing all work identically. The one difference: backend.definition is null for a delivered node - the executable resolves at run time through implementedBy, not from a compiled-in closure.
How delivered nodes reach your palette
A delivered node is in your palette only after the full operator chain completes:
Publish. A plugin version is published to the registry, signed by the Rensei identity.
Mount. An operator links a plugin source to your org - a pinned registry plugin id and version.
Reconcile. The plugin-source reconciler materializes one delivered_nodes row per verb-with-a-node-block, scoped to your org.
Connect the integration. You connect the matching integration under Settings → Integrations so the node's requiredProviders gate opens.
Enable. Because delivered nodes default off (see below), an operator or org-admin toggles the node on in the Node Catalog.
Until the integration is connected, the node's provider group appears as a locked group ("Typefully - connect to unlock N nodes") rather than vanishing silently. Connecting the integration unlocks it.
Availability and gating
Delivered and compiled nodes pass through one unified resolver (resolveNodeAvailability), which evaluates five gates in fixed precedence - the first one that fires decides the verdict:
| # | Gate | Fires when | Applies to |
|---|---|---|---|
| 1 | delivery-not-installed | The node's plugin is not installed/allowed for the org | Delivered only (builtins skip) |
| 2 | provider-disabled | An operator turned the whole provider off | Both |
| 3 | node-disabled | The node is disabled per-org (or off by default) | Both |
| 4 | integration-missing | A requiredProviders integration is not connected | Both |
| 5 | deprecated | The node is marked deprecated | Both |
Per-org and per-project enable/disable state lives in a typed store (org_node_state), not the old organizations.features JSON blob. A project-scope override wins over an org-scope row, which wins over the code default. Operators and org-admins write this store from the catalog surfaces; see Node Catalog for the management UI and the per-node explain drawer that renders this exact gate trace.
The requiredProviders integration gate is the same mechanism that hides all github_issues.* nodes until GitHub Issues is connected - see Node Palette filtering. Delivered nodes always declare requiredProviders, so a fresh org sees a locked group, never a row of nodes that fail at run time.
Governance: delivered nodes default OFF
This is the rule most likely to surprise a workflow author:
SDK / plugin-delivered nodes are OFF by default and must be opted in per org. Installing or reconciling a plugin does not auto-enable its nodes. First-party builtin nodes keep their existing default-on behavior; only delivered nodes are opt-in.
When a delivered node has no explicit org_node_state row, the resolver computes a default-off verdict (node-disabled with deliveredDefaultOff: true) - there is no database row and no migration, just a computed default that an explicit "enabled" row overrides. The practical effect: after an operator mounts a plugin and you connect its integration, the nodes still won't appear in the palette until someone enables them in the Node Catalog (operator) or under your org's Settings → Workflows → Node availability tab (org-admin). This keeps a freshly installed catalog from flooding every org's palette with nodes nobody asked for.
The experimental lifecycle tag is orthogonal to enablement: an enabled-but-experimental node still appears, with a badge. A delivered plugin declares its own lifecycleTag per node.
The installed catalog
The specific set of delivered plugins available to your org is operator-managed - it depends on which plugin sources an operator has mounted and which nodes are enabled in the Node Catalog. Your palette and the gated Node Catalog are the source of truth for what is installed; this page does not enumerate a fixed list, because the catalog changes without a platform redeploy - that is the whole point of delivery. Some delivered plugins run entirely in-platform (their Provider Family makes the outbound API call directly); others are declarative-only until their Provider Family ships.
Two net-new builtin (compiled-in, not delivered) nodes also shipped, and follow the normal default-on behavior:
rss.fetch- fetch and parse RSS 2.0 / Atom feeds (no credential).llm.structured- schema-constrained LLM output.
Related pages
- Node Overview - the full node taxonomy and the compiled-node path
- Node Palette - how the palette filters by connected integration
- Integrations Overview - the connect / credential model that opens the
requiredProvidersgate - Plugin Sources - operator: mount + reconcile a delivered-node source
- Node Catalog - operator + org-admin: enable, disable, and explain node availability