Muonroi.Tenancy 1.0.0-alpha.16

This is a prerelease version of Muonroi.Tenancy.
dotnet add package Muonroi.Tenancy --version 1.0.0-alpha.16
                    
NuGet\Install-Package Muonroi.Tenancy -Version 1.0.0-alpha.16
                    
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="Muonroi.Tenancy" Version="1.0.0-alpha.16" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Muonroi.Tenancy" Version="1.0.0-alpha.16" />
                    
Directory.Packages.props
<PackageReference Include="Muonroi.Tenancy" />
                    
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 Muonroi.Tenancy --version 1.0.0-alpha.16
                    
#r "nuget: Muonroi.Tenancy, 1.0.0-alpha.16"
                    
#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 Muonroi.Tenancy@1.0.0-alpha.16
                    
#: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=Muonroi.Tenancy&version=1.0.0-alpha.16&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Muonroi.Tenancy&version=1.0.0-alpha.16&prerelease
                    
Install as a Cake Tool

Muonroi.Tenancy

ASP.NET Core runtime layer for multi-tenancy: request-scoped tenant resolution middleware, per-tenant Redis cache, and OpenTelemetry metrics for auth failures.

NuGet License: Apache 2.0

Muonroi.Tenancy is the ASP.NET Core runtime package of the Muonroi multi-tenancy stack. It provides TenantResolutionMiddleware — which extracts the tenant ID from the HTTP request (header, path segment, or subdomain), validates it against the caller's JWT claim, and propagates it through TenantContext.CurrentTenantId for the duration of the request. The package also ships RedisTenantCache, a Redis wrapper that namespaces all keys under tenant:{tenantId}: for clean per-tenant isolation, and TenantResolutionTelemetry, which emits OTel-native counters for auth failures.

This package depends on Muonroi.Tenancy.Abstractions (contracts) and Muonroi.Tenancy.Core (context propagation and DI helpers).

Installation

dotnet add package Muonroi.Tenancy --prerelease

Quick Start

Register core tenancy services from Muonroi.Tenancy.Core, then add the middleware:

// Program.cs
using Muonroi.Tenancy.Core.Legacy;   // AddTenantContext
using Muonroi.Tenancy;               // TenantResolutionMiddleware

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

// Register tenant context and connection-string factory
builder.Services.AddTenantContext(options =>
{
    builder.Configuration.GetSection("MultiTenant").Bind(options);
});

WebApplication app = builder.Build();

// Place after UseAuthentication / UseAuthorization so the JWT claim is available
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<TenantResolutionMiddleware>();

app.MapControllers();
app.Run();

After the middleware runs, read the resolved tenant ID anywhere in the request pipeline:

string? tenantId = TenantContext.CurrentTenantId;

Per-tenant Redis cache

// Inject IConnectionMultiplexer (registered via StackExchange.Redis)
IConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379");
var cache = new RedisTenantCache(redis, tenantId: "acme");

await cache.SetAsync("settings:theme", "dark", expiry: TimeSpan.FromHours(1));
RedisValue theme = await cache.GetAsync("settings:theme");

Features

  • Multi-source tenant resolution: checks the custom request header (X-Tenant-Id via CustomHeader.TenantId), then the first URL path segment, then the subdomain — in that order of precedence.
  • Format validation: tenant IDs must match ^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$; malformed input returns 400 Bad Request.
  • JWT claim cross-check: if a resolved tenant ID conflicts with the tenant_id JWT claim the middleware returns 401 Unauthorized; a missing claim also yields 401.
  • AsyncLocal propagation: TenantContext.CurrentTenantId is set for the request scope and cleared in the finally block — safe for async code.
  • OpenTelemetry tracing: tenant ID is written to Activity.Current as both a tag (tenant.id) and baggage for distributed trace correlation.
  • Per-tenant Redis isolation: RedisTenantCache prefixes every key with tenant:{tenantId}: and uses SCAN (not KEYS) for bulk enumeration.
  • OTel metrics: TenantResolutionTelemetry exposes a Counter<long> instrument (muonroi.tenancy.auth_failures) with failure_reason, header_tenant_id, and claim_tenant_id dimensions.

Configuration

Tenant context options live in Muonroi.Tenancy.Core. Bind them from appsettings.json:

{
  "MultiTenant": {
    "DefaultTenantId": "default",
    "ConnectionStrings": {
      "acme": "Server=acme-db;Database=acme;...",
      "beta": "Server=beta-db;Database=beta;..."
    }
  }
}

Register in DI using the extension from Muonroi.Tenancy.Core:

builder.Services.AddTenantContext(options =>
    builder.Configuration.GetSection("MultiTenant").Bind(options));

API Reference

Type Purpose
TenantResolutionMiddleware ASP.NET Core middleware that resolves, validates, and propagates the tenant ID per request
RedisTenantCache Redis wrapper that namespaces all keys under tenant:{tenantId}: to enforce per-tenant isolation
TenantResolutionTelemetry Static helper exposing AuthFailureCounter (Counter<long>) and RecordAuthFailure(reason, header, claim)
TenantContext (from Muonroi.Tenancy.Core) static string? CurrentTenantId — AsyncLocal holder for the active tenant

Samples

  • MultiTenantSaaS — SaaS pricing API demonstrating per-tenant rule evaluation with license-tier enforcement

Compatibility

  • Target framework: net8.0
  • Requires: Microsoft.AspNetCore.App framework reference, StackExchange.Redis
  • License: Apache-2.0 (OSS)
  • Muonroi.Tenancy.Abstractions — contracts: ITenantContext, ITenantIdResolver, MultiTenantOptions, quota interfaces
  • Muonroi.Tenancy.Core — core implementations: TenantContext, DefaultTenantIdResolver, TenantSchemaSelector, AddTenantContext DI extension
  • Muonroi.Tenancy.SiteProfile — site-profile layer for multi-site deployments with per-site EF Core / Dapper infrastructure

License

Apache-2.0. See LICENSE-APACHE for details.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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-alpha.16 55 6/22/2026
1.0.0-alpha.15 72 5/31/2026
1.0.0-alpha.14 65 5/15/2026
1.0.0-alpha.13 64 5/2/2026
1.0.0-alpha.12 80 4/2/2026
1.0.0-alpha.11 74 4/2/2026
1.0.0-alpha.9 73 3/30/2026
1.0.0-alpha.8 158 3/28/2026
1.0.0-alpha.7 73 3/27/2026
1.0.0-alpha.5 70 3/27/2026
1.0.0-alpha.4 68 3/27/2026
1.0.0-alpha.3 68 3/27/2026
1.0.0-alpha.2 72 3/26/2026
1.0.0-alpha.1 84 3/8/2026