How to install DKNet.Minimal.Template, scaffold a new solution from it, and run, test, migrate,
and publish that solution.
dotnet nuget add source \
--username <YOUR_GITHUB_USERNAME> \
--password <YOUR_GITHUB_PAT_WITH_READ_PACKAGES> \
--store-password-in-clear-text \
--name github \
"https://nuget.pkg.github.com/baoduy/index.json"
dotnet new install DKNet.Minimal.Template --nuget-source "https://nuget.pkg.github.com/baoduy/index.json"
dotnet new dknet-minimal -n MyCompany.MyService
Every parameter below is declared in src/.template.config/template.json. Each one carries a
replaces token: a literal string that dotnet new finds and rewrites throughout the generated
tree. Passing a parameter does not set a variable — it substitutes text, so the default value you
see in this repo is exactly the string that gets replaced.
| Parameter | Flag | Datatype | Default | replaces token |
What it rewrites, and where |
|---|---|---|---|---|---|
| Name | -n, --name |
string | MyMinimalApp |
— (sourceName: Minimal) |
Not a replaces symbol. sourceName rewrites the identifier Minimal in every file name, folder name, namespace and project reference, and renames DKNet.Templates.sln to <Name>.sln. |
| Framework | --Framework |
choice (net10.0) |
net10.0 |
net10.0 |
<TargetFramework> in Directory.Packages.props, and the HintPath on the Aspire.Hosting reference in <Name>.AppHost.csproj. Only net10.0 is offered — the parameter exists so a later TFM can be added, not so you can target an older one. |
| AuthorName | --AuthorName |
string | Steven Hoang |
Steven Hoang |
<Authors> in Directory.Packages.props, inherited by every project. |
| CompanyUrl | --CompanyUrl |
string | https://drunkcoding.net |
https://drunkcoding.net |
<Company> in Directory.Packages.props. |
| RepositoryUrl | --RepositoryUrl |
string | https://github.com/baoduy/DKNet |
https://github.com/baoduy/DKNet |
<PackageProjectUrl> and <RepositoryUrl> in Directory.Packages.props. |
| TenantId | --TenantId |
string | 00000000-0000-0000-0000-000000000000 |
00000000-0000-0000-0000-000000000000 |
Authentication:Schemes:Bearer:MetadataAddress and :ValidIssuer in <Name>.Api/appsettings.json, plus the PlaceholderTenantGuid constant in <Name>.App.Tests/Architecture/AuthPlaceholderConfigTests.cs. Replace before enabling authorization. |
| ApiAudience | --ApiAudience |
string | api://your-api |
api://your-api |
The single entry in Authentication:Schemes:Bearer:ValidAudiences in <Name>.Api/appsettings.json. Replace before enabling authorization. |
dotnet new dknet-minimal -n Acme.OrderService \
--AuthorName "Jane Smith" \
--CompanyUrl "https://acme.com" \
--RepositoryUrl "https://github.com/acme/order-service" \
--TenantId "11111111-2222-3333-4444-555555555555" \
--ApiAudience "api://order-service"
| What | Why | Where to change it after scaffolding |
|---|---|---|
--TenantId |
The shipped GUID is not a real tenant. The bearer scheme fetches its OIDC metadata from https://login.microsoftonline.com/<TenantId>/v2.0/.well-known/openid-configuration; against a non-existent tenant that fetch never yields signing keys, so no token can be validated. |
Authentication:Schemes:Bearer:MetadataAddress and :ValidIssuer in <Name>.Api/appsettings.json |
--ApiAudience |
ValidAudiences deliberately lists only the API’s own audience, so a token issued for any other resource is rejected. Left as api://your-api, every real token fails on audience mismatch. |
Authentication:Schemes:Bearer:ValidAudiences in <Name>.Api/appsettings.json |
ConnectionStrings:AppDb |
Empty in the base file, and the API cannot open a DbContext without it. Supplied automatically only when you launch through the Aspire host. |
<Name>.Api/appsettings.json, or a ConnectionStrings__AppDb environment variable |
The RateLimit numbers |
100 requests and 20 concurrent per second is a placeholder ceiling, not a researched limit for your service. | RateLimit in <Name>.Api/appsettings.json |
Both auth parameters ship as placeholders on purpose — the template wires no identity provider of
its own. Enabling FeatureManagement:RequireAuthorization while they are still in place fails
twice over: the metadata document is fetched from a tenant that does not exist, so the scheme never
obtains its signing keys, and every presented token is rejected on audience mismatch.
--TenantIdalso rewrites thePlaceholderTenantGuidconstant insideAuthPlaceholderConfigTests.cs, because that constant is a literal copy of the token. The test keeps passing after scaffolding, but it now pins your tenant id rather than guarding a placeholder. Full configuration surface, key by key:configuration-reference.md.
Generated layout:
MyCompany.MyService/
├── MyCompany.MyService.sln
├── global.json
├── Directory.Packages.props
├── coverage.runsettings
└── MyCompany.MyService.ApiEndpoints/
├── MyCompany.MyService.Api/ # Minimal API entry point
├── MyCompany.MyService.AppHost/ # .NET Aspire orchestration host
├── MyCompany.MyService.AppServices/ # CQRS actions, validators, event handlers
├── MyCompany.MyService.Domains/ # Entities, aggregate roots
├── MyCompany.MyService.Infra/ # EF Core, repositories, event publisher
├── MyCompany.MyService.Share/ # Constants, options, shared base types
├── MyCompany.MyService.App.Tests/ # xUnit + Shouldly unit/integration tests
└── MyCompany.MyService.App.BDDTests/ # Reqnroll + NUnit BDD tests
The scaffold also copies AGENTS.md, .claude-plugin/, .specify/, .vscode/, the
agents/commands/skills folders of .claude/, and the agents/prompts/skills folders of
.github/ unchanged into every generated solution — the file list in
src/DKNet.Minimal.Template.nuspec is what decides. These are the same AI-assistant
agents/skills/prompts and Spec-Kit workflow the template itself is built with. Everything else in
.github/ (workflows, hooks, instructions, copilot-instructions.md) belongs to this repository
and is not copied. See template-features.md for details.
# API only, no containers
dotnet run --project <Name>.ApiEndpoints/<Name>.Api
# Full stack via Aspire (Redis + PostgreSQL auto-provisioned via Docker)
dotnet run --project <Name>.ApiEndpoints/<Name>.AppHost
dotnet test <Name>.sln --settings coverage.runsettings --collect:"XPlat Code Coverage"
# One project / one feature at a time
dotnet test <Name>.ApiEndpoints/<Name>.App.Tests --filter "FullyQualifiedName~<Feature>"
dotnet test <Name>.ApiEndpoints/<Name>.App.BDDTests --filter "TestCategory=<Feature>"
Run these from inside <Name>.ApiEndpoints/. The scripts always target CoreDbContext in
<Name>.Infra.
./add-migration.sh <MigrationName>
./remove-migration.sh <MigrationName>
cd src
dotnet pack DKNet.Minimal.Template.csproj -c Release -o ./nupkgs
# Test locally before publishing
dotnet new install ./nupkgs/DKNet.Minimal.Template.1.0.0.nupkg
dotnet new uninstall DKNet.Minimal.Template
# Publish
dotnet nuget push ./nupkgs/DKNet.Minimal.Template.1.0.0.nupkg \
--api-key <YOUR_NUGET_API_KEY> \
--source https://api.nuget.org/v3/index.json