Skip to content

Workflow Specification

Tentacles are defined in workflow.yaml at the root of each tentacle directory.

name: my-tentacle # kebab-case, required
version: "1.0" # semver string, required
description: "What it does" # optional
triggers:
- type: manual
- type: cron
name: daily-digest
schedule: "0 9 * * *"
- type: queue
subject: events.incoming
nodes:
fetch-data:
path: ./nodes/fetch-data.ts
process:
path: ./nodes/process.ts
notify:
path: ./nodes/notify.ts
edges:
- from: fetch-data
to: process
- from: process
to: notify
config:
timeout: 30s
retries: 1
# arbitrary keys are preserved
custom_key: "any value"
deployment:
namespace: pd-my-tentacle
contract:
version: "1"
dependencies:
github-api:
protocol: https
host: api.github.com
port: 443
auth:
type: bearer-token
secret: github.token
slack-webhook:
protocol: https
host: hooks.slack.com
port: 443
news-sources:
type: dynamic-target
protocol: https
cidr: "0.0.0.0/0"
dynPorts:
- "443/TCP"
tentacular-postgres:
# exoskeleton-managed: host/port/auth auto-provisioned
FieldTypeRequiredDescription
namestringYesTentacle name. Must be kebab-case.
versionstringYesSemantic version (quoted to prevent YAML number parsing).
descriptionstringNoHuman-readable description.
triggersarrayNoHow the tentacle is initiated. Defaults to manual only.
nodesmapYesNamed nodes with paths to TypeScript files. At least one required.
edgesarrayYesDirected edges between nodes forming a DAG.
configmapNoWorkflow-level configuration passed to nodes via ctx.config.
deploymentmapNoDeployment-specific settings.
contractmapNoSecurity contract declaring external dependencies.
FieldTypeRequiredDescription
typestringYesOne of: manual, cron, queue, webhook (roadmap)
namestringNoNamed trigger for parameterized execution
schedulestringCron onlyCron expression (5-field)
subjectstringQueue onlyNATS subject to subscribe to

Named cron triggers send {"trigger": "<name>"} as input to root nodes, enabling workflows with multiple schedules that branch on trigger identity.

FieldTypeRequiredDescription
pathstringYesRelative path to the TypeScript node file
capabilitiesmapNoLegacy per-node capabilities (deprecated in favor of contract)

Node names must be kebab-case and unique within the tentacle.

FieldTypeRequiredDescription
fromstringYesSource node name
tostringYesTarget node name

Edges must reference defined nodes. Self-loops and cycles are rejected at validation time. The DAG is compiled using Kahn’s algorithm into execution stages — nodes at the same depth level run in parallel.

The config block is open — arbitrary keys are preserved alongside typed fields. In Go, extra keys flow into WorkflowConfig.Extras via yaml:",inline". Nodes access config via ctx.config.

FieldTypeDefaultDescription
timeoutstring30sPer-node execution timeout
retriesinteger0Maximum retry count with exponential backoff
*anyArbitrary keys preserved for node access
FieldTypeDescription
namespacestringTarget Kubernetes namespace

Namespace resolution order: CLI -n flag > deployment.namespace > config file default > default.

FieldTypeRequiredDescription
versionstringYesContract format version (currently "1")
dependenciesmapYesNamed external service declarations
FieldTypeRequiredDescription
protocolstringYeshttps, postgres, nats, etc.
hoststringYesTarget hostname
portintegerYesTarget port
authmapNoAuthentication configuration
auth.typestringNoAuth type string (e.g., bearer-token, api-key)
auth.secretstringNoSecret reference in service.key format
FieldTypeRequiredDescription
typestringYesMust be dynamic-target
protocolstringYesProtocol for dynamic targets
cidrstringYesCIDR range (e.g., 0.0.0.0/0)
dynPortsarrayYesPort/protocol pairs (e.g., 443/TCP)
reasonstringNoHuman-readable explanation

Dependencies prefixed with tentacular- are auto-provisioned by the exoskeleton. No host, port, or auth configuration needed.

tentacular-postgres: # Scoped database schema and role
tentacular-nats: # Scoped subjects and credentials
tentacular-rustfs: # Scoped S3-compatible object storage

Nodes within the same execution stage run in parallel via Promise.all(). Stages execute sequentially based on the topological sort of the DAG.

For a tentacle with edges A→B, A→C, B→D, C→D:

  • Stage 1: [A]
  • Stage 2: [B, C] (parallel)
  • Stage 3: [D]
  • name: must be kebab-case ([a-z0-9-]+)
  • version: must be a quoted semver string
  • At least one node must be defined
  • All edge references must point to defined nodes
  • No self-loops or cycles in the DAG
  • Trigger types must be one of: manual, cron, queue
  • Cron triggers must include a schedule field
  • Queue triggers must include a subject field