You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Enables the Aspire Dashboard to publish and run as a standalone Native AOT executable, with the Fluent UI and runtime changes needed to support it. The Dashboard becomes a dedicated bundle component containing its executable and static assets, rather than running through the aspire-managed dashboard subcommand.
Existing aspire dashboard run, AppHost startup, and CLI profile-capture workflows select the appropriate Dashboard automatically. No new user-facing command-line options are required.
Dashboard runtime and UI
Update Fluent UI integration across grids, dialogs, filters, notifications, charts, resource details, log viewing, and telemetry pages. Add delegate-based in-memory sorting and paging, explicit grid refresh behavior, and loading content without relying on expression compilation.
Use generated JSON metadata for Blazor circuits, HTTP APIs, browser storage, resource graphs, telemetry import/export, and Dashboard telemetry. Replace anonymous or reflection-dependent payloads with explicit serializable types where needed.
Enable Dapper AOT generation and adapt SQLite resource and telemetry queries, parameters, caching, and result mapping for native execution.
Update configuration and authentication option binding, certificate loading, version discovery, and icon lookup for trimming and Native AOT. Preserve required components, icon types, and manifest resources.
Replace MVC's status-code attribute with a local ISkipStatusCodePagesMetadata implementation so API and OTLP error responses remain unchanged without retaining unrelated MVC helpers. The workaround is linked to dotnet/aspnetcore#69217.
Keep detailed first-party trimming/AOT diagnostics enabled. Group known dependency warnings by assembly and scope justified first-party suppressions to their owners. A framework substitution-XML warning remains a documented global exception; this does not make all dependencies warning-free.
Packaging and process lifecycle
Publish, package, sign, download, and assemble the native Dashboard and its static assets for supported platforms. Update bundle documentation, layout tooling, playground configuration, and build inputs accordingly.
Update CLI, hosting, SDK bundle resolution, and profile capture to discover and launch the dedicated Dashboard. Preserve compatible legacy layouts and managed fallback for older AppHosts, while rejecting incomplete native Dashboard layouts.
Validate the requested build configuration and required Dashboard assets during layout creation so stale or incomplete publishes are not silently packaged.
Give the Dashboard its own bundle-version lease to protect its executable and assets during CLI updates. Retain parent-process shutdown handling and explicitly set its content root to the executable directory, independently of the caller's working directory.
Remove Dashboard hosting from the managed command entry point and update startup diagnostics and localized CLI messages for the native layout.
Build and test infrastructure
Update target frameworks and framework dependencies for the Dashboard and affected tests/benchmarks, add the Dapper AOT dependency, and update package-source configuration. The branch currently includes repository-local Dapper packages.
Extend native archive CI with startup/static-asset smoke checks and a Windows browser interactivity test, with explicit outerloop opt-in. Cross-compiled targets are executed only where the runner supports them; the Windows ARM runner is updated for the required native linker.
Update conditional test selection, archive handling, and CLI end-to-end helpers for the new bundle layout, including compatibility smoke coverage and improved failure diagnostics.
Add or update tests for bundle discovery and validation, AppHost launch compatibility, profiling, configuration/security option binding, JSON serialization, browser storage, resource graphs, grids, log viewing, and API/OTLP status-code behavior.
Compatibility and remaining work
File-based resource-service client certificates now use X509CertificateLoader.LoadPkcs12FromFile and accept only PKCS#12/PFX files. As documented in the Dashboard README, configurations using other certificate containers must export the certificate and private key as PKCS#12/PFX or use the certificate-store option.
Native AOT support currently uses experimental Blazor/Fluent UI APIs and documented upstream workarounds. This PR remains a draft with follow-up work expected.
Validation
Native Windows x64 publishes have passed in Debug and Release with warnings treated as errors under the scoped-warning configuration. Windows ARM64 cross-publishing was also exercised during development; the ARM64 executable was not run locally.
The native browser test passed from an unrelated temporary working directory, covering UI loading, interactive settings, browser errors, and bundle-lease cleanup.
An isolated Native AOT audit using the production icon resolver passed all 14,650 icon constructions, plus cache, casing, default, fallback, and missing-name checks.
Focused HTTP, gRPC, and telemetry API regression tests passed, confirming unauthorized responses are not replaced with HTML status pages. Component, CLI compatibility, and workflow-selection tests were also exercised during development.
These are development validation results, not a claim that the full cross-platform matrix was rerun locally for the latest head. Cross-platform coverage is provided by the PR workflows.
Screenshots / Recordings
No screenshots or recordings are included.
Checklist
Is this feature complete?
Yes. Ready to ship.
No. Follow-up changes expected.
Are you including unit tests for the changes and scenario tests if relevant?
Yes
No
Did you add public API?
Yes
If yes, did you have an API Review for it?
Yes
No
Did you add <remarks /> and <code /> elements on your triple slash comments?
Yes
No
No
Does the change make any security assumptions or guarantees?
Yes
If yes, have you done a threat model and had a security review?
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Migrates the Dashboard to Fluent UI v5 and .NET 11 Native AOT, including native bundle discovery, packaging, launch paths, and compatibility fallbacks.
Changes:
Migrates Dashboard components, styling, serialization, and tests to Fluent UI v5.
Packages and launches a standalone Native AOT Dashboard across supported platforms.
Updates CI, signing, dependencies, localization, and bundle tooling.
Reviewed changes
Copilot reviewed 287 out of 289 changed files in this pull request and generated 5 comments.
Appearance is a Fluent component parameter, but this is now a native HTML anchor, so Razor emits an inert appearance="ButtonAppearance.Subtle" attribute. The existing stylesheet also still targets fluent-anchor, which means this link loses the intended subtle Fluent styling and sizing. Please give the native anchor an explicit class and update the scoped CSS, or use the supported v5 Fluent link component.
ASPIRE_DASHBOARD_PATH is an override, but this unconditionally replaces an inherited value with the bundle path. A prebuilt AppHost launched with a custom Dashboard therefore ignores that override, unlike the terminal-host handling immediately below and the regular DotNetAppHostProject path. Guard this default with HasEnvironmentOverride.
var dashboardPath = _layout.GetDashboardPath();
if (dashboardPath is not null)
{
startInfo.Environment[BundleDiscovery.DashboardPathEnvVar] = dashboardPath;
}
The replacement <details> menu has no matching styles. UserProfile.razor.css still targets .fluent-profile-menu, fluent-anchored-region, and .fluent-persona, so opening this control now renders browser-default disclosure content in normal header flow instead of an anchored profile popover. Update the markup and isolated CSS together to preserve the profile menu layout. src/Aspire.Dashboard/Components/Pages/Login.razor:49
Appearance is a Fluent component parameter and has no effect on a native <a> element. The existing .token-help-container fluent-anchor CSS selector also no longer matches, so this action loses its intended Fluent styling. Use a supported Fluent v5 anchor or add an explicit class and corresponding anchor styles.
Posting the native interop investigation for comparison with the fix James is working on. I have stopped my implementation work and have not committed or pushed anything. These findings apply to the downloaded artifact for 63fae8212802c2bf0f58c1d58bb2b085b1741881, not to any subsequent author changes.
Two distinct failures
Keyboard shortcuts:ShortcutManager.OnGlobalKeyDown(AspireKeyboardShortcut) receives a numeric enum argument from JavaScript. The generated DashboardJsonSerializerContext is missing AspireKeyboardShortcut, so native execution falls back to reflection and throws:
EnumConverter<Aspire.Dashboard.Model.AspireKeyboardShortcut>
is missing native code or metadata
Adding [JsonSerializable(typeof(AspireKeyboardShortcut))] to the existing context addresses argument deserialization. This affects shortcuts generally, not just backtick.
Dock resizing: pointer events and the callback itself are working. The initial SetHeightAsync(320, 900) executes and updates the accessible maximum height, but its JavaScript promise never resolves or rejects. The resize coalescer correctly permits only one request in flight, so subsequent drag/keyboard changes cannot be sent. This explains why there is no “Failed to resize the terminal dock” console error: neither catch nor finally runs.
The underlying problem is broader than the dock:
JS interop executes on the renderer dispatcher, so this callback's InvokeAsync(Action) returns Task.CompletedTask.
Its runtime type is Task<System.Threading.Tasks.VoidTaskResult>, despite its declared return type being Task. Suspended async Task methods also have a task type derived from that generic instantiation.
DotNetDispatcher.EndInvokeDotNetAfterTask calls TaskGenericsUtil.GetTaskResult, which inspects the runtime type and dynamically creates TaskResultGetter<VoidTaskResult>.
That closed generic has no native code/metadata. The exception occurs in a discarded continuation, so no EndInvokeDotNet reply is sent.
A small independently published native repro captured the otherwise-unobserved exception after GC. Task.CompletedTask, genuinely asynchronous Task, and single-task Task.WhenAll reproduced it; synchronous void and a genuinely nongeneric TaskCompletionSource.Task were successful controls. On this runtime, single-task WhenAll returns the original task, so it is not a reliable workaround.
Why fixing just the resize callback is insufficient
I temporarily made the purely synchronous resize callback return void. Dragging then worked, and adding the enum metadata made backtick close the dock. Those visible effects were not proof of successful interop completion.
OnGlobalKeyDown(400) still remained pending after closing the dock.
Ordinary Blazor DispatchEventAsync click calls also remained pending after their UI effects.
There are 14 application [JSInvokable] methods returning Task in this baseline, covering shortcuts, mobile navigation, detached-window coordination, resource selection/context menus, viewport changes, resource-service reconnection, chart navigation, and terminal state updates. That count excludes framework/FluentUI callbacks. I have removed the experimental individual callback changes; they are not a complete remedy.
Shared workaround that passed the native repro
The bounded workaround I tested is an RdXmlFile containing these two closed generic roots:
The project includes it with <RdXmlFile Include="rd.xml" />.
Rooting only the JSInterop getter advances the failure to the JSON converter: JSInterop then tries to serialize the internal VoidTaskResult. Both roots were needed in my repro.
With both roots, I republished the dashboard with the original SetHeightAsync and all other callbacks unchanged. The actual native browser scenarios passed, including explicit fulfillment of every recorded interop promise—not just visible UI updates—for drag/keyboard resizing, repeated updates, bounds, backtick, settings, and ordinary event dispatch. The two-root workaround plus the enum registration requires three production files, with no JavaScript/component changes or SDK/feed changes.
Isolated local publishes used SDK 11.0.100-rc.1.26425.128 with roll-forward disabled; the minimal repro's ILCompiler packages were 11.0.0-rc.1.26425.128. This reproduces on the PR's RC1 toolchain, without adopting dailies.
15/15 targeted cases passed against the shared-root experimental native publish: 13 serialization cases and two native browser configurations, standalone and connected to an empty resource-service fixture.
The strengthened browser completion assertions fail in both configurations without the shared roots, even after visible interactions appear fixed. Existing focused dock component/initialization tests also passed, 75/75.
Local publish commands emitted a missing-full-Xcode diagnostic and returned nonzero, but did generate and publish Mach-O executables that were independently launched and exercised. This is native runtime evidence, not a claim of a clean macOS publish.
Do not rely solely on the dashboard's (AOT) label for reproduction: PublishAot=true can disable dynamic-code support during an ordinary managed build, which also triggers that label.
This is an interim compatibility workaround, not a framework-wide correctness fix. It relies on private framework names and must be revalidated when upgrading. It preserves the current {} serialization of VoidTaskResult (null for genuinely nongeneric task/void controls).
I also checked a faulted async callback: it sends a failure reply rather than hanging, but EndInvokeDotNetAfterTask then falls through into result extraction and produces a secondary unobserved exception. The roots do not fix that separate control-flow issue.
The preferred upstream approach is to handle declared nongeneric Task/ValueTask without reflecting over or serializing VoidTaskResult, ensure result-processing failures are returned to JavaScript, and stop after sending an error response. The same problematic paths are still present in the inspected ASP.NET Core main revision:
No upstream issue has been filed by this investigation. The most important check for the author's fix is successful/rejected promise settlement, especially ordinary clicks and shortcuts: a functioning-looking page can still hide this failure.
The reason will be displayed to describe this comment to others. Learn more.
Copilot review overview
🔵 Needs a closer look
The broad experimental runtime and packaging change needs human sign-off, and Dashboard lease metadata currently persists an arbitrary command-line argument.
Review effort: Balanced Findings: None
Previously missed (1)
In code that hasn't changed since last review
Avoid persisting sensitive pass-through arguments in bundle lease files
src/Aspire.Dashboard/Program.cs:13
The dedicated executable no longer has a leading dashboard subcommand, so the first argument is now an arbitrary configuration switch. This value is serialized into the bundle lease file, and pass-through switches can contain the browser token or API key; after an abnormal exit that metadata can remain on disk. Use a fixed non-sensitive command name instead of persisting args[0].
The reason will be displayed to describe this comment to others. Learn more.
Copilot review overview
🔵 Needs a closer look
It is an explicitly incomplete cross-platform runtime and packaging migration, and the changed CI routing currently misses a runtime-only CLI Dashboard consumer.
PR #19565 ("Enable Native AOT dashboard with Fluent UI v5") is a very large (219-file) change enabling the Aspire Dashboard to run as a standalone Native AOT executable. Most of the churn is internal (Fluent UI/Blazor updates, AOT/trimming work, packaging/bundling, CI). Of the 6 triggered signals, most turned out to be internal implementation detail with no docs-relevant surface change on inspection:
dashboard_user_facing_page_changed — Razor page edits (Login, Metrics, Resources, StructuredLogs, TraceDetail, Traces) are Fluent UI v5 migration/rendering changes only; no new user-facing behavior, options, or workflow.
new_public_type — ValueDirectionChange enum and PlotlyTraceData record are internal chart/telemetry types, not public API surface consumers interact with.
target_framework_changed — Aspire.Dashboard.csproj moved to net11.0/PublishAot=true; an internal packaging detail, not something users configure.
dashboard_api_endpoint_changed — TelemetryApiService.cs change is only a JSON serializer-context refactor (AOT-safe serialization), not a wire/API contract change.
cli_command_file_changed / cli_resource_strings_changed — DashboardRunCommand.cs and DashboardCommandStrings.resx were touched only to rename an internal managedPath variable/local method parameter and update one error-message string (removes the internal "aspire-managed" binary name reference). No new CLI options, flags, or behavior changes for users.
The one concrete, user-facing, documented-surface change I found is in src/Aspire.Dashboard/README.md: file-based resource-service client certificates now use X509CertificateLoader.LoadPkcs12FromFile and only accept PKCS#12/PFX files (no longer DER/PEM, PKCS#7, Windows serialized certs, or Authenticode-signed files). This directly affects the Dashboard:ResourceServiceClient:ClientCertificate:FilePath option already documented on aspire.dev.
Docs change made:
`src/frontend/src/content/docs/dashboard/co
(summary truncated)
Note
This draft PR needs human review before merging.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1719
Description
Enables the Aspire Dashboard to publish and run as a standalone Native AOT executable, with the Fluent UI and runtime changes needed to support it. The Dashboard becomes a dedicated bundle component containing its executable and static assets, rather than running through the
aspire-managed dashboardsubcommand.Existing
aspire dashboard run, AppHost startup, and CLI profile-capture workflows select the appropriate Dashboard automatically. No new user-facing command-line options are required.Dashboard runtime and UI
ISkipStatusCodePagesMetadataimplementation so API and OTLP error responses remain unchanged without retaining unrelated MVC helpers. The workaround is linked to dotnet/aspnetcore#69217.Packaging and process lifecycle
Build and test infrastructure
Compatibility and remaining work
File-based resource-service client certificates now use
X509CertificateLoader.LoadPkcs12FromFileand accept only PKCS#12/PFX files. As documented in the Dashboard README, configurations using other certificate containers must export the certificate and private key as PKCS#12/PFX or use the certificate-store option.Native AOT support currently uses experimental Blazor/Fluent UI APIs and documented upstream workarounds. This PR remains a draft with follow-up work expected.
Validation
Screenshots / Recordings
No screenshots or recordings are included.
Checklist
<remarks />and<code />elements on your triple slash comments?