> ## Documentation Index
> Fetch the complete documentation index at: https://yukta.infenia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# External I/O

> PROCESS_EXECUTOR, HTTP_REQUEST, SQL_QUERY, FILE_READ, FILE_WRITE and KV_STORE: plugins that reach outside the workflow.

These plugins run commands, call HTTP services, query databases, read and write files and keep shared state. Five of them need an optional module in the server build; `KV_STORE` is always included. See [Built-in plugins](/plugins/overview) for the modules.

Most of them share one failure pattern, set with `failureMode`:

| `failureMode` | A failed call |
| - | - |
| `ERROR` (default) | Fails the node. The node's engine policy (retry, error routing) then applies. See [Failure handling](/concepts/failure-handling) |
| `CONTINUE` | Keeps the run alive. `PROCESS_EXECUTOR` and `HTTP_REQUEST` emit a result that describes the failure. `SQL_QUERY`, `FILE_READ` and `FILE_WRITE` send the original message to the `error` port with the failure details attached |

With `failureMode` set to `CONTINUE`, `PROCESS_EXECUTOR` and `HTTP_REQUEST` can also stamp each message with the port `success` or `failure` (`routeByExitCode`, `routeByStatus`) so edges can route on the outcome without a separate `BRANCH` node.

<Warning>
  `PROCESS_EXECUTOR`, `SQL_QUERY`, `FILE_READ`, `FILE_WRITE` and `HTTP_REQUEST` act with the permissions of the Yukta server. Anyone who can author a workflow can use them. Treat workflow authorship as a trusted operation and see [Security](/operations/security).
</Warning>

## PROCESS\_EXECUTOR

Runs an operating system command for each message and emits the result. Requires the `process-executor` module.

The server refuses every command until you allow some. Set `yukta.process.allowed-commands` to a list of executable names, exactly as they appear first in `command`. With that list set, `useShell` is refused. For trusted authors only, `yukta.process.allow-any=true` allows any executable.

| Option | Type | Default | Meaning |
| - | - | - | - |
| `command` | list of strings | none, required | The program, then its arguments. Each element may use `${...}` variables |
| `inputMode` | `NONE`, `ENV` or `STDIN` | `NONE` | How the message reaches the process. `ENV`: metadata as `YUKTA_METADATA_<KEY>` variables and the payload as `YUKTA_PAYLOAD`. `STDIN`: the payload on standard input. Text payloads go as written, other payloads as JSON |
| `workingDir` | string | the server's directory | Working directory. Variables allowed |
| `timeout` | number | `300` | Seconds the process may run. Positive. Variables allowed |
| `env` | map | empty | Extra environment variables. Variables allowed. Explicit entries win over `inputMode` ones |
| `useShell` | boolean | `false` | Run through `/bin/sh -c` (or `cmd.exe /c` on Windows). Arguments with special characters are quoted, so pipes and `&&` do not act as operators |
| `captureStderr` | boolean | `false` | Capture standard error separately. When `false`, it is merged into standard output |
| `maxOutputLines` | number | `10000` | Most lines kept per stream. `0` means unlimited |
| `maxOutputBytes` | number | `10485760` | Most bytes kept per stream (10 MiB). `0` means unlimited |
| `outputFormat` | `STRUCTURED`, `RAW`, `JSON` or `PASSTHROUGH` | `STRUCTURED` | The shape of the payload, see below |
| `failureMode` | `ERROR` or `CONTINUE` | `ERROR` | `ERROR` fails the node on a non-zero exit code or a timeout |
| `routeByExitCode` | boolean | `false` | Emit on `success` (exit code 0) or `failure`. Needs `failureMode` `CONTINUE` |
| `includeOutput` | boolean | `true` | Put the text of stdout and stderr in the payload (`STRUCTURED`, `JSON`) |
| `includeInput` | boolean | `false` | Put the incoming payload under `input` (`STRUCTURED`, `JSON`) |

Output ports: `default`, or `success` and `failure` when `routeByExitCode` is on.

| `outputFormat` | Payload |
| - | - |
| `STRUCTURED` | `{"exitCode", "success", "timedOut", "durationMillis", "outputTruncated", "stdout", "stderr", "input"}` (`stdout`, `stderr` with `includeOutput`; `input` with `includeInput`) |
| `RAW` | The standard output as one string |
| `JSON` | The `STRUCTURED` object plus `output`, the standard output parsed as JSON (null when the process failed). A parse failure follows `failureMode`; with `CONTINUE`, `output` is null and `parseError` is set |
| `PASSTHROUGH` | The incoming payload, unchanged |

The metadata always gets `exitCode`: the real exit code, or `-1` when the process timed out. A timed-out process is stopped, then killed after a short grace period. Standard output and error lines appear live in the execution logs while the process runs.

Variables whose names contain `PASSWORD`, `SECRET`, `TOKEN`, `KEY` and similar words cannot be read with `${env...}` or `${sys...}`. With `inputMode` `ENV`, the message is visible in the process's environment, so do not use it for messages that carry secrets. See [Variables and expressions](/building/variables-and-expressions).

Default timeout: `timeout` plus 10 percent (at least 5 seconds), so 330 seconds with the default. Up to 256 messages are handled at once, with results emitted in order.

This node runs a check and routes on its exit code:

```json theme={"system"}
{
  "nodeId": "tests",
  "type": "PROCESS_EXECUTOR",
  "config": {
    "command": ["sh", "-c", "echo 'tests: 42 passed'"],
    "timeout": 120,
    "failureMode": "CONTINUE",
    "routeByExitCode": true
  }
}
```

```json theme={"system"}
[
  { "source": "tests", "target": "report-pass", "sourcePort": "success" },
  { "source": "tests", "target": "report-fail", "sourcePort": "failure" }
]
```

## HTTP\_REQUEST

Makes one HTTP request per message and emits the response. Requires the `http-request` module.

| Option | Type | Default | Meaning |
| - | - | - | - |
| `url` | string | none, required | The request URL. Variables allowed, for example `https://api.example.com/cart/${payload.userId}` |
| `method` | `GET`, `POST`, `PUT`, `PATCH`, `DELETE` or `HEAD` | `GET` | The HTTP method |
| `headers` | map | empty | Request headers. Values may use variables and `decrypted:<key>` secrets |
| `body` | string or object | none | The request body. Text goes as written; an object goes as JSON. Text may use variables |
| `timeout` | number | `30` | Seconds to wait for the response. Positive |
| `failureMode` | `ERROR` or `CONTINUE` | `ERROR` | `ERROR` fails the node on a non-2xx status or a transport error |
| `routeByStatus` | boolean | `false` | Emit on `success` (a 2xx status) or `failure`. Needs `failureMode` `CONTINUE` |

Output ports: `default`, or `success` and `failure` when `routeByStatus` is on.

The payload is `{"status", "success", "headers", "body"}`. `success` is true for a 2xx status. `body` is parsed when the response declares a JSON content type, and is text otherwise. The metadata gets `status`. With `CONTINUE`, a transport error (no connection, timeout) is emitted with `status` 0 and the error text as `body`. A non-2xx answer is data, not an error, until `failureMode` is `ERROR`.

The server does not restrict which hosts a request can reach, and the URL can come from the message. Treat the URL as trusted input.

Default timeout: `timeout` plus 5 seconds. Up to 256 messages are handled at once, with results emitted in order.

```json theme={"system"}
{
  "nodeId": "probe",
  "type": "HTTP_REQUEST",
  "config": {
    "url": "https://api.example.com/health",
    "method": "GET",
    "timeout": 5,
    "failureMode": "CONTINUE",
    "routeByStatus": true
  }
}
```

## SQL\_QUERY

Runs one parameterised SQL statement per message. Requires the `sql-query` module, and the driver for your database must be part of the server build.

| Option | Type | Default | Meaning |
| - | - | - | - |
| `url` | string | none, required | The database connection URL, for example `jdbc:postgresql://db:5432/app` |
| `username` | string | none | Database user |
| `password` | string | none | Database password, used as written |
| `sql` | string | none, required | The statement, with `?` placeholders |
| `params` | list | empty | Payload field names, bound to the placeholders in order |
| `maxRows` | number | `1000` | Most rows returned, positive |
| `timeoutSeconds` | number | `30` | Statement timeout in seconds, positive. It also sets the node's idle timeout |
| `failureMode` | `ERROR` or `CONTINUE` | `ERROR` | `CONTINUE` sends the original message to the `error` port |

Output ports: `default`, plus `error` when `failureMode` is `CONTINUE`.

Values are always bound as parameters and never pasted into the statement, so they cannot change its structure. A parameter takes its value from the named field of the incoming payload, which must be an object. For a statement that returns rows the payload becomes `{"rows": [...], "count": n}`, with column names in lower case. For any other statement it becomes `{"updated": n}`.

A connection is opened for each message and messages are handled one at a time. Credentials in `url`, `username` and `password` are not variable-resolved and are stored as written in the workflow definition.

```json theme={"system"}
{
  "nodeId": "find-user",
  "type": "SQL_QUERY",
  "config": {
    "url": "jdbc:postgresql://db:5432/app",
    "username": "reader",
    "password": "change-me",
    "sql": "SELECT id, name FROM users WHERE email = ?",
    "params": ["email"],
    "maxRows": 10,
    "timeoutSeconds": 15
  }
}
```

## FILE\_READ

Reads a UTF-8 text file from a sandbox directory. Requires the `file-io` module.

| Option | Type | Default | Meaning |
| - | - | - | - |
| `baseDir` | string | none, required | The sandbox directory. Paths outside it are refused |
| `path` | string | the payload's `path` field | The file, relative to `baseDir` |
| `maxBytes` | number | `10485760` | Largest file to read (10 MiB). A larger file is an error |
| `failureMode` | `ERROR` or `CONTINUE` | `ERROR` | `CONTINUE` sends the original message to the `error` port |

Output ports: `default`, plus `error` when `failureMode` is `CONTINUE`. The payload becomes `{"path": "<absolute path>", "content": "<text>"}`.

## FILE\_WRITE

Writes or appends text to a file in a sandbox directory and passes the message on unchanged. Requires the `file-io` module.

| Option | Type | Default | Meaning |
| - | - | - | - |
| `baseDir` | string | none, required | The sandbox directory. Paths outside it are refused |
| `path` | string | the payload's `path` field | The file, relative to `baseDir`. Missing folders are created |
| `contentField` | string | `content` | The payload field that holds the text. A payload that is not an object is written as it is |
| `append` | boolean | `false` | Append the text plus a line break instead of overwriting the file |
| `failureMode` | `ERROR` or `CONTINUE` | `ERROR` | `CONTINUE` sends the original message to the `error` port |

Output ports: `default`, plus `error` when `failureMode` is `CONTINUE`.

For both file plugins, a path is normalised and anything that escapes `baseDir`, by `..` or through a symbolic link, is refused. `baseDir` and `path` are used as written, not variable-resolved. The sandbox limits what a path can reach, not who can author the workflow, since `baseDir` is itself part of the workflow. Messages are handled one at a time.

```json theme={"system"}
{
  "nodeId": "save-report",
  "type": "FILE_WRITE",
  "config": { "baseDir": "/var/lib/yukta/reports", "path": "daily.log", "append": true }
}
```

## KV\_STORE

Reads and writes small shared state: counters, flags and last-seen values. It is always part of the server.

| Option | Type | Default | Meaning |
| - | - | - | - |
| `op` | `get`, `put`, `incr` or `delete` | none, required | The operation. Lower case |
| `key` | string | the payload's `key` field | The key, used as written |
| `namespace` | string | none | A prefix that keeps the keys of different uses apart |
| `maxEntries` | number | `10000` | Most distinct keys. A new key beyond it fails the node |

Output port: `default`. The payload becomes `{"key": "...", "value": ...}`.

| `op` | Does | `value` in the result |
| - | - | - |
| `get` | Reads the key | The stored value, or null |
| `put` | Stores `payload.value` (required) | The stored value |
| `incr` | Adds `payload.value` (a whole number, default 1) to a stored whole number, creating it when absent | The new total |
| `delete` | Removes the key | The removed value, or null |

The store belongs to the whole server: every workflow and execution shares it. By default it lives in memory and is lost on restart. With the PostgreSQL module and `yukta.kv.store-type=postgres` it survives restarts and is shared between servers, and `incr` is a single atomic statement. See [Persistence](/operations/persistence). Messages are handled one at a time.

```json theme={"system"}
{
  "nodeId": "count-failures",
  "type": "KV_STORE",
  "config": { "op": "incr", "key": "build-failures", "namespace": "ci" }
}
```

Each message that reaches this node adds 1 to `ci:build-failures`.

## Limits and defaults

| Plugin | Timeout default | Other defaults |
| - | - | - |
| `PROCESS_EXECUTOR` | `timeout` 300 s; node 330 s | output 10000 lines and 10 MiB per stream; 256 at once |
| `HTTP_REQUEST` | `timeout` 30 s; node 35 s | 256 at once |
| `SQL_QUERY` | `timeoutSeconds` 30 s, also the node timeout | `maxRows` 1000; one connection per message |
| `FILE_READ` | node 30 s | `maxBytes` 10 MiB |
| `FILE_WRITE` | node 30 s | none |
| `KV_STORE` | node 30 s | `maxEntries` 10000 |

## Related

* [Failure handling](/concepts/failure-handling)
* [Security](/operations/security)
* [Variables and expressions](/building/variables-and-expressions)
* [Time limits](/concepts/time-limits)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.