Elarion

Configuration

Every configuration key and MSBuild property Elarion reads, in one place.

This page collects the runtime configuration sections and build properties Elarion reads. Most are optional with sensible defaults.

Modules

Feature modules are enabled by default. Disable one by name:

{
  "Modules": {
    "Clients": { "Enabled": false }
  }
}
KeyDefaultEffect
Modules:{Name}:EnabledtrueDisables a feature module's handlers, endpoints, JSON metadata, and scheduled jobs. Ignored for core modules.

See Modules.

Scheduler

Read by AddElarionScheduler(IConfiguration):

{
  "Scheduler": {
    "Enabled": true,
    "MaxConcurrentExecutions": 8,
    "MaxRetainedCompletedJobs": 1024,
    "MaxMisfireCatchUpRuns": 32
  }
}
KeyDefaultEffect
Scheduler:EnabledtrueMaster switch for the in-memory scheduler. When false, descriptor-declared jobs are not enqueued and the runtime IJobScheduler enqueue/schedule APIs throw InvalidOperationException (a disabled instance never drains its queue).
Scheduler:MaxConcurrentExecutionsEnvironment.ProcessorCount (at least 1)Global cap on concurrent job executions.
Scheduler:MaxRetainedCompletedJobs1024How many terminal job states are kept in memory.
Scheduler:MaxMisfireCatchUpRuns32Upper bound on catch-up runs for the CatchUp misfire policy.

See Scheduling.

Scheduled job placeholders

[ScheduledJob] string properties (FixedRate, FixedDelay, Cron, Enabled, …) support Spring-style configuration placeholders, re-resolved per occurrence:

PlaceholderBehavior
${Jobs:Interval}Resolve the key; throw during resolution if missing.
${Jobs:Interval:-15m}Resolve the key; fall back to 15m if missing or blank.

See Schedules.

Feature flags

Declared flags are evaluated through IFeatureFlagService. Code-defined flags ([FeatureFlag]) are answered by their resolver; the values of backend-evaluated flags ([BackendFeatureFlag]) come from the provider package the host wires.

Batteries-included config-driven flags

AddElarionFeatureManagement(IConfiguration) (from Elarion.FeatureFlags.FeatureManagement) wires the Microsoft.FeatureManagement OpenFeature provider behind the OpenFeature-backed IBackendFeatureFlagEvaluator, so declared backend flags read their values out of the conventional FeatureManagement configuration section:

{
  "FeatureManagement": {
    "BetaCheckout": true,
    "PricingExperiment": {
      "Variants": [
        { "Name": "control", "ConfigurationValue": "control" },
        { "Name": "treatment", "ConfigurationValue": "treatment" }
      ],
      "Allocation": {
        "DefaultWhenEnabled": "control",
        "Percentile": [
          { "Variant": "treatment", "From": 0, "To": 50 },
          { "Variant": "control", "From": 50, "To": 100 }
        ]
      },
      "EnabledFor": [ { "Name": "AlwaysOn" } ]
    }
  }
}

BetaCheckout is a boolean gate (drives a [FeatureGate("BetaCheckout")]); PricingExperiment is a variant flag whose allocated variant selects a [FeatureVariant("PricingExperiment", Variant = "treatment")] implementation.

SectionRead byEffect
FeatureManagementAddElarionFeatureManagement(configuration)Standard Microsoft.FeatureManagement schema (booleans, filters, variants/allocations) surfaced to [FeatureGate]/[FeatureVariant].

Bring-your-own provider

AddElarionOpenFeature() (from Elarion.FeatureFlags.OpenFeature) registers the OpenFeature-backed IBackendFeatureFlagEvaluator and takes its flag values from whatever OpenFeature provider the host registers separately (services.AddOpenFeature(b => b.AddProvider(...)) — LaunchDarkly, ConfigCat, flagd, Flagsmith, …), not from an Elarion-owned config section. It maps the explicit FeatureEvaluationContext into the OpenFeature EvaluationContext off-HTTP (targeting key plus the user id / groups / tenant), so per-user targeting works under JSON-RPC, MCP, and HTTP alike. Configure the provider per its own documentation.

See Feature flags.

JSON-RPC schema generation (MSBuild)

Properties read by Elarion.AspNetCore.SchemaGeneration during build:

PropertyDefaultPurpose
ElarionJsonRpcGenerateSchemafalseEnables the build-time export targets.
ElarionJsonRpcGenerateSchemaOnBuildsame as aboveControls automatic generation during dotnet build.
ElarionJsonRpcSchemaOutputPath$(BaseIntermediateOutputPath)rpc-schema.jsonExact output file path.
ElarionJsonRpcSchemaOutputDirectory$(BaseIntermediateOutputPath)Output directory (with …FileName).
ElarionJsonRpcSchemaFileNamerpc-schema.jsonOutput file name.
ElarionJsonRpcSchemaEnvironmentDevelopmentEnvironment used while loading the app.
ElarionJsonRpcSchemaApplicationArgumentsemptyArgs passed to the app entry point.
ElarionJsonRpcSchemaGenerationOptionsemptyExtra tool arguments.

See Schema generation.

Proxy identity

AddElarionProxyIdentity(configuration, environment) binds the ProxyIdentity section (package Elarion.AspNetCore.ProxyIdentity). Outside Development it refuses to start unless Enabled is true and the section is complete, or Fixed:Enabled is true with a Fixed:Subject.

KeyDefault
ProxyIdentity:Enabledfalse
ProxyIdentity:Issuer— (required when enabled)
ProxyIdentity:JwksUrl / ProxyIdentity:MetadataAddress— (neither: {Issuer}/.well-known/openid-configuration)
ProxyIdentity:RequireHttpsMetadatatrue
ProxyIdentity:Audiences— (required unless AllowAnyAudience)
ProxyIdentity:AllowAnyAudiencefalse
ProxyIdentity:TokenHeader / ProxyIdentity:TokenCookie— (at least one)
ProxyIdentity:Claims:{Subject,Email,EmailVerified,Name,Groups,Roles}sub, email, email_verified, name, groups, roles
ProxyIdentity:EmailTrustClaim
ProxyIdentity:LogoutUrl—
ProxyIdentity:ClockSkew00:01:00
ProxyIdentity:AutomaticRefreshInterval01:00:00
ProxyIdentity:RefreshInterval00:01:00
ProxyIdentity:LastKnownGoodLifetime00:00:00 (off)
ProxyIdentity:Development:*developer / developer@example.com, switch header X-Elarion-Dev-User, cookie elarion-dev-user
ProxyIdentity:Development:{RolesHeader,RolesCookie}X-Elarion-Dev-Roles, elarion-dev-roles
ProxyIdentity:Development:{GroupsHeader,GroupsCookie}X-Elarion-Dev-Groups, elarion-dev-groups
ProxyIdentity:Fixed:Enabledfalse (requires Enabled: false; allowed in every environment)
ProxyIdentity:Fixed:{Subject,Email,Name,Groups,Roles}— (Subject required when enabled)

The user switch value - (ProxyIdentityDefaults.DevelopmentSignedOutUser) signs nobody in for the request; it is reserved, so no stand-in user (Development:Subject/Email, Development:Users:*:Subject/Email) may use it.

See Proxy identity.

Current user (options, not config)

AddElarionCurrentUser(options => …) configures claim mapping in code rather than appsettings:

OptionDefault
UserIdClaimType"sub"
EmailClaimType"email"
RoleClaimTypeClaimTypes.Role
DefaultRolesWhenAuthenticatedempty

See Current user.

Authorization messages (options, not config)

AddElarionAuthorization(o => …) registers AuthorizationOptions as a plain singleton; configure it in code:

builder.Services.AddElarionAuthorization(o => o.ResourceNotFoundMessage = "Not found.");
OptionDefault
PermissionClaimType"permission" — the claim [RequirePermission] (and [ResourceFilter].BypassPermission) reads
ForbiddenMessageFormat"Access denied." — {0} echoes the unmet requirement when included
UnauthorizedMessage"Authentication required."
ResourceNotFoundMessage"The requested resource was not found." — the answer of [RequireResource(DenyAs = ResourceDenial.NotFound)]; keep it identical to your handlers' own not-found message

See Authorization and Resource authorization.

Idempotency purge (options, not config)

AddElarionIdempotencyEntityFrameworkCore<TDbContext>(configure => …) takes an optional Action<IdempotencyPurgeOptions> configured in code rather than appsettings. It tunes the background IdempotencyKeyPurgeService that reclaims expired keys:

OptionDefault
PollingInterval1 hour

Complementary to the per-handler [Idempotent(RetentionHours = …)], which sets how long a completed key is retained; PollingInterval sets how often the worker sweeps out keys past that retention.

See Idempotency.

Outbox (options, not config)

AddElarionOutbox<TDbContext>(configure => …) (from Elarion.Messaging.Outbox) takes an optional Action<OutboxOptions> configured in code rather than appsettings. It tunes the durable integration-event tier and its background OutboxDeliveryService:

OptionDefault
RunDeliveryWorkertrue (false disables claiming only; publishers still require the complete generated consumer catalog for fan-out/role resolution)
PollingInterval1 second
BatchSize100
MaxDeliveryAttempts10
LeaseDuration2 minutes
BaseRetryDelay5 seconds (exponential backoff base; Zero retries on the next poll)
MaxRetryDelay1 hour
RetentionPeriod7 days (null keeps delivered rows forever)
PurgeInterval1 hour (how often the worker runs the retention purge)
SerializerOptionsnull (canonical IElarionJsonSerialization)

See Event backends.

JSON serialization (options, not config)

Every subsystem shares one canonical JsonSerializerOptions, configured in code (not appsettings) via ConfigureElarionJson(Action<ElarionJsonOptions>). AddElarion(configuration) already contributes every enabled module's source-generated context, so most hosts configure nothing:

builder.Services.ConfigureElarionJson(o => {
    o.PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower;   // default: CamelCase
    o.TypeInfoResolvers.Add(SomeExtraContext.Default);          // an extra source-gen context
});
OptionDefault
PropertyNamingPolicyJsonNamingPolicy.CamelCase
PropertyNameCaseInsensitivetrue
DefaultIgnoreConditionWhenWritingNull
EnableReflectionFallbackfalse (AOT-strict; a type missing from every source-gen context throws)
TypeInfoResolversTransport envelope + module contexts, composed first-match-wins
OverrideTypeInfoResolversempty (host-priority resolvers composed ahead of every TypeInfoResolvers entry — the way to override a type a transport envelope context also registers)
PostConfigurenone (escape hatch for converters/encoder)

See Serialization.

Unexpected errors (options, not config)

ConfigureElarionUnexpectedErrors(o => …) (from Elarion.Abstractions) sets the answer JSON-RPC, MCP and generated [HttpEndpoint] routes give for an exception that escaped the handler pipeline. Calls accumulate; a later call overrides only the hooks it sets. Both hooks receive an UnexpectedErrorContext (Exception, Transport, Operation, the call's scoped Services); a hook that throws falls back to the default.

OptionDefault
Message"Internal error" (UnexpectedErrorOptions.DefaultMessage)
Referencethe current trace id (Activity.Current.TraceId), or a fresh id when no trace is active; return null to send none
LeaveToHostanswer every exception. Generated HTTP routes only: true lets the exception reach the host's exception handling (the developer exception page, an IExceptionHandler); JSON-RPC and MCP always answer
builder.Services.ConfigureElarionUnexpectedErrors(o =>
    o.Message = _ => "Something went wrong. Please contact support and quote the reference.");

See Unexpected errors.

Request validation (options, not config)

AddElarionValidation(configure => …) (from Elarion.Validation) registers the default IRequestValidator over Microsoft.Extensions.Validation and takes an optional Action<ValidationOptions> configured in code rather than appsettings. It also calls AddElarionJson(); the generated per-module validation resolvers register themselves through each module's ConfigureDefaultServices, so most hosts pass nothing:

builder.Services.AddElarionValidation();
OptionDefault
MaxDepth32 (cycle protection when walking the request object graph)

See Validation.

Handler context enrichment (options, not config)

Every handler is wrapped in a context-enrichment decorator that is on by default — it needs no registration. AddElarionUserContextEnrichment(configure => …) (from Elarion) takes an optional Action<UserContextEnrichmentOptions> configured in code rather than appsettings; call it only to narrow the payload, opt into email, or disable the built-in user enricher:

// on by default — nothing to register; call only to change the defaults
builder.Services.AddElarionUserContextEnrichment(o => o.IncludeEmail = true);   // opt into user.email (PII)
builder.Services.AddElarionUserContextEnrichment(o => o.Enabled = false);       // disable the built-in user enricher
OptionDefault
Enabledtrue (the built-in user enricher runs; host-registered enrichers are unaffected)
IncludeRolestrue (emit user.roles / UserRoles)
IncludePermissionstrue (emit user.permissions / UserPermissions from the permission claim type)
IncludeEmailfalse (email is PII — opt in to emit user.email / UserEmail)
MaxItems16 (upper bound on roles/permissions joined into a tag)

Contribute your own trace tags / log-scope items by registering an IHandlerContextEnricher:

builder.Services.AddElarionHandlerContextEnricher<TenantContextEnricher>();

The log-scope keys only surface if the host enables IncludeScopes on its logging exporter. See Telemetry & observability › User & request context.

Connections (options, not config)

AddElarionConnections(options => …) (from Elarion.Connections) takes an optional Action<ElarionConnectionsOptions> configured in code rather than appsettings — kernel-wide behavior every connection adapter applies identically (per-endpoint and per-connection knobs stay on the adapters):

builder.Services.AddElarionConnections(o => o.DefaultInvokeTimeout = TimeSpan.FromSeconds(10));
OptionDefault
DefaultInvokeTimeout30 seconds (applied whenever an InvokeAsync call carries no per-call ClientInvokeOptions.Timeout; null = no default — the call is bounded only by its token)
MaxIdentityMetadataEntries32 (adapter-owned identity metadata entries accepted at registration or promotion)
MaxIdentityMetadataKeyLength128 (UTF-16 chars per metadata key)
MaxIdentityMetadataValueLength1024 (UTF-16 chars per metadata value)
MaxPrincipalIdentities16 (identities per connection principal)
MaxPrincipalClaims256 (claims across the principal and every actor identity)
MaxPrincipalActorDepth16 (actor-identity nesting depth)

The layering is per-call Timeout > DefaultInvokeTimeout > unbounded; pass Timeout.InfiniteTimeSpan per call to make a single invoke unbounded without touching the default. See Connections.

TCP endpoints (options, not config)

Each AddElarionTcpConnectionListener/Dialer call (and each runtime Apply…) configures one endpoint in code via ElarionTcpListenerOptions/ElarionTcpDialerOptions; per-connection overrides come from the session's Settings returning TcpConnectionSettings:

OptionDefault
Framerrequired (LengthPrefixedTcpFramer, DelimitedTcpFramer, or custom)
MaxInboundFrameBytes1 MiB (total unconsumed wire bytes per inbound frame — prefix/header/body/trailer included)
MaxOutboundFrameBytes1 MiB (total framed wire bytes per outbound message)
InitialReadBufferBytes / InitialSendBufferBytes8 KiB / 4 KiB (send buffer is pooled and trimmed after oversized frames)
MaxPendingSends256 (bounded outbound admission — queued plus in-progress; at capacity a send throws TcpSendQueueFullException)
ShutdownGracePeriod5 seconds (graceful-close drain window before stragglers are force-aborted; shutdown then awaits every connection task)
HandshakeTimeout10 seconds (framed application authentication deadline)
Tlsnull (TcpServerTlsOptions on listeners / TcpClientTlsOptions on dialers; own HandshakeTimeout default 10 s; TLS always completes before framing)
IdleTimeoutnull (arms the codec's OnIdleAsync)
NoDelaytrue
Transport"tcp" (bounded telemetry tag)
MaxConcurrentConnections (listener)null (shed excess accepted sockets at the cap)
ReconnectMinDelay / ReconnectMaxDelay (dialer)1 s / 30 s (jittered exponential backoff)

On this page