Dashboard Starter is an open-source starter for React, ASP.NET Core, and SQL Server: a working, production-shaped dashboard app you can clone, rename, and build your real product on. Most starters stop where the hard part begins, so this one covers the boring middle, from real auth and permissions to a server-side data grid, tests, and a deploy pipeline. I'm building it in public and writing up every significant decision, so you can take what's useful and argue with the rest.
Status: Step 8 done: every visitor gets a private demo sandbox on the live demo at dashboard.mvmorten.com, cleaned up by a background job. Post 8 tells how. Next: the CRUD module.
Step 8 gives every visitor a private demo sandbox instead of a shared demo account. Why the shared account kept needing workarounds, how sandboxes expire, and the background job that cleans them up without bypassing Row-Level Security.
Roles, permissions, and a Members page. Why the code checks permissions and never role names, why permissions come from the database instead of the cookie, and how a tenant always keeps an admin, even when two admins demote each other at once.
Forgot password, reset, and change password, built on step 5's sign-in. Where a reset link can leak, why the form gives everyone the same answer, and why email moved from Azure to Resend when Microsoft announced ACS's retirement.
The live demo now sits behind a real sign-in: our own user tables, Argon2id, and a plain cookie, with no Identity and no JWT. How it works, and the deploy that kept failing because I merged before the infrastructure existed.
The single health page is now a real app shell: a sidebar, a top bar with a tenant switcher, and light and dark themes. Built on plain CSS and the browser's own elements, with exactly three new dependencies, and a lot of what I chose not to add.
Step 3 was supposed to be the app shell. Instead, Dashboard Starter is live on Azure and deploys itself on every merge. Why I deployed before the first feature, how it fits together, and the three ways the first deploy failed.
Before writing step 2's code, I went back over the stack from post 1 and changed a surprising amount of it. What I changed, what I questioned and kept, and what the repo does now.