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

Ironflow v0.50.0

Ironflow v0.50.0 lets a Rego policy deny an emit by event name, and records the authenticated emitter on every event. It also reserves the Ironflow-owned names (ironflow., $, ironflow_, MOD_, current) and selects the request environment by ID only. Read the breaking changes before you upgrade: 19 items need action.

  • Emitting ironflow.invoke.<fn> needs functions:invoke on function <fn>, and NATS ingest rejects the name. A custom role with events:emit and no functions:invoke loses access. Grant functions:invoke, or call InvokeFunction. A trigger on another function (>, ironflow.invoke.*) no longer matches the event (#3109)
  • Entity stream AppendEvent now checks events:emit as well as entities:append. Grant events:emit to any custom role or key that appends (#3109)
  • The header X-Ironflow-Environment is now X-Ironflow-Environment-ID, and the query parameter env is now env_id, on every REST, WebSocket and Connect path. The old spellings return 400. The SDKs, CLI, MCP server, dashboard and desktop app already send the new spelling (#3088, #3087)
  • A selector value that is not current and not an env_... ID returns 400. The server keeps no environment-name lookup and no cache. ironflow env list prints IDs (#3088, #3087)
  • IRONFLOW_ENV, --env, the SDK environment options and the MCP env argument take an environment ID, for example env_default. prod or default returns 400
  • Go HeaderEnvironment is now HeaderEnvironmentID. JS HEADERS.ENVIRONMENT is now HEADERS.ENVIRONMENT_ID. DEFAULT_ENVIRONMENT is removed from @ironflow/core; use "env_default" or send no environment
  • Python SDK: x_ironflow_environment= is now x_ironflow_environment_id= (secrets and outbox methods), and env= is now env_id= (capacity and outbox methods)
  • The CLI, Node serve, the Node and Python pull workers, Python serve and Python register no longer send "default". With no environment set, the CLI sends current and the others send no header. The discovery file and the desktop IRONFLOW_ENV hold env_default where they held default
  • Browser SDK: with no environment set, the offline queue database name and destination change from "default" to "". Writes queued by an older SDK do not drain. Drain the queue before you update, or set environment to an ID
  • An Emit, Trigger or entity AppendEvent with a name that starts with ironflow. returns invalid_argument. Emit and Trigger accept ironflow.invoke.<fn>. Rename your own events. See Reserved Names in the reference (#3092)
  • ironflow/cron.<fn> is now ironflow.cron.<fn>, and ironflow/function.invoked is now ironflow.function.invoked. Stored events keep the old names. Update your triggers and subscriptions. An InvokeFunction Idempotency-Key that crosses the upgrade returns 409; use a new key (#3092)
  • approve() takes the event name to wait for: approve("deploy-api.approved", { ttl }). The agent.approve.<name> convention is removed in the Go, Node and Python SDKs. The A2A --caller-input lists <function-id>/<event-name> (#3092)
  • An event name that starts with $ returns invalid_argument on Emit and Trigger (#3092)
  • Registering an event schema for a name a user cannot emit returns invalid_argument (every ironflow. and $ name except ironflow.invoke.<fn>). The old call did nothing (#3092)
  • A webhook event_prefix under ironflow. or $, or ironflow alone, returns invalid_argument when you save the source. Stored sources stay. A delivery whose event name starts under an owned root fails with 400 (#3092)
  • Projection names that start with ironflow_ return invalid_argument for managed, external and SQL projections. A stored projection stays, and its next re-register fails (#3092)
  • Creating a KV bucket that starts with MOD_ returns 400 (#3092)
  • Creating or renaming an environment to current returns 400. An environment already named current stays, but a PUT that keeps the name fails until you rename it (#3089, #3092)
  • TriggerBatch reports a failed item. Go TriggerBatch returns []TriggerBatchResult, and Node and browser triggerBatch() return TriggerBatchResult[], not EmitResult. TriggerResponse has a new error field (code, message). Check error on each result. Go TriggerBatchResult embeds EmitResult, so r.EventID still works (#3107)
  • A Rego policy can deny an emit by event name. Every user-door emit (Emit, Trigger, TriggerSync, TriggerBatch, pub/sub Emit, entity appends, webhook ingest) is checked as events:emit on the event IRN. A denied emit stores nothing and returns PermissionDenied (403). A policy can limit a webhook source to its own names with the new subject.webhook_source_id (#3109)
  • Every event records its authenticated emitter in emitted_by: apikey:<id>, user:<id>, platform_user:<id>, webhook:<source-id>, nats or platform. The engine sets it from the credential. It shows on event reads, stream reads, the waitForEvent result in the Node, Go and Python SDKs, and the approve() result. Read it to check who approved (#3109)
  • The $stream.deleted tombstone records the caller who deleted the stream (#3118)
  • The dashboard audit page shows the webhook source as the actor of a webhook denial (#3127)
  • A fresh install of the Python SDK no longer fails on import. protobuf-py 0.7.0 broke connectrpc 0.12.1, so ironflow-py now requires protobuf-py>=0.3,<0.7. If you run an older ironflow-py, add protobuf-py<0.7 to your requirements (#3111)
  • A database fault in the environment org check returns 500, not 403 or 404. A client now retries, and a store fault no longer records a cross-tenant probe. On the platform /api/v1/capacity/* views, env_id=current means no filter (#3088)
  • A bad projection name is rejected with invalid_argument before it is stored. The SDK runners no longer retry internal forever (#3092)
  • The pub/sub reserved-prefix error lists all five prefixes, including topic: (#3092)
  • Entity stream reads reject an ironflow: ID in any case (#3092)
  • A Trigger or Emit call that the caller cancels returns canceled or deadline_exceeded, not internal. A canceled TriggerBatch emits no more items (#3107)
  • TriggerSync checks the emit name before its debounce check, so a denied name does not reveal that a debounced function listens on it (#3119, #3125)
  • A denial inside a handler names its subject and layer in the audit row. A webhook denial records webhook_source_id, and a policy denial records policy_set_version and policy_set_mode (#3117)
  • A webhook delivery refused for an unresolved organization writes an authz.decision.denied audit row (#3128)
  • A webhook ingest reads the environment once (#3105)
  • Bump DOMPurify to 3.4.17, ip-address to 10.7.4 and vitest to 4.1.12 for security advisories (#3113, #3099)
  • Add Reserved Names to the docs sidebar, and an entity-streams explainer video with a Go example (#3112, #3106)