Skip to content

Backend Local Setup (Windows)

This guide walks you through setting up the LBS.Foundry backend locally on Windows. It assumes you have already completed the steps in prerequisites.md (pgAdmin, PostgreSQL, Docker Desktop, .NET SDK, Visual Studio, Node.js, pnpm, Git).


1. Set up the local databases (pgAdmin)

Set up the databases first, before cloning the repo. This way, when you build and run the project later, everything is already wired up and won't fail with "database does not exist" errors.

The backend connects to two PostgreSQL databases that must exist before the project can run: foundry and ballr. You will create them inside pgAdmin (installed in the prerequisites step).

1.1 Open pgAdmin

  1. Press the Windows key, search for pgAdmin 4, and open it.
  2. If it asks for the master password, set one — your choice. Remember it; it only protects pgAdmin itself, not Postgres.

1.2 Create a Server Group named DEV

This keeps your local dev servers organized and separate from any production servers you might add later.

  1. In the left sidebar (Object Explorer), right-click Servers.
  2. Choose Create → Server Group...
  3. Name: DEV
  4. Click Save.

1.3 Register the local Postgres server inside DEV

  1. Right-click the new DEV group → Register → Server...
  2. In the dialog that opens, fill in two tabs:

General tab:

Field Value
Name Foundry

Connection tab:

Field Value
Host name/address localhost
Port 5432
Maintenance database postgres (default)
Username postgres
Password supremo
Save password? ON (toggle the switch)
  1. Click Save.

Verify: the Foundry server should now appear under DEV, and you should be able to expand it without being asked for a password again.

1.4 Create the foundry and ballr databases

  1. In the left sidebar, expand DEV — you should see Foundry underneath.
  2. Right-click Foundry → Create → Database...
  3. In the General tab:
  4. Database: foundry (lowercase, exactly)
  5. Owner: postgres (default)
  6. Click Save.

Repeat for the second database:

  1. Right-click Foundry again → Create → Database...
  2. Database: ballr (lowercase, exactly)
  3. Click Save.

Note about scheduler: the appsettings.Development.json template below also references a scheduler database, but it is auto-created by the application on first run, so you do not need to create it manually here. If you hit an error about it later, just create it the same way as above.

Verify your setup:

  • Expand DEV → Foundry → Databases — you should see at least postgres, foundry, and ballr.
  • Right-click foundryQuery Tool → run SELECT 1; — it should return 1.

Warning: database names are case-sensitive in the connection strings. Make sure you typed foundry and ballr in lowercase, with no spaces or typos.


2. Create the source folder

Open PowerShell:

mkdir C:\Users\$env:USERNAME\source\luckboxstudios
cd C:\Users\$env:USERNAME\source\luckboxstudios

3. Clone the repo

git clone https://github.com/luckboxstudios/LBS.Foundry.git
cd LBS.Foundry

If you get a permission error, request access from your manager to the LBS.Foundry repo.


4. Add the Luckbox GitHub Packages NuGet source

The project depends on private NuGet packages hosted on GitHub Packages. You need to add this as a package source so dotnet restore can find them.

4.1 Create a GitHub Personal Access Token (PAT)

  1. Go to https://github.com/settings/tokens.
  2. Click Generate new token (classic).
  3. Give it a descriptive name (e.g. nuget-luckbox).
  4. Under Select scopes, check:
  5. read:packages
  6. Click Generate token and copy it immediately — you will not see it again.

Tip: save the token in your password manager. You will need it again if you clear your NuGet sources or set up a new machine.

4.2 Add the source

Option A — CLI (quickest):

Open PowerShell and run:

dotnet nuget add source "https://nuget.pkg.github.com/luckboxstudios/index.json" --name "Luckbox" --username "YOUR_GITHUB_USERNAME" --password "YOUR_PAT" --store-password-in-clear-text

Replace YOUR_GITHUB_USERNAME with your GitHub username and YOUR_PAT with the token you just created.

Option B — edit NuGet.Config manually:

  1. Press Win + R, type %APPDATA%\NuGet and press Enter. This opens the folder containing your user-level NuGet configuration.
  2. Open the file NuGet.Config in VS Code or Notepad++. If it does not exist, create it.
  3. Replace the entire file contents with the following:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
    <add key="Luckbox" value="https://nuget.pkg.github.com/luckboxstudios/index.json" allowInsecureConnections="True" />
  </packageSources>
  <packageSourceMapping>
    <packageSource key="Luckbox">
      <package pattern="*" />
    </packageSource>
    <packageSource key="nuget.org">
      <package pattern="*" />
    </packageSource>
  </packageSourceMapping>
  <packageSourceCredentials>
    <Luckbox>
      <add key="Username" value="YOUR_GITHUB_USERNAME" />
      <add key="ClearTextPassword" value="YOUR_PAT" />
    </Luckbox>
  </packageSourceCredentials>
</configuration>
  1. Replace YOUR_GITHUB_USERNAME with your GitHub username and YOUR_PAT with the token you created in step 4.1.
  2. Save the file.

Note: both options store credentials in %APPDATA%\NuGet\NuGet.Config, not in the repository. The packageSourceMapping section tells NuGet which source to use for each package pattern — nuget.org is the default fallback for all packages, and Luckbox handles private packages.

4.3 Verify

dotnet nuget list source

You should see Luckbox [Enabled] pointing to https://nuget.pkg.github.com/luckboxstudios/index.json.


5. Create the configuration files

You will create two config files manually. Below are the full templates — copy them, paste into the file, and fill in the secrets where indicated.

Placeholder legend

Throughout the templates below you will see three kinds of placeholders. Here is what each one means and what to do:

Placeholder What it means What you should do
Request for this info A team-only secret that only your manager or a senior dev can give you Message your manager and ask for the value
Fetch this on your account Something you generate yourself from your own dashboard on the platform Log into the platform, go to API Keys, copy the value
Create your own You make this up yourself (e.g. a JWT secret) Generate any long random string — full instructions below

File 1 — launchsettings.json

Path:

C:\Users\<YOU>\source\luckboxstudios\LBS.Foundry\src\Aspire\LBS.AspireHost\Properties\launchsettings.json

Replace <YOU> with your actual Windows username.

How to create:

  1. Navigate to C:\Users\<YOU>\source\luckboxstudios\LBS.Foundry\src\Aspire\LBS.AspireHost\Properties\ in File Explorer.
  2. If the file already exists, open it in VS Code or Notepad++ and replace its contents.
  3. If it does not exist, create a new file named exactly launchsettings.json.
  4. Paste the contents below.
  5. Replace each marker with the value your manager gives you.
{
  "$schema": "https://json.schemastore.org/launchsettings.json",
  "profiles": {
    "https": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "applicationUrl": "https://localhost:17097;http://localhost:15037",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "DOTNET_ENVIRONMENT": "Development",
        "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21191",
        "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22279",
        "POSTGRES_HOST_AUTH_METHOD": "trust"
      }
    },
    "http": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "applicationUrl": "http://localhost:15037",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "DOTNET_ENVIRONMENT": "Development",
        "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19218",
        "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20252"
      }
    }
  },
  "EventStoreDbConnection": "Request for this info",
  "EventStoreSchema": "marten_testing"
}

Values to ask your manager for:

  • EventStoreDbConnection — the EventStore DB connection string

File 2 — appsettings.Development.json

Path:

C:\Users\<YOU>\source\luckboxstudios\LBS.Foundry\src\Aspire\LBS.AspireHost\appsettings.Development.json

Replace <YOU> with your actual Windows username.

How to create:

  1. Navigate to C:\Users\<YOU>\source\luckboxstudios\LBS.Foundry\src\Aspire\LBS.AspireHost\ in File Explorer.
  2. Create a new file named exactly appsettings.Development.json.
  3. Paste the contents below.
  4. Fill in placeholders following the legend above.
{
  "ConnectionStrings": {
    "foundry": "Server=localhost;Database=foundry;Port=5432;User Id=postgres;Password=supremo;Maximum Pool Size=100",
    "scheduler": "Server=localhost;Database=scheduler;Port=5432;User Id=postgres;Password=supremo;Maximum Pool Size=100",
    "ballr": "Server=localhost;Database=ballr;Port=5432;User Id=postgres;Password=supremo;Maximum Pool Size=100",
    "redis": "localhost:6379"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Aspire.Hosting.Dcp": "Warning",
      "LBS.Domain.Core.User.Services.DiscordTokenService": "Debug",
      "LBS.Core.Integration.Discord": "Debug"
    }
  },
  "Clerk": {
    "SecretKey": "Request for this info",
    "Authority": "Request for this info",
    "AuthorizedParty": "http://localhost:3000",
    "WebhookSecret": "Request for this info"
  },
  "Jwt": {
    "Secret": "create your own",
    "Issuer": "LBS.Foundry",
    "Audience": "LBS.Foundry.Web",
    "ExpirationMinutes": 60,
    "RefreshTokenExpirationDays": 30
  },
  "Modelling": {
    "SupercoachModelApi": "Request for this info"
  },
  "Linear": {
    "ApiKey": "",
    "TeamId": "",
    "ApiUrl": "https://api.linear.app/graphql",
    "ParentId": "",
    "AssigneeId": ""
  },
  "Discord": {
    "BotToken": "Request for this info",
    "ApplicationId": "Request for this info",
    "PublicKey": "Request for this info",
    "VerifiedRoleName": "Verified",
    "VerificationChannelName": "verification",
    "LogLevel": "Debug",
    "Verification": {
      "HmacSecret": "Request for this info",
      "BaseUrl": "https://localhost:7125",
      "TokenExpiryMinutes": "60"
    }
  },
  "ClickHouse": {
    "QueryEndpoint": {
      "KeyId": "Request for this info",
      "KeySecret": "Request for this info"
    },
    "Native": {
      "Host": "localhost",
      "Port": 8123,
      "Database": "foundry",
      "Username": "default",
      "Password": "supremo",
      "UseSsl": false
    }
  },
  "Email": {
    "ApiKey": "test-apikey",
    "FromEmail": "test@lbs.com",
    "FromName": "LBS Test Email"
  },
  "UserSeeding": {
    "Enabled": true,
    "SkipIfUsersExist": false,
    "Users": [
      {
        "Email": "admin@luckboxstudios.com",
        "FirstName": "Admin",
        "LastName": "User",
        "UserName": "admin",
        "Password": "Admin123!",
        "AccountType": "Human",
        "Status": "Active",
        "Roles": [ "Admin", "Member" ],
        "EmailVerified": true
      },
      {
        "Email": "system@luckboxstudios.com",
        "FirstName": "System",
        "LastName": "Account",
        "UserName": "system",
        "Password": "System123!",
        "AccountType": "ServiceAccount",
        "Status": "Active",
        "Roles": [ "ServiceAccount", "Member" ],
        "EmailVerified": true
      }
    ]
  },
  "BallrUrl": "http://localhost:3000",
  "Redis": {
    "ChannelPrefix": "foundry",
    "KeyPrefix": "subscriptions",
    "SubscriptionTtlSeconds": 3600,
    "TtlRefreshThresholdSeconds": 1800
  },
  "StatsPerform": {
    "Nrl": {
      "UserName": "Request for this info",
      "Password": "Request for this info"
    }
  },
  "MailerLite": {
    "Enabled": false,
    "BaseUrl": "https://connect.mailerlite.com",
    "ApiKey": ""
  }
}

Values you create yourself

Jwt.Secret — how to generate one

The JWT secret must be a string that is exactly 32 characters long — no more, no less. The format the project expects looks like:

"Secret": "32_CHARACTERS_LONG"

Replace 32_CHARACTERS_LONG with any random string that is exactly 32 characters.

Easiest way (PowerShell one-liner):

Open PowerShell and run this — it generates a random 32-character alphanumeric string:

-join ((48..57) + (65..90) + (97..122) | Get-Random -Count 32 | ForEach-Object { [char]$_ })

You will see output like:

aB3kP9qR2xT7mY4wL8nC5vH1jD6gF0s

That is exactly 32 characters. Copy it and paste it into appsettings.Development.json:

"Jwt": {
  "Secret": "aB3kP9qR2xT7mY4wL8nC5vH1jD6gF0s"
}

Alternative (in-browser):

  • Go to https://www.random.org/strings/ — set Length to 32, check Numeric digits + Lowercase + Uppercase, generate, copy.
  • Or open browser DevTools console (F12) and run:
Array.from({length:32}, () => "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[Math.floor(Math.random()*62)]).join('')

Verify your secret is exactly 32 characters:

"aB3kP9qR2xT7mY4wL8nC5vH1jD6gF0s".Length

This should print 32. If it prints anything else, regenerate.

Warning: do not use anything predictable like 12345678901234567890123456789012, your name padded with zeros, or a copy of someone else's secret. Each developer should have their own unique JWT secret for local dev.

Tip: save it. If you ever rebuild your machine, reuse the same JWT secret so existing local tokens still validate. Stash it in your password manager.

Values to ask your manager for

Message your manager with this list:

  • Clerk.SecretKey
  • Clerk.Authority
  • Clerk.WebhookSecret
  • Modelling.SupercoachModelApi
  • Discord.BotToken
  • Discord.ApplicationId
  • Discord.PublicKey
  • Discord.Verification.HmacSecret
  • ClickHouse.QueryEndpoint.KeyId
  • ClickHouse.QueryEndpoint.KeySecret
  • StatsPerform.Nrl.UserName
  • StatsPerform.Nrl.Password

Warning: never commit these files to Git — they contain secrets. They are already in .gitignore, but double-check before pushing.


6. Build the SDK

First, navigate into the LBS.Foundry folder. In PowerShell:

cd C:\Users\$env:USERNAME\source\luckboxstudios\LBS.Foundry

Then run:

pnpm sdk:refresh

This single command builds the .NET solution, regenerates the TypeScript SDK, and compiles it.


7. Build the project in Visual Studio

Important — start Docker Desktop FIRST. The Foundry API uses Aspire, which spins up containers (Redis, ClickHouse, etc.) when the project runs. You do not need to install or configure Redis separately — Aspire manages it via Docker. If Docker is not running, the project will fail to start with errors like Docker engine not available or hang on container startup.

Before pressing F5:

  1. Open Docker Desktop from the Start Menu.
  2. Wait for the whale icon in the system tray to turn green / steady (not animated).
  3. Run docker ps in PowerShell. If it returns without errors, Docker is ready.
  4. Keep Docker Desktop running the entire time you are working on Foundry.

Then in Visual Studio:

  1. Open Visual Studio.
  2. File → Open → Project/Solution.
  3. Navigate to the LBS.Foundry folder and open the LBS.slnx file.
  4. Press Ctrl + Shift + B to build.
  5. Press F5 to run.

8. Verify it works

Once running, open these URLs in your browser:

Service URL
Foundry API Docs <yourhost>/scalar/v1
Ballr API Docs <yourhost>/api-docs/index.html

<yourhost> is shown in the Visual Studio output console (something like https://localhost:7XXX).


9. Test the Foundry frontend login

Field Value
Email Request for this info
Password Request for this info

Ask your manager for the test account credentials.