# Rename your project

> One dotnet new command renames the .NET 10 solution, namespaces, database, Keycloak realm and Aspire resources to your name, and leaves the Appointment domain untouched.

Source page: https://appointme.dev/docs/guides/rename-your-project/

* **You'll get:** A renamed copy that builds and passes tests
* **Time:** A few minutes
* **Touches:** Every project, plus config and infra names
* **Before you start:** .NET 10 SDK; Docker and Node to run

## Generate a renamed project

**Install the template, run `dotnet new appointme -n <Name>`, and commit the output before you change anything.**

1. **Install the template.** Pin the version so you can generate the same output again later.

   ```bash
   dotnet new install BravoDev.AppointMe.Templates::1.2.1
   ```

2. **Generate.** `-n` is the only option the template takes. The output goes into a new `Contoso.Booking/` folder. Pass `-o <dir>` to put it somewhere else.

   ```bash
   dotnet new appointme -n Contoso.Booking
   ```

3. **Commit the untouched output.** The template doesn’t create a git repository, and nothing in the generated files records which version made them. Commit right away and put the version in the message, because [taking updates later](/docs/guides/upstream-updates/) starts from this commit.

   ```bash
   cd Contoso.Booking
   git init
   git add -A
   git commit -m "Generate from BravoDev.AppointMe.Templates 1.2.1"
   ```

4. **Build and test.** Both pass on a fresh copy, with no code changes.

   ```bash
   dotnet build Contoso.Booking.sln
   dotnet test Contoso.Booking.sln
   ```

5. **Run it.** Trust the dev certificate once per machine, then start the Aspire host. The [quick start](/docs/start/quick-start/) covers what comes up and where.

   ```bash
   dotnet dev-certs https --trust
   cd src/Contoso.Booking.Aspire && dotnet run
   ```

nuget.org has 1.2.1; these docs describe main

Two of main’s unreleased changes show up straight away. A 1.2.1 project builds with `NU1902` and `NU1903` warnings about `System.Security.Cryptography.Pkcs` and `.Xml`, which main fixes, and 1.2.1 names the generated API client `src/api/contoso.booking.ts` where main uses `src/api/client.ts`. To generate from main, pack the template yourself, as shown in [If you cloned instead](#if-you-cloned-instead).

## What `-n` renames

**Every brand token becomes your name, in whichever form the place it sits in allows.**

| What                                    | In AppointMe                           | With `-n Contoso.Booking`                          | Form                         |
| --------------------------------------- | -------------------------------------- | -------------------------------------------------- | ---------------------------- |
| Namespaces, projects, folders, solution | `AppointMe.Crm`, `AppointMe.sln`       | `Contoso.Booking.Crm`, `Contoso.Booking.sln`       | As typed                     |
| SQL database                            | `Database=AppointMe`                   | `Database=Contoso.Booking`                         | As typed                     |
| Connection string name                  | `AppointMeSql`                         | `ContosoBookingSql`                                | Separators removed           |
| Keycloak realm and its file             | `appointme`, `appointme-realm.json`    | `contoso.booking`, `contoso.booking-realm.json`    | Lowercase                    |
| Cookies and the demo login              | `appointme.auth`, `demo@appointme.dev` | `contoso.booking.auth`, `demo@contoso.booking.dev` | Lowercase                    |
| Aspire resources, Compose containers    | `appointme-sql`, `appointme-api`       | `contoso-booking-sql`, `contoso-booking-api`       | Kebab-case                   |
| Aspire project type in `Program.cs`     | `AppointMe_Api`                        | `Contoso_Booking_Api`                              | Underscores                  |
| Azure names in `infra/main.bicep`       | `acrappointme…`, `stappointme…`        | `acrcontosobooking…`, `stcontosobooking…`          | Lowercase letters and digits |

Each form exists because the target rejects the others. Aspire resource names can’t contain dots or underscores (its ASPIRE006 check), C# identifiers can’t contain dots, and Azure registry and storage account names allow only lowercase letters and digits.

The template picks the form from the character that follows each match. This symbol handles the dotted form, and it fires only before one of nine characters:

.template.config/template.json

```json
"identityBucket": {
  "type": "derived",
  "valueSource": "name",
  "valueTransform": "identity",
  "replaces": "AppointMe",
  "fileRename": "AppointMe",
  "onlyIf": [
    { "before": "." }, { "before": " " }, { "before": "\"" }, { "before": "'" },
    { "before": "\n" }, { "before": "\r" }, { "before": ";" }, { "before": "-" }, { "before": "<" }
  ]
},
```

`templates/README.md` in the upstream repo lists every symbol and why it exists. If you add an identifier that contains `AppointMe` and the character after it isn’t on any list (`AddAppointMeBilling` is followed by `B`), the template won’t rename it.

### What it leaves alone

The lowercase brand token `appointme` is the first nine letters of `appointment`. A plain find-and-replace would also rewrite the `Appointment` aggregate, the `/appointments` route and the `appointments.statistics:view` permission.

So the template never matches the lowercase token on its own. It matches guarded forms such as `appointme-`, `appointme.` and `appointme"`, and `appointmen…` matches none of them. The PascalCase `AppointMe` can’t collide, because `Appointment` has a lowercase `m`.

| Left as it is                                                                 | Why                                                                                                                |
| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `Appointment`, `/appointments`, `appointments.statistics:view`                | Domain vocabulary, not the brand. CI’s template smoke test counts every `Appointment` before and after the rename. |
| Module names `Identity`, `Organizations`, `Crm`, `Booking`                    | They aren’t brand tokens, so `-n Contoso.Booking` gives you `Contoso.Booking.Booking`.                             |
| `Company` and `Authors` in `Directory.Build.props`                            | Still `BravoDev`. Only the repository name inside `RepositoryUrl` changes.                                         |
| `LICENSE`                                                                     | MIT requires you to keep BravoDev’s copyright notice. Add your own line next to it.                                |
| `Password1` and the Keycloak client secrets in `appsettings.Development.json` | Every generated project starts with the same local values.                                                         |

Fix the metadata and placeholders before you deploy: [What to change first](/docs/start/what-to-change-first/) has the list.

### Why is the name the only option?

The modules are wired together through events. Booking builds its own copies of CRM customers and of Organizations companies and employees from their domain events, so a switch that dropped a module would generate code that doesn’t compile.

With one parameter there’s one combination to test. `.github/workflows/template.yml` runs `templates/smoke-test.sh` on every pull request: it packs the template, generates `Contoso.Booking`, builds and tests it, lints its frontend and checks that no brand token is left. To drop a module, generate first and then follow [Remove a module](/docs/guides/remove-a-module/).

## Naming rules for `-n`

**Use a valid C# namespace such as `Contoso.Booking`. `dotnet new` accepts any name, so a bad one only shows up when you build.**

| Name              | What happens                                                                                                                                                    |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Contoso.Booking` | Builds. The documented form: company, then product.                                                                                                             |
| `Contoso`         | Builds. A single segment is a valid namespace too.                                                                                                              |
| `my-booking-app`  | `dotnet new` reports success. The build then fails with hundreds of `CS1022` and `CS0116` errors, because the code now says `namespace my-booking-app.Shared…`. |

* **Each dot-separated segment must be a C# identifier:** letters, digits and underscores, not starting with a digit. No hyphens and no spaces.
* **Use PascalCase segments.** Namespaces take the name exactly as typed, so `contoso.booking` gives you lowercase namespaces.
* **Keep it short if anyone builds on Windows without long-path support.** The name appears in nearly every path, often twice: `src/CRM/Contoso.Booking.Crm/Contoso.Booking.Crm.csproj`.

## The generated README and demo login

**A generated project gets its own README, written for your copy, with the demo login already renamed.**

It comes from `templates/overlay/README.md`, not from the repo’s marketing README. It covers running the stack, the demo user, and a “Change these before deploying” list. The local Keycloak realm ships with a demo account, which `Authentication:SuperAdmins` in `appsettings.Development.json` makes a super admin. Its credentials follow your name:

| Demo login | Rule                          | For `Contoso.Booking`      |
| ---------- | ----------------------------- | -------------------------- |
| Email      | `demo@<name, lowercased>.dev` | `demo@contoso.booking.dev` |
| Password   | `<name without separators>1`  | `ContosoBooking1`          |

To skip the login form, open `https://localhost:5173/api/v1/login/demo`. `Demo:Enabled` switches it. It’s on in `appsettings.Development.json`, and also in `appsettings.Devtest.example.json`.

The package also leaves out files that belong to upstream:

| Not in a generated project                                          | Why                                                                                  |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| `CHANGELOG.md`, the root `README.md`, `docs/images/`                | Upstream release material                                                            |
| `src/AppointMe.Api/appsettings.Devtest.json`                        | BravoDev’s deployment config. You get `appsettings.Devtest.example.json` to fill in. |
| `templates/`, `.template.config/`, `.github/workflows/template.yml` | The packaging project, and the job that publishes BravoDev’s NuGet package           |
| `.claude/settings.json`, `docs/superpowers/`                        | Upstream’s own tooling settings and plans                                            |

Everything else ships, renamed: `CLAUDE.md` and the agent skills, the Bicep files in `infra/`, and the `devtest.yml`, `codeql.yml` and `secret-scan.yml` workflows.

## If you cloned instead

**There’s no rename script in the repo. To rename a clone, pack the template from it and generate from the package.**

| You want                                                      | Do this                                                                                                                      |
| ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| The easiest upstream merges                                   | Keep the `AppointMe` names. A clone with upstream’s names can [merge release tags](/docs/guides/upstream-updates/) directly. |
| Your own names, and you haven’t changed anything yet          | Use `dotnet new` from nuget.org, as above                                                                                    |
| Your own names, keeping work you’ve already done in the clone | Pack the clone, then generate from the package                                                                               |

1. **Pack the template from the clone’s root.** The package takes your working tree, minus build output and the withheld files above. Its version comes from `<Version>` in `templates/AppointMe.Templates.csproj`, which is still `1.2.1` on main.

   ```bash
   dotnet pack templates/AppointMe.Templates.csproj -c Release -o ../appointme-pack
   ```

2. **Install the package and generate.** If another copy of the template is installed, uninstall it first, because both use the same package id.

   ```bash
   dotnet new uninstall BravoDev.AppointMe.Templates
   dotnet new install ../appointme-pack/BravoDev.AppointMe.Templates.1.2.1.nupkg
   dotnet new appointme -n Contoso.Booking -o ../Contoso.Booking
   ```

3. **Look for brand tokens the template didn’t know.** These are the smoke test’s own leftover checks, and both should print nothing. A hit in your own code needs a rename by hand.

   ```bash
   cd ../Contoso.Booking
   grep -rIl "AppointMe" . --exclude-dir=node_modules --exclude-dir=obj --exclude-dir=bin --exclude-dir=dist
   grep -rIoE 'appointme([^n]|$)' . --exclude-dir=node_modules --exclude-dir=obj --exclude-dir=bin --exclude-dir=dist
   ```

4. **Start a new repository, and uninstall the template.** The generated folder has no git history: your history stays in the clone. Commit the output as in step 3 at the top of this page, then run `dotnet new uninstall BravoDev.AppointMe.Templates`.

Gotcha

Don’t run `dotnet new install ./` on the clone itself. It installs, but the package’s exclusions don’t apply to a folder. The output then gets the clone’s `.git` folder, which the rename corrupts (`git status` fails with `fatal: unknown index entry format`), plus `CHANGELOG.md`, `appsettings.Devtest.json` and `template.yml`. Install the `.nupkg` instead.

## Verify it works

**A good rename builds, passes its tests and leaves no brand token behind.**

| Check                          | Command                                                     | Expect                                           |
| ------------------------------ | ----------------------------------------------------------- | ------------------------------------------------ |
| The backend builds             | `dotnet build Contoso.Booking.sln`                          | `0 Error(s)`                                     |
| The tests pass                 | `dotnet test Contoso.Booking.sln`                           | No failures: 217 tests from 1.2.1, 221 from main |
| No brand token is left         | The two `grep` commands above                               | No output                                        |
| The frontend passes its checks | `npm ci && npm run check` in `src/Contoso.Booking.Frontend` | No errors                                        |
| The stack starts               | `dotnet run` in `src/Contoso.Booking.Aspire`                | You can sign in as `demo@contoso.booking.dev`    |

## Troubleshooting

| Symptom                                                                  | Cause                                                                                           | Fix                                                                 |
| ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| Hundreds of `CS1022` and `CS0116` errors in a fresh project              | `-n` isn’t a valid namespace, for example `my-booking-app`                                      | Generate again with a dotted PascalCase name                        |
| `dotnet new` doesn’t know `appointme`                                    | The template isn’t installed                                                                    | `dotnet new install BravoDev.AppointMe.Templates::1.2.1`            |
| The output contains `.git`, `CHANGELOG.md` or `appsettings.Devtest.json` | You installed the clone folder, not a package                                                   | `dotnet new uninstall <folder>`, then pack and install the `.nupkg` |
| `AppointMe` still appears in your own code                               | The character after it isn’t one the template routes                                            | Rename it by hand                                                   |
| Keycloak won’t import the realm after you renamed it by hand             | Keycloak requires the file to be named `<realm>-realm.json` (“File name / realm name mismatch”) | Keep the file name and its `"realm"` value in step                  |

## Next

* [What to change first](/docs/start/what-to-change-first/) (Guide)
* [Take updates from upstream](/docs/guides/upstream-updates/) (Guide)
* [Quick start](/docs/start/quick-start/) (Tutorial)
