Skip to main content

Execution model

One binary​

A pipeline compiles into one binary. That binary executes in two roles; the code is the same, the launch environment assigns the role:

RoleWhere it runsWhat it does
runthe run's process: a container launched by the server, or a process launched by the userdrives the run
machinea container on an agent's machineexecutes the actions addressed to that machine

There is no third form: everything the user writes executes in one of these two instances of the same binary.

Who connects to whom​

There are exactly three kinds of connections in the system, all leading to the server — outbound. The internal infrastructure — Temporal (the durable core), the image registry, the blob store — is invisible and unaddressable from outside:

The pipeline, the agent, and the browser know one address: the server's port.

The run​

A run starts through the server. The identifier names exactly one logical execution, in every state. The same request under the same id — the pipeline and the params — answers exists and returns that run, open or closed, however it closed: a client whose answer was lost asks again and nothing starts twice. Another request under a taken id is a conflict (ALREADY_EXISTS), never a second execution; a re-run is a new id. The horizon of this promise is the namespace's retention of executions: after it, an id is a new name. Absent is said only when the service says so — a server that could not ask Temporal answers UNAVAILABLE, not NOT_FOUND.

Under the pipeline's queue policy a firing that arrives while a run is live is queued: it has its run id at once and starts when the live run ends. The queue holds one firing — cron does not pile up behind a long run — and a newer firing displaces the older one, which is named in the answer and never runs.

There are two ways to give a run its executor:

  • Managed — an image is given at start: the server launches the container itself and removes it after completion.
  • Inplace — the user launches the binary themselves, anywhere: local development, their own orchestrator. The server only starts the run's process.

A run is recoverable: the execution history lives in Temporal, not in the process. A crash, a restart, a network drop lose nothing — when the executor comes back, the run continues from where it stopped. Successfully completed actions never execute twice.

Actions on a machine​

An action is always addressed to an agent and executes on its machine — not in the run's process. One container per (agent × run) pair, brought up by the first action:

The agent is a host, not an executor: it brings the container up and watches it, but never looks inside the actions.

The recording pass​

Before the run starts, the pipeline function executes once in recording mode:

  • nothing executes — declarations of actions and resources only get registered;
  • reads from resources return optimistic zeros (booleans — true), so guard conditions do not cut the walk short;
  • everything found — actions, resource kinds — is known to the executor before work begins.

This is why actions and resources are declared right where they are used, with no separate registration. The price: pipeline code must survive a pass with zero-valued data.

Run completion​

Success, failure, and cancellation end down the same road: