LaunchKit.AspNetCore.Tools
1.0.0
dotnet tool install --global LaunchKit.AspNetCore.Tools --version 1.0.0
dotnet new tool-manifest
dotnet tool install --local LaunchKit.AspNetCore.Tools --version 1.0.0
#tool dotnet:?package=LaunchKit.AspNetCore.Tools&version=1.0.0
nuke :add-package LaunchKit.AspNetCore.Tools --version 1.0.0
LaunchKit.AspNetCore
One command turns an ASP.NET Core MVC app into a site that is ready to go live.
For ASP.NET Core MVC on .NET 8. Apache-2.0 licensed.
Every website needs the same unglamorous things before it can launch: a privacy policy, terms of use, a cookie policy, error pages that do not leak stack traces, a sitemap, and a robots file. Writing them takes an afternoon. Keeping the sitemap accurate takes forever.
LaunchKit does all of it in one command, and stays out of your way afterwards.
Features
- Legal pages — substantial Privacy Policy, Terms of Use and Cookie Policy starter templates, not placeholders.
- Custom error pages — polished, self-contained 404 and 500 pages that return the right HTTP status and never leak exception details.
- Error handling wired up — LaunchKit reads your
Program.cswith Roslyn and inserts only the middleware that is missing. - Route-aware sitemap — your MVC controllers are parsed, public GET routes are resolved, and
sitemap.xmlis generated from them. - robots.txt — created if absent, carefully merged if you already have one.
- llms.txt — the llmstxt.org index, built from the same routes, so language models can find your pages.
- Safe to re-run — anything you have edited is preserved. Only the generated indexes are refreshed.
- No runtime dependency — the generated code depends on ASP.NET Core and nothing else.
Installation
Install-Package LaunchKit.AspNetCore
Or with the .NET CLI:
dotnet add package LaunchKit.AspNetCore
Quick start
In the Visual Studio Package Manager Console, with your web project selected as the Default Project:
Add-LaunchKit -BaseUrl "https://example.com"
If the command is not recognized yet, give the console a couple of seconds and try again. Visual Studio registers commands from NuGet packages in the background after the console banner appears, so the very first attempt can be a moment too early. If it still isn't there, load it directly — no restart needed:
Import-Module "$env:USERPROFILE\.nuget\packages\launchkit.aspnetcore\1.0.0\tools\LaunchKit.psd1"Reopening the solution has the same effect. This is how NuGet exposes console commands to PackageReference projects; the older install-time hook is no longer supported.
Done. LaunchKit creates your legal pages, creates custom error pages, configures error handling,
scans your public routes, and generates sitemap.xml, robots.txt and llms.txt.
LaunchKit.AspNetCore
────────────────────────────────────────────
Project
MyWebsite.csproj
Legal
✓ Privacy Policy created
✓ Terms of Use created
✓ Cookie Policy created
✓ LaunchKitLegalController.cs created
✓ wwwroot/css/launchkit.css created
Errors
✓ 404 page created
✓ 500 page created
✓ LaunchKitErrorController.cs created
↻ exception handler UseExceptionHandler("/error/500")
↻ status code pages UseStatusCodePagesWithReExecute("/error/{0}")
↻ Program.cs updated (backup: Program.cs.launchkit.bak)
SEO
14 public routes discovered 6 controllers scanned
✓ wwwroot/sitemap.xml 17 URLs
✓ wwwroot/robots.txt created
✓ wwwroot/llms.txt created
LaunchKit setup completed successfully.
The Add-LaunchKit command
Add-LaunchKit
| Parameter | Purpose |
|---|---|
-BaseUrl |
Public origin, e.g. https://example.com. Needed for sitemap.xml, robots.txt and llms.txt. |
-CompanyName |
Legal entity named in the generated legal pages. |
-WebsiteName |
Display name of the site used in the generated legal pages. |
-ContactEmail |
Contact address printed in the legal pages. |
-ProjectName |
Target a specific project instead of the console's Default Project. |
-WhatIf |
Report every change without writing anything. |
Nothing is mandatory. Anything LaunchKit can infer safely, it infers; anything it cannot, it says so.
Add-LaunchKit `
-BaseUrl "https://example.com" `
-CompanyName "Acme Inc." `
-ContactEmail "hello@example.com"
Your answers are stored in launchkit.json next to the project file and reused on every later run:
{
"baseUrl": "https://example.com",
"companyName": "Acme Inc.",
"websiteName": "Acme",
"contactEmail": "hello@example.com",
"effectiveDate": "2026-09-09",
"launchKitVersion": "1.0.0"
}
No secrets are ever written to it. Commit it.
Base URL
https://example.com and https://example.com/ are normalised to the same canonical origin. A URL
without a scheme is assumed to be https. Anything that is not a valid absolute http/https URL is
rejected with an explanation rather than written into your sitemap.
sitemap.xml requires absolute URLs, so the first run without a -BaseUrl does everything except
the three SEO files and tells you exactly how to finish:
SEO
! sitemap.xml skipped - no BaseUrl configured
! robots.txt skipped - no BaseUrl configured
! llms.txt skipped - no BaseUrl configured
What gets generated
Controllers/
LaunchKitLegalController.cs /privacy, /terms, /cookies
LaunchKitErrorController.cs /error/404, /error/500
Views/
LaunchKitLegal/
Privacy.cshtml
Terms.cshtml
Cookies.cshtml
LaunchKitError/
NotFound.cshtml
ServerError.cshtml
wwwroot/
css/launchkit.css
sitemap.xml
robots.txt
llms.txt
launchkit.json
The legal pages use your existing layout and a small stylesheet whose classes are all prefixed
lk-, so they cannot collide with your own CSS, Bootstrap or Tailwind. No JavaScript, no CDN, no
frontend framework.
The error views are deliberately different: they set Layout = null and inline their own CSS. An
error page that depends on the rest of the application can fail for the same reason the request
failed.
Routes it adds
| URL | Serves | Controller |
|---|---|---|
/privacy |
Privacy Policy | LaunchKitLegalController.Privacy |
/terms |
Terms of Use | LaunchKitLegalController.Terms |
/cookies |
Cookie Policy | LaunchKitLegalController.Cookies |
/error/404 |
Not-found page, HTTP 404 | LaunchKitErrorController.NotFoundPage |
/error/500 |
Server-error page, HTTP 500 | LaunchKitErrorController.ServerError |
/error/{code} |
Any other 4xx/5xx | LaunchKitErrorController.Status |
If your project already serves one of these URLs, LaunchKit leaves it to you and does not create a competing route.
After the first run
Three things are worth doing once, by hand. LaunchKit deliberately does not touch your layout or your navigation.
1. Check what went into Program.cs
LaunchKit inserts the two pieces below. This is the full shape it aims for in a default .NET 8 MVC
Program.cs — the lines it adds are marked:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/error/500"); // <- added by LaunchKit when you have no handler
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/error/{0}"); // <- added by LaunchKit
app.UseHttpsRedirection();
app.UseStaticFiles(); // required: serves sitemap.xml, robots.txt, llms.txt and the CSS
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Two things to check:
app.UseStaticFiles()must be present. Without it/sitemap.xml,/robots.txt,/llms.txtand/css/launchkit.csswill all return 404. Every MVC template includes it; minimal hand-written pipelines sometimes do not. On .NET 9+ templates the equivalent call isapp.MapStaticAssets().UseStatusCodePagesWithReExecutemust come beforeUseRouting. LaunchKit places it there; if you move things around later, keep that order.
If LaunchKit could not edit the file safely it changes nothing, tells you why, and prints exactly
what to paste. Either way the original is saved as Program.cs.launchkit.bak.
2. Point the exception handler at LaunchKit's 500 page
The default MVC template ships with its own handler, and LaunchKit will not overwrite your code. If you want the generated 500 page, change this one line yourself:
app.UseExceptionHandler("/Home/Error"); // before
app.UseExceptionHandler("/error/500"); // after
You can then delete Views/Shared/Error.cshtml, Models/ErrorViewModel.cs and the Error action
on HomeController if you have no other use for them.
3. Link the pages from your layout
Nothing links to the legal pages until you do. Add them to the footer in
Views/Shared/_Layout.cshtml:
<footer class="border-top footer text-muted">
<div class="container">
© @DateTime.Now.Year - My Company
· <a asp-controller="LaunchKitLegal" asp-action="Privacy">Privacy</a>
· <a asp-controller="LaunchKitLegal" asp-action="Terms">Terms</a>
· <a asp-controller="LaunchKitLegal" asp-action="Cookies">Cookies</a>
</div>
</footer>
Plain anchors work just as well if you are not using tag helpers:
<a href="/privacy">Privacy</a>
<a href="/terms">Terms</a>
<a href="/cookies">Cookies</a>
The default template's footer links to asp-action="Privacy" on HomeController. Repoint it at
LaunchKitLegal as above, or delete that action and its view so there is one privacy page rather
than two.
Error handling behaviour
LaunchKit parses Program.cs with Roslyn, finds your request pipeline, and adds only what is
missing:
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/error/500");
}
app.UseStatusCodePagesWithReExecute("/error/{0}");
- Existing error handling always wins. If you already call
UseExceptionHandler, LaunchKit leaves it alone and tells you so — including the one in the default MVC template, which points at/Home/Error. Switching to LaunchKit's 500 page is a one-line change it prints for you. - Middleware is never duplicated, and running the command again changes nothing.
- Comments, spacing and top-level statements are preserved; only new lines are inserted.
Program.csis copied toProgram.cs.launchkit.bakbefore it is touched.- If the pipeline cannot be located confidently, LaunchKit changes nothing and prints the snippet for you to paste.
The generated LaunchKitErrorController sets the status code explicitly, so /error/404 really
answers 404 and /error/500 really answers 500. The 500 page shows the ASP.NET Core trace
identifier and nothing else: no exception message, no stack trace, no paths, no configuration.
Sitemap generation
Routes are discovered by parsing your controllers, not by running your app. Attribute routes,
controller-level routes, [controller]/[action] tokens, areas and conventional
{controller=Home}/{action=Index} routes are all understood.
Included by default: concrete public GET pages — home, about, contact, pricing, features, your
own content routes, and the generated /privacy, /terms and /cookies.
Excluded by default:
| Excluded | Why |
|---|---|
POST, PUT, PATCH, DELETE |
Not pages. |
[ApiController] and /api/* |
Not pages. |
/admin, /manage, /internal, _* |
Not public. |
/error, /404, /500, /Home/Error |
Error pages do not belong in a sitemap. |
/account, /login, /register |
Authentication endpoints. |
/swagger, /health, /metrics |
Tooling endpoints. |
[Authorize] without [AllowAnonymous] |
Not reachable by a crawler. |
/products/{id}, /users/{username} |
LaunchKit cannot invent real values for these. |
| Conventional actions taking parameters | Same reason. |
LaunchKit is deliberately conservative. A missing URL is easy to add; a sitemap full of broken ones is not.
The output is plain, valid sitemap XML — deduplicated, deterministically ordered, correctly escaped,
with no invented lastmod, changefreq or priority values:
<?xml version="1.0" encoding="utf-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/</loc>
</url>
<url>
<loc>https://example.com/about</loc>
</url>
</urlset>
Excluding a route
using LaunchKit.AspNetCore;
[HttpGet("internal-dashboard")]
[LaunchKitSitemapIgnore]
public IActionResult Dashboard() => View();
[LaunchKitSitemapIgnore] works on an action or a whole controller. Its counterpart,
[LaunchKitSitemapInclude], forces a route in that LaunchKit would otherwise filter out — though it
will still never emit a route with unresolved parameters.
Both attributes are read from source. They cost nothing at runtime.
llms.txt
LaunchKit also writes wwwroot/llms.txt, served at /llms.txt. It is the markdown index described
at llmstxt.org: a short, machine-readable map of the site for language models
and AI crawlers, built from the same routes as the sitemap.
# Acme
> One sentence describing this site. Replace this line - LaunchKit does not invent one.
## Pages
- [Home](https://example.com/)
- [About](https://example.com/about)
- [Getting Started](https://example.com/getting-started)
## Legal
- [Privacy Policy](https://example.com/privacy)
- [Terms of Use](https://example.com/terms)
- [Cookie Policy](https://example.com/cookies)
Two deliberate choices:
- Titles come from the URL, never from guesswork:
/getting-startedbecomes "Getting Started". - The summary line is left blank on purpose. LaunchKit cannot know what your site does, and a wrong description is worse than an obvious placeholder. Write one sentence and it is kept forever — later runs refresh the page list around it and never overwrite your text.
If you write your own llms.txt from scratch, delete the generated comment on the first line.
LaunchKit then treats the file as yours and never touches it again.
Running Add-LaunchKit again
Added a new controller six months later? Run it again.
Add-LaunchKit
Project
MyWebsite.csproj
existing LaunchKit setup detected
Legal
· Privacy Policy existing file preserved
· Terms of Use existing file preserved
· Cookie Policy existing file preserved
Errors
· 404 page existing file preserved
· 500 page existing file preserved
· Program.cs already configured
SEO
16 public routes discovered 8 controllers scanned
↻ wwwroot/sitemap.xml 19 URLs
Sitemap changes:
+ /partners
- /old-products
· wwwroot/robots.txt unchanged
↻ wwwroot/llms.txt page list refreshed
LaunchKit is up to date.
LaunchKit rescans your routes and refreshes the sitemap without overwriting your customised legal and error pages.
Existing-file safety
The rule is simple: your code always wins.
| File | Ownership |
|---|---|
| Legal pages, error views | Yours after the first run. Never overwritten. |
| Generated controllers | Yours after the first run. Never overwritten. |
launchkit.css |
Yours after the first run. Never overwritten. |
Program.cs |
Yours. Minimal insertions only, backed up first, existing config kept. |
sitemap.xml |
LaunchKit-managed. Regenerated on every run. |
robots.txt |
LaunchKit-managed only if it created it; otherwise merged conservatively. |
llms.txt |
LaunchKit-managed only if it created it. Your summary line is always kept. |
| Your layout, nav, footer | Yours. Never touched — see "After the first run" above. |
Route collisions are handled the same way. If your project already serves /privacy, LaunchKit
generates the page but not a conflicting route:
· Privacy Policy existing file preserved
· route /privacy your own route already serves this
The same applies to /terms, /cookies, /error/404 and /error/500. LaunchKit also never writes
outside your project directory, and never executes any of your code — it only parses it.
Outside Visual Studio
Add-LaunchKit needs the Visual Studio Package Manager Console. On macOS, Linux, VS Code or Rider,
install the cross-platform CLI, which runs the same engine:
dotnet tool install --global LaunchKit.AspNetCore.Tools
launchkit --base-url "https://example.com"
Run launchkit --help for the full option list. Add the LaunchKit.AspNetCore package too if you
want the [LaunchKitSitemapIgnore] attribute.
Legal disclaimer
The generated Privacy Policy, Terms of Use and Cookie Policy are starter templates, not legal advice. They do not make your site compliant with the GDPR, UK GDPR, CCPA/CPRA, PIPEDA, LGPD or any other law, and no such compliance is claimed or implied.
Every generated page carries a developer-facing comment listing what must be reviewed. Read it, adapt the text to the data you actually collect and the jurisdiction you actually operate in, and have the result reviewed by a qualified lawyer before you publish.
Supported versions
| Target framework | .NET 8.0 |
| Project type | ASP.NET Core MVC (SDK-style, Microsoft.NET.Sdk.Web) |
| Requires | .NET 8 SDK or newer on PATH |
| Visual Studio | 2022 17.8 or newer, for the Package Manager Console command |
Razor Pages, Minimal APIs and dynamic sitemap providers are not supported in v1. The route analyzer is written so they can be added without reshaping anything.
Support and feedback
Found a bug, hit an edge case LaunchKit handled badly, or want a feature? Email me and I will get back to you:
Useful things to include: your .NET version, the LaunchKit output you saw, and your Program.cs
if the problem was with error-handling configuration.
License
Apache License 2.0. Copyright © 2026 Muhammad Rehman Tahir. The full licence text ships in the package.
Author
Muhammad Rehman Tahir — engr.mrehmantahir@gmail.com
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 109 | 9/10/2026 |
Initial release. Add-LaunchKit scaffolds legal pages, custom error pages, ASP.NET Core error handling, sitemap.xml and robots.txt, and is safe to re-run.