* feat(javascript): bound JavaScript expression execution
JavaScript expressions were evaluated with no execution constraints at all: no
timeout, no statement limit, no memory limit, no recursion limit, and the
ambient `CancellationToken` — already available at the call site and already
passed into `IJavaScriptEvaluator.EvaluateAsync` — was never handed to Jint.
An expression as simple as `while (true) {}` therefore occupied the calling
thread for the lifetime of the process, and cancelling the workflow did not
stop it.
This adds:
* `JintOptions.ExecutionTimeout` — wall-clock limit for a single expression,
defaulting to 30 seconds. Deliberately generous so that existing expressions
are unaffected; set to `null` to remove the limit.
* `JintOptions.MaxStatements`, `JintOptions.MemoryLimit` and
`JintOptions.MaxRecursionDepth` — opt-in resource limits, off by default.
* The cancellation token is now passed to Jint, so cancelling a workflow aborts
a script that is still running.
The security assessment documents already described a JavaScript execution
timeout as present; they now describe what is actually configurable.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0179sA2T7HuRfRfSc2JirFik
* test(javascript): bound the constraint tests and cover the memory limit
Three of the execution-constraint tests removed the execution timeout entirely
and then ran `while (true) {}`, relying solely on the constraint under test to
stop them. If that constraint regressed, the test did not fail — it ran until
the CI job was killed, taking the rest of the suite with it.
Each such test now registers a generous 30 second failsafe timeout instead of
disabling the timeout. That is two orders of magnitude more than any of these
constraints needs (the slowest aborts in ~270 ms), so it cannot become a flaky
failure on a loaded machine, and `AssertAbortedByAsync` reports a failsafe trip
as exactly that rather than as an unexplained exception type mismatch.
The cancellation test also no longer races a wall-clock timer against engine
construction: the script signals the token itself through a host function, so
cancellation is guaranteed to land while the expression is running. The test
went from a 250 ms wall-clock wait to 5 ms and has no timing dependency left.
Adds the missing `MemoryLimit` test — the one configurable limit the suite did
not exercise. Doubling a string crosses the limit within a couple of dozen
statements, so it asserts `MemoryLimitExceededException` in ~40 ms and bounds
how far past the limit the process can get before the check fires.
Finally, the `ExpressionExecutionContext` is now built on the test host's
`IServiceProvider` rather than a throwaway empty one, matching every other test
in this project. An empty provider does not reflect real evaluation and can hide
failures in notification handlers that resolve services.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0179sA2T7HuRfRfSc2JirFik
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
24 KiB
Elsa Workflows — Technology Profile
| Field | Value |
|---|---|
| Repository / project | elsa-workflows/elsa-core |
| Git ref | release/3.7.0 |
| Version | 3.7.0 |
| Assessment date (UTC) | 2026-05-20T00:00:00Z |
| Assessment method | Static code analysis, Documentation review |
| Model and tools | Claude Sonnet 4.6 |
| Assessment scope | All 50 source projects in src/ — primary focus: Elsa.Workflows.Core, Elsa.Workflows.Runtime, Elsa.Workflows.Management, Elsa.Http, Elsa.Identity, Elsa.Tenants, Elsa.Resilience, Elsa.Scheduling, Elsa.Alterations |
| Related documents | elsa-core-architecture-patterns.md, elsa-core-software-quality.md, elsa-core-iso25010.md |
⚠️ AI-assisted assessment — human review required
This document was produced by an AI model (Claude Sonnet 4.6) using static code analysis and structured pattern catalogs. AI-assisted assessments are based on pattern recognition and reasoning over source files and documentation. They may contain incorrect assumptions, missed context, or findings that do not apply to your specific operational environment.
This document is a draft input to a human review process — not a final approved report. All findings should be verified by a qualified engineer or architect familiar with the codebase before being acted upon, communicated externally, or used as the basis for architectural or compliance decisions.
1. What Elsa Workflows Core Is
Elsa Workflows (elsa-core) is an open-source .NET library and engine for embedding workflow execution inside any .NET application. It provides the data model, execution runtime, persistence abstractions, expression evaluation, security, and optional HTTP/messaging integrations needed to author and run both short-running and long-running business processes entirely within a .NET host process.
Workflows can be authored in three ways: as typed C# classes derived from WorkflowBase, as JSON definitions loaded at runtime from a store or file system, or as ElsaScript .elsa text files parsed and compiled at startup. All three representations compile to the same in-memory activity graph that the execution engine evaluates.
What Elsa Workflows Core is NOT:
- It is not the Elsa Studio visual designer front-end. The Studio is a separate Blazor WebAssembly application that lives in a distinct repository (
elsa-studio). This assessment covers only the engine and server library (elsa-core). - It is not a cloud-hosted or managed workflow service. There is no SaaS offering — all components run in-process inside the consuming application.
- It is not a BPMN 2.0 engine and does not parse BPMN XML. The activity model is proprietary.
- It is not a low-code/no-code platform by itself. Designer capabilities require the separate Elsa Studio front-end.
- It is not a replacement for a message broker or event bus. While it can consume events via its bookmark/stimulus model, it relies on external infrastructure (RabbitMQ, Azure Service Bus via MassTransit) for durable message transport in distributed deployments.
- It is not a reporting or monitoring tool. Observability is delegated to OpenTelemetry exporters and external platforms.
Scope statement: This profile covers the release/3.7.0 branch of elsa-workflows/elsa-core, specifically the 50 projects under /src. The primary focus modules are listed in the header.
2. Core Architecture
2.1 Target Frameworks and Deployment
Elsa targets net8.0, net9.0, and net10.0. The Directory.Packages.props file manages separate package version sets per framework, enabling multi-targeting from a single NuGet package publish. Binaries ship as Symbol Packages (.snupkg) with Source Link enabled. The server application (Elsa.Server.Web) is a standard ASP.NET Core Web project; no custom runtime host is required.
2.2 Module and Feature System
Every feature is a class that derives from FeatureBase. Features declare dependencies via [DependsOn] attributes, which the IModule implementation resolves into a topologically ordered apply sequence. Consumer code opts in through fluent extension methods on IServiceCollection:
services.AddElsa(elsa =>
{
elsa
.UseWorkflowManagement(management => management.UseEntityFrameworkCore(ef => ef.UseSqlite()))
.UseWorkflowRuntime(runtime => runtime.UseEntityFrameworkCore(...).UseDistributedRuntime())
.UseIdentity(identity => identity.UseConfigurationBasedUserProvider(...))
.UseCSharp()
.UseJavaScript()
.UseLiquid()
.UseHttp()
.UseScheduling();
});
The top-level AddElsa method creates an IModule backed by AppFeature → ElsaFeature, which itself depends on WorkflowsFeature, FlowchartFeature, DefaultWorkflowRuntimeFeature, and WorkflowManagementFeature. All optional modules (identity, tenancy, resilience, expressions) are additive.
2.3 Execution Model
Elsa uses an async, scheduler-driven execution model. The entry point is IWorkflowRunner, which constructs a WorkflowExecutionContext and drives it through an IWorkflowExecutionPipeline. The pipeline is a middleware chain built with IWorkflowExecutionPipelineBuilder; the default middleware stack is:
| Middleware | Role |
|---|---|
EngineExceptionHandlingMiddleware |
Catches unhandled engine-level exceptions |
DefaultActivitySchedulerMiddleware |
Runs the scheduler loop, dequeuing and invoking ActivityWorkItem entries until the queue is empty |
The inner scheduler loop calls IActivityInvoker, which drives each activity through its own IActivityExecutionPipeline. The default activity pipeline includes:
| Middleware | Role |
|---|---|
ExceptionHandlingMiddleware |
Activity-level exception trapping and incident recording |
ExecutionLogMiddleware |
Writes WorkflowExecutionLogEntry records |
LoggingMiddleware |
Structured logger state injection |
NotificationPublishingMiddleware |
Mediator notifications before/after activity execution |
DefaultActivityInvokerMiddleware |
Resolves IActivity.ExecuteAsync and completes the context |
WorkflowExecutionContext holds the full mutable state of a running workflow instance: the activity scheduler, bookmark set, completion callbacks, output register, variable storage, correlation ID, tenant ID, and a reference to the WorkflowGraph. ActivityExecutionContext is the per-activity scope; it exposes ExpressionExecutionContext for expression evaluation and delegates state changes back to its parent WorkflowExecutionContext.
2.4 Activity Model
All activities implement IActivity. Composite activities that schedule children implement IComposite or derive from Container. Activities that can start a workflow from an external event implement ITrigger. Ports (named outcomes such as "True", "False", or "Done") are declared via [Port] attributes or [FlowNode] on flowchart activities.
The built-in activity library covers:
| Category | Activities |
|---|---|
| Sequence/control flow | Sequence, Flowchart, Fork, If, Switch, While, For, ForEach, Break, Parallel, ParallelForEach |
| Flowchart-specific | FlowDecision, FlowFork, FlowJoin, FlowSwitch |
| I/O | WriteLine, ReadLine, SetVariable, SetName, Correlate |
| Lifecycle | Start, End, Finish, Fault, Complete, Inline |
| HTTP | HttpEndpoint, SendHttpRequest, FlowSendHttpRequest, WriteHttpResponse, DownloadHttpFile, WriteFileHttpResponse |
| Scheduling | Timer, Cron, StartAt, Delay |
| C# | RunCSharp |
2.5 Bookmark and Stimulus Model
Long-running workflows suspend by creating one or more Bookmark objects. A bookmark records the activity node, activity instance, a hashed payload key (the stimulus hash), optional callback method name, and AutoBurn/AutoComplete flags. The engine transitions the workflow to WorkflowStatus.Running / WorkflowSubStatus.Suspended when any bookmarks remain after the scheduler queue empties.
External events resume suspended workflows via the IStimulusSender service. SendAsync hashes the inbound stimulus, looks up matching bookmarks via IBookmarkBoundWorkflowService, and either resumes existing instances or triggers new ones if triggers match. This decouples the event source (HTTP endpoint, message consumer, scheduled timer, custom trigger) from the workflow host.
// Activity creates a bookmark to suspend and wait for an external event
await context.CreateBookmarkAsync(new CreateBookmarkArgs
{
Payload = new HttpEndpointBookmarkPayload(path, method),
CallbackMethodName = nameof(HandleRequest)
});
2.6 Workflow Runtime Variants
| Runtime | Class | Use Case |
|---|---|---|
| Local | LocalWorkflowRuntime |
Single-node; no distributed locking. Development and single-instance deployments only. |
| Distributed | DistributedWorkflowRuntime |
Multi-node; wraps local execution in a DistributedWorkflowClient that acquires a DistributedLock.Core lock before running a workflow instance. Prevents concurrent execution of the same instance across pods. |
Both runtimes expose IWorkflowRuntime, which produces IWorkflowClient instances. The DistributedBookmarkQueueWorker additionally acquires a cluster-wide lock before processing the bookmark queue, preventing duplicate processing on multi-pod deployments. The Medallion.Threading library provides the underlying distributed lock abstraction.
2.7 Workflow State Serialization and Persistence
WorkflowState is the serializable snapshot of a running instance. It contains status, correlation, bookmarks, activity execution contexts (as ActivityExecutionContextState), variable values, incidents, and completion callbacks. The IWorkflowStateExtractor converts a live WorkflowExecutionContext to WorkflowState and back.
Persistence is organized into two store groups:
Management stores (workflow definitions and instances):
IWorkflowDefinitionStore— CRUD forWorkflowDefinitionentities (versioned, withDefinitionId+ integer version)IWorkflowInstanceStore— CRUD forWorkflowInstanceentities
Runtime stores (execution data):
IBookmarkStore,IBookmarkQueueStoreIActivityExecutionStoreIWorkflowExecutionLogStore,IActivityExecutionLogStoreITriggerStoreIKeyValueStore
All stores are interface-backed. Provided implementations:
| Backend | Module(s) |
|---|---|
| In-memory | Default in Elsa.Workflows.Runtime (lost on restart — not for production) |
| EF Core — SQLite | Elsa.Persistence.EFCore.Sqlite |
| EF Core — SQL Server | Elsa.Persistence.EFCore.SqlServer |
| EF Core — PostgreSQL | Elsa.Persistence.EFCore.PostgreSql |
| EF Core — MySQL | Elsa.Persistence.EFCore.MySql |
| EF Core — Oracle | Elsa.Persistence.EFCore.Oracle |
| Blob storage | Elsa.WorkflowProviders.BlobStorage (workflow definitions only, via FluentStorage) |
MongoDB and Dapper adapters referenced in the README and older documentation are not present in the release/3.7.0 source tree. They may exist as third-party or commercial packages outside this repository.
2.8 Expression Engine
Elsa provides a pluggable expression evaluation system via IExpressionHandler implementations registered per language name. Each input on an activity accepts an Expression object with a Type discriminator (language name) and a Value payload.
| Language | Provider module | Engine |
|---|---|---|
| C# | Elsa.Expressions.CSharp |
Microsoft.CodeAnalysis.CSharp.Scripting (Roslyn) |
| JavaScript | Elsa.Expressions.JavaScript |
Jint 4.x |
| Python | Elsa.Expressions.Python |
pythonnet 3.x (requires Python runtime on host) |
| Liquid | Elsa.Expressions.Liquid |
Fluid.Core 2.x |
| ElsaScript DSL | Elsa.Dsl.ElsaScript |
Custom regex-based parser + compiler |
| Literal / Delegate | Elsa.Expressions |
Native .NET |
The JavaScript engine (Jint) supports optional CLR access (AllowClrAccess) and optional getConfig access for reading IConfiguration values; both are disabled by default for security. Script execution is bounded by JintOptions.ExecutionTimeout (30 seconds by default) and by the ambient CancellationToken; MaxStatements, MemoryLimit and MaxRecursionDepth are available as additional opt-in limits. Python requires a Python runtime path configured via PYTHONNET_PYDLL or application settings.
3. Key Capabilities
3.1 Workflow Authoring
| Capability | Detail |
|---|---|
| Code-first C# | Derive from WorkflowBase or WorkflowBase<TResult>, implement Build(IWorkflowBuilder) |
| JSON definitions | Store WorkflowDefinition entities; loaded via IWorkflowDefinitionStore at runtime |
| ElsaScript text DSL | .elsa files; JavaScript-inspired syntax compiled to Elsa activity graphs |
| Activity host registration | Mark a CLR class as an activity host; public async methods are auto-discovered as activities via HostMethodActivity |
| Visual designer | Requires the separate Elsa Studio Blazor app |
3.2 Workflow Execution
| Capability | Detail |
|---|---|
| Synchronous in-process execution | IWorkflowRunner.RunAsync executes synchronously within the calling scope |
| Background dispatch | IWorkflowDispatcher queues execution to a background worker via IBackgroundTaskDispatcher |
| Parallel branches | Fork, FlowFork, Parallel, ParallelForEach schedule sibling activities concurrently within a single execution context |
| Long-running / suspended | Workflow suspends when bookmark queue drains; resumes on stimulus |
| Workflow versioning | WorkflowDefinition carries DefinitionId + integer Version; running instances track DefinitionVersion |
| Workflow import/export | WorkflowDefinitionExporter / WorkflowDefinitionImporter; bulk ZIP export with optional transitive consumer inclusion |
| Child workflows | DispatchWorkflow / BulkDispatchWorkflows activities; parent can wait for child completion |
| Instance activation strategies | SingletonStrategy, CorrelationStrategy, CorrelatedSingletonStrategy — prevent duplicate instances |
| Execution log | Per-activity execution records written via IActivityExecutionStore; configurable log persistence mode |
| Commit strategies | Configurable checkpointing: per-activity, per-workflow, or global defaults |
3.3 State Management
| Capability | Detail |
|---|---|
| Variable storage drivers | WorkflowInstanceStorageDriver (in-memory state); WorkflowStorageDriver (external store); extensible via IStorageDriver |
| Incident strategies | FaultStrategy (halt on first fault), ContinueWithIncidentsStrategy (record and continue) |
| Correlation | Workflows carry an optional CorrelationId; bookmark lookup uses correlation for routing |
| Heartbeat | WorkflowHeartbeatGenerator updates an UpdatedAt timestamp; RestartInterruptedWorkflowsTask uses inactivity thresholds to detect and restart stuck instances |
3.4 HTTP Integration
| Capability | Detail |
|---|---|
HttpEndpoint trigger |
Registers an ASP.NET Core route; creates a bookmark; resumes workflow on inbound request |
SendHttpRequest |
Outbound HTTP call; response properties accessible as activity output |
WriteHttpResponse |
Writes arbitrary response body/status to the current HTTP context |
DownloadHttpFile |
Downloads a remote file and returns a stream |
| SAS token support | Elsa.SasTokens module provides time-limited signed URLs for webhook callbacks |
| Resilience per request | HTTP activities participate in the IResilientActivityInvoker pipeline |
3.5 Scheduling
| Capability | Detail |
|---|---|
Timer |
Fires at a fixed interval; creates a bookmark on each tick |
Cron |
Fires on a Cronos cron expression schedule |
StartAt |
Fires once at a specific DateTimeOffset |
Delay |
Suspends the workflow for a fixed duration |
| Recurring background tasks | Configurable schedules per task type via RecurringTaskOptions |
3.6 Identity and Authentication
| Capability | Detail |
|---|---|
| JWT authentication | IdentityTokenOptions configures signing key, issuer, audience, access/refresh token lifetimes |
| API key authentication | AspNetCore.Authentication.ApiKey integration |
| Users | IUserStore, IUserManager, IUserProvider; configuration-based or store-backed providers |
| Applications | IApplicationStore for OAuth client credentials |
| Roles and permissions | IRoleStore, IRoleManager; default admin role uses wildcard permission ("*") |
| Default admin bootstrap | DefaultAdminUserFeature creates a seeded admin user from DefaultAdminUserOptions if none exists |
3.7 Multi-Tenancy
| Capability | Detail |
|---|---|
| Tenant store | ITenantStore with ConfigurationTenantsProvider (appsettings) and StoreTenantsProvider (EF Core) |
| Tenant resolution pipeline | Pluggable pipeline via ITenantResolutionPipeline; resolvers execute in order until one resolves |
| Built-in HTTP resolvers | HeaderTenantResolver, HostTenantResolver, RoutePrefixTenantResolver |
| Identity resolvers | ClaimsTenantResolver (JWT claim), CurrentUserTenantResolver (user record lookup) |
| Tenant isolation | EF Core query filters include TenantId; empty string = default tenant; null = tenant-agnostic |
| ASP.NET Core middleware | Elsa.Tenants.AspNetCore provides TenantResolutionMiddleware |
3.8 Resilience
| Capability | Detail |
|---|---|
| Activity-level retry | Activities implement IResilientActivity; IResilientActivityInvoker wraps execution in a Polly ResiliencePipeline<T> |
| Strategy catalog | IResilienceStrategyCatalog with IResilienceStrategySource implementations |
| Configuration-backed strategies | ConfigurationResilienceStrategySource reads from appsettings.json under Resilience:Strategies |
| Transient exception detection | ITransientExceptionDetector / DefaultTransientExceptionStrategy |
| Retry attempt recording | IRetryAttemptRecorder stores attempt history into ActivityExecutionContext |
| Distributed lock resilience | Elsa.Common wraps distributed lock acquisition in a Polly retry pipeline |
3.9 Alterations API
| Alteration type | Effect |
|---|---|
Cancel |
Cancels all workflow instances in the alteration plan |
CancelActivity |
Cancels a specific running activity by activity ID or activity instance ID |
Migrate |
Migrates a workflow instance to a specified newer definition version |
ModifyVariable |
Overwrites a named variable's value in the running instance |
ScheduleActivity |
Forces a specific activity to be scheduled for execution |
3.10 Workflow Definition Management
| Capability | Detail |
|---|---|
| Definition versioning | DefinitionId (logical) + Version (integer); IsPublished and IsLatest flags |
| Import/export | ZIP archive with deterministic file names; optional recursive consumer inclusion |
| Consumer graph | IWorkflowReferenceGraphBuilder resolves all workflows that embed a given definition as a sub-workflow |
| Read-only mode | UseReadOnlyMode(true) disables mutation endpoints |
| Workflow providers | Extensible via IWorkflowProvider; blob storage provider available |
4. Integration Context
4.1 Inbound Triggers
| Source | Mechanism |
|---|---|
| HTTP request | HttpEndpoint activity; ASP.NET Core route registered at startup |
| Scheduled timer | Timer, Cron, StartAt, Delay — self-managed via recurring tasks |
| External event / message | Custom ITrigger or IStimulusSender.SendAsync call from message consumer |
| Manual start via API | POST /workflow-definitions/{id}/execute via Elsa.Workflows.Api |
| Activity bookmark resume | IStimulusSender / IWorkflowResumer matching on stimulus hash |
4.2 Persistence Infrastructure
| Concern | Options |
|---|---|
| Relational databases | SQLite, SQL Server, PostgreSQL, MySQL, Oracle (all via EF Core) |
| Workflow definition files | Blob storage via FluentStorage (Azure Blob, local file, others) |
| Distributed locking | DistributedLock.Core abstraction; file-system provider (default), database providers available |
4.3 Messaging
MassTransit (MassTransit, MassTransit.RabbitMQ, MassTransit.Azure.ServiceBus.Core) is listed in Directory.Packages.props, indicating first-class support for RabbitMQ and Azure Service Bus. However, in the release/3.7.0 source tree no MassTransit integration module is present under src/modules. MassTransit integration likely ships as a separate extension package or commercial add-on.
4.4 Observability
| Signal type | Integration |
|---|---|
| Distributed traces | OpenTelemetry (OpenTelemetry.Extensions.Hosting, OTLP exporter, ASP.NET Core / HTTP / SqlClient instrumentation) |
| Logs | ILogger<T> throughout; Serilog integration available |
| APM | Datadog APM (Datadog.Trace.Bundle 3.32.0) in Directory.Packages.props; separate Dockerfile and Docker Compose for Datadog |
4.5 Reference Server and Docker
Elsa.Server.Web is the reference ASP.NET Core server. Docker Compose files (docker/docker-compose.yml) provide pre-configured containers for PostgreSQL, SQL Server, MySQL, Oracle, MongoDB, RabbitMQ, Redis, and SMTP4Dev. A load balancer reference app (Elsa.Server.LoadBalancer) demonstrates YARP-based multi-instance deployment.
5. Known Limitations
Designer / Studio gaps:
- The Elsa Studio designer supports only Flowchart activities.
SequenceandStateMachinecomposite types cannot be designed visually; they are code-only or JSON-only. - Starting a workflow from the designer is restricted to trigger-free, input-free workflows.
- UI input validation in the designer is not implemented.
- The workflow instance viewer does not yet render input/output values.
Runtime:
LocalWorkflowRuntimehas no cluster safety. Must not be used on multi-pod deployments without switching toDistributedWorkflowRuntime.- The distributed runtime uses distributed locks to serialize execution per workflow instance. High-throughput scenarios with many concurrent short-running instances may experience lock contention.
- Heartbeat-based interrupted workflow detection relies on polling (
RestartInterruptedWorkflowsTask). There is no push-based dead-instance detection. - Near-real-time resumption under load depends on correct tuning of
TriggerBookmarkQueueRecurringTaskpolling schedules.
Persistence:
- MongoDB and Dapper adapters are referenced in documentation but are absent from the
release/3.7.0source tree. - In-memory stores (the default when no EF Core module is registered) do not survive process restarts and are unsuitable for production use.
- EF Core migrations must be applied before version upgrades that include schema changes.
Expressions:
- Python expressions require a native Python runtime installed on the host. Container deployments must include Python in the base image.
- Enabling
AllowClrAccessfor JavaScript expressions provides unrestricted .NET type access from within Jint scripts — a significant security risk for user-defined workflows. - The ElsaScript DSL uses a regex-based parser, limiting its reliability for complex or nested syntax.
Multi-tenancy:
- Background jobs and scheduled tasks use a separate
TenantTaskManagerthat must be explicitly wired; missing configuration causes tenant context loss during background execution. - The tenant-ID convention changed in 3.6.0 (empty string for default,
nullfor tenant-agnostic). Existing databases require migration before upgrading past 3.5.x.
Security:
- The identity module's
DefaultSecretHasheruses SHA-256 for password hashing — not a key-derivation function. Production deployments using the built-in identity provider are vulnerable to offline dictionary attacks. - The
DefaultAdminUserFeaturecreates a seeded admin user with wildcard permissions. Leaving default credentials unchanged is a documented risk. - The sample
appsettings.jsoncontains a default JWT signing key committed to source control; production deployments must override via secrets management.
Observability:
- Execution log records can grow very large for deeply nested workflows. No automatic archival or TTL is applied to execution log records.
- No first-party metrics dashboard or SLA tracking; operators must instrument via OpenTelemetry and external platforms.
Roadmap gaps (as of 3.7.0):
- No StateMachine visual designer support.
- No native MassTransit integration module in the
src/tree. - No MongoDB or Dapper persistence modules in the
src/tree. - OpenTelemetry Redis instrumentation is listed in dependencies but there is no built-in Elsa Redis workflow state store.