Running and hosting the Explorer
This page documents the Orleans.Lattice.Explorer packages, which are in progress, in the documentation for Orleans.Lattice 9.9.0 (release line 9.9), built 2026-10-04. It is also published as markdown, with every table and list, at running-the-explorer.md, and llms.txt lists every page.The Orleans.Lattice Explorer is an auth-aware Blazor Server console for a running cluster. It connects to the cluster over the Lattice gRPC facades rather than joining Orleans membership, so it can run as its own head or be embedded in another ASP.NET Core app. The native areas are compiled into the UI and decide whether to show themselves by probing the facades available at the configured endpoint.
Two ways to run it
The supported web head is the embeddable hosting library
Orleans.Lattice.Explorer.Web. Both the standalone process and an embedded host
use the same two extension methods:
AddLatticeExplorerWeb(...)registers interactive Razor components, the Explorer UI, the connection and configuration services, the cookie-backed credential store, the app frame host, the native areas, the launcher environment bootstrap, and, in Development, the circuit-fault logging rule described under Diagnosing a console that stops responding.MapLatticeExplorer()maps the Explorer under the configured base path: static assets, theauth/loginandauth/logoutserver form-post endpoints, the Lattice App frame route, and the interactive Razor components.
The standalone Orleans.Lattice.Explorer.WebHost program is those calls plus the
standard ASP.NET exception, HSTS, HTTPS redirection and antiforgery middleware.
To embed the console, reference Orleans.Lattice.Explorer.Web, call
AddLatticeExplorerWeb during service registration, call UseAntiforgery, and
then call MapLatticeExplorer on the application.
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Orleans.Lattice.Explorer.Web;
var builder = WebApplication.CreateBuilder();
builder.Services.AddLatticeExplorerWeb(options =>
{
options.BasePath = "/explorer";
});
var app = builder.Build();
app.UseAntiforgery();
app.MapLatticeExplorer();
Package shape
The Explorer is four packages and one standalone host:
Orleans.Lattice.Explorer.Web- the ASP.NET Core hosting library withAddLatticeExplorerWebandMapLatticeExplorer, and the one package a host references; the other three restore with it.Orleans.Lattice.Explorer.Core- connection, configuration, authentication, tenant and session services shared by heads.Orleans.Lattice.Explorer.UI- the Razor UI package. Its static web assets are served from_content/Orleans.Lattice.Explorer.UI/.Orleans.Lattice.Explorer.AppKit- the static app-frame kit served by the app frame route for Lattice Apps.Orleans.Lattice.Explorer.WebHost- the standalone executable head, built fromsrc/lattice.explorer/Web. It is a project in this repository, not a NuGet package.
The optional Entra sign-in providers ship as their own packages:
Orleans.Lattice.Explorer.Entra and
Orleans.Lattice.Explorer.Entra.Web.
The native areas live in the UI package. There is no public area registration API; third-party user interfaces are added as Lattice Apps.
Configuration
AddLatticeExplorerWeb accepts LatticeExplorerWebOptions. The options most
commonly set by a host are:
BasePath- the mount point for the console. It defaults to/and is normalised to a single leading slash with no trailing slash.ConfigFilePath- an explicit JSON configuration document path. When unset, the web head usesLATTICE_EXPLORER_CONFIG, then the per-user local app-data default.UseEnvironmentBootstrap- whentrue(the default), the head seeds the first-run endpoint fromLATTICE_EXPLORER_ENDPOINTand related environment variables when no configuration has been persisted.AllowEnvironmentCredentialSeed- whenfalse(the default), the web head refuses to applyLATTICE_EXPLORER_USERNAMEandLATTICE_EXPLORER_PASSWORDto browser circuits. Enable it only for a single-operator deployment.AllowInteractiveEndpointConfiguration- whenfalse(the default), the browser cannot edit, test or save the process-wide endpoint configuration. Pre-provision the JSON document or use the environment bootstrap instead. See The connection dialog.- The
DataProtection*properties - optional shared ASP.NET Data Protection key ring configuration for multi-replica hosted-web sign-in.
See Configuration for the complete option table, launcher environment variables, persisted document schema, and connection settings.
The connection dialog
AllowInteractiveEndpointConfiguration decides what the browser may do with the
cluster endpoint, because the connection dialog's Test connection dials, from
the head, whatever address the visitor types. On a head anyone can reach, that
would be a host and port probe into the head's own network.
- Not opted in (the default). The header's connection indicator has no
Connection settings entry, and the connection dialog is read-only, titled
Cluster connection: it shows the configured endpoint and says it is set by
the deployment and cannot be changed from the browser. With no endpoint
configured, including on first run, it explains that the deployment sets one
through
LATTICE_EXPLORER_ENDPOINTor a pre-provisioned configuration document. There is no form, no test and no save, and the head refuses a connection test even if one is asked for. - Opted in. Connection settings opens the editable Connect to a cluster dialog: the endpoint, Insecure loopback development mode, Allow unencrypted HTTP/2 (h2c), Test connection and Save and connect. On first run it has no Cancel or Close.
A connection test reports one of three outcomes, in fixed words, never the endpoint's own status text or an exception message, which would describe whatever answered at an address the visitor chose:
| Outcome | Hint |
|---|---|
| Reachable | None. |
| Reachable - sign-in required | The endpoint answered and asks for a sign-in, which you can do after saving. |
| Unreachable | No Lattice API answered at this address. Check the endpoint and its transport settings. |
The probe is always anonymous: the circuit's credential and the configuration's metadata headers are never sent to an unconfirmed address, and an authentication refusal still counts as reachable. The configuration's transport headers, such as an origin-lock header for a fronting proxy, are sent only when the test targets the endpoint the head is configured for, and are dropped for any other. A probe that does not answer within 15 seconds is Unreachable.
Mounting under a subpath
Set BasePath to mount the console under a subpath such as /explorer. The web
head branches the ASP.NET pipeline at that prefix. Inside the branch the prefix
is moved into PathBase, so the Razor components keep their root-relative page
routes and the Explorer endpoints do not collide with routes owned by the host
application.
The proxy in front of the app must preserve the prefix for BasePath to apply.
If the proxy strips the prefix before forwarding, leave BasePath at / and let
the proxy own the public path.
Lattice App frame route
Lattice Apps run inside the Explorer's app frame. The frame bootstrap document is served by the Explorer head at:
{BasePath}/_apps/frame/v1/frame.html
The route serves the AppKit static assets from
_content/Orleans.Lattice.Explorer.AppKit/appkit/v1. The bootstrap document has
its own sandboxed Content-Security-Policy with frame-ancestors 'self'. The web
head sends X-Frame-Options: DENY on every response, this route included, and
only the route's own endpoint lifts it, for a file it actually serves. The
exemption is not a path match, so a co-hosted route or fallback that answers
under the same path keeps the anti-framing header, as do every Explorer page,
asset and SignalR endpoint.
Deployment: prefer an isolated head
The Explorer web head is a Blazor Server application. A connected browser owns a stateful SignalR circuit, so a multi-instance deployment needs session affinity for Explorer traffic.
Run the Explorer as an isolated head where possible: its own process or
deployment, pointed at the cluster's gRPC endpoint. That scopes sticky routing to
the low-traffic admin console and avoids imposing affinity on the cluster's own
front door. If you co-host the Explorer in another app, scope any affinity rule
to the Explorer BasePath rather than to unrelated traffic.
Static web assets in a thin host
An isolated head can be a thin project with no Razor files of its own. The
Explorer packages provide the Razor components and static assets, which are
served automatically by a published host and under the Development environment.
When you run from build output (for example with dotnet run) under a
non-Development environment, call builder.WebHost.UseStaticWebAssets() so those
assets are mapped and the console is styled. The Explorer sample host does
exactly that.
If a container build restores and publishes in separate stages, make sure the
publish stage restores with the full host source available, or do not use
--no-restore. To check the Blazor client asset, request:
{BasePath}/_framework/blazor.web.js
A 200 response with a non-empty body means the interactive circuit script was
published.
SignalR receive size
The app frame bridge sends each frame request to .NET as one JavaScript interop
message. The web head raises Blazor Server's HubOptions.MaximumReceiveMessageSize
to at least 160 KiB so those messages are not refused by the default 32 KiB
limit.
Security response headers
MapLatticeExplorer installs response-header middleware for every Explorer
response. At the root mount the middleware is added to the application pipeline;
under a subpath it is the first middleware inside the isolated branch.
| Header | Value | Notes |
|---|---|---|
Content-Security-Policy |
default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; frame-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self' |
script-src is 'self' alone: the console serves no inline script, so an injected inline script or on* handler does not run. style-src keeps 'unsafe-inline' for the inline style attributes interactive components emit. Providers can add extra form-action sources through ExplorerContentSecurityPolicyOptions. |
X-Frame-Options |
DENY |
Emitted on every Explorer response. Only the app-frame route's endpoint removes it, and only for a file it serves; see Lattice App frame route. |
X-Content-Type-Options |
nosniff |
Prevents MIME sniffing. |
Referrer-Policy |
no-referrer |
Avoids leaking tree, key, tenant or subject context in a referrer. |
Permissions-Policy |
camera=(), microphone=(), geolocation=(), interest-cohort=() |
Disables browser features the console does not use. |
The middleware sets a header only when it is absent already. A host that maps the Explorer on an endpoint builder that is not also the ASP.NET middleware pipeline is refused at startup, because the console would otherwise be served without its security headers.
The app-frame route has its own headers. Files on /_apps/frame/v1/ carry
X-Content-Type-Options: nosniff, Referrer-Policy: no-referrer,
Cross-Origin-Resource-Policy: cross-origin, Access-Control-Allow-Origin: *,
and an immutable cache lifetime. frame.html also carries the sandboxed app
frame CSP.
Diagnosing a console that stops responding
When Blazor Server meets an unhandled exception in a circuit, it terminates the
circuit: the console stays on screen but no longer answers, and the browser shows
only that an unhandled exception occurred on the current circuit. The framework
logs that termination at Error under the
Microsoft.AspNetCore.Components.Server.Circuits category, with the exception
and its stack trace.
A host whose logging filters silence that category (for example
"Microsoft": "None") would lose the record, so in Development the web head
keeps it: AddLatticeExplorerWeb adds a logging filter rule that shows the
Microsoft.AspNetCore.Components.Server.Circuits category at Error or above.
A host that already names that category at Error or below keeps its own rule,
and outside Development nothing is added, so production logging stays the host's
decision. The rule logs nothing new: the record is the framework's own, carrying
the exception and the circuit id, and no user input or values.
To also send the fault's detail to the browser while developing, set Blazor
Server's CircuitOptions.DetailedErrors to true in Development; the web head
does not change it.
Sign-in endpoints
The web head maps two local form-post endpoints below the Explorer mount:
POST auth/loginsigns in with the submitted Basic username and password.POST auth/logoutclears the local State API credential.
Both endpoints validate antiforgery tokens and redirect back to the Explorer base
href. They are mapped by the public AuthEndpoints.MapExplorerAuthEndpoints
extension, which MapLatticeExplorer calls with the base href as the redirect
target. A federated provider such as the hosted-web Entra package can publish a
separate sign-out path so the identity menu posts there instead and ends the
browser identity-provider session as well.