- Comparisons & Licensing
- Comparison
Comparison
Last verified: September 2026. Vendor facts decay — please file an issue if anything below is out of date.
Every comparison below answers the same question: where does the product start?
The starting point determines what’s native and what’s bolted on. Ironflow starts from the recorded fact — every event and every recorded workflow step is the same kind of thing in one continuous history. Competitors start from workflows, event streams, or function triggers. They can add the other capabilities, but the foundation determines which ones emerge naturally and which feel stapled on.
High-Level Comparison
| Feature | Ironflow | Temporal | Inngest | Hatchet | Restate | Kurrent | Kafka |
|---|---|---|---|---|---|---|---|
| Starting Point | The recorded fact | The workflow | The function trigger | The durable task | The state machine | The event stream | The append-only log |
| Architecture | Single Binary (Embedded NATS) | Cluster (Server + DB + UI; ES/OpenSearch optional) | Managed cloud, or self-hosted server + Redis | Server + PostgreSQL (RabbitMQ optional) | Single binary; HA cluster (GA) | Server (clustering Enterprise-only from 26.2) | Cluster (KRaft brokers, ZooKeeper removed in 4.0) |
| Unified History | Native — events and execution in one timeline | No — separate systems | No — events retained per plan window, runs separate | No — execution event log only | No — retention-bounded journal | Partial — events only, no execution | No — log only, assemble everything |
| Local DX | Zero-Config | temporal server start-dev (in-memory by default) | npx inngest-cli dev (no Docker) | Docker (Hatchet Lite) or embedded mode (Go stable; TS/Python beta) | Single binary (brew/npm) | Docker (deb/rpm on Linux; no macOS package) | Tarball + JVM, single-node KRaft |
| Execution | Push (HTTP) & Pull (HTTP polling) | Pull (gRPC) | Push (HTTP) & Connect (WebSocket workers) | Workers (bidirectional gRPC stream) | Push (HTTP/2, Lambda) | N/A (no execution) | N/A (no execution) |
| State Store | SQLite / Postgres | MySQL / PG / Cassandra | SQLite / PG + Redis (self-host) or managed | PostgreSQL | Embedded RocksDB + replicated log | Event Store | Topic partitions |
| Learning Curve | Low | High (Complex primitives) | Low | Low | Medium | Medium (ES concepts) | High (Ops-heavy) |
vs. Temporal
Temporal starts from the workflow. Ironflow starts from the recorded fact. Temporal can add event storage, but workflow steps and domain events will always be separate systems that happen to coexist — a bundle. In Ironflow, a workflow step completing IS a recorded fact, the same kind of thing as a domain event. That’s why you can query “show me everything that happened to order-123” and see both in one timeline. That capability emerges from the foundation — you can’t bolt it on.
Temporal is the gold standard for durable execution at enterprise scale. If you need millions of concurrent workflows across a distributed cluster, Temporal is battle-tested.
Where Ironflow differs: Temporal treats workflows and events as separate concerns. You can emit signals and queries, but the workflow history and your domain events live in different systems. In Ironflow, they’re the same history — which is why history navigation (time-travel debugging), history correction (hot patching), and unified entity timelines work out of the box.
| Ironflow | Temporal | |
|---|---|---|
| Setup | ironflow serve | temporal server start-dev (in-memory; --db-filename for SQLite) or self-hosted cluster |
| Unified history | Events + execution in one timeline | Workflow history only |
| Debugging | History navigation, correction, editing | Replay-based, Web UI |
| Scale model | Single node (SQLite) or multi-node cluster (Postgres + external NATS) | Distributed cluster (millions) |
Choose Temporal if you need massive distributed scale with proven enterprise support, or its Durable AI integrations (OpenAI Agents SDK, LangGraph, Pydantic AI). Choose Ironflow if you want events and recorded execution in one history and the tools to correct that history in production.
vs. Inngest
Inngest starts from the function trigger. Events kick off workflows and are retained for a plan-dependent window (hours to days) so you can inspect and replay them — but they are execution inputs, not a domain history. In Ironflow, events are permanent recorded facts. Workflow steps are permanent recorded facts. The trigger and the execution live in the same history. Inngest’s event store and run store are two systems sharing a product, not one unified history.
Inngest delivers excellent developer experience for serverless workflows. If you’re building on Vercel/Next.js and want managed infrastructure, Inngest is polished. Self-hosting is supported (SSPL with an Apache-2.0 future grant; SQLite or Postgres plus Redis), and the Connect mode adds persistent WebSocket workers alongside push.
Where Ironflow differs: Inngest treats events as triggers with a retention window — queryable via Insights, replayable in bulk, then gone. There are no entity streams, no projections to derive, no entity timeline to browse. Ironflow treats events as permanent facts that power projections, entity streams, and time-travel.
| Ironflow | Inngest | |
|---|---|---|
| Events | Permanent recorded facts | Triggers retained for a plan window (1h–3d lookback) |
| Event sourcing | Native (entity streams, projections) | Not available |
| Hosting | Self-hosted (single binary) | Managed cloud or self-hosted (SSPL) |
| Execution modes | Push + Pull | Push + Connect (WebSocket workers) |
Choose Inngest if you want a managed cloud service with zero ops for serverless functions. Choose Ironflow if you want your events to be permanent history, not disposable triggers.
vs. Hatchet
Hatchet starts from the durable task. Workers hold a gRPC stream to the engine, PostgreSQL is the source of truth, every checkpoint persists, and the dashboard replays the execution log. It’s a sharper Temporal: same shape, simpler ops, MIT-only. But the durability is execution-shaped, not domain-shaped — if you want your domain events and your workflow steps in the same history, Hatchet keeps them in different stores.
Hatchet is the cleanest “Temporal but Postgres-only” pitch in the market. If your team already runs Postgres, doesn’t need a separate broker, and wants a familiar workflow-engine model, Hatchet is sharp. Its new embedded mode bundles the engine and Postgres in-process for local dev (stable in Go, early beta in TypeScript and Python).
Where Ironflow differs: Hatchet’s “durable” means the execution event log — task started, task completed, retry attempted, plus durable event waits scoped to a run. Ironflow’s “durable” means the recorded fact — your domain events and your workflow steps in one continuous history. That’s why entity timelines, projections, and time-travel across workflows + events all work in Ironflow and don’t exist in Hatchet.
| Ironflow | Hatchet | |
|---|---|---|
| Storage | SQLite (dev) / Postgres (prod) + embedded NATS | PostgreSQL (source of truth and queue; RabbitMQ optional) |
| Durability scope | Domain events + execution steps in one history | Execution event log only |
| Execution modes | Push + Pull | Workers over bidirectional gRPC only |
| Agent surface | agent() handler with ctx-injected tool / llm / approve / memory / spawn + MCP server (ironflow mcp) | Generic tasks; child tasks as tools, compatible with Claude / OpenAI agent SDKs (no agent API) |
| History tools | Time-travel, hot patching, scoped injection, TUI/DAP | Dashboard replay |
Choose Hatchet if you want a sharper Temporal with Postgres-only operational simplicity for plain task workloads. Choose Ironflow if you want events and execution in one history, agent-native primitives, and history navigation/correction built in.
vs. Kurrent (EventStoreDB)
Kurrent starts from the event stream. Ironflow starts from the recorded fact. Kurrent records your data changes — and now offers SQL over them (Queries UI, Flight SQL) and atomic multi-stream appends. But your workflow execution, your authorization decisions, your debugging sessions will always live somewhere else. In Ironflow, those are all recorded facts in the same history. Cross-concern time-travel and an auditable history emerge from that unification.
Kurrent is the pioneer of event sourcing databases. If you need a dedicated, high-performance event store with mature subscription APIs, KurrentDB is the original. Note the license shift: it is source-available (Kurrent License v1), and from 26.2 multi-node clustering, read-only replicas, archiving and encryption-at-rest are Enterprise-only, while single-node production stays free.
Where Ironflow differs: Kurrent stores your domain events but doesn’t execute workflows. Its answer to sagas is multi-stream atomic append; for durable execution you need a separate orchestrator (Temporal, MassTransit, etc.). In Ironflow, workflow steps and domain events are both recorded facts in the same history — so you can see everything that happened to an entity in one view.
| Ironflow | Kurrent | |
|---|---|---|
| Event storage | Built-in (entity streams) | Dedicated event store |
| Workflow execution | Built-in (durable, memoized) | Not available (need separate tool) |
| Projections | Built-in (managed + external) | JS projections (V1 maintenance, V2 experimental); SQL is query-only and license-gated |
| Unified history | Events + execution together | Events only |
Choose Kurrent if you need a dedicated event store with mature subscription APIs and are fine with clustering behind an Enterprise license. Choose Ironflow if you want event sourcing and workflow execution unified in one history.
vs. Kafka
Kafka gives you the philosophy (append-only log) without the platform. You get the foundation but you assemble everything yourself — consumers, projections, workflow orchestration, monitoring. Ironflow gives you the same philosophy as a complete platform. Same starting point, none of the assembly.
Kafka is the industry standard for high-throughput event streaming. If you need to process millions of events per second across a distributed cluster, Kafka is proven.
Where Ironflow differs: Kafka gives you the log and leaves you to build everything on top — consumers, state management, workflow orchestration, projections, monitoring. Ironflow gives you the same append-only philosophy as a complete platform with workflows, projections, time-travel, and a dashboard.
| Ironflow | Kafka | |
|---|---|---|
| Setup | Single binary, <500ms boot | JVM + KRaft (single node for dev, multi-broker cluster for prod) |
| Workflows | Built-in durable execution | Not available (need Flink, Temporal, etc.; share-group queues since 4.2 are delivery, not orchestration) |
| Projections | Built-in | Not available (Kafka Streams is a client library you deploy) |
| Dashboard | Built-in | Not included (need Confluent, AKHQ, etc.) |
Choose Kafka if you need massive throughput event streaming for data pipelines. Choose Ironflow if you want the append-only philosophy as a complete platform.
vs. Restate
Restate starts from the distributed state machine. Ironflow starts from the recorded fact. Restate provides durable execution with an embedded state store and a SQL-queryable journal, but that journal is retention-bounded (24h by default) and events and execution remain separate concerns. In Ironflow, they’re the same history.
Restate offers durable execution with an elegant virtual object model, shipped as a single binary (BSL 1.1) with GA multi-node clusters, Restate Cloud, and Vercel AI SDK / OpenAI / LangChain agent integrations. If you’re building distributed state machines with strong consistency, Restate is innovative.
Where Ironflow differs: Restate focuses on durable execution and state management but doesn’t provide event sourcing, projections, or unified entity timelines. Ironflow’s recorded-fact foundation means all of these emerge naturally.
| Ironflow | Restate | |
|---|---|---|
| Event sourcing | Built-in | Not available |
| Projections | Built-in | Not available |
| State model | Event-sourced (derived from history) | Virtual objects (mutable state) |
| Unified history | Events + execution in one timeline | Execution journal only (24h default retention) |
Choose Restate if you want distributed virtual objects with strong consistency guarantees. Choose Ironflow if you want execution and events in one continuous history.
Key Differentiators
1. One History
Every competitor keeps two records: the workflow engine’s execution log and, somewhere else, your domain events. Restate’s journal expires after 24 hours by default. Inngest retains events for a plan window. Hatchet logs execution only. Kurrent keeps events and has no execution. Ironflow keeps one record: a domain event and a completed step are the same kind of fact in the same timeline.
- Ask “what happened to order-123” and get events, steps, and projections in one answer
- Projections and entity streams derive from the same history that workflows write to
- Nothing to join, sync, or reconcile across an event store and an orchestrator
2. History You Can Edit (The “Workflow DVR”)
Competitors added replay and introspection this year. None added correction. Because every step is a recorded fact, Ironflow can change the record and continue from it:
- History Correction (hot patching): Edit a failed step’s output in production and resume execution.
- History Navigation (time-travel debugging): Scrub through any execution frame-by-frame.
- History Editing (scoped injection): Pause a running workflow, modify step data, resume.
- TUI Debugger: Step through production runs in your terminal.
- DAP Support: Attach VS Code to a production run ID.
3. Push + Pull From One History
Restate is push-only; Temporal and Hatchet are worker-only; Inngest added Connect for persistent workers next to push. Ironflow runs both modes against the same history, so a serverless push function and a long-running pull worker write to the same entity timeline.
- Push Mode: Best for Next.js, Lambda, and serverless. Ironflow calls you.
- Pull Mode: Best for AI/ML, video processing, and long-running tasks. Your worker polls Ironflow over HTTP.
Single-binary local dev (embedded NATS JetStream + SQLite, cold boot to /health under 500ms in the TestBootTime bench — see Benchmarks) is still there. It is no longer the differentiator: Restate ships a single binary too, and Hatchet, Inngest and Kafka all run Docker-free locally now.
Code Comparison
Ironflow (TypeScript)
import { createFunction } from "@ironflow/node";
const processOrder = createFunction( { id: "process-order", triggers: [{ event: "order.placed" }], recording: true, // Every step becomes a recorded fact }, async ({ event, step }) => { const payment = await step.run("charge", () => stripe.charge(event.data.total)); await step.sleep("wait-for-inventory", "5m"); return { success: true }; });Temporal (Go)
func OrderWorkflow(ctx workflow.Context, order Order) (OrderResult, error) { options := workflow.ActivityOptions{ StartToCloseTimeout: time.Minute } ctx = workflow.WithActivityOptions(ctx, options)
var result ChargeResult err := workflow.ExecuteActivity(ctx, ChargeActivity, order).Get(ctx, &result) if err != nil { return nil, err }
workflow.Sleep(ctx, 5*time.Minute) return OrderResult{Success: true}, nil}When to Choose Ironflow
- You want one history, not many: Events, workflow steps, and projections in one unified timeline per entity.
- You need to fix a production run, not just replay it: Hot patching, scoped injection, and time-travel are correction tools, not viewers.
- You need an auditable history: With recording enabled, the audit trail is derived from the same history, not a separate layer to build.
- You are event-sourcing already: Entity streams, projections, and upcasters are native, and the workflow that reacts to an event lands in the same stream.
- You are building an AI agent:
agent()handlers withapprove/memory/spawnwrite to the same history as everything else, so an agent run is debuggable like any other run. - You are on the edge: Single Go binary for limited-resource environments.
Performance & Scale
Published numbers come from the in-tree bench suite (make bench + k6 load tests). See Benchmarks for thresholds, methodology, and how to reproduce on your hardware.
| Metric | Ironflow | Temporal | Inngest | Hatchet | Restate |
|---|---|---|---|---|---|
| Setup Time | <1 minute (single binary) | Minutes (start-dev); hours for a self-hosted cluster | Minutes (dev server) | Minutes (embedded mode or Docker) | Minutes (single binary) |
| Boot Time | <500ms (TestBootTime warn threshold) | Not published | N/A | ~25ms p95 dispatch (server boot not published) | Not published |
| Load-test p95 | event-emission <500ms, mixed-workload <1s, function-invoke <10s | Not directly comparable | Cloud-managed | Not published | Not published |
| Throughput (vendor-published) | Not yet published — depends on hardware + storage backend | 450k actions/s (Cloud) | Not published | 10k tasks/s load test; ~25ms p95 dispatch | 84k actions/s, 3-node bench (v1.2) |
| Memory Footprint | Not yet published | Not published | N/A (Cloud) | Not published | ~1.5 GiB minimal, ~4.75 GiB default (documented) |
Summary
Ironflow is the first Continuous History platform — events and execution unified in one recorded history, with the tools to navigate and correct it.
Other platforms start from workflows (Temporal), function triggers (Inngest), event streams (Kurrent), or append-only logs (Kafka) and can add the missing pieces. Since April they have: single binaries, Docker-free dev servers, retained events, SQL over journals, agent SDKs. But the result is still a bundle — separate systems coexisting in one product. The emergent capabilities (unified entity timelines, cross-concern time-travel, history correction) don’t appear because the foundation isn’t unified. Ironflow’s foundation is.