CommunityToolkit / CommunityToolkit/Aspire
[Integration]: Temporal hosting and client integrations
- Dominant language
- C#
- Stars
- 627
- Forks
- 196
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 35
Description
### Aspire issue link
_No response_
### Overview
[Temporal](https://temporal.io) is a durable execution platform for workflows. There is currently no Temporal integration in the toolkit, so Aspire users have to hand-roll a container resource for the dev server and hand-wire the client.
This proposes two integrations, donated from packages that already exist and are published on NuGet:
- **`CommunityToolkit.Aspire.Hosting.Temporal`** — runs the Temporal CLI dev server (`temporalio/admin-tools`) as a container resource, exposing the gRPC frontend (7233) and Web UI (8233), with the full `temporal server start-dev` flag surface, data persistence helpers, connection properties, and a health check.
- **`CommunityToolkit.Aspire.Temporal.Client`** — registers `ITemporalClient` and hosted Temporal workers from the Aspire connection string, with settings bound from `Aspire:Temporal:Client`, keyed clients, a health check, self-wired OpenTelemetry, and **mTLS + API key** authentication for Temporal Cloud and self-hosted clusters.
### Usage example
AppHost:
```csharp
var builder = DistributedApplication.CreateBuilder(args);
var temporal = builder.AddTemporalServer("temporal")
.WithLogFormat(TemporalLogFormat.Json)
.WithNamespace("greetings")
.WithDataVolume();
builder.AddProject("worker")
.WithReference(temporal)
.WaitFor(temporal);
builder.Build().Run();
```
Worker:
```csharp
builder.AddTemporalWorker("temporal", taskQueue: "greeting-tasks")
.AddWorkflow()
.AddScopedActivities();
```
Consumer starting a workflow:
```csharp
builder.AddTemporalClient("temporal", settings => settings.Namespace = "greetings");
// ...
app.MapPost("/greet", async (GreetRequest request, ITemporalClient client) =>
{
var handle = await client.StartWorkflowAsync(
(GreetingWorkflow wf) => wf.RunAsync(request.Name),
new WorkflowOptions(id: $"greeting-{Guid.NewGuid():N}", taskQueue: "greeting-tasks"));
return Results.Ok(await handle.GetResultAsync());
});
```
Temporal Cloud, via the client integration:
```csharp
builder.AddTemporalClient("temporal", settings =>
{
settings.Namespace = "my-namespace.a1b2c";
settings.ApiKey = builder.Configuration["Temporal:ApiKey"]; // implies TLS
});
```
### Additional context
**Origin and licensing.** This is a donation of the published [`InfinityFlow.Aspire.Temporal`](https://www.nuget.org/packages/InfinityFlow.Aspire.Temporal) and [`InfinityFlow.Aspire.Temporal.Client`](https://www.nuget.org/packages/InfinityFlow.Aspire.Temporal.Client) packages, originally created by @ElanHasson and [sains1](https://github.com/sains1) and MIT licensed. The original authors consent to the port under the toolkit's licence and governance, and ask only for origin credit in the package READMEs.
**Implementation is already done and green**, on a branch ready to become a PR whenever you're happy with the proposal:
https://github.com/ElanHasson/Aspire/tree/feature/temporal-integration
- 45 files, ~3,400 lines. Both packages, `examples/temporal/` (AppHost + ApiService + Worker + ServiceDefaults), and two xUnit v3 test projects.
- 87 tests passing (56 hosting incl. 6 `[RequiresDocker]` container tests, 31 client). The container tests cover a real end-to-end workflow execution and data-volume persistence across a restart.
- Multi-targets net8.0/net9.0/net10.0, central package management, `[AspireExport]` coverage on the hosting surface, XML docs throughout, no hand-written `api/` files.
**A few deliberate deviations from a straight port**, which I'd value your opinion on:
- Enums are prefixed (`TemporalLogLevel`, not `LogLevel`) — the unprefixed names would collide with `Microsoft.Extensions.Logging.LogLevel` in `Aspire.Hosting.ApplicationModel`.
- `WithServicePort`/`WithUiPort`/`WithHttpPort` renamed to `WithHostPort`/`WithUiHostPort`/`WithHttpApiEndpoint` per the naming guidance.
- The gRPC endpoint is declared `http` + http2 transport rather than `https`; the dev server serves plaintext h2c, and `AsHttp2Service()` would also mark the HTTP/1.1 Web UI.
- `WithDynamicConfigValue(string, object)` became four typed overloads with distinct `[AspireExport]` IDs. Happy to reshape into a union/DTO if you'd prefer one generated method name.
- Data volumes mount at `/home/temporal`, not `/data` — the image runs as uid 1000, and a named volume only inherits that ownership where the mount point already exists in the image.
**Out of scope for the first PR**, proposed as follow-ups:
- The executable-based dev server resource from the original package (`AddTemporalServerExecutable`), which shells out to a locally installed `temporal` CLI. The original package's README called it unreliable and cited dotnet/aspire#1637 and temporalio/cli#316, but **both of those are long closed**, so that justification no longer holds. I left it out to keep this PR to a single resource shape and because it needs a CLI-presence story for CI, not because of a known upstream blocker. Happy to include it here or as a follow-up — your call.
- A Temporal Cloud external-reference resource (`AddTemporalCloud`) with publish/deploy graphs. That's a different archetype with its own secret-parameter handling and deserves its own proposal.
### Help us help you
Yes, I'd like to be assigned to work on this item
Contributor guide
Research direction
Review the proposed implementation on the feature/temporal-integration branch, including examples/temporal/ and the two xUnit v3 test projects. Start by checking the hosting and client integration surfaces and the listed naming, transport, volume, and export decisions. Done means the proposal is accepted, the 87 tests remain passing, and the package scope is agreed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, docker
- Domain
- backend, devops, distributed-systems
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100