Quick Start Guide¶
Get the Cargonerds application running locally with a single command. The whole stack —
SQL Server, Redis, RabbitMQ, the database migrator, three ABP .NET hosts and the Next.js
frontend — is orchestrated by the .NET Aspire
AppHost (src/Cargonerds.AppHost). You start that one project; it brings up everything else.
If you just want the short version
Clone → pwsh .githooks/install.ps1 → abp install-libs → generate openiddict.pfx →
dotnet run --project src/Cargonerds.AppHost → log in as admin / 1q2w3E*. The rest of
this page explains each step and what to do when something goes wrong.
How the local stack fits together¶
The AppHost is a DistributedApplication
whose resource graph is declared in src/Cargonerds.AppHost/Program.cs. In run mode
(local dotnet run / F5) every infrastructure dependency is a real Docker container and the
.NET hosts run as local processes. The wait edges below are what make the order deterministic:
the migrator must finish before Auth/Api start, and the frontend waits for the API.
flowchart TD
subgraph infra["Docker containers (persistent)"]
DB["spark-db<br/>SQL Server :14330"]
REDIS["spark-redis<br/>Redis (+ Insight/Commander)"]
MQ["spark-rabbitmq<br/>RabbitMQ (+ mgmt plugin)"]
end
MIG["db-migrator<br/>Cargonerds.DbMigrator<br/>(runs to completion)"]
AUTH["auth :44345<br/>Cargonerds.AuthServer"]
API["api :44354<br/>Cargonerds.HttpApi.Host"]
ADMIN["admin :44381<br/>Cargonerds.Blazor"]
RT["realtime :4200<br/>frontend/realtime (Next.js)"]
DB --> MIG
MIG -->|WaitForCompletion| AUTH
MIG -->|WaitForCompletion| API
REDIS --> AUTH
REDIS --> API
MQ --> AUTH
MQ --> API
AUTH --> API
AUTH --> ADMIN
API --> ADMIN
API --> RT
AUTH --> RT
You do not start SQL Server / Redis / RabbitMQ yourself, and you do not run the DbMigrator separately — Aspire does both. See Architecture Overview and Aspire Integration for the full picture, and Running Locally for environment toggles and infra-only workflows.
Prerequisites¶
Make sure you have the following installed (see Prerequisites for details and version pins):
- .NET 10 SDK + the .NET Aspire workload — Download
- Node.js 22.x (the
frontend/realtimeapp declares"engines": { "node": "22.x" }) — Download - Docker Desktop, running — Download
- Git and PowerShell (
pwsh) - ABP CLI —
dotnet tool install -g Volo.Abp.Cli(the repo pinsAbpVersion10.1.1 incommon.props)
Docker must be running before you start the AppHost
The final line of Program.cs is builder.Build().EnsureDockerRunningIfLocalDebug().Run();.
That Nextended.Aspire guard fails fast with a clear message if Docker Desktop is not running
during local debugging — so start Docker first.
1. Clone the repository¶
2. Install Git hooks and frontend deps¶
.githooks/install.ps1 does three things:
- Sets
core.hooksPathto.githooksso the Husky.Net pre-commit hook (CSharpier + Prettier) is active. - Runs
dotnet tool restoreto restore the local .NET tools (CSharpier + Husky). - Runs
npm iinsidefrontend/.
It also adds /.claude and .worktrees to .git/info/exclude (per-clone ignore for AI worktrees;
see AGENTS.md). The script requires PowerShell (pwsh); it ships natively on
Windows, and on Linux/macOS you install PowerShell Core. Formatting also runs automatically on every
commit — see the Development Workflow for what the hook formats and how
to run it manually.
3. Install ABP client-side libraries¶
This downloads the JavaScript/CSS libraries the Blazor/MVC UIs expect (the LeptonX theme assets and ABP's bundled scripts). ABP normally runs this when a solution is first created; after a fresh clone you must run it yourself, and again whenever a new client-side package dependency is added.
4. Generate the OpenIddict signing certificate¶
The AuthServer needs an openiddict.pfx certificate for token signing/encryption. From the repo root:
In Development, ABP's OpenIddict module uses a
development encryption/signing certificate automatically, so this file is primarily what
non-Development runs expect. CargonerdsAuthServerModule only calls
AddProductionEncryptionAndSigningCertificate("openiddict.pfx", configuration["AuthServer:CertificatePassPhrase"]!)
outside Development; the passphrase default lives in
src/Cargonerds.AuthServer/appsettings.json
(AuthServer:CertificatePassPhrase = d9efcd01-fbd8-42b1-a9d1-a5cf44afe714).
The default password is for development only
d9efcd01-fbd8-42b1-a9d1-a5cf44afe714 is the well-known development password and is fine for
local work. Use a different certificate and password in production — see the root README.md
(it also documents the extra Linux steps to trust the cert) and the
OpenIddict docs.
5. Configure connection strings and secrets¶
For a default local run you can skip this step — the solution ships with working defaults and Aspire injects the database connection string for you. Read this section only if you need to point at your own database or a real Hub data source.
What Aspire injects automatically¶
When you launch the AppHost, each .NET host is started with USE_ASPIRE_CONFIG=true (set by
AddProjectWithDefaults in src/Cargonerds.AppHost/Extensions/ResourceBuilderExtensions.cs). That
flag makes the host load its committed appsettings.aspire.json, whose {token} placeholders are
resolved to live Aspire endpoints/connection strings at startup. For example
src/Cargonerds.HttpApi.Host/appsettings.aspire.json resolves {auth}, {api}, {redis} and
{messaging} to the real containers and services Aspire just started. The Default (Spark) database
connection string is supplied by Aspire via .WithReference(db, "Default").
USE_ASPIRE_CONFIG is mandatory under the AppHost
Per EnvVars.UseAspireConfig: "If not set the default ABP configuration will be used which is not
compatible with the Aspire AppHost." The AppHost always sets it for the four hosted projects.
If you ever launch a single host directly (IIS Express / dotnet run on that project) you must
set USE_ASPIRE_CONFIG=true yourself, or it falls back to the static appsettings.json values.
The two databases¶
There are two connection strings, and they are selected independently:
| Connection string | Constant | Holds | Selected by |
|---|---|---|---|
Default |
ConnectionStringNames.SparkDb |
ABP Identity, OpenIddict, Settings, Features, AuditLogging, CmsKit, BlobStoring (DB) and Hangfire storage | Aspire db container locally; AZURE_ENVIRONMENT in the cloud |
Hub |
ConnectionStringNames.HubDb |
Hub domain DB (shipments, organizations, tracking, pricing) | SPARK_ENVIRONMENT (defaults to dev) |
The local default for Default (in the AuthServer/API appsettings.json) is LocalDB:
Server=(LocalDb)\MSSQLLocalDB;Database=CargonerdsAppDb;Trusted_Connection=True;TrustServerCertificate=true
— but under Aspire it is overridden by the injected container connection string. The Hub DB has no
local default; it comes from the appsettings.spark.<env>.json files. With SPARK_ENVIRONMENT
unset, SparkEnvironment.FromName(null) resolves to dev, so the backend talks to the Hub-Dev
database out of the box. See Configuration & appsettings for the full
layering and token system.
Running against your own local Hub database
To use a local Hub DB instead of Hub-Dev, set SPARK_ENVIRONMENT=local and create
src/Cargonerds.HttpApi.Host/appsettings.spark.local.user.json with your local
App:CargonerdsCustomerId (the file is git-ignored). Details and the full per-environment matrix
are in Running Locally.
Secrets / user-secrets¶
The only ABP user-secrets file in play is appsettings.secrets.json, which holds the
AbpLicenseCode and is loaded by ABP's AddAppSettingsSecretsJson() in each host's Program.cs.
The AppHost project carries a UserSecretsId (cd27754a-…) so you can store machine-local overrides
there if needed, but a default clone does not require populating it.
Committed credentials
Several appsettings.*.json files (appsettings.azure.*.json, appsettings.spark.*.json,
appsettings.rohlig.json, appsettings.secrets.json) contain real SQL/Redis/RabbitMQ
credentials, API keys and the ABP license that are checked into the repo. Treat them as sensitive,
and never re-commit rotated secrets. This is a known issue flagged in the configuration analysis.
6. Run the application¶
This single command:
- Provisions SQL Server (
spark-db, fixed TCP port14330), Redis (spark-redis, with RedisInsight + Redis Commander side-cars) and RabbitMQ (spark-rabbitmq, management plugin) as persistent Docker containers (ContainerLifetime.Persistent, so data survives restarts). - Runs the
db-migrator(Cargonerds.DbMigrator) to apply EF Core migrations and seed data, then waits for it to complete (WaitForCompletion) before starting Auth and Api. - Starts
auth(Cargonerds.AuthServer),api(Cargonerds.HttpApi.Host) andadmin(Cargonerds.Blazor). - Discovers and starts every frontend under
frontend/that has aDockerfile(currently the realtime Next.js app) viaAddAllFrontends(), wiringAPI_URL,AuthServer__Authority,APP_URLand the MapTiler key throughWithNextAuth. - Registers the documentation site as a container resource — but with
WithExplicitStart(), so it is not started automatically; start it from the dashboard when you want it. - Opens the Aspire dashboard, which lists the live URLs, health status, logs and distributed traces for every resource.
What comes up — services and run-mode ports¶
Always read live URLs from the Aspire dashboard; endpoints are assigned dynamically. In local run
mode these HTTPS ports are fixed in src/Cargonerds.AppHost/RunModeServicePorts.cs
(the realtime port is pinned in Extensions/DistributedAppBuilderExtensions.cs):
| Aspire resource | Project | URL | Notes |
|---|---|---|---|
auth |
Cargonerds.AuthServer |
https://localhost:44345 |
OpenIddict / login UI |
api |
Cargonerds.HttpApi.Host |
https://localhost:44354 |
REST API + Swagger |
admin |
Cargonerds.Blazor |
https://localhost:44381 |
LeptonX admin shell |
realtime |
frontend/realtime (Next.js) |
http://localhost:4200 |
the main app UI |
db-migrator |
Cargonerds.DbMigrator |
console | runs to completion, then exits |
documentation |
this MkDocs site | started on demand from the dashboard | WithExplicitStart() |
First run is slow
The first launch pulls the SQL Server / Redis / RabbitMQ images and runs all migrations, so it
takes noticeably longer than subsequent runs. The persistent data volumes (spark-db,
spark-redis) mean later starts are much faster — the migrator finds the schema already current.
Speeding up iteration¶
The AppHost reads two env vars (see EnvVars.cs) that relax the inter-service wait/health gating when
you are iterating on a single service:
SKIP_WAITING_IN_BACKEND=1— don't wait for migrator/auth/health before starting dependents.EXPLICIT_FRONTEND_START=1— require manually starting the frontends from the dashboard.
7. Log in¶
Use the seeded administrator account (created by the DbMigrator's identity seeding):
- Username:
admin(emailadmin@cargonerds.com) - Password:
1q2w3E*
Open the realtime app (http://localhost:4200) or the admin Blazor app
(https://localhost:44381) and sign in — both redirect to the AuthServer (auth) for
authentication via OpenIddict. The admin user is a
full administrator managed by ABP Identity.
Trust the dev HTTPS certificate
The backend hosts run on HTTPS. If your browser refuses the localhost certificate, trust the
ASP.NET Core dev cert (dotnet dev-certs https --trust) — see
Certificate errors below.
Verify the installation¶
- Aspire dashboard: every resource should show a healthy / running state, and
db-migratorshould show as completed. - API: the
apiresource exposes the ABP application-configuration endpoint at/api/abp/application-configuration(used as its readiness check); Swagger is at{api}/swagger. The AppHost also wires a/health-statuscheck viaWithHealthStatusCheck(). - Admin UI: the
adminresource has a/health check and should render the LeptonX shell after login.
Common issues¶
Docker not running¶
Start Docker Desktop before running the AppHost — EnsureDockerRunningIfLocalDebug() aborts
startup otherwise.
Port already in use¶
The fixed run-mode ports must be free: 44345 (auth), 44354 (api), 44381 (admin), 4200
(realtime), and the SQL container's TCP 14330. The SQL endpoint is un-proxied
(IsProxied = false), so a stray local SQL Server on 14330 will conflict. Stop whatever is bound to
the port, or adjust RunModeServicePorts.cs (and the FrontendPorts map for realtime).
Certificate errors¶
Then regenerate openiddict.pfx (step 4).
Service-discovery token error at startup¶
If a host throws an InvalidOperationException listing "Available: …" services, an
appsettings.aspire.json value references a {name} token that has no matching Aspire endpoint or
connection string. This usually means a resource was renamed/removed in Program.cs but the token was
left behind. See Configuration & appsettings.
Database / connection issues¶
Inspect the spark-db, spark-redis and spark-rabbitmq containers from the Aspire dashboard or
docker ps. The Default connection string is injected by Aspire; for Hub data, check your
SPARK_ENVIRONMENT value (defaults to dev). A typo'd SPARK_ENVIRONMENT crashes startup
(SparkEnvironment.FromName throws on unknown names) rather than silently defaulting — see
Running Locally.
Next steps¶
- Running Locally — environment variables, SPARK selection, infra-only workflows
- Architecture Overview
- Layered Architecture
- ABP Patterns
- Aspire Integration
- Configuration & appsettings
- Modules Overview
- Development Workflow
- Debugging