All versions since v0.3.0 (2026-09-28)
v0.3.0 (2026-09-28)
Added
-
Copy-pasteable recipes for everyday tasks — The documentation site now has a Recipes section with complete examples for recurring tasks, including start forms, dialogs with assignees, retries and final errors, deadlines, external bindings, identity hooks, and new versions. Each example has a test that checks it.
-
Workflow tests drive child workflows — Tests can deploy child definitions, complete their assigned dialogs, and await results through nested workflows. They report which child-workflow request is unsupported and when a child definition is unavailable.
-
Queries stream bounded results — Queries honor their declared filters, and large result sets are processed in bounded batches with explicit cancellation and error reporting.
-
Workflow definitions can decode custom values — Workflow Definitions can use their own value format for persisted data without changing other definitions.
-
Request steps can poll durably — Request activities can use configurable polling intervals and backoff while preserving cancellation, hard timeouts, and one successful result.
-
Authorization follows signed-in actors — Views and commands now derive authority from the signed-in Account and its provider-scoped Principals. Relationship-aware policies apply across the application and GraphQL API, and the system denies access when it cannot verify that authority.
-
Authorization handles large workspaces — Workflow Instance, Step, and Dialog lists remain bounded and use current authorization data. Reads reject stale or failed authorization data, and the system reports when an Account exceeds the configured Principal limit instead of truncating access.
-
Workflow tests use real PostgreSQL — Workflow Tests run ordinary test files against the production workflow behavior in an isolated PostgreSQL schema. Authors can start workflows as declared Accounts, complete assigned Dialogs, replace external Bindings with typed fixtures, wait for durable state, and inspect events through the test interface.
-
Parallel workflow steps survive restarts — Workflow code can wait for all, any, or the first result from parallel Steps. IdentityFlow preserves completed and canceled branches, so replay does not repeat abandoned work or lose a suspended Step after a restart.
-
Workshop path is executable — The participant documentation now runs from environment verification through a typed workflow, real-database tests, fixture-backed vacation reassignment, and a same-machine second deployment. Optional capabilities and live integrations remain clearly separated from the guaranteed path.
-
Operators can replay dead letters — The Operations view lets authorized operators replay a selected dead-letter item. Replay follows the same authorization, locking, validation, and audit rules as other workflow commands.
-
Workflows start with declared parameters — A Workflow’s declared start parameters travel with its deployed definition, and the start page builds its form from that declaration. Workflows that declare no parameters, an empty parameter set, or parameters of their own all start from the browser, and a submitted value the Workflow never declared is not sent.
-
Deployments can activate their reviewed authorization — A configuration can declare the Access Configuration it reviewed, and startup activates it, reports its digest, and skips the work when that Access Configuration is already active. Declaring the reviewed digest as well turns an unreviewed change to the authorization configuration into a refused start instead of a silent one. Without this declaration nothing is activated, exactly as before.
-
Training deployments have their own mode —
NODE_ENV=workshopruns a training deployment of the shipped program over loopback without having to call itself a development deployment. -
Dialogs declare the answer they accept — A pending Dialog now carries a description of the answer its schema accepts, and the browser builds the answer form from that description. If the answer cannot be described, the Dialog remains answerable as raw JSON. The declaring schema still decides what is accepted when the Workflow resumes.
-
Navigation follows available work — The application now has one persistent navigation. Each section appears only when the signed-in Account can access matching work, so a deployment’s catalog, Instances, operations, and failed work are not shown to someone who cannot use them.
-
Access overview shows signed-in identities — Every screen shows the signed-in Account, and an access overview lists the Principals reported by the identity provider together with the Workflow Families those Principals can reach for each Access Function. It reads current access rather than configuration, so it cannot show rights the Account does not have.
-
Start forms capture run context — The start form now collects a label, a description, and the recipients for a run alongside the Workflow’s own parameters. Relationship-aware access is decided against those recipients, so it is no longer silently empty.
-
Workflow data reads as data — Task context, business data, recorded decisions, and start results are rendered as labelled values instead of a raw JSON block, and fall back to JSON only where nesting stops being readable.
-
Operators can reassign pending Dialogs — A pending Dialog can be reassigned to a different set of Principals from the Instance it belongs to, with a recorded reason. This was the one Workflow command with no way to reach it.
-
Denied requests stay in context — A page an Account cannot reach now explains the refusal inside the application and offers a way back to work it can reach, instead of replacing everything with a bare status page. Denied and non-existent pages read the same, so the page cannot reveal what the refusal is withholding.
-
Forms offer declared choices — A parameter or answer whose schema permits fixed values is offered those values under the names the schema gives them, and a list is offered as a multiple choice. Nobody has to type an identifier written elsewhere, and only a declared value is submitted. The declaring schema still decides what is accepted.
-
Linux x64 archives support WSL2 — IdentityFlow and the Workflow Test distribution are delivered for Linux x64 alongside macOS Apple Silicon. Windows users can run the Linux archives through WSL2.
-
Workshop exercises build skills progressively — The workshop now includes a 21-module tutorial with a rising curve. Each exercise runs on its own with a red starting lane and a green solution.
Changed
-
Workshop setup needs one command — macOS Apple Silicon and Linux x64 participants, plus Windows participants using WSL2, can check prerequisites, repair their downloads, and install dependencies from any folder, with one recovery action at a time.
-
Workshop starters use npm by default — The starter installs from a package lock with npm, while pnpm and yarn remain optional.
-
Start forms install their schema support — The API package now installs the schema support needed to turn a Workflow’s declared start parameters into the contract used by browser start forms.
-
Transient database failures retry safely — Retryable PostgreSQL connection and transaction failures now leave scheduled workflow work eligible for another execution instead of marking the workflow errored.
-
Task search stays responsive — Task searches no longer block the browser while they score results and stop work for an outdated search, while matching and ordering remain unchanged.
-
Starter commands simplify workshop runs — Participants can run
npm run workshop:startfor a foreground run,npm run workshop:start -- --backgroundfor a detached run,npm run workshop:status,npm run workshop:logs,npm run workshop:logs -- --follow,npm run workshop:stop,npm run workshop:stop -- --volumesto reset the database, andnpm run workshop:inventoryto inspect deployed Workflow Definitions. -
Starter setup requires supported Node — Setup requires Node.js 26.8.1 exactly and names the installer when another version is found.
-
Sign-in choices explain each Account — The Starter’s synthetic sign-in page now explains what each Account is for and shows its division and region, so participants can choose the right Account before opening the application.
Breaking
-
Changed bundles now stop startup — BREAKING: Deploying a different bundle under an already-deployed Workflow Definition name and version now refuses to start instead of continuing with the stored bundle. Migration: Raise the Workflow Definition version before starting; reinstalling dependencies with a different package manager may also change the bundle.
-
GraphQL now uses fixed views — BREAKING: GraphQL no longer exposes generic Account, Principal, Workflow, Instance, Step, and Event queries or broad mutations. Migration: Move reads to
startCatalog,initiationHistory,assignedDialogs,participationHistory,business,operations,audit, ordeadLetters; usestartWorkflow,completeDialog,pauseInstance,resumeInstance,terminateInstance,reassignDialog, orreplayDeadLetterfor writes, and derive authority from the authenticated session instead of client input.
Fixed
-
Other origins cannot read GraphQL — The GraphQL API no longer grants cross-origin access. Before, it allowed credentialed requests from any origin, so a page on another subdomain or port of the same site could read data and run mutations as the signed-in account. A UI served from the same origin as IdentityFlow is unaffected.
-
Version upgrade guidance is accurate — The documentation now explains that running workflows continue on newer compatible versions of their definition, which changes require a new major version, and that instances of an older major keep running after its definition is removed from the source.
-
Workshop start survives moved image tags — When a registry points a fixture image tag at a newer build,
npm run workshop:startnow fetches the exact image the Starter records and uses it, instead of stopping every fresh machine at the image check. Setup namesworkshop:startas the repair. -
Older programs reject newer databases — A program now refuses to start when the database records an unknown migration instead of rerunning migrations. Start the newer program or restore an older database before retrying.
-
Workflow execution releases locks promptly — Execution now advances and releases its instance lock without waiting for unrelated traffic or database heartbeats, including when a workflow resumes after a timer. Due retries can then resume on quiet engines.
-
Retried child workflows remain testable — Previously, a Workflow Test kept the failed child from the first attempt after the parent retried, so the replacement Dialog could not be completed and the parent could not finish. It now follows the replacement child.
-
Concurrent Dialog starts remain available — Dialogs assigned to the same Principal can start concurrently without a Step failing and waiting for its retry.
-
Sign-in redirects retain browser protections — Unauthenticated page redirects keep the site’s referrer, content-type, framing, and HTTPS transport protections.
-
Sign-in keeps transaction cookies bounded — A browser keeps at most five outstanding OIDC sign-ins, while the normal ten-minute expiry still removes abandoned transactions.
-
Sign-in works from any page — Opening a page other than the sign-in address, or opening the application under a second name for the same machine, no longer fails with a bad-request response. A page that needs a signed-in Account now starts sign-in and returns there afterwards. Sign-in first uses the configured callback address. Two sign-ins started in different tabs both complete. When sign-in still fails, the message names the cause: missing sign-in, mismatched callback, expired or already used sign-in, or a refusal by the identity provider.
-
Dialog completion survives long test runs — In a Workflow Test that had been running for a while, typically after a few waits had timed out, completing a pending Dialog could fail even though the same test passed on its own. IdentityFlow Test now keeps its support process available for the length of a run, so completion succeeds however long the test runs.
-
Test runs end on the summary — When console interception is disabled, a long run could print hundreds of repeated error blocks after the test summary and bury the first failure. The run now ends with the summary.
-
Failed test starts name the cause — When IdentityFlow Test could not start its worker, for example because the environment included an option the runtime refused, the run used to report only that startup had failed. The message now includes the runtime’s reason.
-
Unsupported workflow calls fail promptly — Workflow Tests now report which API and Step reached an unsupported request or subworkflow call.
-
Workflow Tests match production waits — A Workflow under test now handles paused Dialogs and parallel Steps like a deployed Workflow. Tests preserve completed and canceled branches, so a test covers the same suspension and cancellation behavior without special test-only code.
-
Recipients explain accepted subjects — “One subject per line” left whoever was starting a Workflow with no way to know what a subject looks like or whether the field was required. It now shows the signed-in Account’s own subject as the example, offers it on a button, says that it is optional, and points at where a subject is listed.
-
Narrow screens keep tasks usable — Below 768 pixels, the task list and the open task become separate screens instead of a split view. The switch now happens after the page is live, so the initial layout remains visible until the screen is ready.
-
Signing out works from the menu — Selecting “Sign out” now sends the request and returns the browser to sign-in, where the next Account is chosen.
-
A started Workflow shows its Instance — After “Start Workflow” the page scrolled back to the top of an empty form while the confirmation sat below the button, out of view, so a second click filed a second Instance. A successful start now opens the new Instance, and the button is disabled while the start is on its way.
-
The sidebar opens expanded — On a browser that had never toggled it, the sidebar opened collapsed to icons, hiding the section names and the signed-in Account. It now starts expanded; a collapsed sidebar is still remembered.
-
Workflow data renders without breaking pages — Values that look like dates but are not valid dates, such as order numbers or legacy zero-dates, now display as written instead of breaking screens that show Workflow data. Only complete timestamps are formatted as times.
-
Timestamp fields accept declared values — A parameter or answer declared as a timestamp now uses a control that produces the instant the declaration accepts.
-
Declared defaults survive an untouched form — An optional list or flag left alone was submitted as empty or false, overriding the default the Workflow declared rather than letting it apply.
-
Dialog answers follow their schemas — Completing a Dialog no longer sends one fixed answer to every Dialog. Each answer is built from the pending Dialog’s declared schema, so the resumed Instance does not fail because the answer shape is wrong.
-
Workflow discovery avoids hidden caches — Startup no longer searches hidden directories, such as dependency or build caches, while looking for Workflow modules. A cache beside the workflows can no longer consume file-watch capacity and prevent discovery. Repeated watch failures now produce a short summary instead of one message per affected file.
-
Invalid workflow parameters fail early — Parameters a Workflow does not accept are refused when the Workflow starts instead of producing a failed Instance that never ran a Step. Over the GraphQL API the refusal is a generic error that does not name the reason.
-
Session cookies follow the served address — Whether a session cookie uses transport protection now follows the configured callback URL rather than the deployment mode, so an HTTPS development deployment marks it too.
-
Plain HTTP stays limited to loopback — A callback or issuer URL may use plain HTTP only for literal loopback addresses, never for the name
localhost, whose meaning a resolver decides. -
Workflow metadata preserves lists — Lists in Workflow Definition metadata stay lists instead of becoming objects keyed by position.
v0.3.1 (2026-10-19) Latest
Added
-
Readiness endpoint for probes —
GET /-/readyanswersreadywith status200once IdentityFlow has finished starting, andnot readywith503from the moment it starts to stop, whether onSIGTERM,SIGINTor a fatal error, so load balancers stop sending traffic over connections that are still open. It needs no sign-in and does not contact PostgreSQL or the OIDC provider, so Kubernetes startup, readiness and liveness probes and load balancer health checks can use it instead of a TCP check or the sign-in redirect. -
Scrape metrics with Prometheus — With
OTEL_METRICS_EXPORTER=prometheusthe IdentityFlow program serves Prometheus metrics on a separate port,9464at/metrics, never on its public HTTP port. Withotlpit sends metrics, and withOTEL_TRACES_EXPORTER=otlptraces, to your OpenTelemetry collector. You get Node.js runtime metrics, outgoing HTTP request durations and traces of the workflow engine’s work, reported as serviceidentity-flowwith the program version. WithoutOTEL_*exporter settings the program exports nothing and opens no extra port, as before. Traces replace URL query values withREDACTED, and the process command line is attached only when you opt in. The endpoint has no authentication: do not publish the port. See Observability and Install and run IdentityFlow for a Kubernetes example. -
Command callers can tell rejected input apart — Code that calls IdentityFlow’s commands directly can now recognise a command whose input was rejected with
isCommandInputError. The error lists the path and message of each rejected value, and other validation failures no longer look like the caller’s mistake.
Breaking
-
GraphQL lists drop totals and facets — BREAKING: The
totalandfacetsfields are gone from every GraphQL list, together with theStringFacet,WorkflowFacetsandInstanceFacetstypes. They never returned data: selecting them failed the whole list. Migration: Removetotalandfacetsfrom your queries; page withpageInfoto read every row. -
GraphQL business data can be null — BREAKING:
BusinessInstanceDetail.businessDatais now nullable (JSONinstead ofJSON!).business.findreturnsbusinessData: nullfor an instance that has no business data, such as an active instance that was never paused, and for an instance that failed with anError, such as most errored or timed-out instances (a failure recorded as plain data, such as a thrown object, still shows that data). Before, selectingbusinessDataon those instances failed the whole query withINTERNAL_SERVER_ERROR. Migration: Regenerate typed GraphQL clients and handlenullwherever you readbusinessData. -
Workflow Test telemetry uses stable HTTP names — BREAKING: Workflow Test runs that export traces or metrics through
OTEL_*settings now report HTTP attributes under their stable names, such ashttp.request.methodinstead ofhttp.methodandhttp.response.status_codeinstead ofhttp.status_code; the HTTP duration metrics are nowhttp.server.request.durationandhttp.client.request.duration, measured in seconds instead of milliseconds, andOTEL_SEMCONV_STABILITY_OPT_INno longer brings back the old names. Migration: Update dashboards and queries that use the old names or millisecond values.
Changed
-
Find the right documentation path — The site separates a numbered tutorial, task guides and reference. Pages open with a visual summary and the result or decision they support; setup choices and first actions come before optional technical detail.
-
Documentation explains with figures — Concept, guide, reference and recipe pages now show how workflows, identities, configuration and the GraphQL API fit together in figures that state one claim each, and mark the tokens that matter in code and configuration lines; on phones those lines wrap and list their notes underneath. Several statements that did not match IdentityFlow’s behavior were corrected along the way, among them how
OTEL_LOG_LEVELworks, whereNODE_CONFIG_DIRis searched, which requests get405, and how operator reassignment and assignee source refreshes change a dialog’s assignees. The workshop download no longer asks Linux users to rename files inbin/. -
Downloads come as two archives — A server download holds the Linux program, a README, the changelog and an example configuration. A workshop download holds the starter project with the program and the test runner for macOS and Linux, and its setup installs them from beside the project.
-
Workflow Tests run without superuser — The PostgreSQL server that Workflow Tests start for you now connects as a database owner without superuser rights, the same rights a managed PostgreSQL service gives IdentityFlow. A statement that needs superuser rights now fails in the test instead of first on a managed server.
-
Workflow test waits allow four seconds —
flow.waitFornow waits up to 4,000 milliseconds by default instead of 1,000, so a dialog or step that takes a moment longer on a busy machine no longer fails the test. A wait that cannot succeed still reportsWF_TEST_WAIT_TIMEOUTbefore Vitest’s default five-second test timeout. -
Stopping a workflow stops its children — When a workflow is terminated, fails, finishes early or stops waiting for a child workflow, that child workflow is now terminated too instead of continuing on its own. While a child is executing at that moment, terminating the parent may ask you to retry shortly.
Fixed
-
Workshop accepts Docker Desktop images — Workshop verification and startup now recognize pinned fixture images and the recorded offline archive identities in Docker Desktop’s containerd image store as well as classic image stores, while still rejecting different images and unsupported platforms.
-
Fatal errors now exit non-zero — When an uncaught exception or unhandled rejection stops IdentityFlow, it reports the error, shuts down and exits with status
1instead of0. A failing shutdown step also exits1, and an error raised while shutting down no longer floods the output with endless copies of itself. When the Engine runs outside the IdentityFlow server,SIGINTandSIGTERMnow end the process with status130and143. -
Identity and assignee changes apply promptly — Admitted identity and assignee changes are processed as soon as they arrive instead of waiting up to 30 seconds, and newly granted access becomes visible without the same delay.
-
Retries and deadlines run on time — A retry or Dialog deadline could be skipped or wait up to 30 seconds when IdentityFlow was busy or the database clock ran slightly ahead of the server. It now runs as soon as it is due.
-
Stopped workflows leave no open dialogs — Terminating a workflow, or letting it fail or time out, now closes its open steps, so assignees no longer see dialogs they can never complete. Workflows stopped before this change keep their open steps.
-
First workshop start waits for the database — On a fresh database,
npm run workshop:startno longer stops withIFLOW_DATABASE_CREDENTIAL_MISMATCHand the advice to discard the database while PostgreSQL is still setting itself up; it waits until PostgreSQL accepts connections, and reports a credential mismatch only when PostgreSQL rejects the password. -
Workshop Starter archive ships no local files — An archive built from a checkout that already ran the workshop no longer contains its local configuration (
*.local.conf), secret files (*.secret), logs or coverage reports, whatever the letter case of their names, and the build checks the finished archive for them. A symbolic or hard link inside the project now stops the build instead of shipping whatever it points to. -
Documentation site loads no analytics — flow.identity-hub.io no longer loads the Cloudflare Web Analytics beacon, so reading the documentation sends no data to a third party.
-
Invalid dialog answers can be corrected — Completing a dialog with an answer that its schema rejects, including custom checks, now fails right away with the path and message of each rejected field:
BAD_USER_INPUTover GraphQL, a command input error for direct callers, and a rejectedcompleteActivityin Workflow Tests. Nothing is recorded, the dialog stays open, and a corrected answer completes it. Before, the answer was accepted and then errored the step and the instance. Instances that already recorded such an answer keep their errored outcome. The instance page of an instance that failed with anError, such as most errored or timed-out instances, opens again and shows no business data; before, it failed. -
GraphQL lists page past the first page — Selecting
pageInfo { hasNextPage endCursor }on a GraphQL list no longer fails the list withINTERNAL_SERVER_ERROR. PassendCursorasafterto get the next page untilhasNextPageisfalse. A list argument passed asnull, such asafter: nullfor the first page, now counts as left out instead of failing the list. -
Invalid parameters are rejected up front — Starting a workflow from a hook, webhook or another workflow with parameters its definition rejects now fails immediately instead of creating a workflow that errors, and a retrying parent no longer creates a failing child on every attempt.
-
GraphQL reports why a command was refused — A refused mutation on
/graphqlnow carries a stableextensions.codeinstead ofINTERNAL_SERVER_ERRORwith “Unexpected error.”:NOT_FOUNDwhen the resource does not exist or you may not see it (both answers are identical),FORBIDDENwhen you can see an Instance but may not pause, resume or terminate it (a refused start, Dialog or dead-letter command reads asNOT_FOUND),BAD_USER_INPUTwith the path and message of each rejected input value (including empty assignee lists, blank reasons and negative revisions, which were masked before),CONFLICTwhen the resource changed or is in the wrong state, andLOCKEDwhen it is briefly locked and a retry may succeed. Other failures stay masked. -
Malformed Jaeger trace headers tolerated — Workflow Test runs configured to read Jaeger trace headers no longer stop on a malformed header.
-
Answered workflows no longer stall — A workflow whose engine stopped or lost its database connection right after a step completed now continues automatically, even while another step is still waiting.
-
A principal cache TTL of 0 starts the engine — Setting
principalCacheTtl: 0now turns the principal cache off as documented; before, the engine failed to start with aTypeError. A numeric0now counts as zero milliseconds everywhere a duration is accepted. -
Incomplete databases are refused at startup — IdentityFlow now refuses to start on a database that is missing an earlier migration and names the missing migration, instead of starting with part of its schema absent.
-
Database outages preserve Step outcomes — A retryable database failure while saving a Step result no longer records a Step failure or terminates the Instance. Recovery retains the existing at-least-once behavior.
-
Rejected task results count once — When a task’s result fails its schema, that attempt now appears in the instance history as one step failure instead of two.
-
Crash reports omit environment variables — The IdentityFlow program no longer writes its environment, which holds database, OIDC and integration credentials, into
report.*.jsondiagnostic reports generated after fatal Node.js errors. If you shared such a report before, rotate the credentials it contained. -
Exercise 11 names its own auditor rule — Exercise 11 now asks for the auditor rule
approver-can-audit-vacation-requeston thevacation-requestfamily in its worksheet, trainer note and checks. Before, it asked forapprover-can-audit-vacation, the name the Starter’sauthorization.tsalready gives the vacation approval rule, which read as a copy-paste error. -
Exercise 11’s request form lists employees by name — The vacation request that Exercise 11 deploys from Exercise 10 now offers the workshop accounts by name in its Employee field, such as
Requesting employee (requester). Before, the field was free text that expected an OIDC subject, which participants could only find on My access. -
Second target keeps existing folders — The second-target workshop exercise now refuses a target folder that already contains files, or that is, contains, or lies inside the project, instead of deleting it before copying.
-
Second-target exercise runs to completion — The second-target workshop exercise starts IdentityFlow in the background and continues once it is ready, instead of waiting until it is stopped.
-
Workshop setup stops at linked bin/ files — On macOS, setup now stops before it copies or changes anything when a file in
bin/is also linked to a file outside the project. It names the file and asks you to replace it with a regular copy in Finder, then rerun setup. The outside file keeps its contents, permissions and quarantine state. -
Withdrawn access hides detail reads — Opening a Workflow in the start catalog, an Instance’s business or operations details, or an assigned Dialog no longer shows data when access was withdrawn, or a Principal refresh became required, after the request checked access but before it read the data. These reads now agree with the matching lists.
-
Audit trail records only real reads — Navigation and tab visibility checks no longer record audit reads; opening audit data still does.
-
Refused starts no longer reveal unloadable workflows — Starting a workflow you may not start now answers
NOT_FOUND, exactly like a workflow that does not exist, even when its stored definition can no longer be loaded, for example after an incompatible upgrade. Before, such a start failed withINTERNAL_SERVER_ERROR, which told the caller the workflow existed. IdentityFlow now checks that you may start a workflow before it loads the workflow’s code; when you may start it and it cannot be loaded, the start still fails withINTERNAL_SERVER_ERROR. -
Reported configuration errors stop startup — When the YAML, TOML, JSON or HOCON parser reports a syntax error, a HOCON substitution or include cannot be resolved, or
NODE_CONFIG_STRICTis not valid JSON, IdentityFlow stops with an error that names the line and column where available, without printing configuration text. YAML, TOML and JSON errors also name the kind of error and the file name, or sayan included configuration filefor a file reached through an include. Truncated JSON files are still skipped without an error, so another configuration file can be selected. Before, these errors could copy configuration values into logs. If your logs contain such an error, rotate credentials that may have appeared in it.