Table of Contents

DI Extensions Cheatsheet

Derived page. The behaviour described here is specified by the host-composition capability under openspec/specs/. That specification is the source; this page explains and illustrates it. Where the two disagree, the specification is right and this page is a bug.

The full menu of Add*Services() extensions Stratara exposes, by package.

Umbrella extensions (IHostApplicationBuilder)

These wire entire worker / host concerns in one call. Pick one per host.

Extension Brings Use for
builder.AddBackendServices() Mediator, Identity, Session, Security, Resilience ASP.NET API hosts
builder.AddCommandWorkerServices() Common framework + command-handling worker (interactive lane) Worker hosts that consume the command topic
builder.AddHeavyCommandWorkerServices(dop?) Common framework + dedicated heavy-command worker Worker hosts that drain long-running IHeavyCommand commands on a separate lane, so they don't starve interactive commands
builder.AddEventProjectionWorkerServices() Common framework + projection worker Worker hosts that update read-models
builder.AddSagaWorkerServices() Common framework + saga worker Worker hosts that orchestrate processes
builder.AddEventStreamHashWorkerServices() Common framework + event-stream-hash worker Worker hosts that hash event streams for tamper-evidence
builder.AddOutboxWorkerServices() Common framework + outbox-drain worker Worker hosts that publish from outbox_entry to the bus

AddCommonFrameworkServices() is called transitively by every worker / backend extension above — you don't call it yourself.

À la carte (IServiceCollection)

What the umbrellas compose. Reach for these when a host needs one concern and not the rest — a tool that dispatches commands but runs no worker, a test host, a migration runner.

Extension What it does
services.AddMediator() IMediator as a scoped service
services.AddEventSourcing() The core event-sourcing services (event source, aggregation, snapshots) as scoped, plus the default trusted-type resolver
services.AddMapping() The mapper the event-sourcing stack uses to materialize typed events from persisted rows
services.AddSessionContext() The scoped session context and its accessor. Pair with app.UseMiddleware<SessionContextMiddleware>() in an ASP.NET host
services.AddIdentity() The scoped identity accessors that resolve from the ambient session context
services.AddBackgroundTasks() The in-process background-task queue and its hosted service (capacity 100 pending items)
services.AddOutboxDispatcher() ICommandOutboxDispatcher + IEventBundleOutboxDispatcher (scoped) and the bus they publish through
services.AddAuthorizingCommandOutboxDispatcher() Wraps the dispatcher so [RequireRole] / [RequirePermission] are enforced on the outbox path too, keeping the inner dispatcher resolvable
services.AddPipelineBehaviorWithResult<T>() Registers an open-generic pipeline behaviour for the result-returning request shape
services.AddTrustedTypeResolver() The default ITrustedTypeResolver if none is registered. Idempotent
services.AddTrustedType<T>() Adds one type to the trusted-type allowlist — for types produced but never handled, such as a snapshot type no projection or saga anchors
services.AddEventUpcaster<T>() Registers an IEventUpcaster and ensures the pipeline exists
services.AddEventUpcasterPipeline() The default upcaster pipeline if none is registered. Idempotent; every AddEventUpcaster overload calls it

Workers without the composite

The hosted services the Add*WorkerServices() umbrellas wire. Register one directly when the host already has the framework services and needs a second lane.

Extension What it runs
services.AddMediatorWorker() The interactive command worker — subscribes to the command topic, restores the session context, dispatches through IMediator
services.AddHeavyCommandWorker(dop?) The heavy-command lane, draining the heavy-command topic so IHeavyCommand work cannot starve the interactive lane
services.AddOutboxWorker() The outbox-drain hosted service; binds OutboxOptions from configuration
services.AddProjectionWorker() The projection runtime and its hosted service; binds ProjectionOptions
services.AddSagaWorker() The saga runtime and its hosted service; binds SagaOptions
services.AddEventStreamHashWorker() The event-stream hashing worker and the anchor services behind it

Domain registration (IServiceCollection)

These tell Stratara what to dispatch / project / saga. Call once per assembly that contains the relevant types.

Extension Discovers Side-effect
services.AddCommandHandlersFromAssemblyContaining<T>() ICommandHandler<TCmd> + IQueryHandler<TCmd, TResult> (the unified contract) Per-handler AddScoped
services.AddQueryHandlersFromAssemblyContaining<T>() IQueryHandler<TQuery, TResult> Per-handler AddScoped
services.AddProjectionsFromAssemblyContaining<T>() IProjection impls + their HandleAsync(SomeEvent) overloads Per-projection AddSingleton<IProjection> + event-allowlist registration
services.AddSagasFromAssemblyContaining<T>() ISaga impls + their HandleAsync(SomeEvent) overloads Per-saga AddSingleton<ISaga> + event-allowlist registration
services.AddAggregatesFromAssemblyContaining<T>() IAggregate impls + their Apply(SomeEvent) methods Adds each aggregate and each apply-target event type to ITrustedTypeResolver
services.AddDomainEventTypesFromAssemblyContaining<T>() The Apply(SomeEvent) parameter types of the assembly's aggregates Adds only those event types to ITrustedTypeResolver — no aggregate types, no handler classes. For event-only hosts (projection/saga workers) that must deserialize bus/stream payloads without wiring handler dependencies

Security + integrity

Extension What it does
services.AddStrataraFileKeyStore(configuration) Registers the production file-backed EnvelopeFileKeyStore (KEK-wrapped, versioned per-KeyScope DEKs) + FileMasterKeyProvider + the AES-GCM ISecureBlobEncryptor. Lives in Stratara.Security (dependency-light). Call before AddSecurity() so it wins the TryAdd race.
services.AddSecurity() Wires ISecureJsonSerializer ([EncryptData]), the AES-GCM blob encryptor, and a Development-only DummyKeyStore fallback (TryAdd, so a real IKeyStore registered first wins). Adds the KeyStoreStartupProbe fail-fast guard.
services.AddBusEnvelopeIntegrity(opts) Opt-in HMAC signing of CommandEnvelope + EventBundle
services.AddStrataraBlobEncryption() The AES-GCM blob encryptor and its factory on their own, without the rest of AddSecurity()
services.AddStrataraErasure() Composes the membership, API-key, setting and key-material sweeps into one erasure operation. Registers no store of its own — the four it sweeps must already be registered

Validation

Extension What it does
services.AddStrataraValidation() Registers the validation pipeline behavior. Call before other AddPipelineBehavior* so it runs outermost.
services.AddValidatorsFromAssemblyContaining<T>() Discovers + registers every concrete IValidator<T> in the marker's assembly as scoped.

Tenant isolation

Extension What it does
services.AddStrataraTenantIsolation() Registers the tenant-isolation pipeline behavior. Acts only on requests implementing ITenantScopedRequest; rejects a request whose TenantId ≠ the session's data-owner tenant with TenantAccessDeniedException (→ HTTP 403). Call after AddStrataraValidation().
services.AddStrataraTenantIsolation(o => o.Mode = TenantIsolationMode.Strict) Strict mode — additionally routes every cross-tenant operation (actor tenant ≠ data-owner tenant) through ICrossTenantAuthorizer. The shipped default denies all; register your own ICrossTenantAuthorizer to grant the cross-tenant case (e.g. a platform admin).

Resilience

Extension What it does
services.AddResiliencePipelines() Registers the four Polly named pipelines — ResilienceNames.MessageBus, .CommandDispatcher, .EventBundleDispatcher, .ConcurrencyConflict
services.AddStrataraResilienceBehavior() Mediator behavior that dispatches IResilientRequest through its chosen pipeline

Use the ResilienceNames constants rather than the literal pipeline strings.

Outbox transport (pick one per host)

Extension Bus
builder.AddMessaging() RabbitMQ — extends IHostApplicationBuilder, and is what the worker composites call
services.AddAzureServiceBus(connectionString) Azure Service Bus (connection-string)
services.AddAzureServiceBusWithManagedIdentity(...) Azure Service Bus (DefaultAzureCredential)

One transport per host — the explicit one wins. AddMessaging() registers IMessageBus for RabbitMQ; the Azure Service Bus extensions replace it, so an explicit AddAzureServiceBus takes effect even after a worker composite wired the RabbitMQ umbrella. Order no longer decides the transport, but registering both in one host is still a smell — pick one.

Write store + database contexts (Stratara.EventSourcing.EntityFrameworkCore)

Extension What it does
services.AddNpgsqlWriteDbContextFactory<TContext>() Npgsql-backed IDbContextFactory<TContext> for the write-store context, plus the default IWriteUnitOfWork if none is registered
services.AddNpgsqlReadDbContextFactory<TContext>() The same for a read-store context
services.AddNpgsqlIdentityDbContextFactory<TContext>() The same for an identity-store context, plus a scoped resolution of the context itself so ASP.NET Identity can inject it directly
services.AddWriteStore(configuration) Binds EventSourcingOptions from the EventSourcing section — snapshot cadence, batch sizes and the other write-side knobs
services.AddCommandAuditing() CommandAuditBehavior for both command shapes — persists an audit row per dispatched command; queries pass through (Stratara.EventSourcing.Pipeline.CommandAudit)

Outbox coordination + projection replay (Stratara.Outbox.RabbitMQ)

Extension What it does
services.AddRedisOutboxLock() Replaces the no-op NullOutboxLock with the Redis-backed one, which is what makes more than one outbox-worker replica safe. Needs an IConnectionMultiplexerAddCaching() from Stratara.Infrastructure registers one. Lease it via OutboxOptions.LockLeaseSeconds
services.AddProjectionReplayState() Registers the Redis-backed projection-replay state and ProjectionReplayOptions with its defaults, so the replay marking is leased (LeaseSeconds, default 300) rather than outliving a crashed replay. Idempotent

Observability (Stratara.ServiceDefaults)

Extension What it does
builder.ConfigureOpenTelemetry() OpenTelemetry logging, metrics and tracing with the default instrumentation (HTTP client, EF Core, RabbitMQ, runtime); wires the OTLP exporter when OTEL_EXPORTER_OTLP_ENDPOINT is set
builder.ConfigureAspNetOpenTelemetry() Adds ASP.NET Core request instrumentation on top, filtering /health and /alive out of tracing
builder.ConfigureSerilog() Serilog as the host's logging provider with Stratara's defaults (destructuring attributes, async console sink, OTLP sink when configured), reading the Serilog configuration section

Test support (Stratara.Testing.EntityFrameworkCore)

Extension What it does
services.AddStrataraTestingEventStore() The event-sourcing write stack over an in-memory store, plus a test key store, encryptor and session context. Register your aggregates with AddAggregatesFromAssemblyContaining<T>() so event payloads resolve

Health checks

Extension What it does
builder.AddDefaultHealthChecks() Baseline self check, tagged live — surfaces on both /health and /alive once MapDefaultEndpoints() is called
healthChecks.AddEventStoreHealthCheck(...) Verifies the write-side database is reachable. Needs the write store registered
healthChecks.AddOutboxHealthCheck(...) Reports depth and age of the outbox backlog, degrading above a pending-entry threshold you pass in

Identity directory (Stratara.Identity.EntityFrameworkCore)

TContext is any DbContext whose model includes the directory tables — derive from IdentityDirectoryDbContext<TContext> or call modelBuilder.ApplyIdentityDirectoryModel() in your own OnModelCreating.

Extension What it does
services.AddTenantMembershipStore<TContext>() EF ITenantMembershipStore (tenant_membership, active_tenant) — shares the request's context
services.AddTenantMembershipStoreFromContextFactory<TContext>() Same store, a fresh context per operation (needs AddDbContextFactory<TContext>())
services.AddMembershipAuthorization() IAuthorizationProvider over tenant-scoped membership roles
services.AddMembershipAuthorization<TUser>() Above ∪ global ASP.NET Identity roles
services.AddMembershipCrossTenantAuthorizer(opts?) ICrossTenantAuthorizer for strict tenant isolation (membership OR a configured platform role)
services.AddPermissionCatalog(c => …) Declares the permission vocabulary + role grants (throws on an undeclared grant)
services.AddCatalogPermissionResolver() IPermissionResolver — membership roles through the catalog
services.AddCatalogPermissionResolver<TUser>() Above ∪ global ASP.NET Identity roles
services.AddSettingCatalog(c => …) Declares the setting vocabulary (defaults, IsInherited, IsEncrypted)
services.AddSettingStore<TContext>() EF ISettingStore (setting_entry) + the ISettingProvider fallback facade
services.AddSettingStoreFromContextFactory<TContext>() Same pair, a fresh context per operation
services.AddApiKeyStore<TContext>() EF IApiKeyStore (api_key) — issue / import / validate / revoke / sweep
services.AddApiKeyStoreFromContextFactory<TContext>() Same store, a fresh context per operation

The …FromContextFactory variants exist because the plain registrations share one context across every directory store in a request: a database context serves one operation at a time, and a store's commit also commits whatever you have left unsaved on that context. A context per operation removes both, and in exchange a store write no longer joins a transaction you opened on your own scoped context. Calling both variants for the same store leaves whichever ran first in place. See Tenant Membership.

[RequirePermission] is only enforced when the host also registers an authorizing mediator (services.AddAuthorizingMediator<MembershipAuthorizationProvider>()). Without it — or without an IPermissionResolver — the mediator's startup validator throws rather than let a guarded request through unchecked.

ASP.NET specific

Extension What it does
builder.AddAspNetIdentity<TUser, TIdentityDbContext>() Channel-agnostic ASP.NET Core identity wiring (password/schema-v3/passkey defaults — no lockout)
builder.AddAspNetIdentityWithSignInManager<TUser, TIdentityDbContext>() Above + lockout defaults + IStrataraSignInManager wrapper + localization
builder.AddDevelopmentNoOpEmailSender<TUser>() Stub IEmailSender for Development — throws on every other environment name, including Staging (3.4.0; Production-only before)
services.AddMembershipTenantClaim<TUser>() Stamps stratara:tenant_id into every issued principal (claims-factory decorator)
services.AddMembershipTenantClaimsTransformation() Resolves stratara:tenant_id live per request — a tenant switch applies without re-issuing the sign-in
services.AddStrataraPermissionPolicies() Turns every catalog permission into an on-demand policy → [Authorize("sims.read")]
services.AddStrataraExternalLoginProvisioning<TUser>(opts?) JIT create/link of the local account on first external sign-in (fail-closed)
services.AddStrataraProblemDetails() Turns a validation rejection into a 400 with the failures grouped by field, and an authorization or tenant-access refusal into a 403 — one shape for all three
app.MapDefaultEndpoints() /health + /alive endpoints (Stratara.ServiceDefaults.AspNetCore)

Authentication schemes (AuthenticationBuilder)

Extension Scheme
.AddStrataraOpenIdConnect(configuration) Interactive external login, from Identity:OpenIdConnect
.AddStrataraJwtBearer(configuration) API access tokens, from Identity:JwtBearer (multi-issuer by iss)
.AddStrataraApiKey(opts?) StrataraApiKeyX-Api-Key header (opt-in query parameter)
.AddStrataraAuthSchemeSelector(opts?) Policy scheme routing by request shape: API key → Bearer → cookie

The builder.Add* identity rows are extension members of IHostApplicationBuilder in the Microsoft.Extensions.Hosting namespace (Microsoft convention since v3.0.15). The services.Add* rows and the authentication-scheme extensions above live in Microsoft.Extensions.DependencyInjection.