diff --git a/.agents/skills/doc-pr-reviewer/SKILL.md b/.agents/skills/doc-pr-reviewer/SKILL.md
index 4e2d38763..ea66befaa 100644
--- a/.agents/skills/doc-pr-reviewer/SKILL.md
+++ b/.agents/skills/doc-pr-reviewer/SKILL.md
@@ -80,6 +80,40 @@ For each non-`narrative` claim:
- `contradicted` — the source code says something different than the PR claims; include both texts
4. Do not infer from documentation, blog posts, or other branches. The branch you actually checked out and recorded for that repo (per the **Source of truth** section — the PR's matching branch, or the documented fallback when none matches) is the only source of truth.
+## Current-version placeholder review
+
+When a docs PR adds or edits Aspire version strings, check whether the version
+is intended to represent the current release or an intentionally fixed version.
+The docs site provides placeholders for current-version values:
+
+| Placeholder | Use for |
+|-------------|---------|
+| `%ASPIRE_VERSION%` | Full current Aspire version, including patch (for example, `13.5.0`) |
+| `%ASPIRE_VERSION_MAJOR_MINOR%` | Current Aspire major/minor display version (for example, `13.5`) |
+
+Flag hard-coded Aspire versions as a review finding when they appear in current
+copy/paste guidance that should track the active release, including:
+
+- `Aspire.AppHost.Sdk` declarations in project files or file-based apps.
+- `#:package Aspire.*@...` file-based app package directives.
+- Aspire CLI or AppHost sample output that reports the current CLI/AppHost
+ version.
+- Getting started, installation, or "use this today" examples that should move
+ with the release branch.
+
+Do **not** require placeholders for intentionally fixed versions, including:
+
+- What's-new or release-note pages that describe a specific historical release.
+- Upgrade examples that deliberately compare old and new versions.
+- CLI examples where the point is pinning a specific version with `--version`,
+ `-Version`, or `Aspire.ProjectTemplates::...`.
+- Versioned schema URLs, package compatibility notes, minimum-version
+ requirements, third-party dependency versions, container image tags, or issue
+ reproduction snippets.
+
+If the intent is ambiguous, leave a `COMMENT` asking whether the version should
+track the current release instead of requesting changes outright.
+
## Phase A artifact
When Phase A finishes, write down (in memory) a frozen Phase A result containing:
diff --git a/.github/agents/release-verifier.agent.md b/.github/agents/release-verifier.agent.md
index 6fbab2d2b..8efc9bc63 100644
--- a/.github/agents/release-verifier.agent.md
+++ b/.github/agents/release-verifier.agent.md
@@ -121,7 +121,22 @@ Scan the documentation for version strings that should have been updated for thi
Determine the immediately prior version. For `13.2` the prior is `13.1`; for `13.0` the prior is `9.5` (or whatever the last release of the previous major is). Use the existing what's-new files to determine this.
-#### 4b. Scan for stale version references
+#### 4b. Verify shared version placeholders
+
+Read `src/frontend/config/aspire-versions.mjs` and verify the exported current
+version constants match the release being verified:
+
+| Export | Expected value |
+|--------|----------------|
+| `currentAspireMajorMinorVersion` | `{VERSION}` (for example, `13.2`) |
+| `currentAspireVersion` | `{NUGET_VERSION}` (for example, `13.2.0`) |
+
+These constants drive the `%ASPIRE_VERSION_MAJOR_MINOR%` and
+`%ASPIRE_VERSION%` documentation placeholders. If either value is stale, flag it
+as a **critical** failure because generated docs can show the wrong current
+release version.
+
+#### 4c. Scan for stale version references
Search the docs content tree for references to the prior NuGet version that appear **outside** of intentional historical context (e.g., upgrade-from examples that deliberately show the old version).
@@ -139,7 +154,7 @@ grep -rn 'Aspire.AppHost.Sdk.*Version="{PRIOR_NUGET_VERSION}"' src/frontend/src/
--include="*.mdx"
```
-#### 4c. Evaluate each match
+#### 4d. Evaluate each match
For every match found:
@@ -149,7 +164,7 @@ For every match found:
- **Stale (should be updated)**: The reference is in current guidance, installation instructions, or sample code that a user would copy today. Flag these for update.
3. Log each stale reference with file path, line number, and surrounding context.
-#### 4d. Verify new version references
+#### 4e. Verify new version references
Spot-check that key documentation pages reference the release version:
diff --git a/src/frontend/astro.config.mjs b/src/frontend/astro.config.mjs
index 215da4507..90ff53105 100644
--- a/src/frontend/astro.config.mjs
+++ b/src/frontend/astro.config.mjs
@@ -8,6 +8,8 @@ import { cookieConfig } from './config/cookie.config';
import { locales } from './config/locales.ts';
import { headAttrs } from './config/head.attrs.ts';
import { socialConfig } from './config/socials.config.ts';
+import { aspireVersionPlaceholdersIntegration } from './config/aspire-version-placeholders-integration.mjs';
+import { remarkAspireVersionPlaceholders } from './config/remark-aspire-version-placeholders.mjs';
import catppuccin from '@catppuccin/starlight';
import lunaria from './config/lunaria-starlight.mjs';
import mermaid from 'astro-mermaid';
@@ -48,7 +50,9 @@ export default defineConfig({
site: 'https://aspire.dev',
trailingSlash: 'always',
markdown: {
- processor: unified(),
+ processor: unified({
+ remarkPlugins: [remarkAspireVersionPlaceholders],
+ }),
},
redirects: redirects,
integrations: [
@@ -238,6 +242,7 @@ export default defineConfig({
}),
jopSoftwarecookieconsent(cookieConfig),
...(isBuildTimingEnabled ? [buildTiming()] : []),
+ aspireVersionPlaceholdersIntegration(),
],
build: {
concurrency: buildConcurrency,
diff --git a/src/frontend/config/aspire-version-placeholders-integration.mjs b/src/frontend/config/aspire-version-placeholders-integration.mjs
new file mode 100644
index 000000000..ae1a1dc38
--- /dev/null
+++ b/src/frontend/config/aspire-version-placeholders-integration.mjs
@@ -0,0 +1,91 @@
+import { readdir, readFile, writeFile } from 'node:fs/promises';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+import { replaceAspireVersionPlaceholders } from './remark-aspire-version-placeholders.mjs';
+
+// Per-page Markdown copies emitted by `starlight-page-actions` are the only
+// generated artifacts that still contain raw `%ASPIRE_VERSION%` placeholders:
+// that plugin `viteStaticCopy`s `src/content/docs/**/*.{md,mdx}` straight to
+// `dist/**/*.md` through a regex-only transform, so it never runs through the
+// `remarkAspireVersionPlaceholders` remark plugin.
+//
+// Everything else is already handled before it reaches `dist`:
+// - `.html` pages -> rendered via the remark pipeline (placeholders replaced
+// in the mdast before expressive-code renders code blocks)
+// - `llms*.txt` -> `starlight-llms-txt` sources rendered HTML (`render(entry)`)
+// - `reference/**.md` -> generated from API/sample data, not docs content
+//
+// So this post-build pass only needs to touch `.md` files. Scoping it this way
+// (instead of walking every `.html`/`.txt` in `dist`) avoids re-reading the bulk
+// of the output — including the large `llms-full.txt` assets — which is what
+// previously exhausted the Node heap.
+const placeholderCopyExtensions = new Set(['.md']);
+
+// Process the Markdown copies through a small worker pool rather than a single
+// recursive `Promise.all` over the whole tree, so peak memory stays proportional
+// to the concurrency limit instead of the number of files held open at once.
+const DEFAULT_CONCURRENCY = 16;
+
+export function aspireVersionPlaceholdersIntegration() {
+ return {
+ name: 'aspire-version-placeholders',
+ hooks: {
+ 'astro:build:done': async ({ dir }) => {
+ await replaceAspireVersionPlaceholdersInDirectory(fileURLToPath(dir));
+ },
+ },
+ };
+}
+
+export async function replaceAspireVersionPlaceholdersInDirectory(
+ directory,
+ concurrency = DEFAULT_CONCURRENCY
+) {
+ const files = [];
+ await collectMarkdownCopies(directory, files);
+
+ if (files.length === 0) {
+ return;
+ }
+
+ // Normalize to a finite positive integer so a stray NaN/0/negative value can't
+ // collapse the worker pool to an empty array and silently skip every file.
+ const limit = Number.isFinite(concurrency) ? Math.floor(concurrency) : DEFAULT_CONCURRENCY;
+ const workerCount = Math.min(Math.max(1, limit), files.length);
+ let cursor = 0;
+
+ const runWorker = async () => {
+ while (cursor < files.length) {
+ const filePath = files[cursor++];
+ await replaceAspireVersionPlaceholdersInFile(filePath);
+ }
+ };
+
+ await Promise.all(Array.from({ length: workerCount }, runWorker));
+}
+
+async function collectMarkdownCopies(directory, files) {
+ const entries = await readdir(directory, { withFileTypes: true });
+
+ for (const entry of entries) {
+ const resolvedPath = path.join(directory, entry.name);
+
+ if (entry.isDirectory()) {
+ await collectMarkdownCopies(resolvedPath, files);
+ continue;
+ }
+
+ if (entry.isFile() && placeholderCopyExtensions.has(path.extname(entry.name))) {
+ files.push(resolvedPath);
+ }
+ }
+}
+
+async function replaceAspireVersionPlaceholdersInFile(filePath) {
+ const content = await readFile(filePath, 'utf8');
+ const updated = replaceAspireVersionPlaceholders(content);
+
+ if (updated !== content) {
+ await writeFile(filePath, updated);
+ }
+}
diff --git a/src/frontend/config/aspire-versions.mjs b/src/frontend/config/aspire-versions.mjs
new file mode 100644
index 000000000..8c27e3ae9
--- /dev/null
+++ b/src/frontend/config/aspire-versions.mjs
@@ -0,0 +1,7 @@
+export const currentAspireMajorMinorVersion = '13.5';
+export const currentAspireVersion = '13.5.0';
+
+export const aspireVersionPlaceholders = Object.freeze({
+ '%ASPIRE_VERSION_MAJOR_MINOR%': currentAspireMajorMinorVersion,
+ '%ASPIRE_VERSION%': currentAspireVersion,
+});
diff --git a/src/frontend/config/remark-aspire-version-placeholders.mjs b/src/frontend/config/remark-aspire-version-placeholders.mjs
new file mode 100644
index 000000000..0be1f1bd6
--- /dev/null
+++ b/src/frontend/config/remark-aspire-version-placeholders.mjs
@@ -0,0 +1,40 @@
+import { aspireVersionPlaceholders } from './aspire-versions.mjs';
+
+const placeholderEntries = Object.entries(aspireVersionPlaceholders);
+
+export function replaceAspireVersionPlaceholders(value) {
+ return placeholderEntries.reduce(
+ (current, [placeholder, version]) => current.replaceAll(placeholder, version),
+ value
+ );
+}
+
+export function remarkAspireVersionPlaceholders() {
+ return (tree) => {
+ replaceNodeValues(tree);
+ };
+}
+
+function replaceNodeValues(node) {
+ if (!node || typeof node !== 'object') {
+ return;
+ }
+
+ if (typeof node.value === 'string') {
+ node.value = replaceAspireVersionPlaceholders(node.value);
+ }
+
+ if (Array.isArray(node.attributes)) {
+ for (const attribute of node.attributes) {
+ if (attribute && typeof attribute.value === 'string') {
+ attribute.value = replaceAspireVersionPlaceholders(attribute.value);
+ }
+ }
+ }
+
+ if (Array.isArray(node.children)) {
+ for (const child of node.children) {
+ replaceNodeValues(child);
+ }
+ }
+}
diff --git a/src/frontend/config/sidebar/dashboard.topics.ts b/src/frontend/config/sidebar/dashboard.topics.ts
index a108599c3..36b5de2e6 100644
--- a/src/frontend/config/sidebar/dashboard.topics.ts
+++ b/src/frontend/config/sidebar/dashboard.topics.ts
@@ -243,6 +243,27 @@ export const dashboardTopics: StarlightSidebarTopicsUserConfig = {
},
slug: 'dashboard/security-considerations',
},
+ {
+ label: 'Troubleshooting',
+ translations: {
+ da: 'Fejlfinding',
+ de: 'Fehlerbehebung',
+ en: 'Troubleshooting',
+ es: 'Solución de problemas',
+ fr: 'Dépannage',
+ hi: 'समस्या निवारण',
+ id: 'Pemecahan masalah',
+ it: 'Risoluzione dei problemi',
+ ja: 'トラブルシューティング',
+ ko: '문제 해결',
+ 'pt-BR': 'Solução de problemas',
+ ru: 'Устранение неполадок',
+ tr: 'Sorun giderme',
+ uk: 'Усунення неполадок',
+ 'zh-CN': '故障排除',
+ },
+ slug: 'dashboard/troubleshooting',
+ },
{
label: 'Telemetry',
translations: {
diff --git a/src/frontend/config/sidebar/docs.topics.ts b/src/frontend/config/sidebar/docs.topics.ts
index 252aad6fe..93a125caf 100644
--- a/src/frontend/config/sidebar/docs.topics.ts
+++ b/src/frontend/config/sidebar/docs.topics.ts
@@ -90,6 +90,10 @@ export const docsTopics: StarlightSidebarTopicsUserConfig = {
label: "What's new",
collapsed: true,
items: [
+ {
+ label: 'Aspire 13.5',
+ slug: 'whats-new/aspire-13-5',
+ },
{
label: 'Aspire 13.4',
slug: 'whats-new/aspire-13-4',
@@ -936,6 +940,10 @@ export const docsTopics: StarlightSidebarTopicsUserConfig = {
ja: '実行可能リソース',
},
},
+ {
+ label: 'Interactive terminals',
+ slug: 'app-host/withterminal',
+ },
],
},
{
diff --git a/src/frontend/src/content/docs/app-host/configuration.mdx b/src/frontend/src/content/docs/app-host/configuration.mdx
index c66c3dc32..5e433ea09 100644
--- a/src/frontend/src/content/docs/app-host/configuration.mdx
+++ b/src/frontend/src/content/docs/app-host/configuration.mdx
@@ -24,9 +24,14 @@ AppHost configuration is provided through launch profiles:
]}
/>
-In C# AppHosts, profiles live in `launchSettings.json`:
+C# AppHosts come in two forms, and each stores launch profiles differently:
-```json title="launchSettings.json"
+- **Project-based AppHost** (the default `dotnet new aspire-apphost` template): profiles live in `Properties/launchSettings.json`.
+- **File-based AppHost** (created with `aspire new` using the empty C# template): profiles live in `apphost.run.json`. In this template, `aspire.config.json` only points at the entry file — it does **not** contain a `profiles` block.
+
+**Project-based AppHost** — `Properties/launchSettings.json`:
+
+```json title="Properties/launchSettings.json"
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
@@ -45,6 +50,38 @@ In C# AppHosts, profiles live in `launchSettings.json`:
}
}
```
+
+**File-based AppHost** — `apphost.run.json` (profiles) and `aspire.config.json` (entry point only):
+
+```json title="apphost.run.json"
+{
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:17134;http://localhost:15170",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "DOTNET_ENVIRONMENT": "Development",
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21030",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22057"
+ }
+ }
+ }
+}
+```
+
+```json title="aspire.config.json"
+{
+ "appHost": {
+ "path": "apphost.cs"
+ }
+}
+```
+
+
In TypeScript AppHosts, profiles live in `aspire.config.json`:
diff --git a/src/frontend/src/content/docs/app-host/resource-lifetimes.mdx b/src/frontend/src/content/docs/app-host/resource-lifetimes.mdx
index 3335e6aa7..854913c50 100644
--- a/src/frontend/src/content/docs/app-host/resource-lifetimes.mdx
+++ b/src/frontend/src/content/docs/app-host/resource-lifetimes.mdx
@@ -5,6 +5,7 @@ description: Learn how session, persistent, resource-scoped, and parent-process
import { Tabs, TabItem } from '@astrojs/starlight/components';
import { Image } from 'astro:assets';
+import LearnMore from '@components/LearnMore.astro';
import persistentContainer from '@assets/whats-new/aspire-9/persistent-container.png';
import persistentContainerDocker from '@assets/whats-new/aspire-9/persistent-container-docker-desktop.png';
@@ -38,7 +39,13 @@ Use persistent lifetime for resources that are expensive to initialize, need sta
Persistent resources are automatically recreated when the AppHost detects meaningful configuration changes. If the configuration differs, the resource is recreated with the new settings.
:::
-Persistent resources default to proxyless endpoints so a stable local endpoint can remain reachable after the AppHost stops. You can still configure endpoint proxy behavior explicitly. For example, set `isProxied: true` or `IsProxied = true` when you need Aspire's proxy for a specific endpoint, or disable endpoint proxy support when you intentionally want every endpoint on a resource to be proxyless. Persistent executable endpoints must have a concrete `port` or `targetPort`; automatically persisted random executable ports aren't supported.
+Persistent containers use proxied endpoints by default, just like session containers. The proxy runs only while the AppHost is running, so the proxy address isn't reachable after the AppHost stops. Persistent executables and projects default to proxyless endpoints so their direct addresses stay stable and reachable even after the AppHost stops.
+
+You can still configure endpoint proxy behavior explicitly on any persistent resource. Set `isProxied: false` on an individual endpoint, or call `WithEndpointProxySupport(false)` to make every endpoint on a resource proxyless. When a proxyless endpoint doesn't specify a public `port`, Aspire allocates one before the resource is created. For persistent resources, Aspire stores the allocated port in user secrets when user secrets are available and reuses it on later AppHost runs.
+
+
+ For more information, see [Allocate ports for dynamic proxyless endpoints](/fundamentals/networking-overview/#allocate-ports-for-dynamic-proxyless-endpoints).
+
:::danger[Persistent container ≠ persistent data]
Persistent container lifetime doesn't guarantee data durability. For details, see [Container lifetime vs. data durability](#container-lifetime-vs-data-durability).
@@ -155,7 +162,7 @@ await builder.build().run();
-Configure a concrete `port` or `targetPort` for persistent executable endpoints; automatically persisted random executable ports aren't supported.
+The preceding example configures a concrete `port` so the endpoint address is explicit. If you omit the public `port`, Aspire allocates one before the executable starts and reuses it from user secrets on later AppHost runs when user secrets are available.
## Configure a persistent project
diff --git a/src/frontend/src/content/docs/app-host/withterminal.mdx b/src/frontend/src/content/docs/app-host/withterminal.mdx
new file mode 100644
index 000000000..4bd76e3a6
--- /dev/null
+++ b/src/frontend/src/content/docs/app-host/withterminal.mdx
@@ -0,0 +1,171 @@
+---
+title: Interactive terminal sessions
+description: Attach an interactive terminal to any Aspire resource using WithTerminal — access live PTY sessions from the dashboard or the `aspire terminal` CLI commands.
+---
+
+import { Tabs, TabItem } from '@astrojs/starlight/components';
+
+:::danger[Experimental feature]
+`WithTerminal()` is an experimental API. Reference the `ASPIRETERMINAL001` diagnostic ID to opt in:
+
+```csharp
+#pragma warning disable ASPIRETERMINAL001
+builder.AddExecutable("repl", "my-repl", ".")
+ .WithTerminal();
+```
+:::
+
+The `WithTerminal()` extension method attaches an interactive pseudo-terminal (PTY) to any Aspire resource. When a resource is configured with `WithTerminal()`, the Aspire Dashboard replaces the standard console log view with a live terminal session, and the `aspire terminal` CLI command lets you connect directly from your local shell.
+
+## Enable interactive terminals
+
+Call `WithTerminal()` on any executable or project resource in your AppHost:
+
+
+
+```csharp title="AppHost.cs"
+#pragma warning disable ASPIRETERMINAL001
+var builder = DistributedApplication.CreateBuilder(args);
+
+var repl = builder.AddExecutable("repl", "my-repl", ".")
+ .WithTerminal();
+
+builder.Build().Run();
+```
+
+
+```typescript title="apphost.mts"
+import { createBuilder } from './.aspire/modules/aspire.mjs';
+
+const builder = await createBuilder();
+
+const repl = await builder
+ .addNodeApp("guessing-game", "./guessing-game", "game.mjs")
+ .withTerminal();
+
+await builder.build().run();
+```
+
+
+
+## Configure terminal dimensions
+
+Use the optional `configure` callback to set the initial column and row count for the terminal window:
+
+
+
+```csharp title="AppHost.cs"
+#pragma warning disable ASPIRETERMINAL001
+var builder = DistributedApplication.CreateBuilder(args);
+
+var repl = builder.AddExecutable("repl", "my-repl", ".")
+ .WithTerminal(options =>
+ {
+ options.Columns = 100;
+ options.Rows = 28;
+ });
+builder.Build().Run();
+```
+
+
+
+```typescript title="apphost.mts"
+import { createBuilder } from './.aspire/modules/aspire.mjs';
+
+const builder = await createBuilder();
+
+const repl = await builder
+ .addNodeApp("guessing-game", "./guessing-game", "game.mjs")
+ .withTerminal();
+
+await builder.build().run();
+```
+
+
+
+
+TypeScript AppHost currently supports only the parameterless `withTerminal()` call. Support for configuring dimensions in TypeScript is tracked by [microsoft/aspire#18105](https://github.com/microsoft/aspire/issues/18105).
+
+The default dimensions are **120 columns × 30 rows**. The dashboard terminal panel auto-fits to the browser window, so these values primarily affect the PTY allocation on the host side.
+
+## Use with replicated resources
+
+Use `WithTerminal()` with `WithReplicas()` to give each replica its own interactive terminal session:
+
+
+
+```csharp title="AppHost.cs"
+#pragma warning disable ASPIRETERMINAL001
+var builder = DistributedApplication.CreateBuilder(args);
+
+// Three replicas, each with its own interactive terminal.
+var agent = builder.AddExecutable("agent", "my-agent", ".")
+ .WithReplicas(3)
+ .WithTerminal();
+
+builder.Build().Run();
+```
+
+
+```typescript title="apphost.mts"
+import { createBuilder } from './.aspire/modules/aspire.mjs';
+
+const builder = await createBuilder();
+
+const agent = await builder
+ .addNodeApp("agent", "./agent", "index.mjs");
+await agent.withReplicas(3);
+await agent.withTerminal();
+
+await builder.build().run();
+```
+
+
+
+Each replica gets its own independent terminal session and appears as a separate entry in the dashboard resource list (for example, `agent-r0`, `agent-r1`, `agent-r2`).
+
+## Access terminals from the dashboard
+
+When a resource has `WithTerminal()` applied, the **Console Logs** tab for that resource in the Aspire Dashboard is replaced by a live terminal session. You can interact with the running process directly in the browser — type commands, scroll the scrollback buffer, and switch between replicas using the replica selector.
+
+## Access terminals from the CLI
+
+The `aspire terminal` command lets you attach to or inspect terminal sessions from your local shell.
+
+### List terminal-enabled resources
+
+```bash
+aspire terminal ps
+```
+
+This displays a table of every `WithTerminal()`-enabled resource in the connected AppHost, including the current terminal dimensions, the number of attached viewers, and per-replica health status.
+
+To emit structured JSON output for scripting:
+
+```bash
+aspire terminal ps --format json
+```
+
+### Attach to a terminal session
+
+```bash
+aspire terminal attach
+```
+
+If the resource has a single replica, the CLI attaches immediately. For multi-replica resources, pass `--replica` with the 0-based index:
+
+```bash
+aspire terminal attach agent --replica 1
+```
+
+To connect as a secondary viewer — receiving output without driving the session dimensions — add `--viewer`:
+
+```bash
+aspire terminal attach agent --viewer
+```
+
+## See also
+
+- [Executable resources](/app-host/executable-resources/)
+- [Resource lifetimes](/app-host/resource-lifetimes/)
+- [Aspire CLI reference](/reference/cli/overview/)
diff --git a/src/frontend/src/content/docs/community/index.mdx b/src/frontend/src/content/docs/community/index.mdx
index 5a970503d..3b0ed1a3c 100644
--- a/src/frontend/src/content/docs/community/index.mdx
+++ b/src/frontend/src/content/docs/community/index.mdx
@@ -7,7 +7,7 @@ next: false
description: Connect with the Aspire team and community across Discord, GitHub Discussions, livestreams, social channels, and contribution platforms for distributed apps.
banner:
content: |
- Aspire 13.4 is here! — See what's new
+ ✨ Aspire 13.5 is available! — Explore the latest features and improvements
editUrl: false
giscus: false
tableOfContents: false
diff --git a/src/frontend/src/content/docs/da/index.mdx b/src/frontend/src/content/docs/da/index.mdx
index 8ef96e4f7..aaccc9a26 100644
--- a/src/frontend/src/content/docs/da/index.mdx
+++ b/src/frontend/src/content/docs/da/index.mdx
@@ -11,7 +11,7 @@ prev: false
next: false
banner:
content: |
- 🚀 Aspire 13.4 er udgivet! — Se hvad der er nyt i Aspire 13.4.
+ ✨ Aspire 13.5 er udgivet! — Se hvad der er nyt i Aspire 13.5.
hero:
tagline: Din stack, forenklet.
Orkestrér frontends, APIs, containere og databaser ubesværet—ingen omskrivninger, ingen grænser. Udvid Aspire til at drive ethvert projekt.
image:
diff --git a/src/frontend/src/content/docs/dashboard/troubleshooting.mdx b/src/frontend/src/content/docs/dashboard/troubleshooting.mdx
new file mode 100644
index 000000000..78268a41b
--- /dev/null
+++ b/src/frontend/src/content/docs/dashboard/troubleshooting.mdx
@@ -0,0 +1,64 @@
+---
+title: Troubleshoot the Aspire dashboard
+description: Diagnose and resolve common Aspire dashboard issues including connection errors between the dashboard and the AppHost resource service.
+---
+
+import LearnMore from '@components/LearnMore.astro';
+
+This article helps you diagnose and resolve common issues with the Aspire dashboard.
+
+## Connection errors between the dashboard and AppHost
+
+The dashboard displays resource information by connecting to a gRPC resource service hosted by the AppHost. When this connection fails, the dashboard may show an error message or display telemetry data without any resource list or console logs.
+
+### Symptoms
+
+- The dashboard loads but shows no resources in the resource list.
+- Console logs are unavailable for resources.
+- An error banner appears indicating the dashboard can't connect to the resource service.
+- The dashboard log contains errors related to gRPC connection failures.
+
+### Viewing dashboard logs
+
+Enable verbose logging to get more detail about connection failures. Run the AppHost with debug-level output:
+
+```bash title="Run with debug output"
+aspire run --log-level Debug
+```
+
+Review the console output for messages related to the resource service connection, such as gRPC status codes or TLS handshake failures.
+
+### Certificate and TLS errors
+
+When running with HTTPS, the dashboard and AppHost use certificates to secure the gRPC connection. Certificate issues are a common source of connection failures.
+
+**Common causes:**
+
+- The development certificate isn't trusted. Run `aspire certs trust` to trust the development certificate.
+- The certificate has expired or is corrupted. Regenerate it with `aspire certs clean` followed by `aspire certs trust`.
+- A custom certificate is specified but the file path or password is incorrect. Verify the `Dashboard:ResourceServiceClient:ClientCertificate` settings.
+
+### Firewall and network issues
+
+- Confirm that a firewall or security software isn't blocking the connection between the dashboard and the resource service endpoint.
+- The resource service uses gRPC, which requires HTTP/2. Some firewalls, proxies, or network appliances don't support HTTP/2 traffic and may silently drop or downgrade the connection. Ensure that any intermediary between the dashboard and the AppHost allows HTTP/2.
+
+### Ping timeout errors when debugging the AppHost
+
+You may see the following error in the dashboard:
+
+```
+Status(StatusCode="Internal", Detail="Error starting gRPC call.
+HttpRequestException: The HTTP/2 server didn't respond to a ping request
+within the configured KeepAlivePingDelay. (HttpProtocolError)")
+```
+
+This means the dashboard's gRPC client sent an HTTP/2 PING frame to the AppHost's resource service, but didn't receive a response in time.
+
+The most likely cause is that a debugger is paused on the AppHost — when you hit a breakpoint, the process can't respond to HTTP/2 PING frames, triggering the timeout. This error resolves once execution resumes.
+
+If you see this error and you're running without a debugger attached, verify the AppHost process is still alive and responsive.
+
+
+For the full list of configuration options, see [Aspire dashboard configuration](/dashboard/configuration/).
+
diff --git a/src/frontend/src/content/docs/de/index.mdx b/src/frontend/src/content/docs/de/index.mdx
index da9dd1177..08d1cc36c 100644
--- a/src/frontend/src/content/docs/de/index.mdx
+++ b/src/frontend/src/content/docs/de/index.mdx
@@ -11,7 +11,7 @@ prev: false
next: false
banner:
content: |
- 🚀 Aspire 13.4 wurde veröffentlicht! — Was ist neu in Aspire 13.4.
+ ✨ Aspire 13.5 wurde veröffentlicht! — Was ist neu in Aspire 13.5.
hero:
tagline: Dein Stack, vereinfacht.
Orchestriere Frontends, APIs, Container und Datenbanken mühelos—ohne Umschreiben, ohne Grenzen. Erweitere Aspire, um jedes Projekt anzutreiben.
image:
diff --git a/src/frontend/src/content/docs/docs.mdx b/src/frontend/src/content/docs/docs.mdx
index f2a4b5acb..8e9ed378b 100644
--- a/src/frontend/src/content/docs/docs.mdx
+++ b/src/frontend/src/content/docs/docs.mdx
@@ -7,7 +7,7 @@ next:
description: "Browse the official Aspire documentation: get started with C# and TypeScript AppHosts, model distributed apps, deploy to Azure and Kubernetes, and observe in the dashboard."
banner:
content: |
- 🆕 Aspire 13.4 is here! — See what's new
+ ✨ Aspire 13.5 is available! — Explore the latest features and improvements
editUrl: false
tableOfContents: false
pageActions: false
diff --git a/src/frontend/src/content/docs/es/index.mdx b/src/frontend/src/content/docs/es/index.mdx
index 2e7a3ad8e..4e61566d3 100644
--- a/src/frontend/src/content/docs/es/index.mdx
+++ b/src/frontend/src/content/docs/es/index.mdx
@@ -11,7 +11,7 @@ prev: false
next: false
banner:
content: |
- 🚀 ¡Aspire 13.4 ha sido lanzado! — Ver qué hay de nuevo en Aspire 13.4.
+ ✨ ¡Aspire 13.5 está disponible! — Explora las últimas características y mejoras.
hero:
tagline: Tu stack, simplificado.
Orquesta frontends, APIs, contenedores y bases de datos sin esfuerzo—sin reescrituras, sin límites. Extiende Aspire para impulsar cualquier proyecto.
Orchestrez vos frontends, APIs, conteneurs et bases de données sans effort — sans réécriture, sans limites. Étendez Aspire pour propulser n'importe quel projet.
image:
diff --git a/src/frontend/src/content/docs/fundamentals/custom-resource-commands.mdx b/src/frontend/src/content/docs/fundamentals/custom-resource-commands.mdx
index 288af9e1b..9f0e592a6 100644
--- a/src/frontend/src/content/docs/fundamentals/custom-resource-commands.mdx
+++ b/src/frontend/src/content/docs/fundamentals/custom-resource-commands.mdx
@@ -156,7 +156,7 @@ The `WithCommand` API adds the appropriate annotations to the resource, which ar
- `displayName`: The name of the command to display in the dashboard.
- `executeCommand`: The callback that runs when the command is invoked. In C# the type is `Func>`; in TypeScript it's `(context: ExecuteCommandContext) => Promise`.
- `updateState`: A callback that decides the command's enabled state in the dashboard. In C# it's `Func` and supplied via `CommandOptions.UpdateState`; in TypeScript it's `(context: UpdateCommandStateContext) => Promise` and supplied through the `updateState` field on `CommandOptions`.
-- `iconName`: The name of the icon to display in the dashboard. The icon is optional, but when you do provide it, it should be a valid [Fluent UI Blazor icon name](https://www.fluentui-blazor.net/Icon#explorer).
+- `iconName`: The name of the icon to display in the dashboard. The icon is optional. Use a valid [Fluent UI Blazor icon name](https://www.fluentui-blazor.net/Icon#explorer). If the name isn't recognized, the dashboard renders a question-mark circle (❓) icon as a fallback.
- `iconVariant`: The variant of the icon to display in the dashboard, valid options are `Regular` (default) or `Filled`.
## Execute command logic
diff --git a/src/frontend/src/content/docs/fundamentals/networking-overview.mdx b/src/frontend/src/content/docs/fundamentals/networking-overview.mdx
index b2fcaaf7f..148806a87 100644
--- a/src/frontend/src/content/docs/fundamentals/networking-overview.mdx
+++ b/src/frontend/src/content/docs/fundamentals/networking-overview.mdx
@@ -55,7 +55,7 @@ The inner loop is the process of developing and testing your app locally before
- **Endpoints/Endpoint configurations**: Endpoints are the connections between your app and the services it depends on, such as databases, message queues, or APIs. Endpoints provide information such as the service name, host port, scheme, and environment variable. Aspire can create endpoints automatically from resource configuration, and you can also add them explicitly by calling `WithEndpoint`.
- **Proxies**: Aspire automatically launches a proxy for each proxied service binding you add to your app, and assigns a port for the proxy to listen on. The proxy then forwards the requests to the port that your app listens on, which might be different from the proxy port. This way, you can avoid port conflicts and access your app and services using consistent and predictable URLs.
-- **Proxyless endpoints**: Endpoints with `IsProxied` set to `false` connect directly to the resource instead of routing through an Aspire-managed proxy. Starting in Aspire 13.4, endpoints on persistent resources are proxyless by default. Proxyless endpoints must specify a target port so Aspire knows which port the resource listens on.
+- **Proxyless endpoints**: Endpoints with `IsProxied` set to `false` connect directly to the resource instead of routing through an Aspire-managed proxy. Starting in Aspire 13.4, endpoints on persistent executables and projects are proxyless by default. Persistent containers use proxied endpoints by default, the same as session containers.
- **Container networks**: Aspire creates and manages dedicated networks for container resources so containers can discover and communicate with each other during local development.
## How endpoints work
@@ -230,9 +230,9 @@ The underlying service still listens on its own port, and Aspire makes that allo
Most endpoints are proxied by default. A proxyless endpoint skips the Aspire-managed proxy and exposes the resource's listener directly. Use a proxyless endpoint when a proxy would interfere with the resource's lifetime or networking behavior.
-Starting in Aspire 13.4, endpoints on persistent resources are proxyless by default. This lets the persistent resource keep using its direct endpoint across AppHost runs instead of depending on a new proxy instance each time. This also ensures that the ports your service is reachable on stay stable whether the AppHost is running or not.
+Starting in Aspire 13.4, endpoints on persistent executables and projects are proxyless by default. This lets the persistent resource keep using its direct endpoint across AppHost runs instead of depending on a new proxy instance each time. This also ensures that the ports your service is reachable on stay stable whether the AppHost is running or not. Persistent containers use proxied endpoints by default, the same as session containers, so integrations that depend on endpoint allocation before startup continue to work.
-Because there's no proxy to listen on one port and forward to another, every proxyless endpoint must include a target port. The target port tells Aspire which port the resource listens on. You can also set `port` when you need a specific host port, but `targetPort` is still required for proxyless endpoints.
+The following example configures a proxyless HTTP endpoint for an nginx container:
@@ -260,6 +260,50 @@ await web.withHttpEndpoint({ targetPort: 80, isProxied: false });
+### Allocate ports for dynamic proxyless endpoints
+
+Proxyless endpoints can omit the public host `port`. Starting in Aspire 13.5, Aspire allocates a public host port for each dynamic proxyless endpoint before workload resources are created. The allocated port is then available to configuration that resolves endpoint properties, such as `GetEndpoint(...).Property(EndpointProperty.Port)`.
+
+If you set `port`, Aspire uses that value as the public host port. If you omit `port`, Aspire allocates one from the proxyless endpoint port range. The default range is `10000-32767`, and you can override it with the `ASPIRE_PROXYLESS_ENDPOINT_PORT_RANGE` environment variable in `start-end` format. Container endpoints still require `targetPort` so Aspire knows which port inside the container receives traffic.
+
+This lets you write patterns like the following, where a container exposes its allocated public port in an environment variable:
+
+
+
+
+```csharp title="AppHost.cs"
+var database = builder.AddContainer("database", "image")
+ .WithEndpoint(name: "tcp", targetPort: 5432, isProxied: false);
+
+database.WithEnvironment("PUBLIC_PORT", database.GetEndpoint("tcp").Property(EndpointProperty.Port));
+```
+
+
+
+
+```typescript title="apphost.mts" twoslash
+import { EndpointProperty, createBuilder } from './.aspire/modules/aspire.mjs';
+
+const builder = await createBuilder();
+
+const database = await builder.addContainer('database', {
+ image: 'image',
+ tag: 'latest',
+});
+await database.withEndpoint({ name: 'tcp', targetPort: 5432, isProxied: false });
+
+await database.withEnvironment('PUBLIC_PORT', database.getEndpoint('tcp').property(EndpointProperty.Port));
+```
+
+
+
+
+In this example, `PUBLIC_PORT` is set to the endpoint's allocated public host port before the container is created. Because the endpoint is proxyless and doesn't specify `port`, Aspire allocates a port from the proxyless endpoint port range.
+
+:::note
+For persistent resources, Aspire stores allocated proxyless endpoint ports in user secrets when user secrets are available and reuses them on later AppHost runs. If Aspire can't persist the allocated port, configure a fixed public `port` or use a proxied endpoint to avoid recreating the persistent resource on each run.
+:::
+
## Omit the host port
For proxied endpoints, when you omit the host port, Aspire generates a random port for both host and service port. This is useful when you want to avoid port conflicts and don't care about the host or service port. Consider the following code:
@@ -405,7 +449,7 @@ The `AllocatedEndpoint` property allows you to get or set the endpoint for a ser
on.
-The `Name` property identifies the service, whereas the `Port` and `TargetPort` properties specify the desired and listening ports, respectively. `TargetPort` is required for proxyless endpoints, including endpoints with `IsProxied` set to `false` and, starting in Aspire 13.4, endpoints on persistent resources by default.
+The `Name` property identifies the service, whereas the `Port` and `TargetPort` properties specify the desired and listening ports, respectively. `TargetPort` is required for proxyless endpoints, including endpoints with `IsProxied` set to `false` and, starting in Aspire 13.4, endpoints on persistent executables and projects by default.
For network communication, the `Protocol` property supports **TCP** and **UDP**, with potential for more in the future, and the `Transport` property indicates the transport protocol (**HTTP**, **HTTP2**, **HTTP3**). Lastly, if the service is URI-addressable, the `UriScheme` property provides the URI scheme for constructing the service URI.
diff --git a/src/frontend/src/content/docs/get-started/add-aspire-existing-app.mdx b/src/frontend/src/content/docs/get-started/add-aspire-existing-app.mdx
index 6b76b70d5..bbc4cdf7a 100644
--- a/src/frontend/src/content/docs/get-started/add-aspire-existing-app.mdx
+++ b/src/frontend/src/content/docs/get-started/add-aspire-existing-app.mdx
@@ -144,8 +144,8 @@ Use a file-based AppHost when you want a lightweight single-file orchestrator wi
3. Wire the resources in `apphost.cs`:
```csharp title="apphost.cs"
- #:sdk Aspire.AppHost.Sdk@13.2.0
- #:package Aspire.Hosting.Redis@13.2.0
+ #:sdk Aspire.AppHost.Sdk@%ASPIRE_VERSION%
+ #:package Aspire.Hosting.Redis@%ASPIRE_VERSION%
#pragma warning disable ASPIRECSHARPAPPS001
@@ -401,10 +401,10 @@ Common examples include Node.js apps, Vite frontends, Python workers, and Uvicor
```csharp title="apphost.cs — Existing services with hosting integrations"
-#:sdk Aspire.AppHost.Sdk@13.3.0
-#:package Aspire.Hosting.Redis@13.3.0
-#:package Aspire.Hosting.Python@13.3.0
-#:package Aspire.Hosting.JavaScript@13.3.0
+#:sdk Aspire.AppHost.Sdk@%ASPIRE_VERSION%
+#:package Aspire.Hosting.Redis@%ASPIRE_VERSION%
+#:package Aspire.Hosting.Python@%ASPIRE_VERSION%
+#:package Aspire.Hosting.JavaScript@%ASPIRE_VERSION%
var builder = DistributedApplication.CreateBuilder(args);
@@ -481,9 +481,9 @@ aspire add redis
```csharp title="apphost.cs — Existing containers and shared infrastructure"
-#:sdk Aspire.AppHost.Sdk@13.3.0
-#:package Aspire.Hosting.PostgreSQL@13.3.0
-#:package Aspire.Hosting.Redis@13.3.0
+#:sdk Aspire.AppHost.Sdk@%ASPIRE_VERSION%
+#:package Aspire.Hosting.PostgreSQL@%ASPIRE_VERSION%
+#:package Aspire.Hosting.Redis@%ASPIRE_VERSION%
var builder = DistributedApplication.CreateBuilder(args);
@@ -572,8 +572,8 @@ services:
```csharp title="apphost.cs" showLineNumbers
-#:sdk Aspire.AppHost.Sdk@13.3.0
-#:package Aspire.Hosting.PostgreSQL@13.3.0
+#:sdk Aspire.AppHost.Sdk@%ASPIRE_VERSION%
+#:package Aspire.Hosting.PostgreSQL@%ASPIRE_VERSION%
var builder = DistributedApplication.CreateBuilder(args);
diff --git a/src/frontend/src/content/docs/get-started/aspire-sdk.mdx b/src/frontend/src/content/docs/get-started/aspire-sdk.mdx
index 0e602be5a..ad75e3510 100644
--- a/src/frontend/src/content/docs/get-started/aspire-sdk.mdx
+++ b/src/frontend/src/content/docs/get-started/aspire-sdk.mdx
@@ -17,7 +17,7 @@ The [📦 Aspire.AppHost.Sdk](https://www.nuget.org/packages/Aspire.AppHost.Sdk)
The `Aspire.AppHost.Sdk` is defined in the top-level `Project` node's `Sdk` attribute:
```xml title='*.csproj file' {1}
-
+Exe
@@ -35,7 +35,7 @@ The `Aspire.AppHost.Sdk` is defined in the top-level `Project` node's `Sdk` attr
The `Aspire.AppHost.Sdk` is defined in a file-based app's source file using the `#:sdk` directive:
```csharp title='file-based app' {1}
-#:sdk Aspire.AppHost.Sdk@13.3.0
+#:sdk Aspire.AppHost.Sdk@%ASPIRE_VERSION%
var builder = DistributedApplication.CreateBuilder(args);
@@ -115,8 +115,8 @@ When `AspireUseCliBundle` is `true`:
- `Aspire.AppHost.Sdk` still sets AppHost properties and adds the implicit `Aspire.Hosting.AppHost` package.
- Your AppHost uses the DCP and Dashboard versions installed with the Aspire CLI.
- At build time, the SDK resolves the DCP and Dashboard executables in this priority order:
- 1. Explicit `AspireCliBundlePath` / `AspireCliPath` properties in the project file.
- 2. The `aspire` executable on `PATH`.
+ 1. Explicit `AspireCliBundlePath` / `AspireCliPath` properties in the project file.
+ 2. The `aspire` executable on `PATH`.
- If the bundle cannot be found, the build emits error `ASPIRE009` with a message directing you to [get.aspire.dev](https://get.aspire.dev) to install the Aspire CLI.
#### Enable the opt-in
diff --git a/src/frontend/src/content/docs/get-started/install-cli.mdx b/src/frontend/src/content/docs/get-started/install-cli.mdx
index b1d7abb1f..762b881eb 100644
--- a/src/frontend/src/content/docs/get-started/install-cli.mdx
+++ b/src/frontend/src/content/docs/get-started/install-cli.mdx
@@ -16,7 +16,7 @@ import {
Tabs,
} from '@astrojs/starlight/components';
-Aspire provides a command-line interface (CLI) tool to help you create and manage Aspire-based apps. The CLI streamlines your development workflow with an interactive-first experience. This guide shows you how to install the Aspire CLI on your system. Choose the package manager that fits your environment — Homebrew, npm, NuGet, WinGet, or mise — or use the install script for direct setup.
+Aspire provides a command-line interface (CLI) tool to help you create and manage Aspire-based apps. The CLI streamlines your development workflow with an interactive-first experience. This guide shows you how to install the Aspire CLI on your system. Choose the package manager that fits your environment — Homebrew, npm, NuGet, WinGet, mise, or Nix — or use the install script for direct setup.
:::tip[Using VS Code?]
The [Aspire VS Code extension](/get-started/aspire-vscode-extension/) can install the CLI for you. From the Command Palette, run:
@@ -95,6 +95,68 @@ Install the Aspire CLI with [mise](https://mise.jdx.dev/) when you manage develo
mise use -g aspire
```
+
+
+
+Install or run the Aspire CLI from the official Nix flake on Linux and macOS. Nix 2.4 or later with flakes enabled is required.
+
+Run the Aspire CLI directly without installing it:
+
+```bash title="Run with Nix"
+nix run github:microsoft/aspire#aspire-cli
+```
+
+Add the Aspire CLI to your Nix profile for persistent access:
+
+```bash title="Add to Nix profile"
+nix profile add github:microsoft/aspire#aspire-cli
+```
+
+To use the Aspire CLI in a project's `flake.nix`, consume the package output directly:
+
+```nix title="flake.nix — package output"
+{
+ inputs = {
+ nixpkgs.url = "github:NixOS/nixpkgs/";
+ aspire.url = "github:microsoft/aspire";
+ aspire.inputs.nixpkgs.follows = "nixpkgs";
+ };
+
+ outputs =
+ { nixpkgs, aspire, ... }:
+ let
+ systems = [
+ "aarch64-darwin"
+ "aarch64-linux"
+ "x86_64-darwin"
+ "x86_64-linux"
+ ];
+ forAllSystems = nixpkgs.lib.genAttrs systems;
+ in
+ {
+ devShells = forAllSystems (
+ system:
+ let
+ pkgs = nixpkgs.legacyPackages.${system};
+ in
+ {
+ default = pkgs.mkShell {
+ packages = [
+ aspire.packages.${system}.aspire-cli
+ ];
+ };
+ }
+ );
+ };
+}
+```
+
+The Aspire CLI version is pinned by the `aspire` flake input revision and the `eng/nix/versions.json` manifest at that commit. Pin a specific CLI version by locking the `aspire` input to a particular Git revision.
+
+:::note
+When installed via Nix, `aspire update --self` prints the Nix update commands to run rather than downloading a new binary, because the Nix store is read-only. See [`aspire update`](/reference/cli/commands/aspire-update/) for details.
+:::
+
@@ -136,7 +198,7 @@ aspire --version
If that command works, you're presented with the version of the Aspire CLI tool:
```bash title="Aspire CLI — Output" data-disable-copy
-13.4.0+{commitSHA}
+%ASPIRE_VERSION%+{commitSHA}
```
The `+{commitSHA}` suffix indicates the specific commit from which the Aspire CLI was built.
diff --git a/src/frontend/src/content/docs/hi/index.mdx b/src/frontend/src/content/docs/hi/index.mdx
index 0e4933c40..7c1d2f9b1 100644
--- a/src/frontend/src/content/docs/hi/index.mdx
+++ b/src/frontend/src/content/docs/hi/index.mdx
@@ -11,7 +11,7 @@ prev: false
next: false
banner:
content: |
- 🚀 Aspire 13.4 रिलीज़ हो गया है! — Aspire 13.4 में नया क्या है देखें।
+ ✨ Aspire 13.5 रिलीज़ हो गया है! — Aspire 13.5 में नया क्या है देखें।
hero:
tagline: आपका स्टैक, सरल।
फ्रंटएंड, API, कंटेनर और डेटाबेस को आसानी से ऑर्केस्ट्रेट करें—बिना रीराइट, बिना सीमा। किसी भी प्रोजेक्ट को शक्ति देने के लिए Aspire को विस्तारित करें।
image:
diff --git a/src/frontend/src/content/docs/id/index.mdx b/src/frontend/src/content/docs/id/index.mdx
index 4ed94afcc..1bdcb69c8 100644
--- a/src/frontend/src/content/docs/id/index.mdx
+++ b/src/frontend/src/content/docs/id/index.mdx
@@ -11,7 +11,7 @@ prev: false
next: false
banner:
content: |
- 🚀 Aspire 13.4 telah dirilis! — Lihat apa yang baru di Aspire 13.4.
+ ✨ Aspire 13.5 telah dirilis! — Lihat apa yang baru di Aspire 13.5.
hero:
tagline: Stack Anda, disederhanakan.
Orkestrasi frontend, API, container, dan database dengan mudah—tanpa menulis ulang, tanpa batasan. Perluas Aspire untuk mendukung proyek apa pun.
image:
diff --git a/src/frontend/src/content/docs/index.mdx b/src/frontend/src/content/docs/index.mdx
index ae592928b..bcbc548c9 100644
--- a/src/frontend/src/content/docs/index.mdx
+++ b/src/frontend/src/content/docs/index.mdx
@@ -11,7 +11,7 @@ prev: false
next: false
banner:
content: |
- Aspire 13.4 is here! — See what's new
+ ✨ Aspire 13.5 is available! — Explore the latest features and improvements
hero:
tagline: Your stack, streamlined.
Orchestrate frontends, APIs, containers, and databases effortlessly—no rewrites, no limits. Extend Aspire to power any project. Free, open-source, and agent ready.
image:
diff --git a/src/frontend/src/content/docs/integrations/ai/github-models/github-models-connect.mdx b/src/frontend/src/content/docs/integrations/ai/github-models/github-models-connect.mdx
index f62b055b7..7b68bd694 100644
--- a/src/frontend/src/content/docs/integrations/ai/github-models/github-models-connect.mdx
+++ b/src/frontend/src/content/docs/integrations/ai/github-models/github-models-connect.mdx
@@ -19,6 +19,10 @@ import githubIcon from '@assets/icons/github-icon.png';
data-zoom-off
/>
+:::caution[Integration deprecated]
+The GitHub Models service is **no longer available to new customers**. The `Aspire.Hosting.GitHub.Models` integration is sunset as of Aspire 13.5. All public APIs are marked `[Obsolete]` and the package no longer appears in `aspire add` output. The package will ship one final obsolete release on NuGet and will be removed entirely in a future version. For new and existing apps, use the [Azure AI Foundry integration](/integrations/cloud/azure/azure-ai-foundry/azure-ai-foundry-get-started/) instead, which provides access to a broad catalog of models — including OpenAI's GPT models — and supports local development with `RunAsFoundryLocal()`. See [microsoft/aspire#18402](https://github.com/microsoft/aspire/issues/18402) for details.
+:::
+
This page describes how consuming apps connect to a GitHub Model resource that's already modeled in your AppHost. For the AppHost API surface — adding a model resource, API key parameters, organization configuration, and health checks — see [GitHub Models hosting integration](../github-models-host/).
When you reference a GitHub Model resource from your AppHost, Aspire injects the connection information into the consuming app as environment variables. Your app can either read those environment variables directly — the pattern works the same from any language — or, in C#, use the Aspire client integrations for automatic dependency injection, health checks, and telemetry.
diff --git a/src/frontend/src/content/docs/integrations/ai/github-models/github-models-get-started.mdx b/src/frontend/src/content/docs/integrations/ai/github-models/github-models-get-started.mdx
index be3d232f2..e78f6db59 100644
--- a/src/frontend/src/content/docs/integrations/ai/github-models/github-models-get-started.mdx
+++ b/src/frontend/src/content/docs/integrations/ai/github-models/github-models-get-started.mdx
@@ -17,6 +17,10 @@ import githubIcon from '@assets/icons/github-icon.png';
data-zoom-off
/>
+:::caution[Integration deprecated]
+The GitHub Models service is **no longer available to new customers**. The `Aspire.Hosting.GitHub.Models` integration is sunset as of Aspire 13.5. All public APIs are marked `[Obsolete]` and the package no longer appears in `aspire add` output. The package will ship one final obsolete release on NuGet and will be removed entirely in a future version. For new and existing apps, use the [Azure AI Foundry integration](/integrations/cloud/azure/azure-ai-foundry/azure-ai-foundry-get-started/) instead, which provides access to a broad catalog of models — including OpenAI's GPT models — and supports local development with `RunAsFoundryLocal()`. See [microsoft/aspire#18402](https://github.com/microsoft/aspire/issues/18402) for details.
+:::
+
[GitHub Models](https://github.com/marketplace/models) provides access to a broad catalog of AI models — including OpenAI's GPT models, DeepSeek, Microsoft's Phi models, and more — through GitHub's infrastructure and your existing GitHub token. The Aspire GitHub Models integration lets you model a GitHub Model resource as a first-class resource in your AppHost, then hand the connection information to any consuming app — regardless of language.
## Why use GitHub Models with Aspire
diff --git a/src/frontend/src/content/docs/integrations/ai/github-models/github-models-host.mdx b/src/frontend/src/content/docs/integrations/ai/github-models/github-models-host.mdx
index a9053fabd..ff423f159 100644
--- a/src/frontend/src/content/docs/integrations/ai/github-models/github-models-host.mdx
+++ b/src/frontend/src/content/docs/integrations/ai/github-models/github-models-host.mdx
@@ -17,6 +17,10 @@ import githubIcon from '@assets/icons/github-icon.png';
data-zoom-off
/>
+:::caution[Integration deprecated]
+The GitHub Models service is **no longer available to new customers**. The `Aspire.Hosting.GitHub.Models` integration is sunset as of Aspire 13.5. All public APIs are marked `[Obsolete]` and the package no longer appears in `aspire add` output. The package will ship one final obsolete release on NuGet and will be removed entirely in a future version. For new and existing apps, use the [Azure AI Foundry integration](/integrations/cloud/azure/azure-ai-foundry/azure-ai-foundry-get-started/) instead, which provides access to a broad catalog of models — including OpenAI's GPT models — and supports local development with `RunAsFoundryLocal()`. See [microsoft/aspire#18402](https://github.com/microsoft/aspire/issues/18402) for details.
+:::
+
This article is the reference for the Aspire GitHub Models hosting integration. It enumerates the AppHost APIs — with examples for both `AppHost.cs` and `apphost.mts` — that you use to model GitHub Model resources in your [`AppHost`](/get-started/app-host/) project.
If you're new to the GitHub Models integration, start with the [Get started with GitHub Models integrations](/integrations/ai/github-models/github-models-get-started/) guide. For how consuming apps read the connection information this page exposes, see [Connect to GitHub Models](../github-models-connect/).
diff --git a/src/frontend/src/content/docs/integrations/cloud/azure/azure-ai-foundry/azure-ai-foundry-host.mdx b/src/frontend/src/content/docs/integrations/cloud/azure/azure-ai-foundry/azure-ai-foundry-host.mdx
index 606e9fff3..eb0d27c5b 100644
--- a/src/frontend/src/content/docs/integrations/cloud/azure/azure-ai-foundry/azure-ai-foundry-host.mdx
+++ b/src/frontend/src/content/docs/integrations/cloud/azure/azure-ai-foundry/azure-ai-foundry-host.mdx
@@ -188,7 +188,7 @@ The preceding code:
- Adds an Azure AI Foundry resource named `foundry`.
- Adds a Foundry deployment resource named `chat` using the generated `FoundryModel.OpenAI.Gpt5Mini` descriptor.
-If you need a different generated model descriptor, use the corresponding nested type, such as `FoundryModel.Microsoft.Phi4Reasoning`:
+If you need a different generated model descriptor, use the corresponding nested type. The `FoundryModel` class organizes models by provider family: `FoundryModel.Microsoft`, `FoundryModel.OpenAI`, `FoundryModel.Cohere`, `FoundryModel.MistralAI`, and others. For example, to use `FoundryModel.Microsoft.Phi4Reasoning`:
@@ -663,9 +663,11 @@ await builder.build().run();
-When the AppHost starts up, the local foundry service is also started. This requires the local machine to have [Foundry Local](https://learn.microsoft.com/azure/ai-foundry/foundry-local/get-started) installed and running.
+When the AppHost starts, Aspire automatically starts the Foundry Local service using the `foundry` CLI (`foundry service start`). It then discovers the service endpoint, downloads and loads the specified models via CLI commands, and stops the service (`foundry service stop`) when the AppHost shuts down or is disposed. You do not need to pre-start Foundry Local manually.
-The `RunAsFoundryLocal` method configures the resource to run as an emulator. It downloads and loads the specified models locally. The method provides health checks for the local service and automatically manages the Foundry Local lifecycle.
+This requires the `foundry` CLI to be installed and available on `PATH`. For installation instructions, see [Foundry Local get started](https://learn.microsoft.com/azure/ai-foundry/foundry-local/get-started).
+
+The `RunAsFoundryLocal` method configures the resource to use the local service. It downloads and loads the specified models locally, provides health checks for the local service, and fully manages the Foundry Local lifecycle — including start and stop — through the `foundry` CLI.