Elarion

Attributes

The full catalog of Elarion attributes — what each one triggers, its parameters, and their defaults.

A lookup-only catalog of every Elarion attribute, grouped by area. Each entry lists the package, what it applies to, a one-line "triggers", and a parameter table (type, default, meaning). For the behavior behind each attribute see the linked capability or concept page.

Parameters are listed as constructor arguments and init properties. Defaults are the literal defaults baked into the attribute. Enum value tables follow the group they belong to.

Modules & DI

[AppModule]

  • Package: Elarion.Abstractions · Applies to: class
  • Triggers: groups handlers/services/jobs/consumers under a module boundary and emits the {ModuleType}ElarionModuleServices.ConfigureDefaultServices sibling plus host bootstrapping; feature modules are gated by Modules:{Name}:Enabled.
ParameterTypeDefaultMeaning
namestring (ctor)requiredUnique module name; used for the feature-flag key Modules:{Name}:Enabled and logging.
KindAppModuleKind (init)FeatureFeature (optional, can be disabled) or Core (always enabled, ignores feature flags).
DependsOnstring? (init)nullComma-separated module names this module depends on; the generator topologically sorts so dependencies initialize first.

The class may optionally implement convention-based statics: ConfigureServices(IServiceCollection, IConfiguration), MapEndpoints(IEndpointRouteBuilder), GetJsonTypeInfoResolver(), and ConfigureEndpointGroup(IEndpointRouteBuilder).

AppModuleKind: Feature (= 0), Core (= 1).

See Modules.

[ClientFeatures]

  • Package: Elarion.Abstractions (Modules) · Applies to: class (alongside [AppModule])
  • Triggers: opts named feature flags and variants into the generated client-capability manifest. The list is the complete client-visible vocabulary for that module: internal flags are not exposed by discovery, and a disabled module contributes none. A listed name may be a pure UI flag; it does not need a server-side [FeatureGate].
ParameterTypeDefaultMeaning
featuresparams string[] (ctor) → Features (IReadOnlyList<string>)emptyFeature flag or variant names the session endpoint may evaluate and return for this module.

See Client capabilities.

[Service]

  • Package: Elarion.Abstractions · Applies to: class
  • Triggers: source-generated DI registration. Without explicit service types, directly implemented interfaces are used as contracts; with none, the implementation type registers as itself. Base-class interfaces are not treated as explicit contracts.
ParameterTypeDefaultMeaning
serviceTypesparams Type[] (ctor) → IReadOnlyList<Type> ServiceTypesemptyExplicit contract types to register; when empty, contracts are inferred from directly implemented interfaces.
ScopeServiceScope (init)ScopedDI lifetime.

ServiceScope: Scoped, Singleton, Transient.

See Services.

[GenerateContractSetRegistration]

  • Package: Elarion.Abstractions · Applies to: method (a static partial extension method the generator implements)
  • Triggers: runs the ContractSetRegistrationGenerator to compose every implementation of one contract, unconditionally — the module-less counterpart to [Service] for infrastructure seams (protocol packet bindings, codec catalogs, pipeline stages) that live in host assemblies with no [AppModule]. The host authors, names, and places the composition method; the generator fills in the body. Discovery is compilation-local: every non-abstract, non-generic class in the declaring assembly assignable to the contract is registered via TryAddEnumerable (calling the method twice never duplicates the set). The method is pulled by the host's composition root, exactly once, unconditionally; no bootstrapper invokes it and no configuration gates it, so a transport's routing table cannot be silently emptied by a module switch. The method shape is fixed (ELSG019 otherwise): static partial IServiceCollection Name(this IServiceCollection services) on a non-generic static partial class, without a hand-written implementation.
ParameterTypeDefaultMeaning
contractTypeType (ctor) → ContractTypeThe composed contract; must be an interface or abstract class (open generics rejected).
ScopeServiceScope (init)SingletonDI lifetime for every implementation — infrastructure seams are boot-composed.
public static partial class PacketBindingRegistrations {
    [GenerateContractSetRegistration(typeof(IPacketBinding))]
    public static partial IServiceCollection AddPacketBindings(this IServiceCollection services);
}

// host composition root:
services.AddPacketBindings();

Diagnostics ELSG014 (contract not an interface/abstract class) / ELSG015 (empty set) / ELSG016 (same contract declared twice) / ELSG017 (generic implementation) / ELSG018 (implementation also a [Service] under the same contract) / ELSG019 (invalid method shape). See Services — module services vs. contract sets.

[DecoratorList]

  • Package: Elarion.Abstractions (Pipeline) · Applies to: class
  • Triggers: declares the decorator types for an application-owned pipeline profile attribute so the generator can read the list at compile time. (Pipeline profile attributes themselves, e.g. DefaultPipelineAttribute, are application-defined, not framework-shipped.)
ParameterTypeDefaultMeaning
decoratorsparams Type[] (ctor) → DecoratorsemptyOrdered decorator types (open generics), applied outermost-first: the first type is the outermost decorator.

See Decorator pipelines.

Transports

[Handler]

  • Package: Elarion.Abstractions · Applies to: class
  • Triggers: marks a handler as a named operation on the transport-neutral request/reply bus (the handler must implement IHandler<TRequest, TResponse>). The operation name is declared once and shared by every dispatcher transport; Transports selects JSON-RPC, MCP, or both. JSON-RPC and MCP are thin adapters over the one bus, so a handler is "define once, choose surfaces via the Transports flag". REST is a separate opt-in via [HttpEndpoint].

The operation name is optional: [Handler] alone is valid. When omitted, the name is inferred by convention as {module}.{operation}, where operation is the handler type name minus a Handler/Command/Query/Request suffix, camelCased (e.g. module Clients + CreateClientclients.createClient). Supply an explicit name for stable public/wire contracts.

ParameterTypeDefaultMeaning
namestring? (ctor) → NameoptionalThe operation name, e.g. "clients.create". When omitted, inferred by convention as {module}.{operation}.
TransportsHandlerTransports (init, [Flags])All (JsonRpc | Mcp | Connection)Which dispatcher transports expose the handler.
ScopeServiceScope (init)ScopedThe handler's registration lifetime — the same vocabulary and semantics as [Service(Scope = …)]. Singleton removes scope participation from dispatch (the low-allocation choice, ADR-0066) and is compile-time verified: every constructor dependency must be provably singleton (ELSG011/ELSG012) and the pipeline must not attach scope-dependent features (ELSG013).

HandlerTransports: JsonRpc (= 1), Mcp (= 2), Connection (= 4, bidirectional connection dispatch), All (= JsonRpc \| Mcp \| Connection).

See JSON-RPC and MCP.

[HandlerTelemetry]

  • Package: Elarion.Abstractions · Applies to: class (handler or module), assembly
  • Triggers: declares how much always-on observability the generated pipeline gives handlers. Leveled, nearest declaration wins: handler class → module class → assembly → Full. With the effective mode None, the registration generator does not emit the observability decorator at all — no span, no execution metric, no context enrichment, no log scope, no decorator object on the hot path (ADR-0066). Failures are unaffected: they remain Result values and exceptions still propagate to the transport. The typical use is a hot module opting down once while one cold handler inside it re-enables by declaring Full on itself.
ParameterTypeDefaultMeaning
modeHandlerTelemetryMode (ctor) → ModerequiredFull (= 0, the default when the attribute is absent) or None (= 1).

See Telemetry and Connections — the low-allocation dispatch profile.

[McpHandler]

  • Package: Elarion.Abstractions · Applies to: class
  • Triggers: optional MCP tool-name customization for a handler already on the MCP surface (its [Handler] Transports includes Mcp). Purely additive — carries no enable/disable flag. Tool/parameter descriptions come from System.ComponentModel.DescriptionAttribute, not this attribute.
ParameterTypeDefaultMeaning
ToolNamestring? (init)nullOverrides the MCP tool name; when null/empty the name is derived from the handler's operation name via the host's transform (e.g. "clients.create""clients_create").

See MCP.

[HttpEndpoint]

  • Package: Elarion.Abstractions · Applies to: class
  • Triggers: marks a handler as an HTTP/REST endpoint; AppModuleDiscoveryGenerator emits the matching MapGet/MapPost/… registration as an AOT-safe RequestDelegate with generator-owned binding (ADR-0071). The handler must implement IHandler<TRequest, Result<TResponse>>. With no explicit verb the verb is inferred from the request's CQRS marker (ICommand → POST, IQuery → GET); a request with neither marker requires an explicit verb (else ELHTTP004). Carries no ASP.NET Core dependency; a handler may carry both this and [Handler].

The attribute has two constructors:

ConstructorParametersEffect
HttpEndpointAttribute(string route)route (required)Maps at route, inferring the verb from the request's ICommand/IQuery marker.
HttpEndpointAttribute(HttpVerb verb, string route)verb, route (both required)Maps at route with the explicit verb; sets HasVerb = true.

Read-only properties:

PropertyTypeDefaultMeaning
RoutestringrequiredThe route pattern, e.g. "clients/{id}".
VerbHttpVerbdefault(HttpVerb) = GetThe explicit HTTP method, or the default when HasVerb is false.
HasVerbboolfalseWhether Verb was set explicitly; when false the verb is inferred from the CQRS marker.

HttpVerb: Get, Post, Put, Patch, Delete.

A handler responding with Result<ElarionFile> (the in-memory small-file payload — content bytes + content type + file name, in Elarion.Abstractions) is mapped as a file download instead of JSON; the same handler may also carry [Handler], where the payload rides the canonical base64 envelope over JSON-RPC/MCP (and the generated TypeScript client maps it to a native File). Large files use the staged-blob tier instead (ADR-0039). See Files and File payloads.

See HTTP endpoints.

[GenerateModuleBootstrapper]

  • Package: Elarion.AspNetCore · Applies to: assembly
  • Triggers: marks the host assembly to have the fixed-name ElarionBootstrapper static generated by AppModuleDiscoveryGenerator (in the host's root namespace; see ADR-0018) — the single transport-wiring path; emits per-module and aggregate Map/Add methods for HTTP, JSON-RPC, and MCP, all module-scoped and feature-flag gated. Marker only — no parameters.

See Hosting.

[ModuleEndpoints]

  • Package: Elarion.AspNetCore · Applies to: class (static)
  • Triggers: declares endpoint hooks for a module from outside its assembly (typically the host, on behalf of a web-free module assembly). The class declares the same convention hooks a module type may declare — static MapEndpoints(IEndpointRouteBuilder) and/or static ConfigureEndpointGroup(IEndpointRouteBuilder) — and AppModuleDiscoveryGenerator calls them inside the named module's feature gate in MapElarionEndpoints, composed with the module's own hooks (module first, contributors in stable type-name order, group hooks chained). Discovered in the host compilation and via referenced assemblies' Elarion manifests. An unknown module name warns ELMOD004 (hooks skipped); a class with no recognized hook warns ELMOD005.
ParameterTypeDefaultMeaning
moduleNamestring (ctor) → ModuleNamerequiredThe [AppModule] name the hooks contribute to.

See Modules and ADR-0040.

Scheduling

[ScheduledJob]

  • Package: Elarion.Abstractions · Applies to: method | class
  • Triggers: marks a method as a compile-time scheduled job or a class as a runtime-schedulable job type. Methods must declare exactly one of FixedRate, FixedDelay, or Cron; runtime-schedulable classes may declare at most one. String values accept ${Config:Key} and ${Config:Key:-default} placeholders re-resolved per occurrence.
ParameterTypeDefaultMeaning
namestring (ctor) → NamerequiredStable unique job name used in logs, telemetry, and runtime scheduling.
FixedRatestring? (init)nullGrid-aligned interval between due times regardless of run duration. Accepts TimeSpan text or suffixes (50ms, 30s, 15m, 6h, 1d).
FixedDelaystring? (init)nullDelay between completion of one run and start of the next (polling-loop style). Same duration format; misfire policy does not apply.
Cronstring? (init)nullCron expression with 5 (minute-level) or 6 (second-level) fields, evaluated in TimeZone. "-" disables the schedule.
TimeZonestring? (init)null (UTC)Time-zone id used to evaluate Cron; passed to TimeZoneInfo.FindSystemTimeZoneById after placeholder resolution (prefer IANA ids).
InitialDelaystring? (init)nullOptional delay before the first execution (same format as FixedRate). Not valid with Cron.
RunOnStartbool (init)trueWhether interval jobs run once immediately at host start. Not valid with Cron.
Groupstring? (init)nullOptional key serializing jobs that must not run concurrently; jobs sharing a non-empty group share a serialization gate.
OverlapScheduledJobOverlap (init)SkipHow occurrences behave when another is already active.
MisfirePolicyScheduledJobMisfirePolicy (init)FireOnceHow fixed-rate/cron schedules handle missed in-process occurrences.
MaxConcurrentRunsint (init)0Max concurrently executing occurrences when Overlap = AllowConcurrent. 0 means no job-local cap (global scheduler limit still applies); negative is rejected by the generator.
Enabledstring? (init)null (always enabled)"true", "false", or a placeholder, evaluated per occurrence. An unconfigured placeholder without inline default means enabled; an unparseable value means disabled.
PlacementJobPlacement (init)ClusterMulti-node execution: Cluster = exactly one node per occurrence when a cross-instance coordinator is registered; EveryNode = every node runs every occurrence (for jobs maintaining process-local in-memory state). Identical on a single node.

ScheduledJobOverlap: Skip, Queue, AllowConcurrent.

ScheduledJobMisfirePolicy: FireOnce (one overdue, skip intermediate), Skip, CatchUp.

See Scheduling, Schedules, and Overlap and misfire.

Events

[ConsumeEvent]

  • Package: Elarion.Abstractions · Applies to: method | class
  • Triggers: marks an event consumer, discovered by the event-consumer generator and registered as an EventSubscriptionDescriptor.
    • Handler form (preferred): on a class implementing IHandler<TEvent> (sugar over IHandler<TEvent, Result<Unit>>) whose request type is the event; runs through the full decorator pipeline. Always a fan-out subscriber — the event bus is pub/sub-only. A non-Unit Result<T> return is rejected (ELEVT005).
    • Method form: on a method of a [Service] class (no pipeline); plane taken from the message type marker (IDomainEvent/IIntegrationEvent). Always a fan-out subscriber, returning void/Task/ValueTask or the non-generic Result/Task<Result>/ValueTask<Result> (a failed ResultEventConsumerFailedException). A Result<T> with a value is request/reply and is rejected.
ParameterTypeDefaultMeaning
Orderint (init)0Relative invocation order among fan-out subscribers of the same event, ascending. Equal orders run in a stable generator-determined sequence.

See Consuming events and Backends.

Actors

[Actor]

  • Package: Elarion.Actors · Applies to: class
  • Triggers: marks a plain class as an in-memory actor: its public async methods (Task/Task<T>/ValueTask/ValueTask<T>) become request/reply methods on a source-generated typed facade (I{Name}, class name minus the Actor suffix), while methods returning IAsyncEnumerable<T> become facade streams with a generated trailing cancellation token. Calls attach through the activation's mailbox; stream enumeration continues off-mailbox. Keyed when the constructor takes an IActorContext<TKey> (one activation per key, activated on first message, passivated when idle) or KeyType is set; otherwise a process singleton. Registered per module via the generated Add{Module}Actors; requires the assembly opt-in [assembly: GenerateActors] (or [UseElarion]).
ParameterTypeDefaultMeaning
Namestring? (init)class name minus ActorLogical actor name used in telemetry, logging, and the registration.
KeyTypeType? (init)nullExplicit key type for a keyed actor whose constructor does not take an IActorContext<TKey>; must match the context parameter when both are present (ELACT004).
MailboxCapacityint (init)0 (unbounded)Bounded mailbox capacity.
MailboxFullModeActorMailboxFullMode (init)WaitFull-mailbox behaviour: Wait (async backpressure) or Fail (ActorMailboxFullException). No drop modes — facade calls are request/reply.
IdleTimeoutSecondsdouble (init)0 (5 min)Inactivity window before passivation; -1 disables passivation.
CallTimeoutSecondsdouble (init)0 (30 s)End-to-end facade call timeout (queue wait + execution) failing with TimeoutException — the deadlock backstop for actor→actor call cycles; -1 disables it.
PlacementActorPlacementMode (init)LocalSingleHome runs only on the actor-home holder; VirtualShards assigns a keyed actor to a fixed virtual-shard role lease. Register the corresponding PostgreSQL placement recipe; calls elsewhere fail with ActorNotHomedException and are never forwarded.

ActorPlacementMode: Local (default), SingleHome, VirtualShards. ActorMailboxFullMode: Wait (default), Fail.

[Reentrant]

  • Package: Elarion.Actors · Applies to: class (alongside [Actor])
  • Triggers: opts the actor into Orleans-style turn-based interleaving: while one message awaits, the mailbox may start another, but turns never run in parallel (an exclusive scheduler serializes every continuation). State observed across an await may have been changed by an interleaved message; ConfigureAwait(false) in the actor's own code escapes the scheduler and forfeits the guarantee (flagged by ELACT006; libraries the actor calls may use it internally without harm).

[ActorKey]

  • Package: Elarion.Actors · Applies to: method (alongside [ConsumeEvent] on a keyed actor)
  • Triggers: names the consumed event's property that supplies the actor key when the generator cannot infer it (the event has zero or more than one property assignable to the actor's key type). Constructor arg propertyName (use nameof); the property's type must be assignable to the key type, else ELACT008. Not needed when inference is unambiguous or the actor is a singleton. [ConsumeEvent] itself stays an actor-agnostic contract in Elarion.Abstractions — the key selector is an actor concern, so it lives here (ADR-0046).

See Actors.

Caching

[Cacheable]

  • Package: Elarion.Abstractions · Applies to: class
  • Triggers: marks a handler as cacheable and supplies compile-time metadata to the handler-registration generator. Only successful Result<T> responses are cached; failed results are returned but not stored.
ParameterTypeDefaultMeaning
tagsparams string[] (ctor) → TagsemptyLogical cache tags for grouping and invalidation.
DurationSecondsint (init)60Entry lifetime in seconds for both distributed and local cache layers.
ScopeHandlerCacheScope (init)CurrentUserWhether generated keys/tags are scoped to the current user or shared globally.
KeyPropertiesstring[] (init)[]Request property names included in the generated key; when empty, all public request properties are used. Invalid names are reported at build time.

HandlerCacheScope: CurrentUser (= 0), Global (= 1).

[CacheInvalidate]

  • Package: Elarion.Abstractions · Applies to: class
  • Triggers: marks a mutating handler as invalidating cached entries for the given tags. Invalidation runs only after the inner handler returns a successful Result<T>; failed validation or business results do not evict cached reads.
ParameterTypeDefaultMeaning
tagsparams string[] (ctor) → TagsemptyLogical cache tags to invalidate after a successful handler result.
ScopeHandlerCacheScope (init)CurrentUserWhether invalidation applies to the current user's entries or globally shared entries.

Runtime: the cache itself was extracted out of Elarion core (ADR-0017). [Cacheable]/[CacheInvalidate] now require the Elarion.Caching package — the host wires the HybridCache-backed IHandlerCache with services.AddElarionHandlerCaching(). Without it the attributes are inert.

See Caching.

Resilience

[ResiliencePolicy]

  • Package: Elarion.Abstractions · Applies to: class
  • Triggers: marks a static partial type as a source-generated resilience policy (a named execution behavior applied to handlers and scheduled jobs). Retry options create additional attempts after a handled exception; Timeout limits each individual attempt (not a total deadline across retries).
ParameterTypeDefaultMeaning
namestring (ctor) → NamerequiredStable policy name used by [Resilient] and ScheduledJobOptions.ResiliencePolicy.
MaxRetryAttemptsint (init)3Maximum retry attempts after the original attempt (0 = no retries; 3 = up to four total). Supplying this or another retry property enables retry generation.
Delaystring (init)"2s"Base delay before a retry, e.g. 200ms, 2s, 5m, or TimeSpan text. May be changed by Backoff, capped by MaxDelay, randomized by UseJitter.
BackoffResilienceBackoffType (init)ConstantHow retry delays grow between attempts.
MaxDelaystring? (init)nullOptional maximum retry delay after applying Backoff; used with linear/exponential backoff to cap waits.
UseJitterbool (init)falseWhether retry delays include jitter so many failures do not retry at the same instant.
Timeoutstring? (init)nullOptional per-attempt timeout, e.g. 30s or TimeSpan text. Not a total deadline across retries; cancels the attempt token.

ResilienceBackoffType: Constant, Linear, Exponential.

[Resilient]

  • Package: Elarion.Abstractions · Applies to: class | method
  • Triggers: applies a named resilience policy to a generated handler or scheduled-job invocation. On handlers and compile-time jobs the policy executes inline around the current invocation; runtime scheduled jobs can alternatively use ScheduledJobOptions for scheduler-deferred retry.
ParameterTypeDefaultMeaning
policyNamestring (ctor) → PolicyNamerequiredStable policy name registered by a [ResiliencePolicy] type or a manual resilience pipeline registration.

Runtime: the resilience runner was extracted out of Elarion core (ADR-0017). [Resilient] handlers and scheduler-deferred retries now require the Elarion.Resilience package — the host wires the Microsoft/Polly-backed IResiliencePipelineRunner with services.AddElarionResilience(). Breaking: AddElarionScheduler no longer auto-wires the runner, so deferred scheduler retries opt in through this package too.

See Resilience.

Feature flags

[FeatureGate]

  • Package: Elarion.Abstractions (Features) · Applies to: class (AllowMultiple, inherited)
  • Triggers: gates a handler behind one or more feature flags. The handler-registration generator auto-attaches the FeatureGateDecorator<TRequest, TResponse> just inside the authorization gate so the flag is evaluated before the handler runs (before caching and the rest of the pipeline). When the gate is not satisfied the call short-circuits to AppError.NotFound (404) — a disabled feature is indistinguishable from a missing one, so the feature name is never leaked (MS-MVC-style "hide the roadmap"). Transport-neutral: identical under JSON-RPC, MCP, and HTTP. Stacking multiple [FeatureGate] attributes ANDs them. The flag is evaluated through the IFeatureFlagService seam; the default provider ships in Elarion.FeatureFlags.OpenFeature.

The attribute has two constructors:

ConstructorParametersEffect
FeatureGateAttribute(params string[] features)featuresGates on features with Requirement = FeatureRequirement.All.
FeatureGateAttribute(FeatureRequirement requirement, params string[] features)requirement, featuresGates on features with the explicit requirement.

Read-only / settable properties:

PropertyTypeDefaultMeaning
FeaturesIReadOnlyList<string>required (≥ 1 non-blank)The feature names this gate evaluates. A blank/empty set has no effect (ELFEAT002).
RequirementFeatureRequirementAllAll = every feature must be on (AND); Any = at least one must be on (OR).
Negatebool (init)falseWhen true, the gate is satisfied when the feature(s) are off (an inverse / kill-switch gate).

FeatureRequirement: All, Any.

The gated handler's response must be able to represent failure (Result<T> / Result); otherwise the gate cannot short-circuit and ELFEAT001 is reported. A [FeatureGate] with no non-blank feature name reports ELFEAT002. Module flags (Modules:{Name}:Enabled) are compose-time (the module disappears); feature gates are runtime (gradual rollouts, targeting, kill switches without a redeploy).

See Feature flags.

[FeatureVariant]

  • Package: Elarion.Abstractions (Features) · Applies to: class (single, not inherited)
  • Triggers: a modifier on a [Service] registration that binds multiple implementations of one contract; a named feature flag's allocated variant picks which implementation resolves per request. The implementation class must also carry [Service] (which declares the contract(s) and lifetime) — otherwise ELVAR007. The contract is not repeated here: it is whatever [Service] registers under, and each registered contract becomes variant-resolved. Consumers inject the plain contract transparently — a handler injecting it directly is registered behind an async-resolving proxy that awaits the right variant for the current user; outside a handler constructor, inject IVariantServiceProvider<TService>. Runtime seams: IFeatureVariantService + IVariantServiceProvider<T>; the VariantServiceRegistrationGenerator emits the wiring (ADR-0019).
ParameterTypeDefaultMeaning
featurestring (ctor) → FeaturerequiredThe single feature whose allocated variant selects the implementation. All variants of one contract must name the same feature (ELVAR004).
Variantstring? (init)nullThe variant key this implementation serves. Omit it on exactly one implementation to mark it the default (used when no variant is allocated); a contract without a default warns ELVAR003, and a duplicate variant key reports ELVAR001.
IsDefaultbool (init)falseMarks this implementation as a named default: it serves its Variant name and every unallocated/unknown allocation. More than one default per contract reports ELVAR001.

Diagnostics: ELVAR001 (duplicate variant key), ELVAR003 (no default implementation — warning), ELVAR004 (contract bound to more than one selector), ELVAR005 (blank feature name — warning), ELVAR006 (generic implementation unsupported), ELVAR007 (missing [Service]), ELVAR008 (contract also bound by [ConfigurationVariant] — one selection axis per contract). ELVAR002 is intentionally unused.

See Feature flags.

[ConfigurationVariant]

  • Package: Elarion.Abstractions (Features) · Applies to: class (single, not inherited)
  • Triggers: the configuration-selected sibling of [FeatureVariant] — a modifier on a [Service] registration that binds multiple implementations of one contract, with a plain IConfiguration value picking which implementation resolves. Selection is a synchronous configuration read, so there is no async-resolving proxy and no per-scope warm-up: any consumer (handler, service, any assembly) injects the contract directly, each new DI scope observes the current value, and an open scope keeps the implementation it started with. Any configuration provider drives the switch — appsettings.json (with reloadOnChange), environment variables, or the settings bridge (AddElarionSettingsConfiguration) for admin-writable, cluster-propagated runtime switching with no flag provider. The implementation class must also carry [Service] (otherwise ELVAR007); the contract is whatever [Service] registers under. Runtime seam: IVariantServiceProvider<TService> (completes synchronously); the VariantServiceRegistrationGenerator emits the wiring (ADR-0028).
ParameterTypeDefaultMeaning
keystring (ctor) → KeyrequiredThe configuration key whose value selects the implementation (e.g. "Email:Backend"). All variants of one contract must name the same key (ELVAR004), and a contract cannot mix [ConfigurationVariant] with [FeatureVariant] (ELVAR008). A blank key warns ELVAR009.
Valuestring? (init)nullThe configured value this implementation is selected for, matched case-insensitively. Omit it on exactly one implementation to mark it the default (used when the key is absent or its value matches no variant); a contract without a default warns ELVAR003, and a duplicate value — including case-only differences — reports ELVAR001.
IsDefaultbool (init)falseMarks this implementation as a named default: selectable by its Value and the fallback when the key is absent or unknown — so the default state stays writable by name (an admin switches back with "smtp" rather than by removing the key). Recommended for admin-facing switches; more than one default per contract reports ELVAR001.

Diagnostics: ELVAR001 (duplicate value), ELVAR003 (no default implementation — warning), ELVAR004 (contract bound to more than one selector), ELVAR006 (generic implementation unsupported), ELVAR007 (missing [Service]), ELVAR008 (contract also bound by [FeatureVariant]), ELVAR009 (blank configuration key — warning).

See Feature flags.

Idempotency

[Idempotent]

  • Package: Elarion.Abstractions (Idempotency) · Applies to: class (single, not inherited)
  • Triggers: marks a command handler as idempotent — a retried or duplicated request carrying the same idempotency key executes at most once and replays the first result. The handler-registration generator auto-attaches the IdempotencyDecorator, which owns the unit-of-work transaction: it writes the key row atomically with the handler's business writes, lets a database unique constraint reject a duplicate, and replays the stored result. Applies only to ICommand handlers whose response can represent failure (Result<T>/Result). Declarative, transport-neutral, and provider-agnostic — the same shape as [Cacheable]/[FeatureGate].
ParameterTypeDefaultMeaning
RetentionHoursint (init)24How long a completed key is retained and replayable, in hours.
KeyRequiredbool (init)trueWhether a request without an idempotency key is rejected with a 400. Set false to run without idempotency when no key is supplied.
ScopeIdempotencyScope (init)CurrentUserWhether the key is scoped per authenticated user or shared globally.
Fingerprintbool (init)trueWhether a request fingerprint is stored so reusing the key with a different request body is rejected (422).
ConflictBehaviorIdempotencyConflictBehavior (init)ConflictBehavior for a concurrent in-flight duplicate (default 409).
StoreFailuresIdempotencyFailureStorage (init)NoneWhether definitive failures are stored and replayed (default success-only).

IdempotencyScope: CurrentUser (= 0, isolated per authenticated user), Global (= 1, single shared namespace), Consumer (= 2, isolated per event consumer — the inbox; owner is the consuming handler's identity, key is the message id).

IdempotencyConflictBehavior: Conflict (= 0, fail fast with 409 then replay — the IETF/Stripe standard), WaitThenReplay (= 1, block on the key's lock until the first request commits, then replay).

IdempotencyFailureStorage: None (= 0, success-only — a failed result rolls back and stays retryable), Definitive (= 1, also store and replay definitive failures via a savepoint; transient failures stay retryable).

The [Idempotent] attribute, seams, decorator, and in-memory default store live in Elarion.Abstractions/Elarion; the durable PostgreSQL key store ships in Elarion.Idempotency.EntityFrameworkCore (AddElarionIdempotencyEntityFrameworkCore<TDbContext> + [GenerateElarionIdempotencyKeys], below), atop the EF unit of work in Elarion.EntityFrameworkCore.UnitOfWork.

See Idempotency.

[AllowDuplicates]

  • Package: Elarion.Abstractions (Messaging) · Applies to: class (single, not inherited)
  • Triggers: declares that duplicate deliveries are harmless to this handler-form integration-event consumer, opting it out of the default-on inbox (ADR-0022). The inbox attaches automatically to every handler whose request is an IIntegrationEvent — a Consumer-scoped IdempotencyDecorator claiming (consumer, message id) inside the consumer's own transaction, so a redelivered message is acknowledged instead of re-run (claims expire after 24 h, well above the outbox retry window). Declare [AllowDuplicates] when re-running is safe by construction — the effect is naturally idempotent (a conditional transition/upsert), or the only effect is a call to a downstream keyed on IEventContext.MessageId — and the plain transaction decorator returns. The consumer-side mirror of [AllowAnonymous]: a positive property declaration switching off a default guard. Inert on non-integration-event handlers (ELINBX001); domain-event consumers never have an inbox (exactly-once by atomicity); method-form consumers have no pipeline to attach to.

See Handling duplicates.

Auditing

[Auditable]

  • Package: Elarion.Abstractions (Auditing) · Applies to: class (single, not inherited)
  • Triggers: marks a handler for audit recording (ADR-0045) — every invocation produces one structured AuditRecord (actor, action, resource, outcome, correlation, field-level changes). The handler-registration generator auto-attaches two decorators: the outer observer (just outside authorization, recording denials/failures on a detached path that survives the rollback) and the inner success recorder (inside the transaction, so the success record commits atomically with the business writes). Attachment is soft — no registered IAuditTrail, no change to the pipeline. Declarative, transport-neutral, and provider-agnostic — the same shape as [Idempotent]/[FeatureGate].
ParameterTypeDefaultMeaning
Enabledbool (init)trueSet false to opt a handler out under [ElarionAuditDefaults].
Resourcestring? (init)nullFallback resource type only (the record's ResourceType) — used when the handler does not call IAuditScope.SetResource. It never sets the resource id; SetResource(type, id) supplies both and its type wins, so don't set Resource when the handler calls SetResource. Use the same string as the handler's [RequirePermission(resource, …)] (the plural, K8s-RBAC resource name — clients, invoices) so authorization and audit share one vocabulary.

See Audit trail.

[ElarionAuditDefaults]

  • Package: Elarion.Abstractions (Auditing) · Applies to: assembly or module class (single, not inherited)
  • Triggers: opts the scope into audit-by-default: every command handler is audited as if it carried [Auditable], unless it opts out with [Auditable(Enabled = false)]. Queries and event consumers are never audited by defaults (read auditing is per-handler; a delivery scope has no caller). Mirrors [ElarionAuthorizationDefaults].

See Audit trail.

[Audited]

  • Package: Elarion.Abstractions (Auditing) · Applies to: entity class (single, not inherited)
  • Triggers: opts an entity into automatic change capture: while an audited handler runs, the EF change-capture interceptor records field-level {property, oldValue, newValue} diffs for this entity on every flush, attached to the invocation's record. Opt-in on purpose (fail-closed PII stance; keeps framework tables out of capture). ExecuteUpdate/raw-SQL writes bypass the change tracker — the handler records those via IAuditScope.AddChange.

See Audit trail.

[AuditIgnore]

  • Package: Elarion.Abstractions (Auditing) · Applies to: property (single, not inherited)
  • Triggers: excludes a property of an [Audited] entity from automatic change capture — its values never appear in a record. For sensitive columns (password hashes, tokens, personal data) and noisy technical columns.

See Audit trail.

Validation

There is no Elarion validation attribute. Request validation is declared with the standard System.ComponentModel.DataAnnotations attributes on the handler's request DTO — [Range], [MinLength]/[MaxLength]/[Length]/[StringLength], [RegularExpression], [EmailAddress], [Url], [Base64String] — with requiredness from nullability + the required modifier (no [Required] needed). The handler-registration generator auto-attaches the framework ValidationDecorator (just inside the feature gate) for any handler whose request type graph carries validation attributes, and the same attributes are exported to the JSON-RPC schema, MCP tool schemas, the OpenAPI document, and the generated Zod client. Reusable custom constraints subclass a mapped attribute (e.g. [Slug] : RegularExpressionAttribute). Enforcement requires the opt-in Elarion.Validation package (AddElarionValidation()); diagnostics ELVAL001/ELVAL002.

See Validation.

Authorization

Handler requirements are class attributes the handler-registration generator reads to auto-attach the AuthorizationDecorator (the outermost functional gate, just inside the observability decorator's handler span). Different attribute kinds AND together; multiple of one kind AND; OR lives inside a single [RequireClaim]. All are transport-neutral (identical under JSON-RPC, MCP, and HTTP) and evaluate against ICurrentUser — an unauthenticated caller yields AppError.Unauthorized (401), authenticated-but-denied yields AppError.Forbidden (403). The gated handler's response must be able to represent failure (Result<T>/Result) or ELAUTH001 is reported.

[RequirePermission]

  • Package: Elarion.Abstractions (Authorization) · Applies to: class (AllowMultiple, inherited)
  • Triggers: requires the principal to hold a permission to perform a verb on a resource — the Kubernetes-RBAC (resource, verb) shape, sugar for [RequireClaim(PermissionClaimType, "{resource}.{verb}")]. Surfaced into the generated permission catalog (see [GeneratePermissionCatalog]).
ParameterTypeDefaultMeaning
resourcestring (ctor) → ResourcerequiredThe resource the permission applies to, e.g. "properties".
verbstring (ctor) → VerbrequiredThe action on the resource; a Verbs constant or any string (open vocabulary).
Permissionstring (read-only)derivedThe composed claim value enforced — {Resource}.{Verb} (separator ".").

Verbs (constants, open vocabulary): Read ("read"), List ("list"), Create ("create"), Update ("update"), Write ("write"), Delete ("delete"), Manage ("manage").

[RequireRole]

  • Package: Elarion.Abstractions (Authorization) · Applies to: class (AllowMultiple, inherited)
  • Triggers: requires the principal to be in the named role. Also surfaced into the permission catalog.
ParameterTypeDefaultMeaning
rolestring (ctor) → RolerequiredThe required role name.

[RequireClaim]

  • Package: Elarion.Abstractions (Authorization) · Applies to: class (AllowMultiple, inherited)
  • Triggers: requires the principal to carry a claim of the given type; with values supplied the principal must match at least one (OR). The general primitive [RequirePermission]/[RequireRole] build on.
ParameterTypeDefaultMeaning
claimTypestring (ctor) → ClaimTyperequiredThe claim type the principal must carry.
allowedValuesparams string[] (ctor) → AllowedValues (IReadOnlyList<string>)emptyAccepted values (OR); empty means presence-only.

[RequirePolicy]

  • Package: Elarion.Abstractions (Authorization) · Applies to: class (AllowMultiple, inherited)
  • Triggers: requires a named IAuthorizationPolicy (an Elarion-native policy evaluated against ICurrentUser + the request, not the ASP.NET policy engine) to pass.
ParameterTypeDefaultMeaning
policystring (ctor) → PolicyrequiredThe policy name, matched against IAuthorizationPolicy.Name / [AuthorizationPolicy("name")].

[AllowAnonymous]

  • Package: Elarion.Abstractions (Authorization) · Applies to: class (single, inherited)
  • Triggers: marks a handler as public; authorization is skipped entirely and wins over any Require* attribute and over an [ElarionAuthorizationDefaults] deny-by-default scope. Marker only — no parameters.

[AuthorizationPolicy]

  • Package: Elarion.Abstractions (Authorization) · Applies to: class (single, not inherited)
  • Triggers: marks an IAuthorizationPolicy implementation as a named, auto-registered policy (per module, like [Service]), referenced by [RequirePolicy("name")]. Diagnostics ELPOL001/ELPOL002.
ParameterTypeDefaultMeaning
namestring (ctor) → NamerequiredThe policy name [RequirePolicy] resolves.

[ElarionAuthorizationDefaults]

  • Package: Elarion.Abstractions (Authorization) · Applies to: assembly | class (single, not inherited)
  • Triggers: flips the in-scope handlers to deny-by-default — every handler requires authentication unless [AllowAnonymous]. Resolved most-specific-wins (a [AppModule]-class scope overrides the assembly scope), like [DefaultPipeline].
ParameterTypeDefaultMeaning
RequireAuthenticatedbool (init)trueWhether in-scope handlers require an authenticated principal.

[RequireResource]

  • Package: Elarion.Abstractions (Authorization) · Applies to: class (AllowMultiple, inherited)
  • Triggers: the per-resource point check (the BOLA-class "may I touch this one?" gate). The decorator reads the resource id from the request by a compile-checked path (no reflection) and calls the IResourceAuthorizer seam; the shipped default authorizes from the grants table and fails closed when no backend is registered. An unresolvable Id path is ELAUTH002.
ParameterTypeDefaultMeaning
resourceTypeType (ctor) → ResourceTyperequiredThe resource type being accessed, e.g. typeof(Contact).
Operationstring (init)"read"The operation name (an open ResourceOperation value).
Idstring (init)"Id"Request property path naming the resource id, e.g. nameof(Query.Id) or a dotted path of existing properties.

[ResourceFilter<TEntity>]

  • Package: Elarion.Paging · Applies to: class (generic attribute, where TEntity : class)
  • Triggers: the data-level list filter (Leg B). Declared on a dedicated partial class (the entity stays clean, like [Keyset<T>]); the EF Core generator completes it as an IQueryAuthorizer<TEntity> whose predicate composes the rules as AND(scope rules) AND OR(grant rules) and pushes it into SQL via source.WhereAuthorized(authorizer, currentUser) before paging. Diagnostics ELRES001ELRES005.
ParameterTypeDefaultMeaning
TEntitytype parameter (where TEntity : class)requiredThe entity being authorized.
OwnerPropertystring? (init)nullA grant (OR): the row is visible when this column equals the caller's UserId. Type Guid/string/int/long.
TenantPropertystring? (init)nullA scope (AND): every visible row's column must equal the caller's tenant claim. Type Guid/string/int/long.
TenantClaimTypestring (init)"tenant"The claim type the tenant value is read from.
Sharedbool (init)falseA grant (OR): a correlated EXISTS over the resource-grants table for the caller's user or any of their roles, for the operation WhereAuthorized asks (defaults to Read). Makes the filter a scoped service (no static Specification); requires ResourceTypeName (else ELRES005) and a reference to Elarion.Authorization.EntityFrameworkCore.
ResourceTypeNamestring? (init)nullThe resource-type discriminator stored in the grants table (e.g. "Contact"), used by the Shared EXISTS.
IdPropertystring (init)"Id"The entity key property whose stringified value matches the grant's resource id. Type Guid/string/int/long.

Consume the filter by injecting IQueryAuthorizer<TEntity> (the form that works for Shared). A field-only filter (OwnerProperty/TenantProperty, no Shared) is stateless, so the generator also exposes a static Specification singleton. Both are auto-registered as IQueryAuthorizer<TEntity>, module-feature-gated.

See Authorization and Resource authorization.

EF Core

[EntityConfiguration]

  • Package: Elarion.EntityFrameworkCore · Applies to: class (an IEntityTypeConfiguration<TEntity> implementation)
  • Triggers: marks an IEntityTypeConfiguration<TEntity> implementation as the single source of truth for an entity's participation; it drives both the generated DbSet<TEntity> and the Configure(...) application. The entity itself carries no marker (previously [DbEntity]) — a configured entity is a discovered entity. A plain IEntityTypeConfiguration<T> with no attribute is ignored (no DbSet, no Configure). A single [EntityConfiguration] class may implement IEntityTypeConfiguration<T> more than once; each implemented entity gets its own DbSet and its own Configure(...) call. A class implementing no IEntityTypeConfiguration<T> is reported (ELEFC001).
ParameterTypeDefaultMeaning
scopesparams string[] (ctor) → Scopes (IReadOnlyList<string>)emptyOptional context scopes this configuration belongs to. Omit scopes to participate in unscoped/global contexts.
// Entities/Invoice.cs — plain class, no attribute
public sealed class Invoice { public Guid Id { get; set; } }

// Invoicing/InvoiceConfiguration.cs — the single source of truth
using Elarion.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

[EntityConfiguration]
public sealed class InvoiceConfiguration : IEntityTypeConfiguration<Invoice> {
    public void Configure(EntityTypeBuilder<Invoice> builder) { /* ... */ }
}

[GenerateDbSets]

  • Package: Elarion.EntityFrameworkCore · Applies to: class
  • Triggers: applied to a partial DbContext class; generates a DbSet<T> property for each [EntityConfiguration] entity and ConfigureEntities(ModelBuilder) directly onto that class (reflection-free, AOT-friendly direct ApplyConfiguration<T> calls).
ParameterTypeDefaultMeaning
scopesparams string[] (ctor) → Scopes (IReadOnlyList<string>)emptyOptional context scopes generated for this class. Omit scopes to include every [EntityConfiguration]; a scoped class includes only configurations whose scopes intersect.

[GenerateElarionIdentity<TUser, TRole, TKey>]

  • Package: Elarion.EntityFrameworkCore.Identity · Applies to: class (generic attribute; TUser : IdentityUser<TKey>, TRole : IdentityRole<TKey>, TKey : IEquatable<TKey>)
  • Triggers: on a [GenerateDbSets] partial DbContext; the bundled generator emits the seven Identity DbSets and applies the self-contained snake_case Identity model (keys, composite keys, indexes, table/column names) through the EF generator's per-feature model-config seam — no IdentityDbContext inheritance and no EFCore.NamingConventions dependency. The web-free Identity model; the host wiring is AddElarionIdentity in Elarion.AspNetCore.Identity. Requires [GenerateDbSets] (else ELIDN001).
ParameterTypeDefaultMeaning
SnakeCasebool (init)trueWhether table/column/index names use snake_case.
Schemastring? (init)nullThe schema for all seven Identity tables, or null for the provider's default schema.
TablePrefixstring? (init)nullOptional prefix prepended verbatim to every Identity table name (e.g. "auth_"auth_users). Because Identity spans seven tables, the prefix is the table-name override — there is no per-table name parameter.

[GenerateElarionResourceGrants]

  • Package: Elarion.Authorization.EntityFrameworkCore · Applies to: class
  • Triggers: on a [GenerateDbSets] partial DbContext; the bundled generator emits the DbSet<ResourceGrantEntity> and applies the grants model configuration — the table backing [ResourceFilter(Shared = true)] and the [RequireResource] point check. The hand-written equivalent is modelBuilder.ApplyElarionResourceGrants(). Requires [GenerateDbSets] (else ELRG001).
ParameterTypeDefaultMeaning
SnakeCasebool (init)trueWhether table/column/index names use snake_case.
TableNamestring? (init)nullThe table name, or null for the default (elarion_resource_grants / ElarionResourceGrants depending on SnakeCase).
Schemastring? (init)nullThe schema, or null for the provider's default schema.

See Resource authorization and Identity.

[GenerateElarionIdempotencyKeys]

  • Package: Elarion.Idempotency.EntityFrameworkCore · Applies to: class
  • Triggers: on a [GenerateDbSets] partial DbContext; the bundled generator emits the DbSet<IdempotencyKeyEntity> and applies the idempotency-keys model configuration through the EF generator's model-config seam — the table backing the durable [Idempotent] store. The hand-written equivalent is modelBuilder.ApplyElarionIdempotencyKeys(). Mirrors [GenerateElarionResourceGrants]. Requires [GenerateDbSets] (else ELIDEMEF001).
ParameterTypeDefaultMeaning
SnakeCasebool (init)trueWhether table/column/index names use snake_case.
TableNamestring? (init)nullThe table name, or null for the default (elarion_idempotency_keys / ElarionIdempotencyKeys depending on SnakeCase).
Schemastring? (init)nullThe schema, or null for the provider's default schema.

See Idempotency.

[GenerateElarionAuditing]

  • Package: Elarion.Auditing.EntityFrameworkCore · Applies to: class
  • Triggers: on a [GenerateDbSets] partial DbContext; the bundled generator emits the DbSet<AuditLogEntry> and applies the audit-log model configuration through the EF generator's model-config seam — the append-only table backing the durable IAuditTrail. The hand-written equivalent is modelBuilder.UseElarionAuditing(). Mirrors [GenerateElarionIdempotencyKeys]. Requires [GenerateDbSets] (else ELAUD001).
ParameterTypeDefaultMeaning
SnakeCasebool (init)trueWhether table/column/index names use snake_case.
TableNamestring? (init)nullThe table name, or null for the default (elarion_audit_log / ElarionAuditLog depending on SnakeCase).
Schemastring? (init)nullThe schema, or null for the provider's default schema.

See Audit trail.

[GenerateElarionOutbox]

  • Package: Elarion.Messaging.Outbox · Applies to: class
  • Triggers: on a [GenerateDbSets] partial DbContext; the bundled generator emits DbSet<OutboxMessage> and applies the transactional-outbox model configuration through the EF generator's model-config seam. The hand-written equivalent is modelBuilder.UseElarionOutbox(). Requires [GenerateDbSets] (else ELOBX001).
ParameterTypeDefaultMeaning
SnakeCasebool (init)trueWhether table/column/index names use snake_case.
TableNamestring? (init)nullThe table name, or null for the default (elarion_outbox_messages / ElarionOutboxMessages depending on SnakeCase).
Schemastring? (init)nullThe schema, or null for the provider's default schema.

See Event bus backends.

[GenerateElarionSchedulerClaims]

  • Package: Elarion.Scheduling.EntityFrameworkCore · Applies to: class
  • Triggers: on a [GenerateDbSets] partial DbContext; the bundled generator emits the DbSet<SchedulerClaimEntity> and applies the scheduler-claims model configuration through the EF generator's model-config seam — the table that makes each recurring occurrence execute on exactly one node (ADR-0025). The hand-written equivalent is modelBuilder.UseElarionSchedulerClaims(). Mirrors [GenerateElarionIdempotencyKeys]. Requires [GenerateDbSets] (else ELSCH001).
ParameterTypeDefaultMeaning
SnakeCasebool (init)trueWhether table/column/index names use snake_case.
TableNamestring? (init)nullThe table name, or null for the default (elarion_scheduler_claims / ElarionSchedulerClaims depending on SnakeCase).
Schemastring? (init)nullThe schema, or null for the provider's default schema.

See Scheduling.

[GenerateElarionSettings]

  • Package: Elarion.Settings.EntityFrameworkCore · Applies to: class
  • Triggers: on a [GenerateDbSets] partial DbContext; the bundled generator emits the DbSet<Setting> and applies the settings model configuration through the EF generator's model-config seam — the table backing the EF Core ISettingsStore. The hand-written equivalent is modelBuilder.UseElarionSettings(). Mirrors [GenerateElarionIdempotencyKeys]. Requires [GenerateDbSets] (else ELSET001).
ParameterTypeDefaultMeaning
SnakeCasebool (init)trueWhether table/column/index names use snake_case.
TableNamestring? (init)nullThe table name, or null for the default (elarion_settings / ElarionSettings depending on SnakeCase).
Schemastring? (init)nullThe schema, or null for the provider's default schema.

See Settings.

[GenerateElarionBlobStorage]

  • Package: Elarion.Blobs.PostgreSql · Applies to: class
  • Triggers: on a [GenerateDbSets] partial DbContext; the bundled generator emits the DbSet<StoredBlob> and applies the blob model configuration — both the metadata table and the content table (the content row type is internal: mapped, but no DbSet) — through the EF generator's model-config seam. The hand-written equivalent is modelBuilder.UseElarionBlobStorage(). Mirrors [GenerateElarionIdempotencyKeys]. Requires [GenerateDbSets] (else ELBLB001).
ParameterTypeDefaultMeaning
SnakeCasebool (init)trueWhether table/column/index names use snake_case.
TableNamestring? (init)nullThe metadata table name, or null for the default (stored_blobs / StoredBlobs depending on SnakeCase).
ContentTableNamestring? (init)nullThe content table name, or null for the default (blob_contents / BlobContents depending on SnakeCase).
Schemastring? (init)nullThe schema, or null for the provider's default schema.

See Blob storage.

[GenerateElarionStagedUploads]

  • Package: Elarion.Blobs.PostgreSql · Applies to: class
  • Triggers: on a [GenerateDbSets] partial DbContext; the bundled generator emits the DbSet<StagedUploadRow> and applies the staged-upload model configuration through the EF generator's model-config seam — the table backing the durable IStagedUploadStore that resumable transports (tus) stage into. The hand-written equivalent is modelBuilder.UseElarionStagedUploads(). Mirrors [GenerateElarionBlobStorage], which maps the blob tables a completed upload is written to. Requires [GenerateDbSets] (else ELBLB002).
ParameterTypeDefaultMeaning
SnakeCasebool (init)trueWhether table/column/index names use snake_case.
TableNamestring? (init)nullThe table name, or null for the default (staged_uploads / StagedUploads depending on SnakeCase).
Schemastring? (init)nullThe schema, or null for the provider's default schema.

See Blob uploads.

[GenerateElarionActorSnapshots]

  • Package: Elarion.Actors.PostgreSql · Applies to: class
  • Triggers: on a [GenerateDbSets] partial DbContext; the bundled generator emits the DbSet<ActorSnapshotEntity> and applies the actor snapshot model configuration through the EF generator's model-config seam — the table backing the IActorSnapshotStore behind every IActorState<TState> (ADR-0047). The hand-written equivalent is modelBuilder.UseElarionActorSnapshots(). Mirrors [GenerateElarionSettings]/[GenerateElarionIdempotencyKeys]. Requires [GenerateDbSets] (else ELASN001).
ParameterTypeDefaultMeaning
SnakeCasebool (init)trueWhether table/column names use snake_case.
TableNamestring? (init)nullThe table name, or null for the default (elarion_actor_snapshots / ElarionActorSnapshots depending on SnakeCase).
Schemastring? (init)nullThe schema, or null for the provider's default schema.

See Actor state and placement.

[GenerateElarionRoleLeases]

  • Package: Elarion.Coordination.PostgreSql · Applies to: class
  • Triggers: on a [GenerateDbSets] partial DbContext; the bundled generator emits the DbSet<RoleLeaseEntity> and applies the role-lease model configuration through the EF generator's model-config seam — the leader-election table behind IRoleLease (ADR-0049) and therefore behind [Actor(Placement = ActorPlacementMode.SingleHome)] (ADR-0048). The hand-written equivalent is modelBuilder.UseElarionRoleLeases(). Requires [GenerateDbSets] (else ELROLE001).
ParameterTypeDefaultMeaning
SnakeCasebool (init)trueWhether table/column names use snake_case.
TableNamestring? (init)nullThe table name, or null for the default (elarion_role_leases / ElarionRoleLeases depending on SnakeCase).
Schemastring? (init)nullThe schema, or null for the provider's default schema.

See Actors — multiple instances and single-homing.

[GenerateElarionDeviceIdentity]

  • Package: Elarion.Devices.EntityFrameworkCore · Applies to: class
  • Triggers: on a [GenerateDbSets] partial DbContext; the bundled generator emits the DbSet<DeviceKeyEntity>/DbSet<DevicePairingCodeEntity> and applies the device identity model configuration through the EF generator's model-config seam — the tables backing IDeviceKeyStore and IPairingCodeStore (ADR-0054). The hand-written equivalent is modelBuilder.UseElarionDeviceIdentity(). Mirrors [GenerateElarionActorSnapshots]/[GenerateElarionRoleLeases]. Requires [GenerateDbSets] (else ELDEV001).
ParameterTypeDefaultMeaning
SnakeCasebool (init)trueWhether table/column names use snake_case.
KeyTableNamestring? (init)nullThe device key table name, or null for the default (elarion_device_keys / ElarionDeviceKeys depending on SnakeCase).
PairingCodeTableNamestring? (init)nullThe pairing code table name, or null for the default (elarion_device_pairing_codes / ElarionDevicePairingCodes depending on SnakeCase).
Schemastring? (init)nullThe schema, or null for the provider's default schema.

See Device identity.

[Keyset<TEntity>]

  • Package: Elarion.Paging · Applies to: class (generic attribute, where TEntity : class)
  • Triggers: declares an ordered keyset (seek) definition for TEntity on the annotated partial class. The EF Core generator fills the class with a reflection-free IKeysetDefinition<TEntity> implementation (ordering, seek predicate, opaque cursor codec) plus a static Definition singleton, so handlers page via source.ToKeysetPageAsync(request, MyKeyset.Definition, selector). Declared on a dedicated partial class (not the entity), so an entity may have any number of orderings. Non-partial or nested keyset classes are reported (ELKEY005).
ParameterTypeDefaultMeaning
TEntitytype parameter (where TEntity : class)requiredThe entity type being paginated.
columnsparams string[] (ctor) → Columns (IReadOnlyList<string>)emptyOrdered keyset column names in precedence order; each names a property on TEntity. Ascending by default; a leading - marks the column descending (e.g. "-CreatedAt"). The final column should be unique (typically the primary key) for determinism.

See Entity Framework and Pagination.

SQL row mapping (EF-free tier)

[SqlRecord]

  • Package: Elarion.Sql · Applies to: class, struct (records included)
  • Triggers: marks a row type for SQL mapper generation (ADR-0058). The bundled generator emits a sealed {Type}SqlMapper : ISqlRowMapper<T> — column ordinals resolved by name once per result set (Ordinals struct), synchronous typed GetFieldValue<T> row reads, typed BindParameters (parameters named like columns), TableName/Columns.* constants (including Columns.All and Columns.AllParameters), a static Instance, and a per-assembly AddElarionSqlMappers() DI registration. Column names default to the snake_case of the property name; the table name defaults to the snake_case of the type name (no pluralization). Positional records construct through the primary constructor; nominal records with required/init members through an object initializer. Derived (get-only) members are skipped. There is no reflection fallback: an unmapped type or unsupported shape is a compile error (ELSQL001ELSQL007).
ParameterTypeDefaultMeaning
tableNamestring? (ctor)nullThe table name, or null for the snake_case of the type name.

[SqlColumn]

  • Package: Elarion.Sql · Applies to: property
  • Triggers: overrides the column name of a [SqlRecord] property (default: snake_case of the property name). On a non-writable property this is an error (ELSQL007).
ParameterTypeDefaultMeaning
namestring (ctor)requiredThe column name as it appears in SQL.

[SqlIgnore]

  • Package: Elarion.Sql · Applies to: property
  • Triggers: excludes a [SqlRecord] property from mapping (neither read nor bound). A required property cannot be ignored (ELSQL003).

[SqlJson]

  • Package: Elarion.Sql · Applies to: property
  • Triggers: maps a [SqlRecord] property as a JSON column, (de)serialized through the canonical JSON accessor's JsonTypeInfo<T> (IElarionJsonSerialization, ADR-0023) — the property type must be in a registered JsonSerializerContext. A mapper with JSON columns takes the accessor as a constructor parameter instead of exposing Instance; the generated DI registration wires it. Under [UseElarionSql(Provider = SqlProvider.Npgsql)], parameters bind as NpgsqlDbType.Jsonb.

[UseElarionSql]

  • Package: Elarion.Sql · Applies to: assembly
  • Triggers: provider trigger for the SQL mapper generator (precedent: [UseElarionEntityFrameworkCore]). Provider = SqlProvider.Npgsql emits PostgreSQL-specific parameter typing (jsonb for [SqlJson] columns); without it, emission stays provider-neutral.
ParameterTypeDefaultMeaning
ProviderSqlProvider (init)PortableThe provider to emit provider-specific code for (Portable or Npgsql).

See SQL mapping.

Cross-module communication

[ModuleContract]

  • Package: Elarion.Abstractions (Modules) · Applies to: interface | class
  • Triggers: marks a module's published cross-module surface — the stable type other modules may depend on. Use it sparingly and deliberately: it is for genuine cross-module domain calls, not shared infrastructure (use a port outside the modules) or shared data (use the shared kernel). The owning module keeps the implementation internal; other modules inject the contract. ModuleBoundaryAnalyzer (ELMOD002) is location-based — it allows a [ModuleContract] but reports any other cross-module reference to a type placed inside another module (an entity, DTO, [Service], handler, or [EntityConfiguration]). Types outside every module — the shared kernel and platform ports — are never flagged. Marker only — no parameters.

[ModuleApi]

  • Package: Elarion.Abstractions (Modules) · Applies to: class
  • Triggers: configures how a handler participates in its module's generated typed in-process API (see [GenerateModuleApi]). A configurator, never a gate: every handler is in the default facade automatically (opt-out). Apply only to exclude a handler or to tag it into named scopes (additive). Scope vocabulary mirrors [EntityConfiguration]/[GenerateDbSets].
ParameterTypeDefaultMeaning
scopesparams string[] (ctor) → Scopes (IReadOnlyList<string>)emptyNamed scopes this handler is tagged into so it also appears on matching scoped facades. Empty means the handler participates only in the module's default (unscoped) facade.
Excludebool (init)falseWhen true, the handler is excluded from every generated facade, scoped or default.

[GenerateModuleApi]

  • Package: Elarion.Abstractions (Modules) · Applies to: interface
  • Triggers: marks a partial interface to be filled with a typed in-process API over the owning module's handlers — one method per handler, dispatched typed-direct to IHandler<TRequest, TResponse> (full decorator pipeline, no serialization). Not a transport and absent from the JSON-RPC/MCP schema; module-internal (must not cross boundaries). The interface must be partial and at namespace scope. Diagnostics ELAPI001ELAPI004.
ParameterTypeDefaultMeaning
scopesparams string[] (ctor) → Scopes (IReadOnlyList<string>)emptyThe scopes this facade selects. Empty = default facade (every non-excluded handler in the owning module by longest-prefix namespace match); one or more = only handlers tagged with an intersecting scope via [ModuleApi].

See Cross-module communication.

Assembly opt-ins

[UseElarion]

  • Package: Elarion.Abstractions · Applies to: assembly
  • Triggers: enables the framework-owned assembly generators at once — handlers, services, scheduled jobs, event consumers, resilience policies, actor facades/registration, authorization-policy registration, client-event topics, the permission catalog, and the variant catalog. Application-owned policy attributes such as pipeline defaults remain explicit, as do host/provider triggers such as [GenerateModuleBootstrapper], [GenerateDbSets], and [UseElarionSql]. Marker only — no parameters.

Individual generation triggers

All are Elarion.Abstractions, applied to the assembly, marker-only (no parameters), and subsumed by [UseElarion].

AttributeTriggers
[GenerateModuleHandlers]Per-module handler registration methods (and, when the compilation references Elarion.Validation, the per-module request-validation resolvers).
[GenerateModuleServices]Per-module service registration methods.
[GenerateScheduledJobs]Source-generated scheduler descriptor registration.
[GenerateEventConsumers]Source-generated event-consumer descriptor registration.
[GenerateResiliencePolicies]Source-generated resilience-policy registration.
[GenerateActors]Actor typed facades and per-module actor registration.
[GenerateModuleAuthorizationPolicies]Per-module registration for [AuthorizationPolicy] implementations.
[GenerateClientEventTopics]Per-module client-event topic registration and subscribe-time policy metadata.
[GeneratePermissionCatalog]The typed permission/role catalog harvested from handler requirements.
[GenerateVariantCatalog]The typed feature/configuration variant catalog.

[GeneratePermissionCatalog]

  • Package: Elarion.Abstractions · Applies to: assembly
  • Triggers: runs the PermissionCatalogGenerator to harvest every [RequirePermission]/[RequireRole] in the assembly into a runtime IPermissionCatalog (a K8s-style resource + verb catalog, with Verbs, in Elarion.Abstractions.Authorization) — so admin UIs, role editors, and audits read straight from the handler attributes with no second source of truth to drift. Marker only — no parameters. Subsumed by [UseElarion], or apply it standalone. Diagnostics ELPERM001 (handler with requirements outside any [AppModule]) / ELPERM002 (two permissions colliding on the same typed accessor).
[assembly: GeneratePermissionCatalog]

See Authorization.

[GenerateClientEventTopics]

  • Package: Elarion.Abstractions · Applies to: assembly
  • Triggers: runs the ClientEventRegistrationGenerator to register a topic per IClientEvent contract: grouped under its owning [AppModule] by longest-prefix namespace match, the topic named {module}.{name} (both camel-cased, a trailing Event suffix stripped; [ClientEvent("…")] overrides the full name), with the contract's [RequirePermission]/[RequireRole] attributes becoming subscribe-time requirements and [AllowAnyResource] declaring the resource segment a routing key. Emits Add{Module}ClientEvents(IServiceCollection) wired into the module's ConfigureDefaultServices, so a topic exists just by declaring the contract and disappears with the module's feature gate. Marker only — no parameters. Subsumed by [UseElarion], or apply it standalone. Diagnostics ELCEV001 (contract outside any [AppModule]) / ELCEV002 (duplicate topic name) / ELCEV003 (contracts declared but Elarion.ClientEvents not referenced).
[assembly: GenerateClientEventTopics]

See Client events.

[ClientEvent]

  • Package: Elarion.Abstractions (ClientEvents) · Applies to: class (single, not inherited)
  • Triggers: overrides the topic name the generator infers for an IClientEvent contract — the full name, like an explicit [Handler("module.action")]. Use it to keep a wire name stable across a type rename.
ParameterTypeDefaultMeaning
namestring (ctor) → NamerequiredThe full topic name clients subscribe to (e.g. "invoicing.invoiceChanged").

[AllowAnyResource]

  • Package: Elarion.Abstractions (ClientEvents) · Applies to: class (single, not inherited)
  • Triggers: declares the resource segment of this IClientEvent contract's topic a routing key, not an entitlement: any caller passing the topic's subscribe-time requirements (authenticated, plus any [RequirePermission]/[RequireRole] on the contract) may subscribe to any resource of this topic, without consulting the IClientEventSubscriptionAuthorizer seam. Without it, resource-scoped subscriptions stay fail-closed (denied unless a registered authorizer approves). Deliberately per-topic so a future entitlement-scoped topic is never silently opened by a global "allow everything" authorizer. Imperative form: AllowAnyResource() on the topic options in AddElarionClientEvents. Marker only — no parameters.
[AllowAnyResource]   // the symbol selects which events to receive; it gates nothing
public sealed record QuoteChanged : IClientEvent { /* … */ }

See Client events.

[SubscriptionObserver<TObserver>]

  • Package: Elarion.Abstractions (ClientEvents) · Applies to: class (single, not inherited)
  • Triggers: declares the topic's IClientEventSubscriptionObserver — the producer-side subscription lifecycle. TObserver is called with a per-subscriber sink when a client subscribes (OnSubscribedAsync — the producer-controlled initial value: greet the new subscriber with the current value, so the stream is self-converging) and on debounced interest transitions (OnInterestChangedAsynctrue on the first watcher of a (topic, scope), false only after the last one leaves and the linger elapses, so a browser reload never bounces the producer). Resolved from a fresh DI scope per callback, detached from the subscribe path; the pull sibling is IClientEventInterest.HasSubscribers. Imperative form: ObserveSubscriptions<TObserver>() (+ WithInterestLinger(...)) on the topic options.
ParameterTypeDefaultMeaning
InterestLingerSecondsdouble (init)5The last-watcher departure debounce before OnInterestChangedAsync(active: false) fires.
[SubscriptionObserver<QuoteSubscriptionObserver>(InterestLingerSeconds = 30)]
public sealed record QuoteChanged : IClientEvent { /* … */ }

See Client events.

[GenerateVariantCatalog]

  • Package: Elarion.Abstractions · Applies to: assembly
  • Triggers: runs the VariantCatalogGenerator to emit the assembly's ElarionVariants registry — the compile-time catalog of every [FeatureVariant]/[ConfigurationVariant] switch, aggregated across referenced assemblies from the Elarion manifest (the variant analog of ElarionPermissions). Per switch it emits an accessor class with the selector Key and one const string per value (usable in [AllowedValues(...)]), plus VariantDescriptor data surfaced as All/ByKey/ByModule/Platform (variants outside every module — the platform-adapter placement — carry Module = null). The host seeds runtime consumers explicitly: services.AddElarionVariantCatalog(ElarionVariants.All), optionally with AddElarionVariantValidation() for startup + reload validation. Marker only — no parameters. Subsumed by [UseElarion], or apply it standalone. Diagnostic ELVAR010 (two selectors/values colliding on the same typed accessor).
[assembly: GenerateVariantCatalog]

See Feature flags.

[UseElarionEntityFrameworkCore]

  • Package: Elarion.EntityFrameworkCore · Applies to: assembly
  • Triggers: declares the target database provider so EF Core generators can emit provider-optimized variants (the EF Core analogue of [assembly: UseElarion]). When omitted, generators behave as Portable and emit provider-neutral code.
ParameterTypeDefaultMeaning
ProviderEfCoreProvider (init)Portable (= 0)Target database provider.

EfCoreProvider: Portable (= 0, provider-neutral code), Npgsql (= 1, opts generated keyset seek predicates into PostgreSQL row-value comparisons; requires the Npgsql EF Core provider be referenced).

See Entity Framework.

On this page

Modules & DI[AppModule][ClientFeatures][Service][GenerateContractSetRegistration][DecoratorList]Transports[Handler][HandlerTelemetry][McpHandler][HttpEndpoint][GenerateModuleBootstrapper][ModuleEndpoints]Scheduling[ScheduledJob]Events[ConsumeEvent]Actors[Actor][Reentrant][ActorKey]Caching[Cacheable][CacheInvalidate]Resilience[ResiliencePolicy][Resilient]Feature flags[FeatureGate][FeatureVariant][ConfigurationVariant]Idempotency[Idempotent][AllowDuplicates]Auditing[Auditable][ElarionAuditDefaults][Audited][AuditIgnore]ValidationAuthorization[RequirePermission][RequireRole][RequireClaim][RequirePolicy][AllowAnonymous][AuthorizationPolicy][ElarionAuthorizationDefaults][RequireResource][ResourceFilter<TEntity>]EF Core[EntityConfiguration][GenerateDbSets][GenerateElarionIdentity<TUser, TRole, TKey>][GenerateElarionResourceGrants][GenerateElarionIdempotencyKeys][GenerateElarionAuditing][GenerateElarionOutbox][GenerateElarionSchedulerClaims][GenerateElarionSettings][GenerateElarionBlobStorage][GenerateElarionStagedUploads][GenerateElarionActorSnapshots][GenerateElarionRoleLeases][GenerateElarionDeviceIdentity][Keyset<TEntity>]SQL row mapping (EF-free tier)[SqlRecord][SqlColumn][SqlIgnore][SqlJson][UseElarionSql]Cross-module communication[ModuleContract][ModuleApi][GenerateModuleApi]Assembly opt-ins[UseElarion]Individual generation triggers[GeneratePermissionCatalog][GenerateClientEventTopics][ClientEvent][AllowAnyResource][SubscriptionObserver<TObserver>][GenerateVariantCatalog][UseElarionEntityFrameworkCore]Related references