FlowOrchestrator.PostgreSQL 1.26.0

dotnet add package FlowOrchestrator.PostgreSQL --version 1.26.0
                    
NuGet\Install-Package FlowOrchestrator.PostgreSQL -Version 1.26.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FlowOrchestrator.PostgreSQL" Version="1.26.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FlowOrchestrator.PostgreSQL" Version="1.26.0" />
                    
Directory.Packages.props
<PackageReference Include="FlowOrchestrator.PostgreSQL" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FlowOrchestrator.PostgreSQL --version 1.26.0
                    
#r "nuget: FlowOrchestrator.PostgreSQL, 1.26.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FlowOrchestrator.PostgreSQL@1.26.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FlowOrchestrator.PostgreSQL&version=1.26.0
                    
Install as a Cake Addin
#tool nuget:?package=FlowOrchestrator.PostgreSQL&version=1.26.0
                    
Install as a Cake Tool

FlowOrchestrator

Code-first DAG orchestration for .NET. Runs on Hangfire, in-process, or Azure Service Bus.

NuGet Downloads License Stars

πŸ“– Documentation Β· NuGet Β· GitHub


FlowOrchestrator Dashboard


What's new in v1.25 β€” Enterprise webhook hardening pipeline. The dashboard webhook receive endpoint now ships an opt-in HMAC signature verifier covering 17 partner dialects (GitHub, Stripe, Slack, Shopify, Twilio, Square, Zoom, Linear, Dropbox, Mailgun, MS Teams, Atlassian, Calendly, Bitbucket, Generic, Custom, GitHubLegacy), replay protection with timestamp + nonce store, token-bucket rate limiting, IP allow/deny lists with curated KnownPublisherCidrs presets, body-size cap, DLQ + recent-deliveries log, and a new "Webhooks" dashboard tab. SQL Server + PostgreSQL backends ship for the replay + DLQ stores; in-memory remains the default. Three enforcement modes (Off, Audit, Enforce) let operators dry-run before flipping to reject. EventIds 4000–4010 + four new metrics (webhook_received_total, webhook_rejected_total, webhook_body_bytes, webhook_processing_ms) for observability. Full hardening cookbook.

v1.24 β€” Realtime SSE push for the dashboard (replaces 5-second polling with EventSource). v1.22 β€” Third runtime adapter FlowOrchestrator.ServiceBus; engine rejects triggers for disabled flows across all runtimes. v1.21 shipped server-side timeseries; v1.19 added health checks; v1.18 shipped WaitForSignal; v1.17 shipped When conditions. Full CHANGELOG.


When to choose FlowOrchestrator

βœ… Choose FlowOrchestrator if:

  • You want multi-step DAGs in .NET without standing up a separate workflow server
  • Your team writes C# and wants flows defined as plain code, not JSON or a designer
  • You need conditional branching (When), polling, fan-out (ForEach), human-in-loop (WaitForSignal), and cron in one library
  • You want a built-in dashboard with Timeline, DAG, and Gantt views
  • You want flows that are unit-testable in-process (FlowTestHost) and renderable as Mermaid diagrams in a PR
  • You already use Hangfire β€” or want Azure Service Bus for cloud-native multi-replica scale-out β€” or want zero infrastructure at all (in-process runtime works without Hangfire or a database)

❌ Choose something else if:

  • You need multi-language workflows (Python + Go + .NET) β†’ Temporal
  • You want replay-based deterministic execution β†’ Temporal
  • You're running a service mesh and want workflow as one of several building blocks β†’ Dapr Workflows
  • Non-developers need to author workflows in a visual designer β†’ Elsa Workflows
  • You only need fire-and-forget background jobs with no DAG β†’ Hangfire alone

FlowOrchestrator is intentionally narrow. It is the DAG layer Hangfire is missing β€” nothing more, nothing less.


How it compares

Hangfire FlowOrchestrator Elsa v3 Temporal .NET Dapr Workflows
Background job execution βœ“ βœ“ (via Hangfire) βœ“ βœ“ βœ“
Multi-step DAG with runAfter Manual βœ“ βœ“ Implicit (code) Implicit (code)
Polling pattern (no thread block) Manual βœ“ built-in βœ“ βœ“ durable timers βœ“ durable timers
Code-first C# definitions βœ“ βœ“ βœ“ βœ“ βœ“
JSON / YAML workflow files βœ— βœ— by design βœ“ βœ— βœ—
Visual designer βœ— βœ— by design βœ“ Studio βœ— βœ—
Built-in DAG / Gantt / Timeline UI βœ— βœ“ βœ“ Studio βœ“ Web UI βœ—
Polyglot SDK .NET only .NET only .NET only Go, Java, TS, Python, .NET .NET, Python, JS, Java, Go
Separate server / sidecar required βœ— βœ— Optional βœ“ Required βœ“ Sidecar
Storage you already have SQL Server, PG, Redis SQL Server, PG, in-memory SQL Server, PG, MongoDB Cassandra, MySQL, PG State store of choice
Runtime / dispatcher options n/a Hangfire, in-process, Azure Service Bus Hangfire, Quartz Built-in cluster Built-in actor system
Deterministic replay βœ— βœ— βœ— βœ“ βœ“
External signals / human-in-loop βœ— βœ“ WaitForSignal βœ“ βœ“ βœ“
Operational complexity Low Low Low–Medium High Medium
Learning curve (.NET dev) Low Low Medium Medium–High Medium

FlowOrchestrator deliberately ships fewer features than Temporal or Dapr Workflows. It does not replay. It does not run a separate server. It is for teams that want DAG orchestration inside their existing ASP.NET Core app β€” alongside Hangfire if they have it, or fully in-process if they do not.

Comparison verified 2026-04-30 against Elsa v3, Temporal .NET SDK v1, Dapr .NET SDK v1. PRs welcome to keep it current.


Coming from Hangfire?

// Before β€” recurring job with manual chaining, no DAG, no run history
RecurringJob.AddOrUpdate<NightlyOrdersJob>("nightly-orders",
    job => job.RunAsync(), "0 2 * * *");
// Inside RunAsync: call FetchOrders, then SubmitToWms, then NotifySlack.
// Error branching, retry-per-step, run history, Gantt view β€” all on you.

// After β€” FlowOrchestrator declarative manifest
public sealed class NightlyOrdersFlow : IFlowDefinition
{
    public Guid Id { get; } = new("a1b2c3d4-0000-0000-0000-000000000001");
    public FlowManifest Manifest { get; set; } = new()
    {
        Triggers = { ["cron"] = new() { Type = TriggerType.Cron,
            Inputs = { ["cronExpression"] = "0 2 * * *" } } },
        Steps = {
            ["fetch"]  = new() { Type = "FetchOrders" },
            ["submit"] = new() { Type = "SubmitToWms",
                RunAfter = { ["fetch"]  = [StepStatus.Succeeded] } },
            ["notify"] = new() { Type = "NotifySlack",
                RunAfter = { ["submit"] = [StepStatus.Succeeded] } }
        }
    };
}
// Dashboard, per-step retry, full run history, DAG view β€” included.

And yes β€” your flows are testable. See FlowOrchestrator.Testing for a one-liner test host that runs flows in-process without Hangfire or ASP.NET.

Coming from Temporal or Dapr?

If you don't need replay-based determinism and a separate cluster, here is the simpler model:

// Temporal .NET β€” deterministic replay; requires Temporal Server cluster
[Workflow]
public class OrderWorkflow
{
    [WorkflowRun]
    public async Task RunAsync(string orderId)
    {
        await Workflow.ExecuteActivityAsync(
            (Activities a) => a.FetchOrderAsync(orderId),
            new() { ScheduleToCloseTimeout = TimeSpan.FromMinutes(5) });
        await Workflow.ExecuteActivityAsync(
            (Activities a) => a.SubmitToWmsAsync(orderId),
            new() { ScheduleToCloseTimeout = TimeSpan.FromMinutes(5) });
    }
}
// Requires: Temporal Server (Cassandra / MySQL / PG + Elasticsearch + server cluster)

// FlowOrchestrator β€” same outcome, runs inside your existing ASP.NET Core app
public sealed class OrderFlow : IFlowDefinition
{
    public Guid Id { get; } = new("a1b2c3d4-0000-0000-0000-000000000002");
    public FlowManifest Manifest { get; set; } = new()
    {
        Triggers = { ["manual"] = new() { Type = TriggerType.Manual } },
        Steps = {
            ["fetch"]  = new() { Type = "FetchOrder" },
            ["submit"] = new() { Type = "SubmitToWms",
                RunAfter = { ["fetch"] = [StepStatus.Succeeded] } }
        }
    };
}
// Requires: SQL Server or PostgreSQL you already have, plus Hangfire.

Why FlowOrchestrator?

  • Zero new infrastructure (or your choice) β€” runs inside your existing Hangfire app on SQL Server / PostgreSQL, in-process with a Channel<T> and zero deps, or on Azure Service Bus for cloud-native scale-out.
  • Code-first, always β€” flows are plain C# classes; no YAML, no JSON files, no designer to learn.
  • Built-in dashboard with realtime updates β€” Timeline, DAG, and Gantt views with retry, cancel, and re-run controls; state changes stream over Server-Sent Events the moment they happen, polling only kicks in if the stream stalls.
  • Runtime-agnostic core β€” three runtimes ship today (Hangfire, in-process, Azure Service Bus); add your own without touching flow definitions.

Pick a runtime

FlowOrchestrator separates storage (where flow definitions and run history live) from the runtime adapter (which dispatches and executes steps).

Hangfire runtime InMemory runtime ServiceBus runtime
Step dispatcher IBackgroundJobClient Channel<T> inside the host process Azure Service Bus topic + per-flow subscription
Cron triggers IRecurringJobManager (multi-instance safe) PeriodicTimer (single-instance only) Self-perpetuating scheduled messages on a queue (multi-instance safe)
Survives process restart βœ“ (jobs in Hangfire storage) βœ— (in-memory queue) βœ“ (messages survive in the SB namespace)
Multi-instance horizontal scale βœ“ βœ— βœ“ (workers compete on the subscription)
Extra infrastructure Hangfire + SQL Server / PostgreSQL None Azure Service Bus namespace (or local emulator)
Best for Production workloads on .NET infra Local dev, integration tests, single-node side projects Cloud-native deployments, multi-region scale-out

Storage is independent β€” InMemory storage works only for dev / tests, while SQL Server and PostgreSQL are production-ready under either runtime.


Install

dotnet add package FlowOrchestrator.Core

# Runtime adapter β€” pick one
dotnet add package FlowOrchestrator.Hangfire     # Hangfire-backed (production default)
dotnet add package FlowOrchestrator.InMemory     # In-process Channel<T> (dev / testing / single-node)
dotnet add package FlowOrchestrator.ServiceBus   # Azure Service Bus (cloud-native multi-instance)

# Storage backend β€” pick one
dotnet add package FlowOrchestrator.SqlServer    # or FlowOrchestrator.PostgreSQL
                                                  # FlowOrchestrator.InMemory ships its own storage too

# Optional
dotnet add package FlowOrchestrator.Dashboard    # REST API + SPA dashboard
dotnet add package FlowOrchestrator.Testing      # FlowTestHost β€” in-process integration test helper

Quick Start β€” SQL Server + Hangfire

// Program.cs
builder.Services.AddHangfire(c => c
    .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
    .UseSimpleAssemblyNameTypeSerializer()
    .UseRecommendedSerializerSettings()
    .UseSqlServerStorage(connectionString));
builder.Services.AddHangfireServer();

builder.Services.AddFlowOrchestrator(options =>
{
    options.UseSqlServer(connectionString);       // persist + auto-migrate tables
    options.UseHangfire();                        // Hangfire step dispatcher
    options.AddFlow<OrderFulfillmentFlow>();
});

builder.Services.AddStepHandler<FetchOrdersStep>("FetchOrders");
builder.Services.AddStepHandler<SubmitToWmsStep>("SubmitToWms");
builder.Services.AddFlowDashboard(builder.Configuration); // optional

app.UseHangfireDashboard("/hangfire");
app.MapFlowDashboard("/flows");

Define a flow:

public sealed class OrderFulfillmentFlow : IFlowDefinition
{
    // Always use a fixed GUID literal β€” never Guid.NewGuid()
    public Guid Id { get; } = new("a1b2c3d4-0000-0000-0000-000000000002");
    public FlowManifest Manifest { get; set; } = new()
    {
        Triggers = {
            ["manual"]  = new() { Type = TriggerType.Manual },
            ["webhook"] = new() { Type = TriggerType.Webhook,
                Inputs = { ["webhookSlug"] = "order-fulfillment" } }
        },
        Steps = {
            ["fetch"]  = new() { Type = "FetchOrders" },
            ["submit"] = new() { Type = "SubmitToWms",
                RunAfter = { ["fetch"] = [StepStatus.Succeeded] } }
        }
    };
}

Open http://localhost:5000/flows β€” trigger the flow, watch steps execute in the DAG view, retry any failure.

Quick Start β€” InMemory (zero infrastructure)

For local development, prototypes, and single-node side projects β€” no Hangfire, no database:

// Program.cs
builder.Services.AddFlowOrchestrator(options =>
{
    options.UseInMemory();           // storage in-process
    options.UseInMemoryRuntime();    // Channel<T> dispatcher + PeriodicTimer cron
    options.AddFlow<OrderFulfillmentFlow>();
});

builder.Services.AddStepHandler<FetchOrdersStep>("FetchOrders");
builder.Services.AddStepHandler<SubmitToWmsStep>("SubmitToWms");
builder.Services.AddFlowDashboard(builder.Configuration);

app.MapFlowDashboard("/flows");

All run data is lost on restart β€” see Storage Backends for the full picture, and Production Checklist for why this combo is unsuitable for production.

For PostgreSQL, see πŸ“– Getting Started.


Quick Start β€” Azure Service Bus

For cloud-native deployments where workers scale horizontally across replicas/regions:

// Program.cs β€” runtime is Azure Service Bus, storage stays in your existing DB.
builder.Services.AddFlowOrchestrator(options =>
{
    options.UseSqlServer(connectionString);              // (or UsePostgreSql / UseInMemory)
    options.UseAzureServiceBusRuntime(sb =>
    {
        sb.ConnectionString   = builder.Configuration.GetConnectionString("ServiceBus")!;
        sb.AutoCreateTopology = true;                    // creates topic + sub-per-flow at startup
    });
    options.AddFlow<OrderFulfillmentFlow>();
});

builder.Services.AddStepHandler<FetchOrdersStep>("FetchOrders");
builder.Services.AddFlowDashboard(builder.Configuration);

app.MapFlowDashboard("/flows");

Topology β€” one topic (flow-steps) with one subscription per registered flow (SQL filter on FlowId); plus one queue (flow-cron-triggers) for self-perpetuating cron schedules. The engine's Dispatch many, Execute once invariant (dispatch ledger + claim guard) handles Service Bus's at-least-once delivery model β€” duplicate messages cannot run a step twice.

Local development uses the official Microsoft Service Bus emulator. The included Aspire AppHost wires it via AddAzureServiceBus("servicebus").RunAsEmulator(); run with dotnet run --project ./FlowOrchestrator.AppHost and the flow-servicebus instance comes up on port 5104.


Full documentation

Topic Link
Getting started (all runtimes) getting-started
Core concepts β€” Flow, Step, RunId core-concepts
Step handlers step-handlers
Trigger types triggers
Expression reference (@triggerBody()) expressions
Polling pattern polling
ForEach / fan-out foreach
Dashboard & REST API dashboard
Storage backends storage
Configuration reference configuration
Architecture architecture
Observability observability
Mermaid export mermaid-export
Conditional execution (When) conditional-execution
Human-in-loop (WaitForSignal) wait-for-signal
Testing flows (FlowTestHost) testing
Versioning flows in production versioning
Production deployment checklist production-checklist

Production?

Before changing any deployed flow, read Versioning Flows β€” it explains which manifest changes are safe and which need a maintenance window. Before go-live, walk through the Production Checklist for storage, multi-instance, monitoring, secrets, capacity, and upgrade guidance. Wire AddFlowOrchestratorHealthChecks() into /health so your load balancer can drop traffic when the flow store is unreachable.


Visualize any flow with one line

flow.ToMermaid() returns a Mermaid flowchart string that renders in GitHub READMEs, Notion, Confluence, and any modern Markdown surface β€” no running app required. Here is the sample OrderFulfillmentFlow:

flowchart TD
    classDef trigger fill:#e1f5ff,stroke:#0288d1
    classDef entry fill:#c8e6c9,stroke:#388e3c
    classDef polling fill:#fff9c4,stroke:#f57f17
    classDef loop fill:#f3e5f5,stroke:#7b1fa2

    T_manual["⚑ manual<br/>Manual"]:::trigger
    T_webhook["⚑ webhook<br/>Webhook /order-fulfillment"]:::trigger

    fetch_orders["fetch_orders<br/><i>QueryDatabase</i>"]:::entry
    submit_to_wms["submit_to_wms<br/><i>CallExternalApi</i>"]:::polling
    save_result["save_result<br/><i>SaveResult</i>"]

    T_manual --> fetch_orders
    T_webhook --> fetch_orders
    fetch_orders -- Succeeded --> submit_to_wms
    submit_to_wms -- Succeeded --> save_result

The dashboard ships a Copy Mermaid button on every flow detail page, and the sample app exposes --export-mermaid <flowId> for CI integrations.


Compatibility

Package Target frameworks
FlowOrchestrator.Core net8.0 Β· net9.0 Β· net10.0
FlowOrchestrator.Hangfire net8.0 Β· net9.0 Β· net10.0
FlowOrchestrator.InMemory net8.0 Β· net9.0 Β· net10.0
FlowOrchestrator.SqlServer net8.0 Β· net9.0 Β· net10.0
FlowOrchestrator.PostgreSQL net8.0 Β· net9.0 Β· net10.0
FlowOrchestrator.Dashboard net8.0 Β· net9.0 Β· net10.0
FlowOrchestrator.Testing net8.0 Β· net9.0 Β· net10.0

Star History Chart


License

MIT β€” see the LICENSE file.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.26.0 21 5/10/2026
1.26.0-preview.134 21 5/10/2026
1.26.0-preview.133 26 5/10/2026
1.25.1 34 5/10/2026
1.25.0 29 5/9/2026
1.25.0-preview.129 28 5/9/2026
1.24.0 87 5/3/2026
1.23.0 87 5/3/2026
1.23.0-preview.128 33 5/6/2026
1.23.0-preview.126 41 5/5/2026
1.23.0-preview.125 32 5/5/2026
1.23.0-preview.124 31 5/5/2026
1.23.0-preview.123 41 5/3/2026
1.23.0-preview.120 45 5/3/2026
1.23.0-preview.118 46 5/3/2026
1.22.0 97 5/3/2026
1.22.0-preview.116 46 5/3/2026
1.21.0 82 5/2/2026
1.21.0-preview.114 44 5/2/2026
1.20.0-preview.113 41 5/2/2026
Loading failed