Skip to content

Job Types

A job’s job_type decides what it does, which queue it lands on, and therefore which service executes it.

job_type Queue Executed by Interpreter
http default zilean-worker —
bash scripts-bash zilean-script-worker-bash bash
javascript scripts-node zilean-script-worker-node node
typescript scripts-node zilean-script-worker-node tsx
groovy scripts-groovy zilean-script-worker-groovy groovy

Anything that isn’t http is a script job.

The API enforces the separation rather than ignoring irrelevant fields:

  • A script job must have empty url, query_params, headers and body, and auth_type: "none".
  • An HTTP job must have empty script_body and script_args.

An unrecognised script type routes to a queue that nothing subscribes to. The job sits unworked rather than being executed by whichever worker happened to be available.

The same shape applies to a runtime you simply haven’t deployed: a Groovy job with no Groovy worker running queues indefinitely, visible in River UI as stuck in scheduled/available. Deploy only the runtimes you use, but know that’s the consequence.

Queue subscription is the security boundary

Section titled “Queue subscription is the security boundary”

Each worker subscribes to exactly one queue, and the sets are disjoint — enforced by a test, and each binary logs its queues at startup so you can verify it.

This matters because the HTTP worker and the script workers have deliberately different network reach. A script job can never execute in the HTTP worker’s container, because that container is not subscribed to any script queue. It’s structural, not a policy check that could be bypassed.

  • Bash is the lightest. It is also the forkiest — every pipeline stage and $(…) is a process — so it’s the runtime most likely to hit a process-count ceiling.
  • JavaScript / TypeScript share the Node worker. TypeScript runs through tsx, so no separate build step is needed.
  • Groovy pays 1–2 seconds of JVM startup on every invocation, and enforces a minimum exec_timeout_ms of 10 seconds for that reason.

Both kinds use the same machinery for everything else: schedule types, loop, retry, pooling, overlap policy, global variable interpolation, timeouts, invocation counters and activity history.

The differences are only in how the work is performed and how the loop signal is read:

HTTP Script
Success Status 200–299 Exit code 0
Loop signal read from Response body stdout
Pool index surface pool_idx query param (only when pool_count > 1) ZILEAN_POOL_IDX env var (always)
Error snippet kept 500 chars 2000 chars (stack traces are longer)
Extra permission — jobs:script

See HTTP jobs and Script jobs.