Some logic is too specific for a compute operator, yet too deterministic to warrant a LLM agent: multi-source reconciliations, bespoke business computations, exotic normalizations. For these cases, the code_step node executes code inside a sandbox, with no LLM call and a predictable output.
The code is edited directly, like any other configuration field: no code_step key is locked, and no code-specific approval is required. A change takes effect as soon as the pipeline is published, on the next execution.
The execution model
code_step executes Starlark — a Python dialect designed for isolation: no filesystem access, no network, no import, no while loop or recursion, deterministic, bounded by a time budget. The code only sees:
- The inputs you explicitly map to it (
input_paths). - The tools you authorise it to call (
allowed_tools).
Everything else is out of reach. A code step cannot access a resource you have not handed to it.
Configuration
| Field | Purpose |
|---|---|
| Code | The Starlark source that is executed, edited directly. |
Inputs (input_paths) | variable → selector mapping. Only these context keys are exposed to the code. Internal keys (_*) are rejected. |
Tool allowlist (allowed_tools) | The tools callable via tool(name, args), 50 calls maximum per execution. Each call inherits the tool's own safeguards (SSRF protection, billing, audit, killswitch). |
Output key (output_key) | Where to place the result (JSON-serializable) in the context. Downstream nodes read this key. Empty = result not stored. |
Execution budget (timeout_ms) | Time ceiling: 5,000 ms by default, 30,000 ms maximum. A step that exceeds it fails. |
Where the security boundary is
The language is not the threat: Starlark is sandboxed by design — on its own it can neither read a file, reach the network, nor loop forever. Pure computation is harmless.
The only real capability is the tool() bridge, and it is bounded by the allowlist enforced at runtime: the code can only call the tools you have listed (any other call is refused), and each one carries its own safeguards (SSRF protection, billing, audit, killswitch). The allowlist is as freely editable as the code: the choice of tools is where you scope what a step is allowed to do.
An empty allowlist = no side-effect capability: the step merely computes over its inputs and produces an output.
When to use it
- Yes: deterministic logic you can describe precisely, but which exceeds the built-in
computeoperations (reconciling multiple lists, composite business rules, proprietary formats). - No: a simple filter, aggregation, or projection → a
computeoperator is sufficient. - No: a task that requires judgment or open-ended writing → that is an agent, not code.