Skip to content
IdentityFlow 0.3.0 What the release contains

IdentityFlow runs long-running processes, such as approvals, that you write as async TypeScript functions. It records every step as an event in PostgreSQL, so an instance can wait days for a person, continue after the program restarts, and show afterwards what happened and who did it.

Questions IdentityFlow answers for every instance

Where is my workflow?

The instance page shows its status and each step with its status and times, so you see which step is running, which one waits for a person, and which one failed.

Find an instance

What happened so far?

The Audit tab lists every recorded event in order, with its time and the account that caused it. Events are appended and never changed or deleted.

Read the audit trail

Who may act on it?

A dialog names the principals that may answer it. When an account answers, IdentityFlow asks your identity hook for the account’s current principals and refuses an account without a match.

Identity and access

How a workflow runs

A vacation request looks up the employee, then waits for an approver. The drawing shows what IdentityFlow records at each transition, and what happens when the instance continues after the answer.

How the vacation approval runs and what it records A vacation request starts from a form built from the input schema. The step load employee calls the acme-directory binding and records the result. The dialog approve vacation waits for an account holding the principal role:vacation-approver at the provider acme. Every transition is an event appended to the instance history, in this order: 1 INSTANCE_STARTED, 2 STEP_STARTED and 3 STEP_COMPLETED for load employee, 4 STEP_STARTED and 5 STEP_PENDING for approve vacation, 6 STEP_CONTINUING when the approver answers, 7 STEP_COMPLETED and 8 INSTANCE_COMPLETED. While the dialog waits, no execution of the workflow stays in memory. After the answer, the function runs again from the top: load employee returns its recorded result without calling the directory and appends no event, approve vacation returns the recorded answer after checking it against its schema, and the rest of the function runs for the first time and completes the instance. The workflow Start form built from the input schema employeeId firstDay days reason flow.do load employee calls the binding inside the step acme-directory lookup(employeeId) result recorded with the step flow.dialog approve vacation waits for a person’s answer assignee acme · role:vacation-approver only a matching account may answer instance COMPLETED the function returned { employeeId, decision } Event history of the instance appended in order, never changed 1 INSTANCE_STARTED 2 STEP_STARTED 3 STEP_COMPLETED 4 STEP_STARTED 5 STEP_PENDING 6 STEP_CONTINUING 7 STEP_COMPLETED 8 INSTANCE_COMPLETED first run, until the dialog waits the answer replay Replay after the answer the function runs again from the top No execution stays in memory while it waits. The program can stop and start again; the instance continues from its events. load employee recorded result acme-directory not called no new event approve vacation recorded answer checked against its schema 7 STEP_COMPLETED Rest of the function runs for the first time and returns the result 8 INSTANCE_COMPLETED How the vacation approval runs and what it records A vacation request starts from a form built from the input schema. The step load employee calls the acme-directory binding and records the result. The dialog approve vacation waits for an account holding the principal role:vacation-approver at the provider acme. Every transition is an event appended to the instance history, in this order: 1 INSTANCE_STARTED, 2 STEP_STARTED and 3 STEP_COMPLETED for load employee, 4 STEP_STARTED and 5 STEP_PENDING for approve vacation, 6 STEP_CONTINUING when the approver answers, 7 STEP_COMPLETED and 8 INSTANCE_COMPLETED. While the dialog waits, no execution of the workflow stays in memory. After the answer, the function runs again from the top: load employee returns its recorded result without calling the directory and appends no event, approve vacation returns the recorded answer after checking it against its schema, and the rest of the function runs for the first time and completes the instance. Start form built from the input schema employeeId · firstDay · days · reason flow.do load employee acme-directory lookup(employeeId) result recorded with the step flow.dialog approve vacation acme · role:vacation-approver only a matching account may answer No execution stays in memory while it waits; the program can restart. Replay from the top load employee recorded result acme-directory not called no new event approve vacation recorded answer checked against its schema The rest of the function runs and returns. 1 INSTANCE_STARTED 2 STEP_STARTED 3 STEP_COMPLETED 4 STEP_STARTED 5 STEP_PENDING 6 STEP_CONTINUING 7 STEP_COMPLETED 8 INSTANCE_COMPLETED the approver’s answer
Each numbered dot is an event in the instance’s history, in the order it is appended. The replay after the answer reads the recorded result of load employee instead of calling the directory again.

The workflow behind the drawing

A Workflow Definition pairs an async function with a name, a version and an optional input schema. async (flow) => { … } is a function literal, like Go’s func(flow) { … }, and await waits for a step’s result.

  • flow.do('load employee', …) runs the lookup through a binding, the workflow’s named access to the directory, and records the result. On replay it returns that result without calling the directory.
  • flow.dialog('approve vacation', …) waits until an account with the principal role:vacation-approver at the provider acme answers. The answer is checked against Decision.
  • Code outside a step runs again on every replay, so it must not call other systems. A call inside a step is delivered at least once: if the program stops after the other system accepted it but before IdentityFlow recorded the result, the step runs again. External effects shows how the receiver recognizes a repeated request.
export const vacationApproval = defineWorkflow(
{
name: 'vacation-approval',
version: '1.0.0',
draft: false,
label: 'Vacation approval',
description: 'Request vacation and wait for an approver’s decision.',
schema: VacationRequest,
},
async (flow) => {
const directory = flow.use(AcmeDirectory);
const employee = await flow.do('load employee', () => directory.lookup(flow.params.employeeId));
// The completed lookup is reused when the approval resumes.
const decision = await flow.dialog('approve vacation', { schema: Decision }, () => ({
params: { ...flow.params, ...employee },
assignees: [{ providerId: 'acme', subject: 'role:vacation-approver' }],
}));
return { employeeId: flow.params.employeeId, decision };
},
);

Who IdentityFlow is for

The people who own a process and the people who build it read the same record.

For business stakeholders

Follow each request from the start form to the decision, and show afterwards who decided.

  • Start forms and answer forms in the browser, built from what the workflow declares, with its fixed choices offered as lists
  • Assigned to me lists the dialogs that wait for the signed-in account
  • Each instance shows its status, its steps and their times
  • The audit trail lists every event in order, with the account that caused it and the principal a decision was accepted under
  • Operators pause, resume or terminate an instance, and reassign a pending dialog with a recorded reason
Read about the audit trail

For engineering teams

Write the process as an async TypeScript function and test it like any other code.

  • Steps for work, timers, people, external jobs and child workflows: flow.do, flow.sleep, flow.dialog, flow.request and flow.start
  • Workflow Tests start real instances against PostgreSQL and replace bindings with typed fixtures
  • Your identity hook reports the principals of each signed-in account, and authorization.ts grants access per workflow
  • A GraphQL API for your own UI, with the same access checks as the web application
  • One program serves the web application and the GraphQL API and runs the workflows, without Node.js, on macOS with Apple Silicon and on Linux x64, with PostgreSQL 17 or 18
Read the concepts

What IdentityFlow 0.3.0 does

Each card links to the page that documents it.

Event-sourced history

Every change to an instance is an immutable event appended to its history in PostgreSQL. The state you see is derived from those events, and nothing edits or deletes them, so the same history is the audit record.

Replay without repeated steps

When an instance continues, finished steps return their recorded result instead of running again. Calls to other systems are delivered at least once.

Code-first TypeScript

A Workflow Definition pairs an async function with a name, a version and an optional input schema. Branches, loops and Promise methods are plain TypeScript.

Dialogs with forms

A dialog waits for a person. The browser builds the start form from the input schema and the answer form from the dialog’s schema.

Access from signed-in identities

Access functions decide who may start, view, operate and audit each workflow, based on the principals your identity hook reports.

Workflow Tests on PostgreSQL

Tests start real instances in an isolated schema, act as declared accounts, answer dialogs and replace bindings with typed fixtures.

Bindings and a GraphQL connector

A binding is a workflow’s named access to an external system. 0.3.0 ships a GraphQL connector; for other protocols, the binding builds your own client.

Waits that survive restarts

Sleep until a date, wait for a person, poll an external job, start a child workflow or run steps in parallel. Each wait is recorded, so the program can restart meanwhile.

Versioned, immutable definitions

The code behind a deployed name and version never changes. Running instances continue on newer compatible versions of their definition.

GraphQL API for your own UI

A UI served from the same origin as IdentityFlow lists and answers the signed-in account’s dialogs and starts workflows through the GraphQL API. Reads return only what that account may see, and every command checks its authority.

See it in action

Choose a task on Start here, or follow the tutorial to run the starter’s vacation approval as a requester and an approver. To see IdentityFlow with a process of your own, ask us for a demo.

IdentityFlow is provided to customers directly. If you don’t have the starter project and the IdentityFlow program yet, write to contact@kenoxa.de.