Ironflow is pre-1.0: APIs can change between releases. Pin a version. What is ready →
- Releases
- Ironflow v0.54.0
Ironflow v0.54.0
v0.54.0 adds canonical entity identity for projection handlers, optional event-wait deadlines, and run-frame versions so watchers can drop stale updates. It also changes the PostgreSQL connection-string parser, raises the minimum Go, NATS and Helm-upgrade requirements, and tightens several Helm values. Read Breaking Changes before you upgrade.
Breaking Changes
Section titled “Breaking Changes”- PostgreSQL DSN parsing follows pgx 5.11 (#3294). Query
+is literal, repeated query keys use the last value, and the first@ends userinfo. Credential and certificate discovery follows$HOMEon Linux and macOS, and%USERPROFILE%and%APPDATA%on Windows. Pin file paths that you relied on implicitly: losing an implicitroot.crtwithsslmode=requireremoves certificate-chain verification. Review connection-string escaping first. - Event waits reject live replies stored after their deadline (#3390). Both timeout policies use the server’s acceptance time on live delivery. A publisher timestamp cannot backdate a reply. An optional wait that cannot read event history after the two-minute replay grace fails with an unknown-outcome error. It never returns null on an uncertain replay.
StepClient.waitForEventtypes reflect nullable timeout policies (#3390). Default inline filters stay nonnullable. A variable typed as the generalEventFiltercan carryonTimeout: "return-null", so its result is nullable. Narrow the policy or check the result.- Node workers reject swallowed yielding-step signals (#3390). A handler or parallel branch that catches a yielding step and returns now fails nonretryably. Let the signal propagate, and catch ordinary errors inside
step.run. - Go
TestInterceptor.WaitForEventStepaccepts anoptionalflag and returns*Event(#3390). Custom test interceptors must return nil for optional timeout fixtures. ironflow_cancel_on_failures_totalno longer emitsstage="fallback"(#3316). Upgrading: remove alerts or queries that expect thefallbackstage. Uselist,getrun,markandironflow_cancel_on_reconciled_total.- The Go SDK requires Go 1.26 or later (#3296). The
godirective ofsdk/go/ironflowand theironflow-gomirror moves to 1.26.0. Build with Go 1.26 or later. - A tenant credential that names another organization in
X-Ironflow-Orggets403(#3233). Before, the server ignored the header and served the tenant’s own organization. Upgrading: removeX-Ironflow-Orgfrom requests that use a tenant API key or tenant session. The CLI--as-orgflag and the SDKs are not affected. - External NATS must be 2.14.7 or later (#3299). Move every NATS server through 2.14.9 before you upgrade Ironflow or NATS to 2.15.2. Ironflow refuses older external servers. Shipped servers now run NATS 2.15.2. Ironflow caps EVENTS, STEPS, RESULTS and CANCEL at 1,000 consumers and PUBSUB at 100,000.
- Helm releases with clustered bundled NATS must step through NATS 2.12 first (#3299). The NATS subchart moves from 2.11.10 to 2.14.x. NATS documents upgrades from 2.11 to 2.12 and from 2.12 to 2.14, but not from 2.11 straight to 2.14. Follow Upgrade bundled NATS. Do not use
--reuse-valuesfor this upgrade. - HTTP spans no longer carry the raw request path (#3254).
url.pathon the server span now holds the matched route, orunmatched.http.routecarries the same value. Upgrading: replaceurl.pathwithhttp.routein trace queries. - Helm: the PostgreSQL snapshot-cleanup and backup-verify CronJobs use a pullable image, and the chart rejects a bad
snapshotRetention.days(#3298). The jobs now runalpine/kubectl:1.36.4. A replacement image needs a POSIX shell,kubectl, and adatethat accepts-d @SECONDS. When the cleanup job renders,snapshotRetention.daysmust be a whole number from 1 to 3650. Upgrading: setdaysin that range, or leave it unset for 7. Quote the value so YAML does not read010as 8. - Helm: the PostgreSQL backup-verify job deletes its scratch cluster on SIGTERM and fails on hidden errors (#3331). A failed delete now exits non-zero and names the cluster to remove by hand. When the verify job renders,
backupVerification.timeoutmust be a whole number of seconds from 1 to 86400. Upgrading: settimeoutin that range. RebuildProjectionreturnsfailed_preconditionwhen a rebuild is already running or the projection status does not allow one (#3226). It used to returninternal, which the Go SDK retries. Upgrading: if you retry oninternal, wait for the running rebuild (GetRebuildJob) instead.- On SQLite,
IRONFLOW_SQLITE_MAX_BYTESnow refuses writes past the cap (#3043). The setting never worked before. A write past the cap returns 507 (REST) orfailed_precondition(RPC), and this includesDELETE. Upgrading: if you set this variable, compare it with the size of the database and its-waland-shmfiles before you upgrade.
Features
Section titled “Features”- Projection handlers receive canonical entity identity (#3390). Go, Node and Python handlers receive the entity ID, entity type and per-entity version during replay.
- Optional wait deadlines (#3390). Node
onTimeout: "return-null", Pythonon_timeout="return-null", and GoWaitForEventOptionallet a wait complete with null at its deadline instead of failing. - Run frames carry a
version(#3283). Asystem.run.{id}.{event}frame includes the run version. Watchers drop an older frame that arrives late. The SDK filterscreateRunVersionFilter,RunVersionFilterand the Python equivalent apply this rule. - A retry keeps the error of the attempt before it (#3254, ADR 0139). Each execution of a step is recorded with its start, end and error.
- Refused credentials are counted, traced, logged and audited (#3254). A request refused for its credential now leaves a trace.
- Dashboard: a cancelled run shows why it was cancelled (#573). The Runs list and run detail page show “Cancelled by user” or “Cancelled by event”.
- Example apps: notifier and Slack approvals (
d4bfda152). Two new examples.
- Run and step redaction removes stored error text (#3368).
RedactRunandRedactStepalso clear the error on the run and its child steps. - Pull retries no longer strand in
WAITING(#3358). Retry requeue releases the failed execution before the run waits again. - Managed projections deliver quiet-stream events without waiting for a full batch (#3391).
- A projection rebuild can be cancelled from any node, including after a restart (#3277).
- PostgreSQL: the schema migration lock and the projection rebuild drain hold behind a transaction-mode pooler such as PgBouncer (#3276, ADR 0137).
- A REST worker heartbeating on another node stays connected when the database clock is behind (#3318).
- A run no longer waits for capacity forever when its node dies just after creating it (#3337).
- A failed result publish no longer reports a finished run as a failed dispatch (#3280).
cancelOncancels are no longer lost after a database error (#3232, ADR 0136). AcancelOnspec on the trigger’s own event name no longer cancels the new run.- SQLite search folds non-ASCII case, as PostgreSQL does (#3229). Searching
énow findsÉon both. - A node no longer hangs at boot when a lost node holds the PostgreSQL migration lock (#3227).
- Dashboard: live updates work while a platform operator impersonates an organization (#3234).
/wsreads the organization from theorg_idquery parameter. - A pre-release no longer publishes the Helm chart or the compose files (#3240).
- Helm: snapshot cleanup no longer deletes a snapshot re-created during its run (#3331).
- Cluster debounce backs off between CAS retries while a competing write commits (#3362).
Improvements
Section titled “Improvements”- On PostgreSQL, one REST pull worker takes jobs about twice as fast (#3293).
- HTTP pull workers no longer need a sticky load balancer in a cluster (#3304, ADR 0138).
- A single REST pull worker no longer waits for a JetStream fsync per job (#3253).
- The engine image is built on Alpine 3.24 (#3295). Alpine 3.21 reaches end of life on 2026-11-01.
- The embedded NATS server moves to 2.14.9 (#3299). The Compose NATS image moves with it.
- The docs site shows the last release, not
main(#1631). - SQLite keeps table statistics, so the planner stops guessing between indexes (#3273).
- An API key’s “last used” time updates at most once per minute per key (#3350).
- The engine is built with Go 1.27.3.
go.modpins the toolchain. - Web and Desktop dependencies include security patches. The workspace resolves seroval 1.6.11.