Ironflow is pre-1.0: APIs can change between releases. Pin a version. What is ready →
- Releases
- Ironflow v0.50.0
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.
Breaking Changes
Section titled “Breaking Changes”- Emitting
ironflow.invoke.<fn>needsfunctions:invokeon function<fn>, and NATS ingest rejects the name. A custom role withevents:emitand nofunctions:invokeloses access. Grantfunctions:invoke, or callInvokeFunction. A trigger on another function (>,ironflow.invoke.*) no longer matches the event (#3109) - Entity stream
AppendEventnow checksevents:emitas well asentities:append. Grantevents:emitto any custom role or key that appends (#3109) - The header
X-Ironflow-Environmentis nowX-Ironflow-Environment-ID, and the query parameterenvis nowenv_id, on every REST, WebSocket and Connect path. The old spellings return400. The SDKs, CLI, MCP server, dashboard and desktop app already send the new spelling (#3088, #3087) - A selector value that is not
currentand not anenv_...ID returns400. The server keeps no environment-name lookup and no cache.ironflow env listprints IDs (#3088, #3087) IRONFLOW_ENV,--env, the SDKenvironmentoptions and the MCPenvargument take an environment ID, for exampleenv_default.prodordefaultreturns400- Go
HeaderEnvironmentis nowHeaderEnvironmentID. JSHEADERS.ENVIRONMENTis nowHEADERS.ENVIRONMENT_ID.DEFAULT_ENVIRONMENTis removed from@ironflow/core; use"env_default"or send no environment - Python SDK:
x_ironflow_environment=is nowx_ironflow_environment_id=(secrets and outbox methods), andenv=is nowenv_id=(capacity and outbox methods) - The CLI, Node
serve, the Node and Python pull workers, Pythonserveand Pythonregisterno longer send"default". With no environment set, the CLI sendscurrentand the others send no header. The discovery file and the desktopIRONFLOW_ENVholdenv_defaultwhere they helddefault - Browser SDK: with no
environmentset, 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 setenvironmentto an ID - An
Emit,Triggeror entityAppendEventwith a name that starts withironflow.returnsinvalid_argument.EmitandTriggeracceptironflow.invoke.<fn>. Rename your own events. See Reserved Names in the reference (#3092) ironflow/cron.<fn>is nowironflow.cron.<fn>, andironflow/function.invokedis nowironflow.function.invoked. Stored events keep the old names. Update your triggers and subscriptions. AnInvokeFunctionIdempotency-Keythat crosses the upgrade returns409; use a new key (#3092)approve()takes the event name to wait for:approve("deploy-api.approved", { ttl }). Theagent.approve.<name>convention is removed in the Go, Node and Python SDKs. The A2A--caller-inputlists<function-id>/<event-name>(#3092)- An event name that starts with
$returnsinvalid_argumentonEmitandTrigger(#3092) - Registering an event schema for a name a user cannot emit returns
invalid_argument(everyironflow.and$name exceptironflow.invoke.<fn>). The old call did nothing (#3092) - A webhook
event_prefixunderironflow.or$, orironflowalone, returnsinvalid_argumentwhen you save the source. Stored sources stay. A delivery whose event name starts under an owned root fails with400(#3092) - Projection names that start with
ironflow_returninvalid_argumentfor managed, external and SQL projections. A stored projection stays, and its next re-register fails (#3092) - Creating a KV bucket that starts with
MOD_returns400(#3092) - Creating or renaming an environment to
currentreturns400. An environment already namedcurrentstays, but aPUTthat keeps the name fails until you rename it (#3089, #3092) TriggerBatchreports a failed item. GoTriggerBatchreturns[]TriggerBatchResult, and Node and browsertriggerBatch()returnTriggerBatchResult[], notEmitResult.TriggerResponsehas a newerrorfield (code,message). Checkerroron each result. GoTriggerBatchResultembedsEmitResult, sor.EventIDstill works (#3107)
Features
Section titled “Features”- A Rego policy can deny an emit by event name. Every user-door emit (
Emit,Trigger,TriggerSync,TriggerBatch, pub/subEmit, entity appends, webhook ingest) is checked asevents:emiton the event IRN. A denied emit stores nothing and returnsPermissionDenied(403). A policy can limit a webhook source to its own names with the newsubject.webhook_source_id(#3109) - Every event records its authenticated emitter in
emitted_by:apikey:<id>,user:<id>,platform_user:<id>,webhook:<source-id>,natsorplatform. The engine sets it from the credential. It shows on event reads, stream reads, thewaitForEventresult in the Node, Go and Python SDKs, and theapprove()result. Read it to check who approved (#3109) - The
$stream.deletedtombstone 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-py0.7.0 brokeconnectrpc0.12.1, soironflow-pynow requiresprotobuf-py>=0.3,<0.7. If you run an olderironflow-py, addprotobuf-py<0.7to your requirements (#3111) - A database fault in the environment org check returns
500, not403or404. A client now retries, and a store fault no longer records a cross-tenant probe. On the platform/api/v1/capacity/*views,env_id=currentmeans no filter (#3088) - A bad projection name is rejected with
invalid_argumentbefore it is stored. The SDK runners no longer retryinternalforever (#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
TriggerorEmitcall that the caller cancels returnscanceledordeadline_exceeded, notinternal. A canceledTriggerBatchemits no more items (#3107) TriggerSyncchecks 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 recordspolicy_set_versionandpolicy_set_mode(#3117) - A webhook delivery refused for an unresolved organization writes an
authz.decision.deniedaudit 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)