Skip to content
Ironflow is pre-1.0: APIs can change between releases. Pin a version. What is ready →

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.

  • 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 $HOME on Linux and macOS, and %USERPROFILE% and %APPDATA% on Windows. Pin file paths that you relied on implicitly: losing an implicit root.crt with sslmode=require removes 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.waitForEvent types reflect nullable timeout policies (#3390). Default inline filters stay nonnullable. A variable typed as the general EventFilter can carry onTimeout: "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.WaitForEventStep accepts an optional flag and returns *Event (#3390). Custom test interceptors must return nil for optional timeout fixtures.
  • ironflow_cancel_on_failures_total no longer emits stage="fallback" (#3316). Upgrading: remove alerts or queries that expect the fallback stage. Use list, getrun, mark and ironflow_cancel_on_reconciled_total.
  • The Go SDK requires Go 1.26 or later (#3296). The go directive of sdk/go/ironflow and the ironflow-go mirror moves to 1.26.0. Build with Go 1.26 or later.
  • A tenant credential that names another organization in X-Ironflow-Org gets 403 (#3233). Before, the server ignored the header and served the tenant’s own organization. Upgrading: remove X-Ironflow-Org from requests that use a tenant API key or tenant session. The CLI --as-org flag 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-values for this upgrade.
  • HTTP spans no longer carry the raw request path (#3254). url.path on the server span now holds the matched route, or unmatched. http.route carries the same value. Upgrading: replace url.path with http.route in 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 run alpine/kubectl:1.36.4. A replacement image needs a POSIX shell, kubectl, and a date that accepts -d @SECONDS. When the cleanup job renders, snapshotRetention.days must be a whole number from 1 to 3650. Upgrading: set days in that range, or leave it unset for 7. Quote the value so YAML does not read 010 as 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.timeout must be a whole number of seconds from 1 to 86400. Upgrading: set timeout in that range.
  • RebuildProjection returns failed_precondition when a rebuild is already running or the projection status does not allow one (#3226). It used to return internal, which the Go SDK retries. Upgrading: if you retry on internal, wait for the running rebuild (GetRebuildJob) instead.
  • On SQLite, IRONFLOW_SQLITE_MAX_BYTES now refuses writes past the cap (#3043). The setting never worked before. A write past the cap returns 507 (REST) or failed_precondition (RPC), and this includes DELETE. Upgrading: if you set this variable, compare it with the size of the database and its -wal and -shm files before you upgrade.
  • 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", Python on_timeout="return-null", and Go WaitForEventOptional let a wait complete with null at its deadline instead of failing.
  • Run frames carry a version (#3283). A system.run.{id}.{event} frame includes the run version. Watchers drop an older frame that arrives late. The SDK filters createRunVersionFilter, RunVersionFilter and 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). RedactRun and RedactStep also 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).
  • cancelOn cancels are no longer lost after a database error (#3232, ADR 0136). A cancelOn spec 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). /ws reads the organization from the org_id query 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).
  • 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.mod pins the toolchain.
  • Web and Desktop dependencies include security patches. The workspace resolves seroval 1.6.11.