Skip to main content
Workflows execute inside your app. Hub delivers each run to the route you already mounted, and your app runs it in a sandbox against a tenant-scoped corsair client. Because this means running Hub-delivered code, execution is off by default. You opt in explicitly.

Turn on execution

Add allowWorkflowExecution: true to the hub block of createCorsair:
src/server/corsair.ts
That’s the whole setup. You’ve already mounted the handler for your framework, and Hub delivers runs through it. With the flag off, delivered runs are rejected with “Workflow execution is not enabled.”
Leaving allowWorkflowExecution off is the safe default: it means your app never executes Hub-delivered code. Turn it on only once you’re running workflows you trust.

How a run is shaped

A workflow is a single function you define in your code. It receives your tenant-scoped client, the trigger payload, and a step helper:
Inside, you wrap each unit of work in step(). That’s what turns an ordinary async function into a workflow that survives retries and pauses. This body is authored in Hub; once the workflow exists there, you run it from your app with corsair.workflows.run(id, { payload }).

step(name, fn), run once, durably

Each step runs its function once and memoizes the result. If the run retries, a step that already completed replays its saved output instead of running again, so a half-finished run never double-posts a message or re-charges a card. The step’s identity is its name plus its position in the run, which keeps that memoization stable across attempts.

step.sleep(name, ms), durable pause

step.sleep pauses the run durably. The run unwinds, Hub reschedules it, and a later attempt resumes past the sleep with every earlier step already memoized. A pause costs nothing while it waits, since there’s no process sitting idle.

Limits & rules

These are the current beta constraints. Expect the authoring surface (more step types) to grow.
  • 30-second cap per attempt. Each attempt has a 30s wall-clock budget; long waits belong in step.sleep, not in a running step.
  • Don’t rename or reorder steps mid-run. Step identity is positional, so renaming or reordering steps changes their keys and breaks memoization for in-flight runs. Safe to change between new runs.
  • Sandboxed. Workflow code runs in a hardened realm with no host globals (process, require, fetch, timers) and no eval. It talks to the outside world only through the corsair client it’s handed.
  • Retries are Hub-managed. You don’t configure backoff in code; Hub owns the retry schedule and re-delivers with prior steps memoized.

What’s next

Triggering

Start and list runs from the corsair.workflows client.

Overview

The author, trigger, and execute model, and why it’s built in.