diff --git a/slides/Slides.md b/slides/Slides.md
index 9e0d4c4..5ac543c 100644
--- a/slides/Slides.md
+++ b/slides/Slides.md
@@ -20,7 +20,7 @@ footer: '@Chris_L_Ayers - https://chris-ayers.com'
## Chris Ayers
-### Principal Software Engineer
Azure CXP AzRel
Microsoft
+### Principal Software Engineer
Azure EngOps AzRel
Microsoft
BlueSky: [@chris-ayers.com](https://bsky.app/profile/chris-ayers.com)
LinkedIn: - [chris\-l\-ayers](https://linkedin.com/in/chris-l-ayers/)
@@ -37,9 +37,6 @@ footer: '@Chris_L_Ayers - https://chris-ayers.com'
Your team doesn't use one language β it uses **five**.
-
-
-
**Your stack today**
- Python ML services
- Go microservices
@@ -47,21 +44,24 @@ Your team doesn't use one language β it uses **five**.
- TypeScript/React frontends
- .NET backend APIs
-
-
+**The question:** How do you orchestrate, observe, and wire all of this **from one place**?
-**Your orchestration pain**
-- Docker Compose: manual port wiring, no built-in telemetry, no health-aware startup ordering
-- Each language has its own config format (`.env`, YAML, `application.properties`...)
-- No unified observability β good luck tracing a request across 4 services
-- 15-step README to run locally β "just `docker-compose up`" never works first time
+
-
-
+---
-**The question:** How do you orchestrate, observe, and wire all of this **from one place**?
+
-
+# The Orchestration Pain
+
+**Five stacks. Five toolchains. Zero shared model.**
+
+- π’ **Docker Compose** β manual port wiring, no built-in telemetry, no health-aware startup ordering
+- π **Config sprawl** β `.env`, YAML, `application.properties`, `appsettings.json` β each language has its own format
+- π **No unified observability** β good luck tracing a request across four services in three runtimes
+- π **15-step READMEs** β "just `docker-compose up`" never works first time
+
+
---
@@ -110,26 +110,30 @@ Logs, traces, metrics, and health for every resource β powered by OpenTelemetr
---
-
+
# One Orchestrator for Every Language
**Aspire gives you three things, regardless of language:**
-
-
-
π― **Orchestration**
-Define your entire stack β Python, Go, Java, TypeScript, .NET β in one AppHost file
+Define your entire stack β Python, Go, Java, TypeScript, .NET β in one AppHost file.
π‘ **Service Discovery**
-Endpoints and connection strings auto-injected as environment variables
+Endpoints and connection strings auto-injected as environment variables.
π **Observability**
-One dashboard for logs, traces, and metrics across ALL services via OpenTelemetry
+One dashboard for logs, traces, and metrics across **all** services via OpenTelemetry.
-
-
+
+
+---
+
+
+
+# Your Stack in One File
+
+**One C# AppHost wires Python, React, and .NET β auto-discovery, observability, lifecycle:**
```csharp
var builder = DistributedApplication.CreateBuilder(args);
@@ -153,10 +157,7 @@ builder.AddProject
("api")
builder.Build().Run();
```
-
-
-
-
+
---
@@ -189,35 +190,37 @@ Aspire sets `OTEL_EXPORTER_OTLP_ENDPOINT` automatically β add OpenTelemetry to
---
-
+
# Standalone Dashboard β No AppHost Required
### Already on OTEL? Get the dashboard with zero rewrites.
-
-
-
**The Aspire Dashboard ships as a standalone container.** Point any OTLP-emitting app at it and you get logs, traces, and metrics β no AppHost, no .NET, no commitment.
-- β
**Node.js / Python / Java / Go / Rust** β anything with an OTEL SDK works.
-- β
**Same UI** as the AppHost-managed dashboard.
-- β
**Local-only by default** β OTLP endpoint and dashboard auth keys printed at startup.
-- β
**Use it in CI**, in a Dockerfile, in `docker-compose.yml`, or attached to a Kubernetes pod.
-
-**Migration path:** start with the standalone dashboard for observability today; adopt the AppHost later when you want service discovery, integrations, and `aspire deploy`.
-
-
-
+- β
**Node.js / Python / Java / Go / Rust** β anything with an OTEL SDK works
+- β
**Same UI** as the AppHost-managed dashboard
+- β
**Local-only by default** β OTLP endpoint and dashboard auth keys printed at startup
+- β
**Use it in CI**, in a Dockerfile, in `docker-compose.yml`, or attached to a Kubernetes pod
-**Run it standalone**
```bash
docker run --rm -it -p 18888:18888 -p 4317:18889 \
-d --name aspire-dashboard \
mcr.microsoft.com/dotnet/aspire-dashboard:latest
```
-**Point your Node.js app at it**
+Open `http://localhost:18888` β done.
+
+
+
+---
+
+
+
+# Wire Any OTEL App in 30 Seconds
+
+**Point your Node.js app at the standalone dashboard:**
+
```javascript
// otel.js
import { NodeSDK } from "@opentelemetry/sdk-node";
@@ -233,25 +236,18 @@ const sdk = new NodeSDK({
sdk.start();
```
-Open `http://localhost:18888` β your traces show up live.
-
-
-
+**Migration path:** start with the standalone dashboard for observability today; adopt the AppHost later when you want service discovery, integrations, and `aspire deploy`.
-
+
---
-# OpenTelemetry Setup by Language
+# OpenTelemetry β Python
-**Add OpenTelemetry once per service β Aspire handles the rest:**
-
-
-
+**Wire OTEL once. Aspire injects `OTEL_EXPORTER_OTLP_ENDPOINT` automatically.**
-**Python:**
```python
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc \
@@ -270,10 +266,18 @@ provider.add_span_processor(
trace.set_tracer_provider(provider)
```
-
-
+**One block of boilerplate** β and your Python service is in the dashboard.
+
+
+
+---
+
+
+
+# OpenTelemetry β Node.js
+
+**Same idea, different runtime β and you get auto-instrumentation for free.**
-**Node.js:**
```javascript
const { NodeSDK } =
require('@opentelemetry/sdk-node');
@@ -291,12 +295,9 @@ const sdk = new NodeSDK({
sdk.start();
```
-
-
-
-**Aspire sets `OTEL_EXPORTER_OTLP_ENDPOINT` automatically** β add this boilerplate once per service and traces flow to the dashboard.
+**Auto-instrumentations** capture Express, Fastify, fetch, pg, redis β without code changes.
-
+
---
@@ -373,9 +374,9 @@ String apiUrl = System.getenv("services__api__http__0");
-# Connection Strings
+# Connection Strings β The Pattern
-**Infrastructure resources get connection strings automatically:**
+**Infrastructure resources get connection strings as environment variables:**
```bash
# Pattern: CONNECTIONSTRINGS__
@@ -384,26 +385,41 @@ CONNECTIONSTRINGS__db=Host=localhost;Port=5432;Username=postgres;Password=...
CONNECTIONSTRINGS__messaging=localhost:9092
```
-**Same pattern in every language:**
-
+**Python β Redis:**
```python
-# Python β Redis
-client = redis.from_url(f"redis://{os.environ['CONNECTIONSTRINGS__cache']}")
+client = redis.from_url(
+ f"redis://{os.environ['CONNECTIONSTRINGS__cache']}"
+)
```
+**Aspire injects the env var** β your service reads it using its language's standard mechanism.
+
+
+
+---
+
+
+
+# Same Pattern, Every Language
+
+**Node.js β Kafka:**
```javascript
-// Node.js β Kafka
-const kafka = new Kafka({ brokers: [process.env.CONNECTIONSTRINGS__messaging] });
+const kafka = new Kafka({
+ brokers: [process.env.CONNECTIONSTRINGS__messaging]
+});
```
+**Java β PostgreSQL:**
```java
-// Java β PostgreSQL via env vars
-String url = "jdbc:postgresql://" + System.getenv("PG_HOST") + ":" + System.getenv("PG_PORT") + "/" + System.getenv("PG_DB");
+String url = "jdbc:postgresql://"
+ + System.getenv("PG_HOST") + ":"
+ + System.getenv("PG_PORT") + "/"
+ + System.getenv("PG_DB");
```
-**Aspire injects environment variables** β your service reads them using its language's standard env var mechanism.
+**Go, Rust, .NET, PowerShell** β same pattern. Aspire sets the env var; your code reads it.
-
+
---
@@ -473,14 +489,14 @@ await builder.build().run();
-# AppHost Languages
+# Two AppHost Languages
-**Author your AppHost in C# or TypeScript today. Orchestrate workloads in any language.**
+**Author your AppHost in C# or TypeScript today β both officially supported.**
-π **C# (.NET)** β `AppHost.cs` Β· **Official**
+π **C# (.NET)** β `AppHost.cs`
```csharp
var builder = DistributedApplication
.CreateBuilder(args);
@@ -495,7 +511,7 @@ Best fit: teams already on .NET tooling.
-π¦ **TypeScript** β `apphost.ts` Β· **Official**
+π¦ **TypeScript** β `apphost.ts`
```typescript
const builder = await createBuilder();
@@ -510,37 +526,42 @@ Best fit: Node.js / TypeScript workspaces.
-**Same model, different syntax.** Both produce the same dashboard, service discovery, health checks, and deployment artifacts. The TypeScript SDK is auto-generated from the same .NET hosting integrations via the **Aspire Type System (ATS)** β there's no separate integration surface to maintain.
+**Same model, different syntax.** Both produce the same dashboard, service discovery, health checks, and deployment artifacts.
-**Workloads inside the AppHost** can be written in C#, JavaScript, Python, Go, Java, Rust, PowerShell, and more β via `AddProject`, `AddJavaScriptApp`, `AddPythonApp`, `AddDockerfile`, `AddContainer`, or `AddExecutable`. Pick the AppHost language that fits your repo; your services don't need to match.
-
-
+
---
-
+
-# Multi-Language Integrations
+# Workloads Can Be Anything
-### Write the integration once. Use it from C# or TypeScript.
+**Pick the AppHost language that fits your repo. Your services don't need to match.**
-
-
+The TypeScript SDK is auto-generated from the same .NET hosting integrations via the **Aspire Type System (ATS)** β there's no separate integration surface to maintain.
-**Aspire Type System (ATS)** β the contract that bridges .NET and guest languages.
+**Workloads inside the AppHost** can be written in:
-- Author your hosting integration **in C#** as you always have.
-- Annotate exported APIs with ATS attributes β `[AspireExport]`, `[AspireExportType]`, `[AspireExportMethod]`.
-- Aspire's analyzer validates the export shape at build time.
-- The CLI **auto-generates a TypeScript SDK** into `.modules/` when a TS AppHost runs `aspire add `.
-- TypeScript callers get fluent, typed methods β same model, different syntax.
+- C#, JavaScript, TypeScript, Python, Go, Java, Rust, PowerShell, and more
-**The trade-off:** the guest process talks to the .NET host over a local JSON-RPC socket (Unix socket / named pipe), authenticated with a per-session token. One IPC hop, no port exposure, no duplicated integration code per language.
+via `AddProject`, `AddJavaScriptApp`, `AddPythonApp`, `AddDockerfile`, `AddContainer`, or `AddExecutable`.
-
-
+
+
+---
+
+
+
+# Write Once with ATS
+
+### Author the integration in C#. Use it from any AppHost.
+
+**Aspire Type System (ATS)** β the contract that bridges .NET and guest languages.
+
+- Author your hosting integration **in C#** as you always have
+- Annotate exported APIs with ATS attributes β `[AspireExport]`, `[AspireExportType]`, `[AspireExportMethod]`
+- Aspire's analyzer validates the export shape at build time
-**C# author**
```csharp
[AspireExport]
public static class MyIntegrationExtensions
@@ -553,7 +574,16 @@ public static class MyIntegrationExtensions
}
```
-**TypeScript caller β zero hand-written bindings**
+
+
+---
+
+
+
+# Use From TypeScript β Zero Bindings
+
+**The CLI auto-generates a TypeScript SDK** into `.modules/` when a TS AppHost runs `aspire add `. TypeScript callers get fluent, typed methods.
+
```typescript
import { createBuilder } from "./.modules/aspire.js";
import { addMyService } from
@@ -563,12 +593,11 @@ const builder = await createBuilder();
const svc = await addMyService(builder, "svc");
```
-*Status: preview feature in 13.x.*
+**The trade-off:** the guest process talks to the .NET host over a local JSON-RPC socket (Unix socket / named pipe), authenticated with a per-session token. One IPC hop, no port exposure, no duplicated integration code per language.
-
-
+*Status: preview feature in 13.x.*
-
+
---
@@ -622,17 +651,22 @@ const svc = await addMyService(builder, "svc");
# `aspire.config.json` β One Config for Every Language
+**This file tells the CLI which language your AppHost uses.**
+
-**This file tells the CLI which language your AppHost uses:**
-
```json
{
- "appHost": { "path": "apphost.py", "language": "python" },
+ "appHost": {
+ "path": "apphost.py",
+ "language": "python"
+ },
"sdk": { "version": "13.2.0" },
"channel": "stable",
- "features": { "polyglotSupportEnabled": true },
+ "features": {
+ "polyglotSupportEnabled": true
+ },
"profiles": {
"default": {
"applicationUrl": "https://localhost:17000"
@@ -652,19 +686,36 @@ const svc = await addMyService(builder, "svc");
- Feature flags use **boolean `true`** (not string `"true"`)
-
+
-**Manage from CLI:**
-- `aspire config list` / `get` / `set`
-- `aspire secret set/list/get/delete`
-- `aspire certs clean/trust`
+
-**Every sample in this talk** has one at its root.
+---
-
-
+
-
+# Manage Config From the CLI
+
+**No manual JSON editing required:**
+
+```bash
+# Read & write config values
+aspire config list
+aspire config get
+aspire config set
+
+# Manage secrets (encrypted at rest)
+aspire secret set
+aspire secret list / get / delete
+
+# Local dev certs
+aspire certs clean
+aspire certs trust
+```
+
+**Every sample in this talk** has an `aspire.config.json` at its root β peek inside.
+
+
---
@@ -706,15 +757,12 @@ aspire export # Capture to zip
---
-
+
# Agent-Ready CLI
### Two MCP servers. One model. Any agent.
-
-
-
**MCP support out of the box β no plugins, no glue.**
- π **CLI MCP** β stdio. Agent spawns `aspire agent mcp` as a subprocess. Set up by `aspire agent init`.
@@ -724,8 +772,15 @@ aspire export # Capture to zip
**Polyglot bonus:** the agent sees Python tracebacks, Go panics, Java stack traces, and Node errors through the same OTEL pipeline.
-
-
+
+
+---
+
+
+
+# Wire It Up in 30 Seconds
+
+**One-time setup, then any MCP-aware agent works:**
```bash
# 1. One-time setup in your AppHost dir
@@ -749,10 +804,7 @@ $ code . # reads .vscode/mcp.json
} } }
```
-
-
-
-
+
---
@@ -893,9 +945,33 @@ $ code . # reads .vscode/mcp.json
-# From Dev to Production
+# Same Model, Two Commands
+
+**One AppHost. Local, staging, production.**
+
+```bash
+aspire run # Local development
+aspire deploy # Deploy to target (Preview)
+aspire publish # Generate artifacts (Preview)
+aspire do # Pipeline step (Preview)
+```
+
+**What Aspire generates from your AppHost:**
+
+- π³ Container images for **all languages**
+- βΈοΈ Azure Container Apps / Kubernetes manifests
+- π Infrastructure wiring (Redis, Postgres, Kafkaβ¦)
+- π Service connections + environment variables
+
+**No separate deploy config.** The AppHost is the contract.
+
+
+
+---
+
+
-**Same AppHost model** for local, staging, and production β one arc, two commands.
+# What That Looks Like
@@ -911,6 +987,9 @@ $ code . # reads .vscode/mcp.json
Dashboard: http://localhost:15888
```
+
+
+
**`aspire deploy` (target)**
```text
β Building images for python, node, .net
@@ -920,29 +999,12 @@ Dashboard: http://localhost:15888
β
Deployed to dev environment in 4m 12s
```
-
-
-
-**Same model, two commands:**
-```bash
-aspire run # Local development
-aspire deploy # Deploy to target (Preview)
-aspire publish # Generate artifacts (Preview)
-aspire do # Pipeline step (Preview)
-```
-
-**What Aspire generates from your AppHost:**
-- Container images for all languages
-- Azure Container Apps / Kubernetes manifests
-- Infrastructure wiring (Redis, Postgres, Kafkaβ¦)
-- Service connections + environment variables
-
-**No separate deploy config.** The AppHost is the contract.
-
-
+**Same code-first AppHost drives both.**
+
+
---