Elarion

Introduction

Elarion is a .NET application framework that turns modules, handlers, and attributes into deterministic, compile-time wiring — no runtime reflection scanning.

Elarion is a .NET application framework for module-based handler pipelines and deterministic, compile-time wiring. Its focused packages add HTTP, JSON-RPC and MCP transports; background work; events and realtime delivery; PostgreSQL-backed coordination and persistence; device connections; and optional EF Core or NativeAOT-friendly SQL data access.

The central idea is simple: your application assemblies define modules and handlers; your host assembly only wires infrastructure, transport, and deployment concerns. Everything that can be discovered from your code — handler pipelines, module registrations, transport maps, serializers, jobs, event consumers, policy catalogs, actor facades, and data-access metadata — is emitted by source generators at compile time instead of scanned by reflection at startup.

[Handler("clients.get")]
public sealed class GetClient(AppDbContext db)
    : IHandler<GetClient.Query, Result<GetClient.Response>> {
    public sealed record Query(Guid Id);
    public sealed record Response(Guid Id, string Name);

    public async ValueTask<Result<Response>> HandleAsync(Query query, CancellationToken ct) {
        var client = await db.Clients
            .Where(c => c.Id == query.Id)
            .Select(c => new Response(c.Id, c.Name))
            .FirstOrDefaultAsync(ct);

        return client is null
            ? AppError.NotFound($"Client {query.Id} was not found.")
            : client;
    }
}

That single class is a use case, a registered service, a JSON-RPC method, an MCP tool for AI agents, and (optionally) a schema-exported TypeScript contract — with no entry added to any Program.cs registration list.

Why Elarion

Compile-time, not reflection

Handlers, services, modules, RPC maps, validation metadata, and scheduled jobs are generated as ordinary DI code. Startup is deterministic and AOT-friendly; missing wiring is a build error, not a runtime surprise.

Modules own their surface

A module is a namespace plus an [AppModule] marker. Add a handler under it and the module publishes it automatically through generated Add{Module}…() methods.

Transport-neutral results

Handlers return Result<T> with a transport-agnostic AppError. The host maps failures to JSON-RPC, HTTP, or any other protocol.

End-to-end JSON-RPC

Mark a handler with [Handler], export a schema at build time, and generate a typed TypeScript + Zod client — without hand-writing DTOs.

AI-native, no extra code

Expose the same [Handler] handlers to AI agents as an MCP server. Tool names, descriptions, and input schemas are generated from your handlers and [Description] attributes at compile time — no separate tool layer, no duplicated schemas.

In-process scheduling

Source-generated scheduled jobs share one scheduler with explicit overlap, misfire, and resilience policies — and full OpenTelemetry instrumentation.

Declarative feature flags

Gate any handler behind a flag with [FeatureGate] — runtime rollouts, kill switches, and per-variant service injection over OpenFeature. Transport-neutral and provider-agnostic, a closed gate returns 404 so the feature name never leaks.

Declarative authorization

Gate any handler with [RequirePermission]/[RequireRole]/[RequireClaim]/[RequirePolicy] — the generator auto-attaches the decorator, so the same rules apply under JSON-RPC, MCP, and HTTP. Opt-in deny-by-default, and no ASP.NET coupling.

Resource & data-level access control

Point-check a resource with [RequireResource], or filter whole result sets at the database with [ResourceFilter<TEntity>] + WhereAuthorized(spec, user) — owner/tenant/role sharing compiled into EF Core predicates, no in-memory post-filter.

Blob storage

Depend on provider-neutral blob contracts in application code and choose PostgreSQL-backed storage in the host.

Observable by default

JSON-RPC, scheduling, caching, and resilience emit OpenTelemetry-compatible traces and metrics through System.Diagnostics — no SDK dependency forced on you.

Choose the right runtime shape

The framework now covers several kinds of work, but they are not interchangeable. Start with the simplest shape whose correctness contract fits:

The philosophy in one line

Auto-detect application patterns, explicitly wire platform capabilities.

Repeating what your code already states — "I handle this request", "this is what a valid command looks like", "this is a module service" — in a separate registration list creates a parallel model that drifts. Elarion declares intent next to the type and generates the wiring. The host stays a thin composition and transport shell that owns authentication, middleware, database providers, telemetry exporters, and deployment.

This is the same broad approach used by mature annotation-driven frameworks such as Spring Boot — components live below a namespace boundary, attributes declare their role, sensible defaults cover the common path — implemented with .NET source generation instead of runtime classpath scanning.

For the full reasoning behind each design decision — compile-time generation, handlers as the use-case boundary, result-based errors, JSON-RPC for internal APIs, and a thin host — see Design & philosophy.

When Elarion is a good fit

  • You are building a modular application or service and want feature modules to be self-contained.
  • You value deterministic startup, AOT compatibility, and inspectable generated code over runtime reflection.
  • You want a typed RPC contract shared between a .NET backend and a TypeScript frontend.
  • You want to expose your application to AI agents as MCP tools without hand-writing a separate tool layer.
  • You want in-process recurring/background work without standing up external job infrastructure.
  • You need realtime, device, or coordination building blocks sized for roughly 1–10 nodes on the PostgreSQL your application already runs.

It is intentionally not a thin wrapper around default ASP.NET Core. You trade some convention freedom for less host boilerplate, inherent modularity, and a clear line between application policy and host mechanics. See How Elarion differs from ASP.NET Core for the full trade-off table.

Next steps

Elarion is pre-1.0. The public surface described here is stable enough to build on, but minor releases may introduce breaking changes until 1.0. See the changelog for details.

On this page