Skip to content

Ironflow v0.28.0

Ironflow v0.28.0 gives @ironflow/browser a durable offline write queue, so a browser client keeps accepting emit and streams.append while the network is down. An emit retry that silently dropped its dispatch is fixed, every declared audit event type now persists on both backends, and a new travel-booking example tells the saga story end to end.

  • RetryRun is removed from the proto, all three SDKs, and the MCP tool surface. It was never implemented: the Connect handler returned Unimplemented unconditionally and no REST route backed POST /api/v1/runs/{id}/retry. Use ResumeRun / resumeRun / ironflow run resume instead. No caller can regress, because the method never returned a success on any surface (#1606)
  • Offline write queue for @ironflow/browser (ADR 0053). Opt in with createClient({ offlineQueue: { identity } }). The client writes emit and streams.append to an IndexedDB outbox before it sends, then drains in strict FIFO with jittered backoff, dead-lettering, cross-tab coordination through Web Locks, and token refresh on 401. Each queued write keeps the credentials and the destination it was enqueued with. This is a durable outbox, not background sync (#1605)
  • Travel-booking showcase example. examples/travel-booking/ books a flight and a hotel as one saga, with a compensation on every step and a chaos panel that drives crash, payment failure, slow payment, and offline from the browser. It covers entity streams, optimistic concurrency, durable steps, step.compensate(), live projections, and time travel (#1602)
  • An idempotent emit retry now re-runs dispatch. Emit commits the event row and then dispatches. When dispatch failed, a retry with the same idempotency key found the durable row, returned 200, and never dispatched again — so the matched functions never ran and the caller was told the write succeeded with no run behind it. An idempotency hit now re-runs DispatchMatched while the row is still processed=false. GetEventByIdempotencyKey also missed the entity columns, so an idempotent EmitEntityEvent retry returned an event with no entity (#1604)
  • All 43 audit event types now persist on both backends. The audit_events.event_type CHECK constraint was a hand-typed copy of the Go constants and had drifted to 28 values, so 15 types (every capacity.*, secret.*, kv.*, cluster.*, plus webhook.source.created and org.export) were rejected at INSERT and lost with only a warn log. The constraint is dropped rather than extended, because Go is the source of truth and no user-supplied value reaches the column. Adding an audit type no longer needs a migration. The dashboard filter grows from 9 to 43 options with search (#1603, #1601)
  • GET /api/v1/functions/{id} is registered. The ironflow_get_function MCP tool had a definition and a passing unit test, and 404’d against a real server (#1606, #1592)
  • Seven offline-queue defects from the pre-landing review are closed, each with a test that was red first. A sibling tab’s announce now resumes delivery at the drain() chokepoint, the pending counter can no longer race below zero and hand the cap free room, and a reconcile no longer evicts every watcher but one (#1615, #1609)
  • Collapsed dashboard sidebar icons are labeled on hover and focus (#1599)
  • The docs header logo and spacing match the marketing site (#1598, #1626)
  • Four public-mirror links that returned 404 now point at the right place (#1614, #1203)
  • New post: “Your Function Outlived Its Host” explains pull-mode workers (#1597)

Ironflow Desktop ships on its own cadence — see the Desktop changelog for its release notes.