esegece.sgcHTML.AspNetCore.Community
2026.10.0
dotnet add package esegece.sgcHTML.AspNetCore.Community --version 2026.10.0
NuGet\Install-Package esegece.sgcHTML.AspNetCore.Community -Version 2026.10.0
<PackageReference Include="esegece.sgcHTML.AspNetCore.Community" Version="2026.10.0" />
<PackageVersion Include="esegece.sgcHTML.AspNetCore.Community" Version="2026.10.0" />
<PackageReference Include="esegece.sgcHTML.AspNetCore.Community" />
paket add esegece.sgcHTML.AspNetCore.Community --version 2026.10.0
#r "nuget: esegece.sgcHTML.AspNetCore.Community, 2026.10.0"
#:package esegece.sgcHTML.AspNetCore.Community@2026.10.0
#addin nuget:?package=esegece.sgcHTML.AspNetCore.Community&version=2026.10.0
#tool nuget:?package=esegece.sgcHTML.AspNetCore.Community&version=2026.10.0
esegece.sgcHTML.AspNetCore
sgcHTML for ASP.NET Core: the server-driven HTML / htmx UI components of sgcHTML, hosted on Kestrel. Two calls host an sgcHTML page, its embedded client assets (htmx, the htmx WebSocket extension, the sgcHTMX bridge, Bootstrap and Chart.js), the PWA manifest / service worker, and the htmx WebSocket channel, all on native Kestrel.
Self-contained. The package is one assembly, esegece.sgcHTML.AspNetCore.dll.
It compiles the sgcHTML components, the ASP.NET Core adapter and the small part
of the sgcWebSockets core they need (JSON, helpers, the WebAuthn relying party
and its crypto) into that single file. It has no NuGet dependency at all, only
the Microsoft.AspNetCore.App shared framework. Target frameworks: net8.0,
net9.0.
Install
Two package ids, the same code:
| Package | Edition |
|---|---|
esegece.sgcHTML.AspNetCore |
Registered (purchased). No notice, no limits, sources included with the purchase. |
esegece.sgcHTML.AspNetCore.Community |
Community (free). Full feature set, a one-time startup notice and a discreet Community Edition notice on every generated page. |
dotnet add package esegece.sgcHTML.AspNetCore
or, for the free edition:
dotnet add package esegece.sgcHTML.AspNetCore.Community
Reference exactly one of the two.
Do not combine with esegece.sgcWebSockets or esegece.sgcHTML
This package defines the same public types as esegece.sgcHTML (and the host
types it needs with the same names as esegece.sgcWebSockets), so a project
that references both would fail with CS0433. The package therefore ships a
build check (error SGCHTML001) that stops the build when
esegece.sgcWebSockets.dll or esegece.sgcHTML.dll is also referenced. Use
one of these two setups, never both:
esegece.sgcHTML.AspNetCore(or.Community) alone, for sgcHTML on ASP.NET Core.esegece.sgcWebSocketsplusesegece.sgcHTML, for sgcHTML on the sgcWebSockets servers together with the rest of the sgcWebSockets library.
Namespaces
The types live in the namespaces esegece.sgcWebSockets (components, engine, router,
AI assistant, passkeys) and esegece.sgcWebSockets.AspNetCore (AddSgcHtml /
UseSgcHtml). That is only a name. This package does not use or ship the sgcWebSockets
library, it is one self-contained assembly with no NuGet dependencies. The names match
sgcHTML on the sgcWebSockets servers, so the same page and component code compiles
unchanged on either host.
Minimal usage
using esegece.sgcWebSockets; // TsgcHTMX_Engine_Server, TsgcHTMX_Router, TsgcHTMXRequest
using esegece.sgcWebSockets.AspNetCore; // AddSgcHtml / UseSgcHtml
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSgcHtml(o =>
{
o.RootPath = "/";
o.WebSocketPath = "/ws";
o.ConfigureEngine = engine =>
{
// the page shell served at RootPath
engine.Template.Title = "sgcHTML on ASP.NET Core";
engine.Template.BodyContent =
"<div class=\"container py-4\">" +
"<h1>Hello from sgcHTML</h1>" +
"<div hx-ext=\"ws\" ws-connect=\"/ws\">" +
"<button class=\"btn btn-primary\" ws-send " +
"hx-vals='{\"path\":\"/hello\"}'>Ping</button>" +
"<div id=\"out\"></div></div></div>";
// an htmx WebSocket route: reply is pushed out-of-band to #out
var router = new TsgcHTMX_Router();
var route = router.Routes.Add();
route.Path = "/hello";
route.OnRoute += (object sender, TsgcHTMXRequest request, ref string response) =>
{
response = "<div id=\"out\" hx-swap-oob=\"true\">Pong</div>";
};
engine.Router = router;
};
});
var app = builder.Build();
app.UseWebSockets(); // REQUIRED before UseSgcHtml
app.UseSgcHtml();
app.Run();
What the middleware serves
| Request | Response |
|---|---|
GET RootPath (default /) |
engine.GetPageHTML() (the page shell) |
GET /htmx.min.js, /bootstrap.min.css, ... |
the embedded asset body |
GET ManifestPath (/manifest.json) |
engine.Template.GetManifestJSON() |
GET ServiceWorkerPath (/sw.js) |
engine.Template.GetServiceWorkerJS() |
WS upgrade at WebSocketPath (/ws) |
accepted; each text frame runs through the engine, the reply is pushed back over the socket |
with the SSE transport on: GET engine.SSE.Endpoint (/htmx-sse) with Accept: text/event-stream |
accepted as an event stream (see "SSE transport") |
with the SSE transport on: POST engine.SSE.PostEndpoint (/htmx-sse/send) |
the upstream message of a stream: 204, 403, 404 or 413 |
with Auth set: router routes, POST Auth.LoginPath and POST Auth.LogoutPath |
served through the engine dispatch (see "Sessions and CSRF") |
| anything else | passed to the next middleware |
SSE transport
The page can take its realtime updates over server-sent events instead of a
WebSocket. Turn it on in ConfigureEngine, either with SSE.Enabled = true or
by setting Template.Transport to htSSE (SSE only) or htAuto (WebSocket,
with the bridge falling back to SSE when the socket cannot be opened):
builder.Services.AddSgcHtml(o =>
{
o.ConfigureEngine = e =>
{
e.Template.Transport = TsgcHTMXTransport.htSSE;
e.SSE.Enabled = true;
};
});
UseSgcHtml then accepts the event stream on engine.SSE.Endpoint and the
upstream POST on engine.SSE.PostEndpoint, the two paths the rendered page
carries. A stream gets retry: 3000 and its connection guid as the first plain
message, : keepalive comments every SSE.KeepAlive seconds, and one event per
pushed fragment (event: <SSE.EventName>, id: <guid>:<n>, one data: line
per line). A stream is bound to the session of its cookies under the same
same-origin rule as a WebSocket handshake, and the last SSE.ReplaySize events
are kept for SSE.ReplayTTL seconds so a reconnect carrying Last-Event-ID
(or ?lastEventId=) gets what it missed, renumbered, from a stream of the same
session.
ISgcHtmlHub.PushFragmentAsync and BroadcastAsync reach both transports, so
the pushing code does not change with the transport. The upstream POST must name
its stream in the X-SGC-Connection header (or the sgc_connection field),
carry the CSRF token when the page is signed in, and stays under 1 MB; it
answers 204 (run), 403 (no token), 404 (unknown or foreign stream) or 413.
With the SSE transport off, nothing above is served and the middleware behaves exactly as before.
Assets + WebSocket only (multi-page apps)
By default UseSgcHtml serves the engine-generated page at RootPath. For a
multi-page app that renders its own pages but still wants the sgcHTML client
assets (htmx, Bootstrap, Chart.js), the PWA manifest / service worker and the
WebSocket push channel, set ServeRootPage = false. The middleware then serves
everything except the page: page requests fall through to next(), so your own
MapGet / MapPost routes own all page routing.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSgcHtml(o =>
{
o.ServeRootPage = false; // the app owns "/" and every other page route
});
var app = builder.Build();
app.UseWebSockets(); // REQUIRED before UseSgcHtml
app.UseSgcHtml(); // serves assets + manifest + sw + the /ws channel only
// the app renders its own pages
app.MapGet("/", (HttpContext ctx) =>
{
string html = /* build the page HTML yourself */ "<html>...</html>";
return Results.Content(html, "text/html");
});
app.Run();
Server-driven updates
The adapter runs the WebSockets itself (the engine has no TsgcWSHTTPServer
bound), so push updates through the DI-registered ISgcHtmlHub instead of the
engine's Server-bound PushFragment / BroadcastFragment:
public sealed class Ticker(ISgcHtmlHub hub) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await hub.BroadcastAsync(
$"<div id=\"clock\" hx-swap-oob=\"true\">{DateTime.UtcNow:T}</div>",
cancellationToken: stoppingToken);
await Task.Delay(1000, stoppingToken);
}
}
}
Sessions and CSRF
Set Auth to a configured TsgcHTMLAuth (or AuthFactory to build one from
the application services) and the adapter serves sgcHTML requests through the
engine's own dispatch, exactly as the self-hosted servers do:
- The page at
RootPath, the engine router routes and the built-inPOSTlogin and logout endpoints (Auth.LoginPath,Auth.LogoutPath) run with the session of the request. The login form postsusername,password,rememberandnext. The login page itself (aGETonLoginPath) is yours, as a router route or an app endpoint. - Routes with
RequireLoginorRequireRolessend anonymous visitors toAuth.LoginPath?next=...(302, or 401 withHX-Redirectfor htmx requests) and answer 403 when the session has none of the listed roles. - With
Auth.CSRFProtectionon (the default), a signed in request with any method other thanGET,HEADorOPTIONSmust carry the session token in theX-CSRF-Tokenheader or thecsrf_tokenform field, or it gets 403. The page renders thecsrf-tokenandcsrf-headermeta tags for that. - Every cookie is written as its own
Set-Cookieline, so a remember-me login and a logout reach the browser complete. - WebSocket messages on
WebSocketPathuse the session of the cookies sent with the upgrade request, but only when the upgrade has noOriginheader or its host matches theHostheader. A remember-me cookie is never redeemed there.
With Auth set, engine routes answer HTTP requests as well, so do not map the
same path in the app. Without Auth the adapter behaves exactly as described
above. The instance passed in Auth stays yours to dispose. One built by
AuthFactory is disposed with the service container.
var store = new TsgcHTMLSessionStore_Memory();
var auth = new TsgcHTMLAuth { SessionStore = store, LoginPath = "/login", AfterLoginPath = "/" };
auth.OnAuthenticate += (object sender, string user, string password, ref bool accept,
ref string userId, ref string displayName, List<string> roles) =>
{
// check the credentials against your user store here
accept = user == "admin" && password == "secret";
userId = user;
displayName = "Administrator";
roles.Add("admin");
};
builder.Services.AddSgcHtml(o =>
{
o.Auth = auth;
o.MapSessionToUser = true;
o.ConfigureEngine = engine => { /* template and router */ };
});
// only for [Authorize] and RequireAuthorization(): a challenge that sends
// anonymous users to the sgcHTML login page
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(o => { o.LoginPath = "/login"; o.ReturnUrlParameter = "next"; });
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseWebSockets();
app.UseAuthentication();
app.UseSgcHtml(); // after UseAuthentication, before UseAuthorization
app.UseAuthorization();
app.MapGet("/api/me", (ClaimsPrincipal user) => user.Identity!.Name).RequireAuthorization();
Signed in users on your own endpoints
With MapSessionToUser = true, every request that the middleware passes on to
your own endpoints and that carries a valid sgcHTML session gets
HttpContext.User set to a principal with the authentication type sgcHTML,
a ClaimTypes.NameIdentifier claim (the user id), a ClaimTypes.Name claim
(the display name) and one ClaimTypes.Role claim per role. [Authorize] and
RequireAuthorization() then work with the same login. A remember-me cookie is
redeemed there too, and its new cookies are added to your response.
The order of the middleware matters. Call UseSgcHtml() after
UseAuthentication() and before UseAuthorization(), and call
UseAuthorization() yourself. When WebApplication adds the authorization
middleware on its own, it places it at the start of the pipeline, ahead of
UseSgcHtml(), so the policy would still see an anonymous user. An anonymous
request to a protected endpoint needs a challenge scheme. The cookie scheme in
the sample only redirects to the sgcHTML login page. It never signs anyone in.
Bring your own LLM
The AI features (the AI assistant behind the Grid, DataTable, PivotTable, Form
and InlineAIPrompt components) do not ship a built-in LLM provider in this
package. Handle TsgcHTMLAIAssistant.OnAIRequest and call whatever model you
use. The handler receives the system prompt, the user prompt and the JSON schema
the answer has to follow, and returns the raw answer, which must be a single
JSON object with no prose and no code fences:
using esegece.sgcWebSockets; // TsgcHTMLAIAssistant
var ai = new TsgcHTMLAIAssistant();
ai.OnAIRequest += (object sender, string aSystemPrompt, string aPrompt,
string aJSONSchema, ref string aResponse) =>
{
// call your own LLM client here (OpenAI, Anthropic, Azure, a local model...)
aResponse = MyLlmClient.Complete(aSystemPrompt, aPrompt, aJSONSchema);
};
grid.AIAssistant = ai; // any component with an AIAssistant property
With no handler assigned the assistant uses its rule based parser.
Passkeys
Passkey sign-in (WebAuthn) is included: TsgcHTMLComponent_WebAuthnLogin and
the WebAuthn relying party it uses (registration, authentication, attestation
with X.509 chain verification and the FIDO metadata service) are compiled into
the package. The 02.AdminCRUD demo shows it end to end.
Not included
These parts of sgcHTML depend on the sgcWebSockets servers or on other
sgcWebSockets APIs, so they are available only with esegece.sgcWebSockets plus
esegece.sgcHTML:
- Web Push (
sgcHTML_WebPush). - MCP Apps (showing an sgcHTML page inside Claude, ChatGPT or VS Code).
- The admin console.
- The HTTP.sys hosts (
TsgcWSServer_HTTPAPIengines). This package hosts on Kestrel only. - The built-in LLM providers (
TsgcAIChatand friends). UseOnAIRequestinstead, see above.
Demos
The demos\61.HTML.AspNetCore suite hosts fifteen sgcHTML apps on Kestrel through
this adapter, mirroring the self-hosted demos\60.HTML set. Each is a
Microsoft.NET.Sdk.Web (net8.0) app that reuses the matching 60.HTML demo's
rendering / DB / auth code and rewrites only the host layer. The multi-route
apps register with AddSgcHtml(o => o.ServeRootPage = false) (see "Assets +
WebSocket only" above) so the app owns its routes, while the adapter still
serves the htmx / Bootstrap / Chart.js assets, the PWA manifest / service worker
and the /ws push hub.
| Folder | App | Port | What it shows |
|---|---|---|---|
07.Site |
sgcSiteWeb |
8092 | query-driven site layouts / themes |
06.Grid |
sgcGridWeb |
8093 | data-grid routes (sort / filter / group / paging / virtual-scroll / export) |
05.HTMX |
sgcHTMXWeb |
8094 | htmx patterns (OOB, search, wizard, inline edit, live validation, DELETE-row, infinite scroll, cascade) |
08.Components |
sgcComponentsWeb |
8095 | component showcase + WebSocket live push (ISgcHtmlHub) + XLSX / PDF export |
10.ShopAssistant |
sgcShopWeb |
8096 | storefront + in-memory session + AI chat (keyless fallback) |
02.AdminCRUD |
sgcAdminWeb |
8097 | admin CRUD: bcrypt auth + passkeys + cookie sessions + SQLite + i18n |
01.ERP |
sgcERPWeb |
8098 | ERP CRUD on the same auth stack |
04.Portal |
sgcPortalWeb |
8099 | role-gated dual area (customer + back-office) |
09.Helpdesk |
sgcHelpdeskWeb |
8100 | ticketing with multipart upload / download (IFormFile) + CSV export |
03.LiveMonitor |
sgcLiveMonitorWeb |
8101 | live dashboard: BackgroundService pushing KPI fragments over the hub |
13.Warehouse |
sgcWMSWeb |
8102 | warehouse management |
14.POS |
sgcPOSWeb |
8103 | point of sale |
15.Reports |
sgcReportsWeb |
8104 | reports |
16.SaaS |
sgcSaaSWeb |
8105 | multi-tenant SaaS app |
17.FieldService |
sgcFieldWeb |
8106 | field service |
Run any demo (it listens on its appsettings.json port), or open the solution
demos\61.HTML.AspNetCore\61.HTML.AspNetCore.sln:
dotnet run --project demos\61.HTML.AspNetCore\08.Components
| Product | Versions 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 is compatible. 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. |
-
net8.0
- No dependencies.
-
net9.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.
| Version | Downloads | Last Updated |
|---|---|---|
| 2026.10.0 | 77 | 9/25/2026 |