Message transports

Configure message transport plugins for communication between workers and server.

The Resonate Server delivers task messages to workers over a configurable set of transports. As of v0.9.7, four transports ship in the server binary:

TransportAddress schemeEnable flagDefault
HTTP pushhttp:// / https://--transports-http-push-enabledtrue
HTTP poll (SSE)(used by SDK clients that long-poll the server)--transports-http-poll-enabledtrue
GCP Pub/Subgcps://--transports-gcps-enabledfalse
Bash execbash://--transports-bash-exec-enabledfalse

Each transport can be turned on or off independently. The full CLI flag list is on the Run a server page; the equivalent resonate.toml keys live under [transports.<name>].

HTTP push#

The HTTP push transport delivers task messages by POSTing to a worker URL embedded in the task address (for example https://workers.example.com/tasks). It's enabled by default and needs no further configuration for unauthenticated targets.

For deployments where the target service requires authentication, the server can sign outbound requests with a static bearer token or a GCP OIDC ID token. See Outbound HTTP push authentication on the Security page.

HTTP poll (SSE)#

The HTTP poll transport lets SDK clients receive tasks over a long-lived Server-Sent Events stream instead of an inbound HTTP push. It's enabled by default and requires no per-deployment configuration; concurrency limits are tunable via --transports-http-poll-max-connections and --transports-http-poll-buffer-size.

GCP Pub/Sub#

The GCP Pub/Sub transport puts task messages onto Pub/Sub topics for consumption by SDK clients using the matching client-side plugin.

Enable it at runtime with the GCP project ID:

code
resonate serve \
  --transports-gcps-enabled true \
  --transports-gcps-project my-gcp-project-id

Or in resonate.toml:

code
[transports.gcps]
enabled = true
project = "my-gcp-project-id"

Or via env var: RESONATE_TRANSPORTS__GCPS__ENABLED=true and RESONATE_TRANSPORTS__GCPS__PROJECT=my-gcp-project-id.

Authentication uses Application Default Credentials (ADC) — make sure the server process has access to credentials with publish rights on the target project.

Bash exec#

The bash exec transport runs an inline shell script for each delivered task. It's intended for local-process workers, lightweight wrappers around CLI tools, agent sandboxes, and self-hosted single-host deployments where running a separate worker process is overkill.

The transport is disabled by default. Enable it explicitly:

code
resonate serve --transports-bash-exec-enabled true

Or in resonate.toml:

code
[transports.bash_exec]
enabled = true

Or via env var: RESONATE_TRANSPORTS__BASH_EXEC__ENABLED=true.

Enabling is the only configuration the transport takes — there are no script-directory or working-directory settings.

Security

The bash exec transport runs arbitrary shell scripts. The local backend runs them as the user running the server, so only enable it on hosts where every promise creator is trusted. The docker and tensorlake backends isolate execution in a container or remote sandbox.

Backends#

The script body is always inline — it travels in the promise's param.data (base64-encoded). The task address selects which backend runs it:

AddressBackendWhat runs
bash://Localbash -c "<script>" on the server host
bash://docker/<image>Dockerdocker run --rm --entrypoint bash <image> -c "<script>" (requires the docker CLI on the server host; the promise environment variables are passed through with -e)
bash://tensorlake/<image>Tensorlakethe script in a Tensorlake sandbox; requires TENSORLAKE_API_KEY in the server's environment. An empty <image> uses Tensorlake's default sandbox

Named scripts (a filesystem path after the scheme, e.g. bash:///path/to/script.sh) are not supportedbash:// carries no path, and the local backend rejects an address that includes one.

Inline scripts#

Set the promise's param.data to the base64-encoded script body:

code
echo -n 'echo "hello from $RESONATE_PROMISE_ID"' | base64
# ZWNobyAiaGVsbG8gZnJvbSAkUkVTT05BVEVfUFJPTUlTRV9JRCI=

Then create a task targeting bash:// (or bash://docker/<image> / bash://tensorlake/<image>) with that base64 as param.data.

Script environment#

The server sets three variables in the script's environment. All three are stable across retries, so a re-delivered task reproduces the same values:

VariableValue
RESONATE_PROMISE_IDthe originating promise id (correlate logs, metrics, or follow-on calls)
RESONATE_PROMISE_CREATED_ATpromise creation time, epoch milliseconds
RESONATE_PROMISE_TIMEOUT_ATpromise timeout, epoch milliseconds

Settle behavior#

The promise is settled from the script's exit status:

  • Exit 0 — the promise resolves with the script's stdout (trimmed of trailing whitespace) as the resolved value.
  • Exit non-zero — the promise rejects. The rejection reason is the script's stderr if non-empty, otherwise the literal string exit code N.

A failure to spawn the backend — for example docker not on PATH, or a missing TENSORLAKE_API_KEY — rejects the promise with a descriptive reason, as does a param.data that is missing or not valid base64/UTF-8.

If the script is killed by a signal (a local kill, a docker OOM, or an exceeded Tensorlake sandbox lifetime), the run is treated as an infrastructure failure rather than a workflow failure: the task is dropped, the lease expires, and the message is re-dispatched to a fresh worker.

While the script runs, the server refreshes the task lease so long-running scripts don't lose it.

For the resonate-bash MCP tool, which drives this transport from Claude Code, see Durable bash.

Planned#

The transports below are specified in the Message Passing Protocol but are not yet implemented in the current server.

Kafka#

Heads up: @resonatehq/kafka NPM plugin is legacy

The published @resonatehq/kafka NPM plugin (v0.1.x) targets the legacy Go-based Resonate Server. It does not work with the current Rust server (v0.9.x). Use the Kafka worker pattern today; native kafka:// transport is on the roadmap below.

Not available in the current server. Native kafka:// transport is on the roadmap.

For Kafka integration today, use a normal Kafka consumer to dispatch a Resonate workflow per message — see the Kafka integration guide and the Kafka worker example.

The published @resonatehq/kafka plugin (and the resonatehq/resonate-transport-kafka-ts repo) targets the legacy Go server and does not work against the current Rust server. Don't install it expecting it to wire up.

SQS#

Not available in the current server. AWS SQS transport support is on the roadmap.