Skip to content

Ironflow v0.29.0

Ironflow v0.29.0 closes the system.* event coverage gaps found by the #1715 audit, so every lifecycle change on a function, worker, outbox, projection, webhook, entity stream, environment, and cron run is now observable from a subscription. SQL projections become indexable and expression-queryable, a typed Python client ships from a new OpenAPI 3.1 spec, and pull workers checkpoint their steps mid-run.

  • Webhook delivery frames carry the outcome in the subject. system.webhook.{sourceId}.delivery becomes system.webhook.{sourceId}.delivery.{success|failure}, so a subscriber that wants failures no longer receives every delivery and inspects the payload. Two leaf values, not four, because NATS wildcards cannot OR two leaf tokens. system.webhook.> subscribers are unaffected. A subscriber pinned to the literal four-segment subject must move to > or to the outcome filter (#1726, #1738)
  • CreateWebhookSourceInput in @ironflow/core drops id and requires name. Nothing regresses on the 0.28.0 train: the server has reserved proto field 1 and required Name since #993, so webhooks.create() returned InvalidArgument on every call. Migration is in the CHANGELOG (#1526, #1783)
  • Full system.* event coverage. Fifteen new subject families close the gaps from the #1715 audit: system.function.{id}.registered and .deleted, system.worker.{id}.health, system.projection_health.*, system.outbox.* DLQ lifecycle, system.stream.* appends and .snapshotted, system.environment.{id}.deleted, and system.run.{id}.created from the cron fire path (#1715, #1722, #1723, #1724, #1725, #1729, #1730, #1747, #1748, #1749, #1743)
  • Indexable, expression-queryable SQL projections. Query results keep their SQL type instead of being stringified at the store boundary — numbers stay numbers, booleans stay booleans, NULL stays null, and exact-precision types come back as strings so money does not round. Projection DDL is now bound statement by statement to a permitted shape (#1641, #1657)
  • Typed Python client and OpenAPI 3.1 spec generated from route schemas. Go route registrations reflect into JSON Schema, the route registry becomes the single schema source of truth (ADR 0054), and the server serves the spec with auth declared. The 15 hand-written platform DTOs are dissolved (#308, #1646, #1649)
  • Webhook management in the JS SDKs. @ironflow/node gains webhooks.getSource, updateSource, rotateSecret, expireSecretPrev, and disableSignatureVerification; @ironflow/browser gains those plus rotateIngestToken. A ConnectRPC parity gate now reconciles the Connect half of the surface, not only REST (#1526, #1783)
  • Pull workers checkpoint steps during execution. Mid-run step results persist without releasing the execution lease. Checkpoint batches number from the worker’s cursor, so a 3000-step ingest no longer triggers an O(n²) blob-download storm (#1670, #1686)
  • Flow map covers the whole topology. Topic nodes, function-to-topic publish edges, entity streams, and cron schedules now appear alongside functions (#1708, #1710, #1712, #1713, #1718)
  • Two RAG examples. examples/rag-core is part 1 of the RAG guide series. examples/financial-rag adds recurring ingest, agentic query, and an eval gate (#1648, #1653, #1669)
  • RBAC fail-open closed, plus two cross-tenant queries. A route that ActionForEndpoint did not map reached its handler with no role requirement. A new gate enumerates every registered non-public route and fails unless it maps to an action or carries a written reason — it found 32 unmapped routes, five times what the original report named (#1645, #1650)
  • UpdateWebhookSource could silently disable signature verification. verify_header and verify_algorithm were full-replace, so a rename on a source with no verify config cleared both columns while verify_secret stayed set. The ingest path then accepted unsigned deliveries while verify_secret_set still read true. Both fields preserve on omit now (#1526, #1783)
  • ConnectRPC RBAC coverage completed. The remaining Connect routes are mapped, and PubSubService write RPCs require events:emit (#1651, #1709, #1717, #1732, #1770)
  • SQL projection hardening. Projection tables are scoped to environments, DDL scans for table references over stripped text, LIKE on a table and nested SQL comments are rejected, backslash and backtick are refused, projection names are bounded, DDL foreign keys are rejected, and SQL callees are bounded on both the DDL and handler paths (#1676, #1677, #1679, #1684, #1687, #1699, #1701, #1685)
  • Missed cron slots are now reported. They were dropped with no record, so an hourly job could miss a day of runs with every signal green. Adds ironflow_cron_slots_skipped_total{environment,function_id} and a Warn line with the slot count and window. A slot counts as missed only when no node claimed it, checked against the dedup bucket. Policy is unchanged and nothing is backfilled (#1639, #1656)
  • Step history survives retries and resumes. Push step sequences continue across resumed segments, pull-worker steps persist before retry decisions, step-write failures now fail the run visibly instead of silently, and step ID collisions are prevented (#1700)
  • Push steps are keyed by unique SDK step IDs. Repeated step names kept separate results, and legacy ID-less rows are healed by run-scoped synthesized IDs (#1647, #1692)
  • Entity IDs, function IDs, worker IDs, and event names are validated before they reach a NATS subject. Empty subject segments are rejected, a webhook source ID collapses onto one segment, event names are bounded at the emit chokepoint, and snapshot entity types are validated (#1733, #1737, #1740, #1750, #1760, #1764, #1767, #1768)
  • Caller-supplied pubsub topics are validated at the publish boundary (#1719, #1736)
  • Cascading environment deletion is atomic and publishes its deletes (#1754, #1769, #1771)
  • A heartbeat timeout announces one disconnect, not two (#1741, #1765)
  • IRONFLOW_API_KEY resolves everywhere the worker already resolved it, and workers no longer 401-loop (#1672, #1673, #1687, #1703, #1787)
  • Cron-created runs are audited (#1766)
  • The flow map warns when the function fetch is truncated (#1721, #1734)
  • Vite configs use import.meta.dirname (#1695)
  • The quickstart cron function records its result (#1711)
  • Marketing get-started CTAs point at Getting Started, not Installation (#1644)
  • The SDK warns when a step.map or step.parallel branch skips the scoped client, which is the most common cause of a branch that runs but records nothing (#1671, #1794)
  • Docs: REST versus ConnectRPC and the Tier-2 generator limit are explained, crash recovery timing and outage blast radius are documented, Python tabs are added across the guides, the embedded-dashboard requirement for serve is stated, and 22 inventory and link gaps from a full-scope audit are closed (#1665, #1688, #1696, #1702, #1781, #1786, #1791)
  • The changelog generator anchors on the real previous release instead of the newest tag that merely starts with v (#1632)
  • CI drops every pull_request trigger and keeps main-only and nightly gates (#1779)
  • The Postgres end-to-end projection tests are actually gated now (#1678, #1682)
  • The Postgres column drift gate runs on macOS bash 3.2 (#1681)