CosmoApiServer.Core 3.2.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package CosmoApiServer.Core --version 3.2.1
                    
NuGet\Install-Package CosmoApiServer.Core -Version 3.2.1
                    
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="CosmoApiServer.Core" Version="3.2.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CosmoApiServer.Core" Version="3.2.1" />
                    
Directory.Packages.props
<PackageReference Include="CosmoApiServer.Core" />
                    
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 CosmoApiServer.Core --version 3.2.1
                    
#r "nuget: CosmoApiServer.Core, 3.2.1"
                    
#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 CosmoApiServer.Core@3.2.1
                    
#: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=CosmoApiServer.Core&version=3.2.1
                    
Install as a Cake Addin
#tool nuget:?package=CosmoApiServer.Core&version=3.2.1
                    
Install as a Cake Tool

CosmoApiServer

A .NET HTTP server built on System.IO.Pipelines and System.Net.Sockets with support for HTTP/1.1, HTTP/2, and HTTP/3 over QUIC. Includes a middleware pipeline, routing, frontend hosting, and real-time primitives.


Contents


Protocols

Protocol Support
HTTP/1.1 Keep-alive, pipelining
HTTP/2 h2c cleartext + ALPN over TLS
HTTP/3 QUIC via MsQuic (Windows) and Network.framework (macOS). Request/response trailers, streamed bodies, NDJSON streaming, QPACK, graceful GOAWAY.
TLS SslStream with ALPN (h2 / http/1.1)
var builder = CosmoWebApplicationBuilder.Create()
    .ListenOn(9092)
    .UseHttps("cert.pfx", "password")
    .UseHttp3();

Quick Start

# Minimal API server
var builder = CosmoWebApplicationBuilder.Create().ListenOn(9092);
var app = builder.Build();

app.MapGet("/health", ctx => {
    ctx.Response.WriteJson(new { status = "ok" });
    return ValueTask.CompletedTask;
});

app.Run();

Frontend Integration

All framework integrations share the same three-layer structure:

Layer Purpose Method
Dev server Spawn and manage the frontend process UseViteDevServer / UseAngularDevServer / UseNextDevServer
Dev proxy Forward framework module paths to the dev server UseViteDevProxy / UseReactDevProxy / UseNextDevProxy / UseAngularDevProxy
Production Serve the built output with compression + SPA fallback UseStaticFrontend (or framework-specific wrapper)

Nuxt / Vite Dev Mode

UseViteDevServer spawns the frontend dev process as a managed IHostedService, blocking server startup until it is ready. UseViteDevProxy forwards Vite module-graph paths to the dev server so the browser can use a single origin.

builder.UseViteDevServer(o =>
{
    o.WorkingDirectory = "frontend";
    o.Command          = "npm";
    o.Arguments        = "run dev";
    o.ReadyPattern     = "Local:";
    o.ReadyTimeout     = TimeSpan.FromSeconds(45);
    o.LogPrefix        = "[nuxt]";
});

builder.UseViteDevProxy(o =>
{
    o.DevServerUrl    = "http://127.0.0.1:3000";
    o.ProxiedPrefixes = ["/@vite", "/@fs", "/@id", "/_nuxt", "/__nuxt", "/__vite_ping"];
});

The ViteDevServerService shuts down the child process tree (Kill(entireProcessTree: true)) when the host stops, freeing the dev port cleanly.

Nuxt Integrated Production

Serves a pre-built Nuxt SPA from .output/public with tiered cache headers, Brotli/GZip compression, and SPA fallback.

builder.UseNuxtIntegrated(
    outputPath: "frontend/.output/public",
    configureFallback: o => o.ExcludedPrefixes = ["/api", "/health"]);

Vue / Vite Production

builder.UseViteFrontend(o =>
{
    o.HtmlTemplatePath = "frontend/index.html";
    o.ManifestPath     = "wwwroot/.vite/manifest.json";
    o.ExcludedPrefixes = ["/api", "/health"];
});

// With server-provided initial state
builder.UseViteFrontend(o =>
{
    o.RenderAsync = ctx => ValueTask.FromResult<ViteRenderResult?>(new ViteRenderResult
    {
        HeadHtml     = "<title>Dashboard</title>",
        InitialState = new { user = "alice", route = ctx.HttpContext.Request.Path }
    });
});

Content Security Policy

Generates a per-request nonce and injects it into inline scripts emitted by ViteFrontendMiddleware.

builder.UseCsp(o =>
{
    o.DefaultSrc = ["'self'"];
    o.ScriptSrc  = ["'self'", "'nonce-{nonce}'"];
    o.StyleSrc   = ["'self'", "'unsafe-inline'"];
    o.ConnectSrc = ["'self'"];
    o.ImgSrc     = ["'self'", "data:"];
});

React + Vite

Dev mode — Vite runs on port 5173 by default:

builder.UseViteDevServer(o =>
{
    o.WorkingDirectory = "frontend";
    o.Command          = "npm";
    o.Arguments        = "run dev";
    o.ReadyPattern     = "Local:";
    o.LogPrefix        = "[react]";
});
builder.UseReactDevProxy();  // proxies /@vite, /@fs, /@id, /@react-refresh
builder.UseViteFrontend(o =>
{
    o.EntryName        = "src/main.tsx";
    o.DevServerUrl     = "http://127.0.0.1:5173";
    o.ExcludedPrefixes = ["/api", "/health"];
});

Productionvite build outputs to dist/ with a manifest:

builder.UseViteFrontend(o =>
{
    o.ManifestPath     = "frontend/dist/.vite/manifest.json";
    o.HtmlTemplatePath = "frontend/index.html";
    o.EntryName        = "src/main.tsx";
    o.ExcludedPrefixes = ["/api", "/health"];
});

Angular

Dev mode — Angular CLI doesn't use Vite, so all traffic is reverse-proxied:

builder.UseAngularDevServer(o =>
{
    o.WorkingDirectory = "frontend";
    // defaults: npx ng serve --host 127.0.0.1, ready pattern "Application bundle generation complete"
});
builder.UseAngularDevProxy();  // reverse-proxies / → http://127.0.0.1:4200

Productionng build outputs to dist/<project>/browser/:

builder.UseAngularFrontend(
    outputPath: "frontend/dist/my-app/browser",
    configureFallback: o => o.ExcludedPrefixes = ["/api", "/health"]);

Next.js

Dev mode — Next.js uses its own HMR paths:

builder.UseNextDevServer(o =>
{
    o.WorkingDirectory = "frontend";
    // defaults: npm run dev, ready pattern "Ready"
});
builder.UseNextDevProxy();  // proxies /__next, /_next, /webpack-hmr
builder.UseViteFrontend(o =>
{
    o.DevServerUrl     = "http://127.0.0.1:3000";
    o.ExcludedPrefixes = ["/api", "/health"];
});

Production (static export) — add output: 'export' to next.config.js, then next build outputs to out/:

builder.UseNextStaticExport(
    outputPath: "frontend/out",
    configureFallback: o => o.ExcludedPrefixes = ["/api", "/health"]);

Production (SSR) — proxy all non-API traffic to next start:

builder.UseReverseProxy(o =>
    o.Routes.Add(new ProxyRoute
    {
        PathPrefix       = "/",
        Destination      = "http://127.0.0.1:3000",
        ExcludedPrefixes = ["/api", "/health"]
    }));

SvelteKit

Dev mode — SvelteKit uses Vite:

builder.UseViteDevServer(o =>
{
    o.WorkingDirectory = "frontend";
    o.Command          = "npm";
    o.Arguments        = "run dev";
    o.ReadyPattern     = "Local:";
    o.LogPrefix        = "[svelte]";
});
builder.UseViteDevProxy(o =>
{
    o.DevServerUrl    = "http://127.0.0.1:5173";
    o.ProxiedPrefixes = ["/@vite", "/@fs", "/@id", "/@svelte-kit"];
});

Production (adapter-static)vite build outputs to build/:

builder.UseSvelteKitStatic(
    outputPath: "frontend/build",
    configureFallback: o => o.ExcludedPrefixes = ["/api", "/health"]);

Production (adapter-node) — proxy to the Node server:

builder.UseReverseProxy(o =>
    o.Routes.Add(new ProxyRoute
    {
        PathPrefix       = "/",
        Destination      = "http://127.0.0.1:3000",
        ExcludedPrefixes = ["/api", "/health"]
    }));

Dev proxy paths by framework

Framework Dev server default port Paths to proxy
Nuxt 3000 /@vite /@fs /@id /_nuxt /__nuxt /__vite_ping
React + Vite 5173 /@vite /@fs /@id /@react-refresh
SvelteKit 5173 /@vite /@fs /@id /@svelte-kit
Next.js 3000 /__next /_next /__nextjs_original-stack-frame /webpack-hmr
Angular 4200 Full reverse proxy (no Vite module graph)

Reverse Proxy

builder.UseReverseProxy(o =>
    o.Routes.Add(new ProxyRoute
    {
        PathPrefix       = "/",
        Destination      = "http://127.0.0.1:3000",
        ExcludedPrefixes = ["/api", "/health"]
    }));

Real-time

Server-Sent Events

MapSse sets text/event-stream headers and calls BeginSseAsync automatically.

app.MapSse("/api/live/metrics", async ctx =>
{
    using var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
    while (!ctx.RequestAborted.IsCancellationRequested &&
           await timer.WaitForNextTickAsync(ctx.RequestAborted))
    {
        var json = JsonSerializer.Serialize(new { cpu = 42.1, memory = 68.3 });
        await ctx.Response.WriteSseAsync(json, eventName: "metric",
            cancellationToken: ctx.RequestAborted);
    }
});

Heartbeat helper prevents proxies from closing idle connections:

var heartbeat = ctx.Response.SendSseHeartbeatsAsync(TimeSpan.FromSeconds(15), ctx.RequestAborted);
// ... event loop ...
await heartbeat;

SignalR

var builder = CosmoWebApplicationBuilder.Create().AddSignalR();
var app = builder.Build();
app.MapHub<ChatHub>("/chat");

public sealed class ChatHub : Hub
{
    public async Task SendMessage(string user, string message) =>
        await Clients.All.SendAsync("ReceiveMessage", user, message);
}

Supports JSON and MessagePack protocols, groups, server-push via IHubContext<T>, server streaming, cancellation, and reconnect-after-restart. Compatible with standard ASP.NET SignalR clients.

gRPC

var builder = CosmoWebApplicationBuilder.Create().AddGrpc();
var app = builder.Build();
app.MapGrpcService<GreeterService>();

public sealed class GreeterService : GrpcServiceBase, IGrpcServiceDescriptor
{
    public IReadOnlyList<GrpcMethodDescriptor> Methods =>
    [
        new GrpcMethodDescriptor("Greeter", "SayHello", GrpcMethodType.Unary, typeof(GreeterService),
            async (svc, ctx, ct) =>
            {
                var req = await ctx.ReadRequestAsync<HelloRequest>(ct);
                await ctx.WriteResponseAsync(new HelloReply { Message = $"Hello {req.Name}" }, ct);
            })
    ];
}

Supports unary and server-streaming. Uses standard 5-byte gRPC framing.


Security

JWT / OAuth / OIDC

builder
    .UseJwtAuthentication()
    .UseOAuthAuthentication()   // JWKS discovery
    .AddAuthorization();

Antiforgery

builder.AddAntiforgery();
// ...
app.UseAntiforgery();

app.MapGet("/form", ctx =>
{
    var svc = ctx.RequestServices.GetRequiredService<IAntiforgeryService>();
    var tokens = svc.GetAndStoreTokens(ctx);
    return TypedResults.Text($"<input name='__RequestVerificationToken' value='{tokens.RequestToken}' />");
});

Rate Limiting

Fixed-window per-IP limiter with X-Forwarded-For support:

builder.UseRateLimiting(opts =>
{
    opts.Limit      = 200;
    opts.Window     = TimeSpan.FromMinutes(1);
    opts.StatusCode = 429;
    opts.TrustProxy = true;
});

Middleware & Pipeline

Request
  ↓ GlobalExceptionHandlerMiddleware
  ↓ CorsMiddleware
  ↓ CspMiddleware
  ↓ ViteDevProxyMiddleware   (dev)
  ↓ ViteFrontendMiddleware   (dev) / NuxtIntegratedMiddleware (prod)
  ↓ RouterMiddleware

Registration follows a builder pattern:

var builder = CosmoWebApplicationBuilder.Create()
    .ListenOn(9092)
    .UseLogging()
    .UseExceptionHandler()
    .UseCors(o => { o.AllowAnyOrigin(); o.AllowAnyMethod(); o.AllowAnyHeader(); })
    .UseSession(new SessionOptions { IdleTimeout = TimeSpan.FromMinutes(20) })
    .UseRequestTimeouts(new RequestTimeoutOptions { DefaultTimeout = TimeSpan.FromSeconds(30) })
    .UseRateLimiting(opts => { opts.Limit = 100; opts.Window = TimeSpan.FromMinutes(1); })
    .UseForwardedHeaders()
    .AddOutputCache()
    .AddHealthChecks();

Routing

app.MapGet("/items/{id}", ctx => { ... });
app.MapPost("/items", ctx => { ... });

// Typed results
app.MapGet("/items/{id}", ctx =>
    id is null ? TypedResults.NotFound() : TypedResults.Ok(new { id, name = "Widget" }));

Exception Handling

builder
    .AddExceptionHandler<ValidationExceptionHandler>()
    .AddExceptionHandler<DatabaseExceptionHandler>();

public sealed class ValidationExceptionHandler : IExceptionHandler
{
    public async ValueTask<bool> TryHandleAsync(HttpContext ctx, Exception ex, CancellationToken ct)
    {
        if (ex is not ValidationException vex) return false;
        ctx.Response.StatusCode = 422;
        await ctx.Response.WriteJsonAsync(new { errors = vex.Errors }, ct);
        return true;
    }
}

Scheduling

builder.AddScheduler();
// ...
app.UseScheduler(scheduler =>
{
    scheduler.Schedule(() => Console.WriteLine("tick")).EveryMinute();
    scheduler.ScheduleAsync(async () => await SyncInvoices()).Cron("0 */1 * * *");
    scheduler.Schedule<CleanupJob>().DailyAt(2, 30);
});

Caching

Output Cache

builder.AddOutputCache();
// ...
app.UseOutputCaching();

var policy = OutputCachePolicy.Build()
    .Expire(TimeSpan.FromMinutes(5))
    .VaryByQuery("page", "sort")
    .Tag("products")
    .ToPolicy();

app.MapGet("/products", async ctx =>
{
    ctx.SetOutputCachePolicy(policy);
    await ctx.Response.WriteJsonAsync(GetProducts());
});

// Tag-based invalidation
var store = ctx.RequestServices.GetRequiredService<IOutputCacheStore>();
await store.EvictByTagAsync("products");

Response Cache (ETag / 304)

builder.UseResponseCaching();
// Handlers set ctx.Response.ETag; returns 304 when If-None-Match matches.

Memory & Distributed Cache

builder
    .AddMemoryCache()
    .AddDistributedMemoryCache();

Diagnostics & Observability

Health Checks

builder.AddHealthChecks()
    .AddCheck("db", () => HealthCheckResult.Healthy("Connected"))
    .AddCheck<MyDbHealthCheck>("database");

app.MapHealthChecks("/health");

Problem Details (RFC 7807)

builder.AddProblemDetails();

app.MapGet("/items/{id}", async ctx =>
{
    var svc = ctx.RequestServices.GetRequiredService<IProblemDetailsService>();
    await svc.WriteAsync(new ProblemDetailsContext
    {
        HttpContext    = ctx,
        ProblemDetails = new ProblemDetails { Status = 404, Title = "Not Found" }
    });
});

Distributed Tracing

W3C traceparent propagation, OpenTelemetry-compatible ActivitySource:

builder.UseTracing("MyService");

Razor Components

Full .razor support with @page, [Parameter], [CascadingParameter], @inject, @bind, EventCallback, and validation components.

@page "/hello/{Name}"
@inherits Microsoft.AspNetCore.Components.ComponentBase
@inject NavigationManager Nav

<h1>Hello, @Name!</h1>
<p>Path: @Nav.Uri</p>

@code {
    [Parameter] public string Name { get; set; }
}

Forms

<EditForm Model="@person" Action="/contact" Method="post">
    <InputText     Name="name"    Value="@person.Name" />
    <InputNumber   Name="age"     Value="@person.Age" />
    <InputSelect   Name="country" Value="@person.Country">
        <option value="us">United States</option>
    </InputSelect>
    <ValidationMessage For="Name" />
    <ValidationSummary />
    <button type="submit">Submit</button>
</EditForm>

Change Detection

var ctx = new EditContext(model);
model.Name = "Bob";
ctx.NotifyFieldChanged("Name");

ctx.IsModified("Name");      // true
ctx.GetModifiedFields();     // ["Name"]
ctx.FieldCssClass("Name");   // "modified valid"
ctx.MarkAsUnmodified();

Samples

Sample Description
samples/HelloWorldSample Minimal server with a single route
samples/CosmoKitchenSink Covers most backend features in one project
samples/FeatureShowcase Auth, SignalR, gRPC, output cache, and more
samples/WeatherApp REST API with JWT auth, DI, streaming, and SQL
samples/NuxtUiSample Nuxt 4 + Nuxt UI frontend backed by Cosmo APIs
samples/LiveOpsSample Real-time dashboard: SSE, CSP, Vite dev server, Nuxt integrated deployment
samples/CosmoBlazorSample SSR with Razor components

LiveOpsSample

cd samples/LiveOpsSample
npm run frontend:install
npm run dev

Benchmarks:

npm run benchmark          # API-only latency (no Nuxt)
npm run benchmark:nuxt     # Cosmo integrated vs Nuxt Nitro standalone

NuxtUiSample

cd samples/NuxtUiSample
npm run dev

Benchmarks

Single-client sequential HTTP/1.1, connection reused across rounds. 100 warmup + 1000 measured per scenario. All times in milliseconds.

macOS arm64

Scenario CosmoApiServer ASP.NET Core
GET /ping P50 0.12ms · 8,600 ops/s P50 0.14ms · 7,300 ops/s
GET /json P50 0.13ms · 8,400 ops/s P50 0.18ms · 5,600 ops/s
GET /large-json P50 0.41ms · 2,400 ops/s P50 0.58ms · 1,700 ops/s
POST /echo P50 0.15ms · 6,500 ops/s P50 0.20ms · 5,000 ops/s
GET /stream P50 0.22ms · 4,500 ops/s P50 0.31ms · 3,200 ops/s

Note: these are single-threaded sequential measurements. Concurrent throughput will differ.

LiveOpsSample API (production mode)

Scenario P50 P99 ops/sec
GET /ping 0.12ms 0.27ms 8,643
GET /health 0.13ms 0.27ms 7,994
GET /api/status 0.12ms 0.27ms 8,432

JSON serialisation of 5 service records (/api/status) adds no measurable latency over a plain text response.


Deployment

HTTP/3 in production

Add UseHttp3() with a valid certificate:

var builder = CosmoWebApplicationBuilder.Create()
    .ListenOn(9092)
    .UseHttps("cert.pfx", "password")
    .UseHttp3();

When running Nuxt through UseNuxtIntegrated, all assets — HTML, JS, CSS — are served by Cosmo over the same QUIC connection. SSE streams benefit from QUIC's per-stream flow control, which avoids head-of-line blocking between concurrent requests.

Cloudflare Pages / Workers

Nuxt can be deployed to Cloudflare using the cloudflare_pages preset:

npx nuxi build --preset=cloudflare_pages

This gives HTTP/3 at the Cloudflare edge automatically. However, there are trade-offs relevant to this architecture:

Cloudflare Cosmo + UseHttp3()
HTTP/3 on browser leg Yes (edge) Yes (direct)
SSE streams Fragile — 100s connection timeout on free plan Native
API colocation No — /api/* still needs an origin server Yes
Cloudflare → origin leg HTTP/2 at best N/A

Cloudflare is suitable for frontends that are mostly static or read-heavy without persistent connections. For SSE-heavy or API-coupled deployments, UseHttp3() on Cosmo keeps the entire stack on one connection and one protocol.


Changelog

v3.2.0

  • Frontend integration for React + Vite, Angular, Next.js, and SvelteKit
  • UseStaticFrontend(outputPath) — generic base for all static SPA deployments
  • UseAngularFrontend, UseNextStaticExport, UseSvelteKitStatic — framework-specific wrappers
  • UseReactDevProxy, UseNextDevProxy, UseAngularDevProxy — pre-configured dev proxies per framework
  • UseAngularDevServer, UseNextDevServer — pre-configured dev server process management
  • LiveOpsSample: benchmark scripts (run-benchmark.sh, run-nuxt-benchmark.sh)
  • Documentation rewrite — structured, framework-agnostic

v3.1.0

  • Server-Sent Events, Content Security Policy, Vite Dev Proxy, Vite Dev Server, Nuxt Integrated, Reverse Proxy
  • samples/LiveOpsSample demonstrating all six features
  • Bug fixes: CORS origin spoofing guard, CSRF path bypass, Retry-After off-by-one, ForwardedHeaders trusted-proxy gate, AntiforgeryMiddleware Content-Type guard, SPA fallback cache-control
  • 373 tests

v3.0.3

  • Vue frontend hosting via ViteFrontendMiddleware
  • samples/NuxtUiSample

v3.0.2

  • HtmlEditorComponent for Razor slices

v3.0.1

  • Razor: InputDate, InputRadioGroup, InputRadio, InputFile, RenderTreeBuilder stubs

v3.0.0

  • HTTP/3 production-ready
  • Rate Limiting
  • H3Interop validation tool (32/32 scenarios)
  • Windows benchmark scripts
  • 313 tests

v2.1.0 — v2.1.4

  • SignalR (JSON + MessagePack), gRPC, Sessions, Request Timeouts, Response Caching, Forwarded Headers, Request Decompression, Distributed Tracing, Endpoint Filters, IHttpContextAccessor
  • Output Caching, Antiforgery, TypedResults, IExceptionHandler, IHostedService, WebSockets

v2.0.5 — v2.0.7

  • Health Checks, Problem Details, Policy-Based Authorization, OAuth/OIDC, Memory Cache, Distributed Cache, IHttpClientFactory
  • Stream flush coalescing, HTTP/3 QPACK, trailers, GOAWAY

Credits

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 (1)

Showing the top 1 NuGet packages that depend on CosmoApiServer.Core:

Package Downloads
CosmoS3

Amazon S3-compatible object storage server built on CosmoApiServer.Core.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.2.3 24 4/12/2026
3.2.1 29 4/12/2026
3.2.0 24 4/12/2026
3.1.0 31 4/12/2026
3.0.3 32 4/11/2026
3.0.2 39 4/10/2026
3.0.1 78 4/9/2026
3.0.0 217 4/4/2026
2.1.4 89 4/3/2026
2.1.3 85 4/3/2026
2.1.2 79 4/3/2026
2.1.1 81 4/3/2026
2.1.0 81 4/3/2026
2.0.9 82 4/3/2026
2.0.8 80 4/3/2026
2.0.7 90 4/3/2026
2.0.6 90 4/2/2026
2.0.5 114 4/1/2026
2.0.4 81 4/1/2026
2.0.3 101 3/31/2026
Loading failed