Skip to content

All versions since v0.3.1 (2026-10-19)

v0.3.1 (2026-10-19) Latest

Added

  • Readiness endpoint for probes — GET /-/ready answers ready with status 200 once IdentityFlow has finished starting, and not ready with 503 from the moment it starts to stop, whether on SIGTERM, SIGINT or 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=prometheus the IdentityFlow program serves Prometheus metrics on a separate port, 9464 at /metrics, never on its public HTTP port. With otlp it sends metrics, and with OTEL_TRACES_EXPORTER=otlp traces, to your OpenTelemetry collector. You get Node.js runtime metrics, outgoing HTTP request durations and traces of the workflow engine’s work, reported as service identity-flow with the program version. Without OTEL_* exporter settings the program exports nothing and opens no extra port, as before. Traces replace URL query values with REDACTED, 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 total and facets fields are gone from every GraphQL list, together with the StringFacet, WorkflowFacets and InstanceFacets types. They never returned data: selecting them failed the whole list. Migration: Remove total and facets from your queries; page with pageInfo to read every row.

  • GraphQL business data can be null — BREAKING: BusinessInstanceDetail.businessData is now nullable (JSON instead of JSON!). business.find returns businessData: null for an instance that has no business data, such as an active instance that was never paused, and for an instance that failed with an Error, such as most errored or timed-out instances (a failure recorded as plain data, such as a thrown object, still shows that data). Before, selecting businessData on those instances failed the whole query with INTERNAL_SERVER_ERROR. Migration: Regenerate typed GraphQL clients and handle null wherever you read businessData.

  • 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 as http.request.method instead of http.method and http.response.status_code instead of http.status_code; the HTTP duration metrics are now http.server.request.duration and http.client.request.duration, measured in seconds instead of milliseconds, and OTEL_SEMCONV_STABILITY_OPT_IN no 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_LEVEL works, where NODE_CONFIG_DIR is searched, which requests get 405, and how operator reassignment and assignee source refreshes change a dialog’s assignees. The workshop download no longer asks Linux users to rename files in bin/.

  • 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.waitFor now 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 reports WF_TEST_WAIT_TIMEOUT before 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 1 instead of 0. A failing shutdown step also exits 1, 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, SIGINT and SIGTERM now end the process with status 130 and 143.

  • 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:start no longer stops with IFLOW_DATABASE_CREDENTIAL_MISMATCH and 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_INPUT over GraphQL, a command input error for direct callers, and a rejected completeActivity in 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 an Error, 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 with INTERNAL_SERVER_ERROR. Pass endCursor as after to get the next page until hasNextPage is false. A list argument passed as null, such as after: null for 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 /graphql now carries a stable extensions.code instead of INTERNAL_SERVER_ERROR with “Unexpected error.”: NOT_FOUND when the resource does not exist or you may not see it (both answers are identical), FORBIDDEN when you can see an Instance but may not pause, resume or terminate it (a refused start, Dialog or dead-letter command reads as NOT_FOUND), BAD_USER_INPUT with the path and message of each rejected input value (including empty assignee lists, blank reasons and negative revisions, which were masked before), CONFLICT when the resource changed or is in the wrong state, and LOCKED when 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: 0 now turns the principal cache off as documented; before, the engine failed to start with a TypeError. A numeric 0 now 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.*.json diagnostic 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-request on the vacation-request family in its worksheet, trainer note and checks. Before, it asked for approver-can-audit-vacation, the name the Starter’s authorization.ts already 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 with INTERNAL_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 with INTERNAL_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_STRICT is 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 say an included configuration file for 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.