Skip to content

Choosing a workflow engine when the licence is the constraint

Podifi · Software Engineer · Sep 2026 — Sep 2026

Selected a durable workflow engine for multi-stage agent work, where the deciding constraints were commercial licensing and who owns the truth.

  • TypeScript
  • Python
  • Temporal
  • Hatchet

Context

Agent work had grown from single-turn assistants into multi-stage pipelines with human conversation between stages. Individual agent runs were durable; progression between them was not, so a crash after accepting work could lose it, and nothing owned which stage was current, when a stage was genuinely complete, or how a tenant's pipeline differed from the default.

Decisions

Which durable workflow engine fits, given the platform must be self-hosted commercially?

Chosen

Self-hosted Temporal, chosen on its MIT licence

Temporal, Hatchet, Conductor and Inngest were all functionally adequate, so capability did not decide it — licensing did. The comparison records no functional verdict for Restate, only its licence terms, so nothing is claimed here about its capability either way. As evaluated in September 2026, Inngest self-hosts under SSPL and Restate under BSL, and source-available terms that restrict offering the software as a service are a commercial problem rather than a technical one; they do not become less of a problem after integration. The runner-up was kept explicitly: if Temporal's operational cost proved prohibitive, Hatchet was the documented fallback, MIT-licensed and Postgres-backed for a lower operational burden.

Rejected alternatives

  • Inngest, already proven in an existing proof of concept

    It was the quickest to stand up, already running here, and genuinely good at the job — which is what made it the tempting answer. Its self-hosted server licence, as it stood at evaluation, was incompatible with shipping the platform commercially, and a licence discovered after integration is far more expensive than one discovered during selection.

  • Start on Temporal Cloud and self-host later

    Defers the operational burden, which is real. It also defers the requirement that actually drove the decision, and a migration undertaken once workflows are in flight is not the same task as choosing correctly at the start.

Where does the truth about a ticket live once an engine tracks its progress?

Chosen

The domain database stays authoritative; the engine holds execution state only

An engine that accumulates the answers to domain questions becomes the system of record by default rather than by decision, and every later migration then has to reconstruct that truth from execution history. Keeping the boundary explicit is what makes the engine replaceable at all.

Rejected alternatives

  • Let the engine hold the workflow's state as the single source of truth

    It removes a synchronisation problem and is the path of least resistance once the engine is already durable. It also hands ownership of domain truth to an infrastructure dependency, which is a decision most teams make by omission rather than on purpose.

Should every service talk to the engine, or should one service own it?

Chosen

One dedicated service owns the engine; others call that service

A durable engine is infrastructure, and spreading its client across every service spreads its upgrade cycle, its failure modes, and its licence surface with it. One owner keeps the dependency substitutable and gives the boundary somewhere to live.

Rejected alternatives

  • Embed the engine's SDK in each service that participates

    Direct, and it removes a network hop that looks like pure overhead. It also couples every service to one vendor's client, so the replaceability the licence decision was made to preserve is given away in the integration.

What event should advance a workflow when an agent can take several turns?

Chosen

Advance only on explicit completion, with approval where required

A finished turn and a finished stage are different events that look identical from the outside, and conflating them is the kind of bug that only appears in conversations long enough to matter. Separating them makes advancement deliberate and, more usefully, testable.

Rejected alternatives

  • Advance whenever an agent finishes responding

    The obvious signal, already available, and correct for single-turn work. It is wrong the moment an agent returns control to a human mid-stage, because the workflow moves on while the actual work is still unfinished.

Outcomes

Every outcome below is shown with the evidence behind it.

Outcomes and their evidence: Choosing a workflow engine when the licence is the constraint
OutcomeResultEvidence
Commercially viable engine selectionValidatedValidated a self-hosted engine choice against commercial licensingGrading candidates on licence and operational burden alongside capability is the standard way to choose infrastructure that must ship inside a commercial product, because capability is the constraint least likely to be binding. Five engines were compared on those axes and the decision recorded before any production wiring began.
A replaceable orchestration boundaryEnabledEnabled an engine boundary that leaves domain truth outside itKeeping the domain database authoritative and the engine confined to execution state is the recognized way to stop infrastructure becoming a system of record by default. Fixing that ownership split in the recorded design, before any production wiring, is what leaves the engine substitutable later; demonstrating the behaviour was the follow-on evaluation, not this decision.

Architecture

The engine owns execution, the domain owns truth

Incoming work reaches a single workflow service that owns the durable engine; no other service talks to the engine directly. The engine drives stage progression and holds execution state, while the domain database remains authoritative for tickets, plans, and assignments, so the engine can be replaced without reconstructing domain truth from execution history. A stage advances only on an explicit completion signal with approval where required, never on an agent merely finishing a turn. Which stages a tenant runs is read as configuration rather than branched in code.