- Core Concepts
- When Ironflow is unavailable
When Ironflow is unavailable
Ironflow is a server, not a library. Almost nothing the SDK exposes is computed in your process — emit, kv(), config(), secrets, entity-stream appends and projection reads are all network calls to the engine. There is no client-side cache to fall back on: sdk/js/node/src/config-client.ts and sdk/js/node/src/kv.ts hold no cache or TTL logic at all.
So the honest answer to “what happens when Ironflow is down” is not one answer. It depends on which of three things is down, and — for the third — on whether the thing you lost comes back.
┌──────────────────────────────────────────────────────────────┐ │ 1. your worker process dies → run resumes, nothing lost │ │ 2. one node of a cluster dies → survivors take over │ │ 3. no engine is reachable → this page │ └──────────────────────────────────────────────────────────────┘Cases 1 and 2 are Crash recovery. This page is case 3.
Sort by whether it comes back
Section titled “Sort by whether it comes back”The useful axis is not “which feature” but “is this recoverable”.
┌─────────────────┬──────────────────────────────────────────────┐ │ lost for good │ cron slots · inbound webhooks past retry │ │ pauses, resumes │ in-flight runs · workers · accepted events │ │ fails at call │ everything else — no cache, no degraded mode │ └─────────────────┴──────────────────────────────────────────────┘Lost for good
Section titled “Lost for good”Cron slots
Section titled “Cron slots”Scheduled runs that fall during the outage do not fire, and are not backfilled. On restart the scheduler computes the next slot from the current wall clock, so a two-hour outage of an hourly job simply loses two runs.
Skipping is a defensible policy — firing a burst of catch-up runs after an outage is often worse than firing none, and most cron bodies are not written to be idempotent across a thundering herd. The problem is that it is currently silent: there is no warning log and no metric for a skipped slot, so an hourly reconciliation job can miss a day of runs with every dashboard green. Tracked in #1639.
If a scheduled job matters, assert on its effect — a freshness check on whatever it produces — rather than trusting that it ran.
Inbound webhooks past the sender’s retry budget
Section titled “Inbound webhooks past the sender’s retry budget”A third party posting to your Ironflow webhook endpoint gets a connection error. What happens next is entirely the sender’s policy, and it is not yours to configure: some providers retry for days, some give up after a handful of attempts, some never retry at all.
Once the sender stops, that event is gone. There is no backfill path, because the event was never in your system to queue.
Stripe ──POST──► ✗ engine down │ └─ retries on ITS schedule, for ITS budget ──► gives up │ permanently lostThis is the only failure on this page that no amount of Ironflow-side durability can fix. If a provider offers an event-replay API, that replay is your recovery mechanism — know before the outage whether yours does.
Pauses, then resumes
Section titled “Pauses, then resumes”| What | Behaviour |
|---|---|
| In-flight runs | Resume from the last completed step once an engine returns. See Crash recovery. |
| Pull workers | Reconnect on a loop and never give up. No configuration needed; the worker logs each attempt. |
| Already-accepted events | Durable in the outbox and JetStream. They flush when the engine returns — see the persistence caveat below. |
| Subscriptions | Reconnect automatically — but whether they resume depends on how you subscribed. See below. |
Subscriptions resume only if you named a consumer group
Section titled “Subscriptions resume only if you named a consumer group”A subscription that passes a consumerGroup gets a stable durable consumer on the server, keyed by environment and group name. It reconnects to the same consumer and picks up its backlog.
A subscription with no consumer group — the default — is a per-connection fan-out subscription. It is not durable. On reconnect it is a new subscription, delivering from that moment on, with no memory of what it missed.
subscribe(pattern, { consumerGroup: 'billing' }) ──► durable, resumes subscribe(pattern, { onEvent }) ──► fan-out, starts at "now"If missing an event during a reconnect would be a correctness problem, name a consumer group.
That said, this matters less during a full outage than it looks: nothing can be published either, because publishing also goes through the engine. The backlog that survives is the work accepted in the moments before the engine went away.
Your application does not crash — it goes quiet
Section titled “Your application does not crash — it goes quiet”Push-mode functions are HTTP endpoints that Ironflow calls. When the engine is down it simply does not call them. Your service stays up, healthy, and idle. Nothing in your process fails, because nothing in your process is being asked to do anything.
This is worth knowing because it shapes your alerting: the absence of Ironflow traffic will not trip a liveness probe. Alert on work completed, not on the process being up.
Fails at the call site
Section titled “Fails at the call site”Everything else throws immediately. There is no degraded mode and nothing is queued:
emit,invoke,cancelRunstreams.appendand entity-stream readskv(),config(), secretsgetRun,listRuns,getProjection- time-travel and audit reads
Handle these the way you handle any dependency that can be unreachable — at the boundary, with a timeout and a fallback appropriate to the call site. A failed config() read on a hot path is an outage in your service too, if you let it be.
Your tooling is down with it
Section titled “Your tooling is down with it”The CLI talks to the engine over HTTP, and the dashboard is embedded in the same binary. ironflow inspect, ironflow secret and the dashboard are all unavailable during an outage — you are blind to exactly the runs you are worried about, at exactly the moment you want to look at them.
Plan for this: your run-state visibility during an Ironflow outage is whatever you have exported to your own observability stack, not the dashboard. See Audit logging.
The one exception
Section titled “The one exception”@ironflow/browser has an opt-in offline queue. Writes go to IndexedDB and flush when connectivity returns.
Its scope is narrow and worth stating precisely:
- It wraps only
emitandstreams.append. Every read still fails. - It is opt-in and requires an
identity. - The rest of the client is unwrapped, on
.client.
It exists for a browser in a tunnel, not for an engine that is down. If your users are on a train, it is the right tool. If your engine is down, it changes nothing for your backend.
Designing for it
Section titled “Designing for it”Three questions worth answering before an outage rather than during one:
- Which of your scheduled jobs would be harmful to skip? Those need an effect-based freshness check, because Ironflow will not tell you they were skipped.
- Which of your inbound webhook providers support replay? That answer is your only recovery path for the outage window.
- What can you see when the dashboard is gone? If the answer is “nothing”, export audit events somewhere you control.
Note that a single-node deployment makes every case on this page reachable at once. Multi-node clustering with PostgreSQL turns most engine failures into case 2 — one node dies, survivors take over — which is a different and much shorter page. See Self Hosting.