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.
Fields are mutually exclusive
Section titled “Fields are mutually exclusive”The API enforces the separation rather than ignoring irrelevant fields:
- A script job must have empty
url,query_params,headersandbody, andauth_type: "none". - An HTTP job must have empty
script_bodyandscript_args.
Unknown types fail closed
Section titled “Unknown types fail closed”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.
Choosing a runtime
Section titled “Choosing a runtime”- 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_msof 10 seconds for that reason.
What’s shared
Section titled “What’s shared”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.