Run the backend (WebAPI + AdminUI) with .NET Aspire
One command brings up SQL Server + Azurite + WebAPI + AdminUI. Full guide: _docs/Aspire-Local-Dev.md.
Prerequisites (one-time)
- .NET 10 SDK —
global.jsonpins10.0.301(noworkloadVersionpin). - Aspire CLI ≥ 13.4.6:
dotnet tool install -g aspire(ordotnet tool update -g aspire). - Docker running (Aspire starts the SQL + Azurite containers).
Run it
aspire run # from the repo root — .aspire/settings.json targets src/AppHost
The dashboard opens automatically. SQL Server + Azurite come up as persistent containers with data volumes; WebAPI + AdminUI start once SQL is healthy; EF migrations apply on WebAPI startup.
- AdminUI → https://localhost:7137 (redirects to SSW Identity to sign in)
- WebAPI → https://localhost:5001 — health
https://localhost:5001/health, Swagger/swagger
Aspire CLI — agent guardrails (distilled)
aspire run is the human flow (foreground, opens the dashboard, blocks the terminal). When an
AI agent drives the AppHost, use the background lifecycle instead so you don't block and so locks
release cleanly. CLI is ≥ 13.4.6; all verbs below exist there.
aspire start --non-interactive # background; --isolated in a git worktree (random ports + isolated secrets)
aspire wait <resource> --non-interactive # block until ready — NEVER curl-poll a health endpoint
aspire ps # list running AppHosts/resources (--include-hidden for proxies/migrations)
aspire describe [<resource>] # inspect state/endpoints · aspire logs <resource> · aspire otel
aspire resource <resource> restart # one resource changed → restart/rebuild IT, don't bounce the whole AppHost
aspire stop # release file locks + ports when done
Rules that prevent agent self-harm:
- Never
dotnet runan AppHost — it bypasses orchestration (no dashboard, nowait/logs, orphaned procs). Useaspire start/aspire run. - File-lock build errors (
MSB3491/CS2012, "file in use") = Aspire is running, not a broken project. Fix:aspire stopfirst, then rebuild. Don't deletebin//obj/,killdotnet, or "reboot". - "Port already in use" →
aspire stopthenaspire start. - This is a worktree — prefer
aspire start --isolatedso it won't collide with another checkout's instance. aspire doctordiagnoses a broken environment (missing SDK/Docker/cert).
We deliberately don't vendor Microsoft's
aspire-skillsplugin (init/aspireify/deployment are irrelevant — our AppHost is wired and we deploy via Azure pipelines, notaspire publish). The guardrails above are the only genuinely useful distillation. Devs who want full Aspire CLI fluency can install it per-machine:copilot plugin install aspire@aspire-skills.
Secrets — one Keeper record, paste once
Keeper is the only external resource. Every stack secret flows from one store: the AppHost
user-secrets (id F76E3E10-FABB-4543-B949-549EEC500823). WebAPI/AdminUI have no
UserSecretsId of their own. Copy the single SSW.Rewards ▸ SSW.Rewards — Aspire Dev Secrets
record from Keeper, then:
rewards-dev secrets edit # opens secrets.json — paste the Keeper record, save
rewards-dev secrets check # ✓/✗ per required key; names exactly what's missing + its Keeper source
rewards-dev secrets path # just print the file path (open with open/notepad to paste manually)
secrets check exits non-zero until every key is present + non-placeholder — gate aspire run on it.
Same actions on the dashboard: mobile-app ▸ Secrets: Validate / Secrets: Open file. Required
keys: firebase-credentials, sendgrid-api-key, email-user, email-password, signing-authority,
sql-sa-password, mobile-google-services-json, mobile-google-service-info-plist. Aspire's own
AppHost:* keys are generated per-machine — not in Keeper.
Aspire injects the connection strings (ConnectionStrings:DefaultConnection/:HangfireConnection),
the Azurite blob string, Firebase/SendGrid/SMTP, and SigningAuthority into WebAPI as env vars.
Dashboard commands
On rewards-sql (DB + tooling): DB: Apply migrations / Add migration… · Install dotnet-ef ·
Install/upgrade Aspire CLI · Trust dev HTTPS cert · Diagnose (aspire doctor).
On mobile-app (virtual lifetime-less resource for the MAUI app): Secrets: Validate / Open file ·
Show current target · Switch API / identity target… · API → Tailscale (one-click) · Tailscale: Status ·
Sync mobile secrets (isolated) · MAUI workload restore · Update .NET workloads. The switch commands shell out to rewards-dev.
Switch which identity / API the apps use
./rewards-dev env <local|staging|prod> (or identity / api, api tailscale) from the repo root.
Writes git-ignored overrides for Mobile + AdminUI + WebAPI. ./rewards-dev show --json prints the
current effective targets (machine-readable); ./rewards-dev help is fully self-teaching.
./rewards-dev is a wrapper over dotnet run --project tools/RewardsDev.
Troubleshooting
sql-sa-password → ValueMissing, SQL never starts — secret parameters only resolve in the Development environment. The committedsrc/AppHost/Properties/launchSettings.jsonsets it; otherwise exportDOTNET_ENVIRONMENT=Development.MSB4242/ workload version not found — make sure you're on the updatedglobal.json(SDK10.0.301, noworkloadVersionpin).- Nothing starts — Docker isn't running.
Aspire.AppHost.Sdkrejected — must be ≥ 13.4.6 (already pinned in the AppHost csproj).