AppEnclave 1.2.0
dotnet add package AppEnclave --version 1.2.0
NuGet\Install-Package AppEnclave -Version 1.2.0
<PackageReference Include="AppEnclave" Version="1.2.0" />
<PackageVersion Include="AppEnclave" Version="1.2.0" />
<PackageReference Include="AppEnclave" />
paket add AppEnclave --version 1.2.0
#r "nuget: AppEnclave, 1.2.0"
#:package AppEnclave@1.2.0
#addin nuget:?package=AppEnclave&version=1.2.0
#tool nuget:?package=AppEnclave&version=1.2.0
AppEnclave π‘οΈ
A High-Performance Multi-tenant Application Micro-kernel for ASP.NET Core.
AppEnclave allows you to host multiple, fully independent ASP.NET Core "Child Apps" within a single "Master App" process, sharing the same port. Each child app operates within its own Enclaveβpossessing its own private Dependency Injection container (IServiceProvider), Middleware pipeline, and Configuration.
π Key Features
- Multi-tenant Micro-kernel: Run multiple instances of the same app or different modules side-by-side.
- Total DI Isolation: Each enclave has its own private
IServiceProvider. No service registration leaks. - Environment Injection: Each tenant gets its own
IWebHostEnvironmentinjected into its DI during the build process, allowing for isolatedContentRootandEnvironmentName. - Zero-Latency Internal Routing: Requests are routed in-memory directly to the child's pipeline. No network overhead, no sockets, just raw performance.
- Low Memory Footprint: Designed without Assembly Load Contexts (ALC) to maximize JIT sharing and minimize RAM usage.
ποΈ Architecture & Constraints
AppEnclave is built for efficiency. To achieve the lowest possible memory overhead, it uses a shared-binary approach:
| Feature | AppEnclave Approach | Benefit |
|---|---|---|
| Process | Single Process | Extremely low overhead, easy monitoring. |
| Assembly Loading | No ALC (Single Context) | Low Memory: Shared JIT code and type metadata. |
| Dependencies | Shared Versions | All apps must use the same library versions. |
| Isolation | Logical & DI Container | Complete separation of app logic and middleware. |
| Performance | In-Process | Faster than YARP or Nginx reverse proxying. |
Because it does not use ALCs, the Master App and all Child Apps must target the same versions of shared libraries (e.g., EntityFramework, Newtonsoft.Json).
π» Quick Start
1. Create an Enclave plugin in Child app
using Microsoft.Extensions.FileProviders;
namespace AppEnclave.Examples.ChildApp
{
public class EnclavePlugin : ITenantPlugin
{
public Task ConfigureServicesAsync(IServiceCollection services, IWebHostEnvironment environment,
IConfigurationManager configuration)
{
// Add services to the container.
services.AddControllersWithViews();
services.AddAuthentication();
services.AddAuthorization();
return Task.CompletedTask;
}
public Task ConfigureAsync(IApplicationBuilder app, IWebHostEnvironment environment)
{
// Configure the HTTP request pipeline.
if (!environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseStaticFiles();
if (!Directory.Exists(Path.Combine(environment.ContentRootPath, @"wwwroot/.well-known")))
{
Directory.CreateDirectory(Path.Combine(environment.ContentRootPath, @"wwwroot/.well-known"));
}
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider =
new PhysicalFileProvider(Path.Combine(environment.ContentRootPath, @"wwwroot/.well-known")),
RequestPath = new PathString("/.well-known"),
ServeUnknownFileTypes = true
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}")
.WithStaticAssets();
});
return Task.CompletedTask;
}
}
}
You can even use that in Program.cs to keep the option to run the child app by itself
using AppEnclave.Examples.ChildApp;
var builder = WebApplication.CreateBuilder(args);
var plugin = new EnclavePlugin();
await plugin.ConfigureServicesAsync(builder.Services, builder.Environment, builder.Configuration);
var app = builder.Build();
await plugin.ConfigureAsync(app, builder.Environment);
app.Run();
2. Register an Enclave in Master app Program.cs
You can map an enclave to a specific path or entire hostname. AppEnclave handles the creation of the isolated container.
using System.Net;
using AppEnclave;
var builder = WebApplication.CreateBuilder(args);
// Master services (shared across all enclaves)
var httpContextAccessor = new HttpContextAccessor();
builder.Services.AddSingleton<IHttpContextAccessor>(httpContextAccessor);
builder.Services.AddHttpsRedirection(options =>
{
options.HttpsPort = 443;
});
builder.Services.AddAuthentication();
builder.Services.AddAuthorization();
builder.Services.ConfigureHttpClientDefaults(b =>
b.ConfigureHttpClient(client =>
{
client.DefaultRequestVersion = HttpVersion.Version20;
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrLower;
}).ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler()));
builder.Services.AddHttpClient();
// Child app for entire host localhost yet allowing subapps on same host
await builder.Services.AddAppEnclaveAsync(options =>
{
options.UseAuthentication = true;
options.AllowSubAppsOnSameHost = true;
options.Hosts = new[] { "localhost" };
options.Plugin = new AppEnclave.Examples.ChildApp.EnclavePlugin();
options.Name = "AppEnclave.Examples.ChildApp";
options.EnvironmentName = "Host";
options.ContentRoot = builder.Environment.ContentRootPath.Replace("AppEnclave.Examples.MasterApp", "AppEnclave.Examples.ChildApp");
options.BinRoot = builder.Environment.ContentRootPath.Replace("AppEnclave.Examples.MasterApp", "AppEnclave.Examples.ChildApp");
});
// Child app for /subapp1 path
await builder.Services.AddAppEnclaveAsync(options =>
{
options.UseAuthentication = true;
options.Hosts = new[] { "/subapp1" };
options.Plugin = new AppEnclave.Examples.ChildApp.EnclavePlugin();
options.Name = "AppEnclave.Examples.ChildApp";
options.EnvironmentName = "SubApp1";
options.ContentRoot = builder.Environment.ContentRootPath.Replace("AppEnclave.Examples.MasterApp", "AppEnclave.Examples.ChildApp");
options.BinRoot = builder.Environment.ContentRootPath.Replace("AppEnclave.Examples.MasterApp", "AppEnclave.Examples.ChildApp");
});
// Child app for /subapp2 path
await builder.Services.AddAppEnclaveAsync(options =>
{
options.UseAuthentication = true;
options.Hosts = new[] { "/subapp2" };
options.Plugin = new AppEnclave.Examples.ChildApp.EnclavePlugin();
options.Name = "AppEnclave.Examples.ChildApp";
options.EnvironmentName = "SubApp2";
options.ContentRoot = builder.Environment.ContentRootPath.Replace("AppEnclave.Examples.MasterApp", "AppEnclave.Examples.ChildApp");
options.BinRoot = builder.Environment.ContentRootPath.Replace("AppEnclave.Examples.MasterApp", "AppEnclave.Examples.ChildApp");
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
// Turns on routing middleware to enable enclaves to work
app.UseAppEnclave();
app.Run();
π Examples
- Simple Web app able to run by itself or as an enclave in master app - Simple webapp able to run by itself or as an enclave in master app.
- Basic Modular Monolith - Simple setup for isolated apps with separate configurations and shared root services.
π Why use AppEnclave?
AppEnclave fills the gap between a messy monolithic pipeline and the heavy resource overhead of Microservices. It is the ideal choice for Modular Monoliths and SaaS Multi-tenant systems where performance and isolation are both critical.
| Feature | Standard app.Map() |
Containers / Sidecars | AppEnclave |
|---|---|---|---|
| DI Container Isolation | β Shared (Leaky) | β Total | β Total (Private) |
| Memory Footprint | π’ Lowest | π΄ High (Multiple Runtimes) | π’ Minimal (Shared JIT) |
| Configuration Isolation | β No | β Yes | β Yes (Per-Enclave) |
Custom IWebHostEnv |
β No (Global) | β Yes | β Yes (Isolated) |
| Port Sharing | β Yes | β No (Requires Proxy) | β Yes (Native) |
| Network Latency | Zero | π΄ High (TCP/Socket) | π’ Zero (In-Process) |
| Dependency Versioning | β Forced Same | β Independent | β οΈ Forced Same (No ALC) |
When to choose AppEnclave?
- Modular Monoliths: You want to keep the code in one repo and process, but you hate when Service A accidentally resolves a dependency meant for Service B.
- SaaS Multi-tenancy: You need to run hundreds of instances of the same app with different
appsettings.jsonand differentWebRootPathwithout paying for massive RAM usage. - Legacy Migrations: You are merging multiple separate ASP.NET Core apps into one unified host without rewriting their internal DI registrations.
| Product | Versions 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. |
-
net10.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.