Skip to content

Run scripts in your deployment (operators)

A runner job completes one recorded calculation run by executing a short Python script inside your deployment and publishing its result through the ordinary publication path. It is how an approved agent — or a person — can have a calculation executed next to the data instead of on a laptop. Runners are off until an operator enables them; ordinary use never needs them.

  1. Install the runtime image once, pinned by digest, on the gateway host (the runner never pulls):

    Terminal window
    podman pull docker.io/library/python@sha256:f77ac9e44ae96ef2c90b8053ea08c31f8be030f824196b0ae4db6d462c84e51f
  2. Start the gateway with:

    Variable Meaning
    OPHIOLITE_RUNNER_IMAGE The installed image (tag or digest). Required; its local image id is recorded in every receipt.
    OPHIOLITE_RUNNER_SLOTS Jobs executed at the same time (default 1).
    OPHIOLITE_RUNNER_RUNTIME Optional OCI runtime, for example runsc (gVisor) where installed.

    The gateway user needs rootless Podman. Without OPHIOLITE_RUNNER_IMAGE the runner operations answer 503 (runner-unavailable); a named image that is not installed stops the gateway from starting with a clear message.

Each job is one container: no network (--network=none), no registry contact (--pull=never), a read-only root filesystem, the input mounted read-only, no Linux capabilities, no privilege escalation, an unprivileged user (65534), 16 MiB of /tmp, and one writable output folder. The script reads /input/curve.json (depth axis, values, curve, unit and the run’s parameters) and writes /output/changes.json, a list of {"index": i, "value": v}. The gateway accepts exactly that one regular file (no links, no pipes, no extra files), at most 1 MiB, finite numbers or null, each sample once, and publishes it as the person who submitted the job. The receipt of the result names the engine, image id and job — and, for an agent, its approved plan.

Who may submit: a project member with permission to run calculations, for one of their own started, unpublished runs on an exact input; an automation credential with the compute scope; or an agent with the compute scope through a plan its person approved (a job always waits for approval, because it publishes). Permissions, the agent’s validity and the project’s ownership (see project moves) are checked again when the job starts and again before it publishes.

Limit Default Exceeded
Memory 256 MiB (swap included) killed: memory
Wall time 60 s killed: time
CPU 1 throttled
Processes 64 the script’s fork fails
Output folder 1 MiB, 4 files killed: output
Logs kept 64 KiB (the newest) older lines dropped
Queue 10 queued jobs per person, 50 per project submission refused (413)

A submitter may ask for lower memory, time and CPU. Jobs are started fairly: the person whose last job started longest ago goes next, so one person’s backlog does not hold others back.

  • Cancel a queued job (it never runs) or a running one (its container is killed); the result stays unpublished.
  • Restart: jobs survive a gateway restart. A container still running is re-attached with its original deadline; one that finished is collected; a result that was already published is recorded without running again; a job whose container is gone runs once more, then fails as interrupted twice. A job cancelled while the gateway was down stays cancelled.
  • Containers are named ophiolite-job-<id> and removed after collection. List leftovers with podman ps -a --filter name=ophiolite-job-.
  • A project with queued or running jobs cannot be moved to another deployment until they finish or are cancelled.

No GPUs, no distributed scheduling, no hosted execution and no Firecracker. Scripts run with the image’s Python only; there is no package installation. The runner code, image choice and notices are listed for the open-source release in the source boundary; publication follows that release process.