Skip to main content

Temporal Execution Model

OrcFlows uses Temporal as its durable execution engine — every workflow run is a real Temporal workflow, and every step is a Temporal activity. This is what gives OrcFlows reliability properties that a typical request/response automation engine doesn't have.

Why Temporal

  • Durability — the full event history of an execution (every step's input, output, retry, and signal) is persisted by Temporal. If the worker process crashes or is redeployed mid-execution, the workflow resumes exactly where it left off.
  • Retries — each step's max_retries/retry_delay becomes a Temporal RetryPolicy on its activity. Transient failures (a flaky API, a rate limit) are retried automatically without re-running earlier steps.
  • Long waits are free — a human-in-the-loop approval step can block on a signal for days without consuming any compute; Temporal just persists the wait state.
  • Observability — the Temporal Web UI (port 8081) shows every workflow execution's full history independent of the OrcFlows UI, useful for debugging at the infrastructure level.

Task queues

The worker (cmd/worker) registers two task queues:

QueuePurpose
goagents-mainWorkflow orchestration (RunWorkflow, RunScheduledWorkflow, poller workflows) plus heavy activities: agent, orchestrator, code, stt, tts, document processing.
goagents-fastLightweight activities (transform, set, condition, http, etc.) that complete in well under a second, so they aren't queued behind long-running agent calls.

Each step type is statically classified as "fast" or not; the workflow function dispatches the activity to the appropriate queue.

Workflow functions

WorkflowStarted by
RunWorkflowManual trigger, webhook, public API run, signal-resumed retries.
RunScheduledWorkflowTemporal Schedule for cron-triggered workflows.
EmailPollerWorkflow, TelegramPollerWorkflow, etc.Temporal Schedules for event/poller triggers — periodically poll an external API and start RunWorkflow for each new item found.

RunWorkflow walks dsl.Steps, building an execution context (steps, trigger, vars, secret, input) that grows as each step completes. Steps whose depends_on/branch_deps are satisfied and whose condition evaluates truthy are dispatched as activities (in parallel where the DAG allows); their outputs are merged back into the context for downstream steps.

Activities

Every node type's Execute(ctx, config, inputs) runs as a Temporal activity (internal/execution/activities.go), registered against nodes.GlobalRegistry. Activity inputs/outputs are the StepInput/StepOutput types from pkg/dsl. Special-purpose activities also exist for:

  • MarkStepWaitingActivity / MarkApprovalCompletedActivity — human-in-the-loop bookkeeping.
  • CompleteWorkflowActivity — final status update + cleanup (e.g. cancelling a timer step).

Retries & resumption

POST /api/v1/executions/{id}/retry starts a fresh RunWorkflow execution with PreloadedOutputs populated from the failed run's completed steps. The workflow function checks outputs[step.ID] before dispatching each activity — if a result is already present (including for approval steps, which won't re-prompt), it's reused and the step is skipped.

Signals

workflow.GetSignalChannel(ctx, "approve-<step_id>") is how human approvals are delivered (see Human-in-the-Loop) — POST /api/v1/executions/{id}/signal calls Client.SignalWorkflow with an arbitrary signal name and JSON payload, so the same mechanism can be used for any "wait for an external event" pattern (e.g. resuming a voice session).

Encryption at rest

Set TEMPORAL_ENCRYPTION_KEY (a base64-encoded 32-byte AES-256 key) to encrypt workflow payloads in Temporal's datastore — recommended in production since step inputs/outputs may contain sensitive data before it's redacted for display.

Next