Substratum 1.0.0-beta.59

This is a prerelease version of Substratum.
dotnet add package Substratum --version 1.0.0-beta.59
                    
NuGet\Install-Package Substratum -Version 1.0.0-beta.59
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Substratum" Version="1.0.0-beta.59" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Substratum" Version="1.0.0-beta.59" />
                    
Directory.Packages.props
<PackageReference Include="Substratum" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Substratum --version 1.0.0-beta.59
                    
#r "nuget: Substratum, 1.0.0-beta.59"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Substratum@1.0.0-beta.59
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Substratum&version=1.0.0-beta.59&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Substratum&version=1.0.0-beta.59&prerelease
                    
Install as a Cake Tool

Substratum

Substratum is an opinionated, production-grade application framework built on ASP.NET Core and FastEndpoints. It eliminates the boilerplate required to bootstrap a modern web API — authentication, authorization, database, caching, logging, OpenAPI docs, cloud storage, and push notifications are all pre-wired and ready to go.

Write your business logic. Substratum handles the rest.

Packages

Package Description Target
Substratum Core runtime library — middleware, auth, EF Core, cloud integrations net10.0
Substratum.Generator Roslyn source generators — permissions, reflection, service registration, endpoint summaries, document groups netstandard2.0
Substratum.Tools CLI tool (dotnet sub) — scaffold projects, endpoints, entities, and manage migrations net10.0

Quick Start

1. Install the CLI

dotnet tool install -g Substratum.Tools

2. Create a new project

dotnet sub new webapp --name MyApp

This scaffolds a complete project with authentication, permissions, EF Core, localization, and health checks — ready to run.

3. Add packages to an existing project

<PackageReference Include="Substratum" Version="1.0.0-beta.58" />
<PackageReference Include="Substratum.Generator" Version="1.0.0-beta.58" />

4. Run the app

await SubstratumApp.RunAsync<AppDbContext, AppPermissions>(args, options =>
{
    options.EntityFramework.Provider = EntityFrameworkProviders.Npgsql;
    options.EntityFramework.ConnectionString = "Host=localhost;Database=myapp;...";
    options.Authentication.JwtBearer.Enabled = true;
    options.Authentication.JwtBearer.Options = new JwtBearerOptions
    {
        SecretKey = "your-256-bit-secret-key",
        Issuer = "https://myapp.com",
        Audience = "myapp-api"
    };
});

Substratum (Core Library)

Endpoints

All endpoints inherit from BaseEndpoint<TRequest, TResponse> and return a standardized Result<TResponse>.

public class GetOrderEndpoint : BaseEndpoint<GetOrderRequest, OrderResponse>
{
    public override void Configure()
    {
        Get("/orders/{id}");
        PermissionsAny(AppPermissions.OrdersGet);
    }

    public override async Task<Result<OrderResponse>> ExecuteAsync(GetOrderRequest req, CancellationToken ct)
    {
        var order = await db.Orders.FindAsync(req.Id, ct);
        if (order is null)
            return Failure(404, "OrderNotFound");

        return Success("OrderRetrieved", new OrderResponse { Id = order.Id, Total = order.Total });
    }
}

Result<T> wraps every response:

Property Type Description
Code int 0 for success, 1 for failure
Message string Human-readable (localized) message
Data T? Response payload
Errors IReadOnlyList<string>? Validation or error details

For endpoints that return no data, use Unit as the response type.

Pagination

public class ListOrdersEndpoint : BaseEndpoint<ListOrdersRequest, PaginatedResult<OrderResponse>>
{
    public override async Task<Result<PaginatedResult<OrderResponse>>> ExecuteAsync(
        ListOrdersRequest req, CancellationToken ct)
    {
        var result = await PaginatedResult<OrderResponse>.CreateAsync(
            db.Orders.Select(o => new OrderResponse { Id = o.Id, Total = o.Total }),
            req.PageNumber,
            req.PageSize,
            ct
        );
        return Success("OrdersRetrieved", result);
    }
}

PaginatedResult<T> includes PageNumber, TotalPages, TotalCount, Items, HasPreviousPage, and HasNextPage.

Base Entity

All domain entities inherit from BaseEntity<T> which provides built-in audit fields:

public sealed class Order : BaseEntity<Guid>
{
    public decimal Total { get; set; }
    public string Status { get; set; }
}
Property Type Description
Id T Primary key
CreatedAt DateTimeOffset Auto-set on creation
UpdatedAt DateTimeOffset Auto-set on update
IsDeleted bool Soft delete flag
DeletedAt DateTimeOffset? Soft delete timestamp

Authentication

Substratum supports four authentication schemes out of the box. Enable any combination — the framework automatically configures a policy scheme that routes to the correct handler.

JWT Bearer
options.Authentication.JwtBearer.Enabled = true;
options.Authentication.JwtBearer.Options = new JwtBearerOptions
{
    SecretKey = "your-256-bit-secret-key",
    Issuer = "https://myapp.com",
    Audience = "myapp-api",
    Expiration = TimeSpan.FromHours(1),
    RefreshExpiration = TimeSpan.FromDays(7) // optional, default 7 days
};

Create tokens with IJwtBearer:

// Access token only (no refresh token store needed)
var (accessToken, sessionId, expiration) = jwtBearer.CreateToken(userId);
Refresh Tokens

For token rotation workflows, implement IRefreshTokenStore and use CreateTokenPairAsync / RefreshAsync:

public class RefreshTokenStore : IRefreshTokenStore
{
    public Task StoreAsync(Guid userId, Guid sessionId, string tokenHash,
        DateTimeOffset expiration, CancellationToken ct)
    {
        // Store the hashed refresh token in your database
    }

    public Task<RefreshTokenValidationResult?> ValidateAndRevokeAsync(string tokenHash, CancellationToken ct)
    {
        // Find token by hash, revoke it, return userId/sessionId (or null if invalid/expired)
    }

    public Task RevokeBySessionAsync(Guid sessionId, CancellationToken ct)
    {
        // Revoke all refresh tokens for a session (logout)
    }

    public Task RevokeAllAsync(Guid userId, CancellationToken ct)
    {
        // Revoke all refresh tokens for a user (logout everywhere)
    }
}

Issue and refresh token pairs:

// Issue access + refresh token pair
var (accessToken, refreshToken, sessionId, accessExpiration, refreshExpiration) =
    await jwtBearer.CreateTokenPairAsync(userId, ct);

// Refresh — atomically revokes the old refresh token and issues a new pair
var result = await jwtBearer.RefreshAsync(refreshToken, ct);
if (result is null)
    return Failure(401, "InvalidRefreshToken");

var (newAccessToken, newRefreshToken, newAccessExpiration, newRefreshExpiration) = result.Value;

Refresh tokens are SHA256-hashed before storage — only hashes are persisted, never raw tokens. RefreshAsync performs atomic token rotation: the old token is revoked and a new pair is issued in a single operation.

options.Authentication.Cookie.Enabled = true;
options.Authentication.Cookie.Options = new CookieOptions
{
    CookieName = "auth_token",
    Expiration = TimeSpan.FromHours(1),
    SlidingExpiration = true,
    SameSite = SameSiteMode.Strict
};

Sign in/out with ICookieAuth:

var (sessionId, expiration) = await cookieAuth.SignInAsync(HttpContext, userId, ct);
await cookieAuth.SignOutAsync(HttpContext, ct);
Basic Authentication
options.Authentication.BasicAuthentication.Enabled = true;
options.Authentication.BasicAuthentication.Options = new BasicAuthenticationOptions
{
    Realm = "MyAPI"
};

Implement IBasicAuthValidator:

public class BasicAuthValidator : IBasicAuthValidator
{
    public async Task<(bool Result, string UserId, string SessionId)> ValidateAsync(
        HttpContext context, string username, string password, CancellationToken ct)
    {
        var user = await db.Users.FirstOrDefaultAsync(u => u.Email == username, ct);
        if (user is null || !passwordHasher.VerifyHashedPassword(user.PasswordHash, password, out _))
            return (false, "", "");

        return (true, user.Id.ToString("N"), Guid.CreateVersion7().ToString("N"));
    }
}
Access Key
options.Authentication.AccessKeyAuthentication.Enabled = true;
options.Authentication.AccessKeyAuthentication.Options = new AccessKeyAuthenticationOptions
{
    KeyName = "X-API-KEY",
    Realm = "MyAPI"
};

Implement IAccessKeyValidator:

public class AccessKeyValidator : IAccessKeyValidator
{
    public async Task<(bool Result, string UserId, string SessionId)> ValidateAsync(
        HttpContext context, string accessKey, CancellationToken ct)
    {
        var key = await db.AccessKeys.FirstOrDefaultAsync(k => k.Key == accessKey && k.IsActive, ct);
        if (key is null) return (false, "", "");
        return (true, key.UserId.ToString("N"), Guid.CreateVersion7().ToString("N"));
    }
}
Supporting Interfaces
Interface Purpose Required By
ISessionValidator Validates active sessions server-side JWT, Cookie
IPermissionHydrator Loads user permissions into claims All schemes
IRefreshTokenStore Stores and validates hashed refresh tokens JWT (when using refresh tokens)
IPasswordHasher Hashes and verifies passwords (PBKDF2, 600k iterations) Built-in service
ITotpProvider TOTP 2FA — generate secrets, QR URIs, validate codes Built-in service
ICurrentUser Access current user's ID (Guid?) Built-in service
Two-Factor Authentication (TOTP)

Substratum provides ITotpProvider — a built-in TOTP service compatible with Google Authenticator, Authy, and other authenticator apps.

Enrollment:

// Generate a secret and QR code URI for the user
var secret = totpProvider.GenerateSecret();
var qrCodeUri = totpProvider.GenerateQrCodeUri(secret, user.Email, "MyApp");
// Store secret in your database, return qrCodeUri to the client for QR display

Verification:

// Validate a 6-digit code from the authenticator app
var isValid = totpProvider.ValidateCode(user.TotpSecret, request.Code);

Login flow with 2FA:

  1. Validate password
  2. If 2FA not enabled → issue token via jwtBearer.CreateToken() or CreateTokenPairAsync()
  3. If 2FA enabled → return a pending state (no token yet)
  4. User submits TOTP code → validate with totpProvider.ValidateCode() → issue token

The framework provides the TOTP utility; you implement the enrollment endpoints and login flow in your own endpoints since these are app-specific.


Permissions

Define permissions as static fields in a partial class implementing IPermissionRegistry. The source generator auto-generates Definitions(), Parse(), and TryParse() methods.

public partial class AppPermissions : IPermissionRegistry
{
    public static readonly PermissionDefinition OrdersGet;
    public static readonly PermissionDefinition OrdersCreate;
    public static readonly PermissionDefinition OrdersEdit;
    public static readonly PermissionDefinition OrdersDelete;
    public static readonly PermissionDefinition UsersGet;
    public static readonly PermissionDefinition UsersCreate;
}

The generator produces:

  • Code: unique short code derived from the field name (e.g. A1B2)
  • Name: snake_case from the field name (e.g. orders_get)
  • DisplayName: human-readable (e.g. Get)
  • Group: inferred from the field name prefix (e.g. Orders group)

Use permissions in endpoints:

public override void Configure()
{
    Get("/orders/{id}");
    PermissionsAny(AppPermissions.OrdersGet);   // user needs ANY of these
    PermissionsAll(AppPermissions.OrdersGet);    // user needs ALL of these
}

Implement IPermissionHydrator to load permissions from your database into claims:

public class PermissionHydrator : IPermissionHydrator
{
    public async Task HydrateAsync(IServiceProvider sp, ClaimsPrincipal principal, CancellationToken ct)
    {
        var userId = principal.FindFirstValue(SubstratumClaimsTypes.UserId);
        var db = sp.GetRequiredService<AppDbContext>();

        var codes = await db.UserPermissions
            .Where(up => up.UserId == Guid.Parse(userId))
            .Select(up => up.Permission.Code)
            .ToListAsync(ct);

        var identity = (ClaimsIdentity)principal.Identity!;
        foreach (var code in codes)
            identity.AddClaim(new Claim("permissions", code));
    }
}

Document Groups

Organize your API into separate OpenAPI documents — each with its own Scalar UI page and optional authentication.

Define groups in Common/Security/DocGroups.cs:

public partial class DocGroups : IDocGroupRegistry
{
    // Default group — shows ALL endpoints at /docs (no tag filtering required)
    public static readonly DocGroupDefinition Default =
        new("API Documentation", "docs", isDefault: true);

    // Filtered groups — show only tagged endpoints
    public static readonly DocGroupDefinition Mobile =
        new("Mobile API", "mobile");

    public static readonly DocGroupDefinition Admin =
        new("Admin API", "admin", AppPermissions.AdminDocsAccess); // protected with permission
}

Use in endpoints:

public override void Configure()
{
    Get("/orders");
    DocGroup(DocGroups.Mobile);
}

This creates:

  • /docs — Scalar UI showing all endpoints (the Default group)
  • /mobile — Scalar UI showing only Mobile API endpoints
  • /admin — Scalar UI showing only Admin API endpoints (requires API key)

How it works:

  • The Default group (isDefault: true) shows all endpoints regardless of tags. Endpoints don't need to call DocGroup() to appear in it. If no Default group is defined, there is no "all endpoints" documentation page.
  • Non-default groups only show endpoints explicitly tagged via DocGroup().
  • Protected groups (with a PermissionDefinition) use Basic Auth — the username is the access key, the password is empty. The browser's native auth dialog prompts the user. Permissions are validated through IAccessKeyValidator and IPermissionHydrator.

Entity Framework

options.EntityFramework.Provider = EntityFrameworkProviders.Npgsql; // or SqlServer, Sqlite
options.EntityFramework.ConnectionString = "Host=localhost;Database=myapp;...";

Supported providers: PostgreSQL, SQL Server, SQLite

Retry Policy
options.EntityFramework.RetryPolicy.Enabled = true;
options.EntityFramework.RetryPolicy.Options = new RetryPolicyOptions
{
    MaxRetryCount = 3,
    MaxRetryDelaySeconds = 2
};
Second-Level Cache
options.EntityFramework.SecondLevelCache.Enabled = true;
options.EntityFramework.SecondLevelCache.Options = new SecondLevelCacheOptions
{
    Provider = SecondLevelCacheProviders.Memory, // or Redis
    KeyPrefix = "app:"
};

// Redis configuration
options.EntityFramework.SecondLevelCache.Options.Redis = new SecondLevelCacheRedisOptions
{
    ConnectionString = "localhost:6379",
    TimeoutSeconds = 300
};

OpenAPI Documentation

options.OpenApi.Enabled = true;
options.OpenApi.Options.Servers = new[]
{
    new OpenApiServerOptions { Url = "https://api.example.com", Description = "Production" },
    new OpenApiServerOptions { Url = "https://staging.example.com", Description = "Staging" }
};

Substratum generates Scalar UI at /docs (or at document group URLs). Features include:

  • Automatic permission documentation on each endpoint
  • Accept-Language header parameter for localized APIs
  • Dark mode theme

Localization

options.Localization.DefaultCulture = "en";
options.Localization.SupportedCultures = ["en", "ar", "fr"];
options.Localization.ResourceSource = typeof(SharedResource);

The framework reads Accept-Language headers and localizes validation messages, error responses, and OpenAPI descriptions.


Cloud Storage

MinIO (S3-Compatible)
options.Minio.Enabled = true;
options.Minio.Options = new MinioOptions
{
    Endpoint = "minio.example.com",
    Region = "us-east-1",
    Secure = true,
    AccessKey = "...",
    SecretKey = "..."
};

Inject IMinioClient to interact with S3-compatible storage.

AWS S3
options.Aws.S3.Enabled = true;
options.Aws.S3.Options = new AwsS3Options
{
    Region = "us-west-2",
    AccessKey = "...",
    SecretKey = "..."
};

Inject IAmazonS3 to interact with AWS S3.

AWS Secrets Manager
options.Aws.SecretsManager.Enabled = true;
options.Aws.SecretsManager.Options = new AwsSecretsManagerOptions
{
    Region = "us-west-2",
    AccessKey = "...",
    SecretKey = "...",
    SecretArns = ["arn:aws:secretsmanager:us-west-2:123456789:secret:my-secret"]
};

Secrets are automatically loaded into IConfiguration.


Firebase

Cloud Messaging
options.Firebase.Messaging.Enabled = true;
options.Firebase.Messaging.Options = new FirebaseMessagingOptions
{
    Credential = Convert.ToBase64String(Encoding.UTF8.GetBytes(serviceAccountJson))
};

Inject FirebaseMessaging to send push notifications.

App Check
options.Firebase.AppCheck.Enabled = true;
options.Firebase.AppCheck.Options = new FirebaseAppCheckOptions
{
    ProjectId = "my-project",
    ProjectNumber = "123456789"
};

Inject IFirebaseAppCheck and call VerifyAppCheckTokenAsync().


Infrastructure

Feature Configuration Default
CORS options.Cors.AllowedOrigins = [...] Disabled
Health Checks options.HealthChecks.Enabled = true /healthz
Static Files options.StaticFiles.Enabled = true wwwroot
Response Compression Always on Gzip + Brotli
Forwarded Headers Always on X-Forwarded-For/Proto
Logging Serilog via appsettings.json Console sink, sensitive data masking
Kestrel Limits Pre-configured 1 MB body, 10k connections, 15s header timeout
Distributed Cache options.DistributedCache.Enabled = true InMemory or Redis
Distributed Cache
options.DistributedCache.Enabled = true;
options.DistributedCache.Options = new DistributedCacheOptions
{
    Provider = DistributedCacheProviders.Redis, // or Memory
    Redis = new DistributedCacheRedisOptions
    {
        ConnectionString = "localhost:6379",
        InstanceName = "DC_"
    }
};

Inject IDistributedCache to use the cache. When Provider is Memory, an in-memory cache is registered (useful for development). When Provider is Redis, a StackExchange Redis-backed cache is registered.


Substratum.Generator

Seven Roslyn incremental source generators that eliminate boilerplate at compile time.

1. SubstratumApp Generator

Generates a [ModuleInitializer] that wires up the framework. Scans for:

  • DbContext implementation
  • IPermissionRegistry implementation
  • ISessionValidator, IPermissionHydrator, IBasicAuthValidator, IAccessKeyValidator, IRefreshTokenStore implementations

Output: SubstratumAppInitializer.g.cs

2. Discovered Types Generator

Scans for all concrete classes implementing FastEndpoints interfaces (IEndpoint, IEventHandler, ICommandHandler, ISummary, IValidator) and collects them into a type list. Respects [DontRegister].

Output: DiscoveredTypes.g.cs

3. Service Registration Generator

Scans for [RegisterService<TInterface>(ServiceLifetime)] attributes and generates DI extension methods.

[RegisterService<IOrderService>(ServiceLifetime.Scoped)]
public class OrderService : IOrderService { }

Output: ServiceRegistrations.g.cs with RegisterServicesFromMyApp() extension method.

4. Reflection Generator

Builds a FastEndpoints reflection cache by analyzing endpoint request/response DTOs — object factories, property setters, and value parsers. Eliminates runtime reflection.

Output: ReflectionData.g.cs

5. Permissions Generator

Generates the Definitions(), Parse(), and TryParse() methods for IPermissionRegistry implementations. Auto-computes permission codes, names, display names, and groups from field names.

Output: {ClassName}.Permissions.g.cs

6. Endpoint Summary Generator

Scans BaseEndpoint<TRequest, TResponse> subclasses and generates OpenAPI summary classes. Auto-detects:

  • Error responses from Failure() calls
  • 400 if a Validator<TRequest> exists
  • 401 if the endpoint is not AllowAnonymous()
  • 403 if permissions are declared

Output: {EndpointName}Summary.g.cs

7. Document Group Generator

Generates Definitions() and a [ModuleInitializer] for IDocGroupRegistry implementations.

Output: {ClassName}.DocGroups.g.cs


Substratum.Tools (CLI)

Install globally:

dotnet tool install -g Substratum.Tools

Commands

dotnet sub new webapp

Scaffold a complete web application:

dotnet sub new webapp --name MyApp

Generates a full project with:

  • Pre-configured Program.cs with SubstratumApp.RunAsync
  • AppDbContext with User, Role, UserSession entities
  • AppPermissions registry
  • SessionValidator and PermissionHydrator
  • Localization resources (EN, AR)
  • Initial EF migration
  • AGENTS.md with AI scaffolding guidelines
dotnet sub new endpoint

Scaffold a new API endpoint:

dotnet sub new endpoint \
  --group Orders \
  --name CreateOrder \
  --route /orders \
  --method Post \
  --permission OrdersCreate \
  --response-type SingleResult

Generates under Features/Orders/CreateOrder/:

  • CreateOrderEndpoint.cs — endpoint handler
  • CreateOrderRequest.cs — request DTO
  • CreateOrderResponse.cs — response DTO
  • CreateOrderRequestValidator.cs — FluentValidation validator
  • CreateOrderSummary.cs — OpenAPI summary
  • CreateOrderSerializerContext.cs — JSON source-gen context

Also inserts the permission definition into AppPermissions.cs automatically.

For paginated endpoints, use --response-type PaginatedResult — the request will include PageNumber and PageSize properties.

All options are interactive — omit any flag and the CLI will prompt you.

dotnet sub new entity

Scaffold a domain entity:

dotnet sub new entity --name Order

Generates:

  • Domain/Entities/Order.cs — entity class inheriting BaseEntity<Guid>
  • Data/Configurations/OrderConfiguration.cs — EF Core configuration

Also inserts public DbSet<Order> Orders => Set<Order>(); into AppDbContext.cs.

dotnet sub migrations add

Add an EF Core migration:

dotnet sub migrations add AddOrderTable
dotnet sub database update

Apply pending migrations:

dotnet sub database update
dotnet sub database sql

Generate an idempotent SQL script:

dotnet sub database sql -o ./deploy.sql

Configuration Reference

await SubstratumApp.RunAsync<AppDbContext, AppPermissions>(args, options =>
{
    // Authentication
    options.Authentication.JwtBearer.Enabled = true;
    options.Authentication.JwtBearer.Options = new JwtBearerOptions { ... };
    options.Authentication.Cookie.Enabled = true;
    options.Authentication.Cookie.Options = new CookieOptions { ... };
    options.Authentication.BasicAuthentication.Enabled = true;
    options.Authentication.BasicAuthentication.Options = new BasicAuthenticationOptions { ... };
    options.Authentication.AccessKeyAuthentication.Enabled = true;
    options.Authentication.AccessKeyAuthentication.Options = new AccessKeyAuthenticationOptions { ... };

    // Database
    options.EntityFramework.Provider = EntityFrameworkProviders.Npgsql;
    options.EntityFramework.ConnectionString = "...";
    options.EntityFramework.CommandTimeoutSeconds = 30;
    options.EntityFramework.RetryPolicy.Enabled = true;
    options.EntityFramework.RetryPolicy.Options = new RetryPolicyOptions { ... };
    options.EntityFramework.SecondLevelCache.Enabled = true;
    options.EntityFramework.SecondLevelCache.Options = new SecondLevelCacheOptions { ... };

    // Localization
    options.Localization.DefaultCulture = "en";
    options.Localization.SupportedCultures = ["en", "ar"];
    options.Localization.ResourceSource = typeof(SharedResource);

    // OpenAPI
    options.OpenApi.Enabled = true;
    options.OpenApi.Options.Servers = [new OpenApiServerOptions { ... }];

    // CORS
    options.Cors.AllowedOrigins = ["https://example.com"];

    // Health Checks
    options.HealthChecks.Enabled = true;
    options.HealthChecks.Options = new HealthChecksOptions
    {
        Path = "/healthz",
        HealthChecksBuilder = hc => hc.AddDbContextCheck<AppDbContext>()
    };

    // Static Files
    options.StaticFiles.Enabled = true;
    options.StaticFiles.Options = new StaticFilesOptions { RootPath = "wwwroot" };

    // Error Handling
    options.ErrorHandling.IncludeExceptionDetails = false;

    // Cloud — MinIO
    options.Minio.Enabled = true;
    options.Minio.Options = new MinioOptions { ... };

    // Cloud — AWS
    options.Aws.S3.Enabled = true;
    options.Aws.S3.Options = new AwsS3Options { ... };
    options.Aws.SecretsManager.Enabled = true;
    options.Aws.SecretsManager.Options = new AwsSecretsManagerOptions { ... };

    // Firebase
    options.Firebase.Messaging.Enabled = true;
    options.Firebase.Messaging.Options = new FirebaseMessagingOptions { ... };
    options.Firebase.AppCheck.Enabled = true;
    options.Firebase.AppCheck.Options = new FirebaseAppCheckOptions { ... };

    // Distributed Cache
    options.DistributedCache.Enabled = true;
    options.DistributedCache.Options = new DistributedCacheOptions
    {
        Provider = DistributedCacheProviders.Redis,
        Redis = new DistributedCacheRedisOptions { ConnectionString = "...", InstanceName = "DC_" }
    };

    // Custom Services
    options.Services.AddScoped<IMyService, MyService>();
});

Interfaces to Implement

Interface Purpose When Required
IPermissionRegistry Define all permissions Always (source-generated)
IDocGroupRegistry Define API document groups When using document groups (source-generated)
ISessionValidator Validate active sessions JWT or Cookie auth
IPermissionHydrator Load user permissions into claims When using permissions
IBasicAuthValidator Validate username/password Basic auth enabled
IAccessKeyValidator Validate access keys Access key auth or protected document groups
IRefreshTokenStore Store and validate refresh tokens JWT with refresh token rotation

All implementations are auto-discovered by the source generators — no manual registration needed.


License

See LICENSE for details.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0-beta.59 9 2/9/2026
1.0.0-beta.58 18 2/9/2026
1.0.0-beta.56 26 2/9/2026
1.0.0-beta.55 27 2/9/2026
1.0.0-beta.54 32 2/8/2026
1.0.0-beta.53 33 2/7/2026
1.0.0-beta.52 31 2/7/2026
1.0.0-beta.51 30 2/7/2026
1.0.0-beta.50 29 2/7/2026
1.0.0-beta.49 29 2/7/2026
1.0.0-beta.48 30 2/7/2026
1.0.0-beta.47 28 2/7/2026
1.0.0-beta.46 29 2/7/2026
1.0.0-beta.45 34 2/7/2026
1.0.0-beta.44 39 2/7/2026
1.0.0-beta.43 38 2/7/2026
1.0.0-beta.42 27 2/7/2026
1.0.0-beta.41 33 2/7/2026
1.0.0-beta.40 32 2/6/2026
1.0.0-beta.30 26 2/6/2026
1.0.0-beta.24 31 2/6/2026
1.0.0-beta.21 31 2/6/2026
1.0.0-beta.20 37 2/6/2026
1.0.0-beta.7 44 2/1/2026
1.0.0-beta.6 47 1/21/2026
1.0.0-beta.5 46 1/21/2026
1.0.0-beta.4 48 1/20/2026
1.0.0-beta.3 47 1/20/2026
1.0.0-beta.2 49 1/20/2026
1.0.0-beta.1 50 1/18/2026