Local development
How to work on the Colden kit on one machine: the whole stack in Compose, a host or the web run from source against the stack's services, the commands of the hosts, email, payments, tests and the usual problems.
The kit runs on one machine the way it runs in a cluster: the same images, the same identity server, the same databases. Compose starts all of it; while you change one part, you run that part from source against the rest. The Compose file itself, its services, ports and variables are described in infra/compose/README.md.
Prerequisites
- .NET SDK 10.0, Node.js 22 with pnpm 12.17.0, and Docker with Compose v2; the versions are in the README.
- Once per clone:
pnpm install --frozen-lockfileandgit config core.hooksPath .githooks(the pre-commit hook). - Once per clone:
cp infra/compose/.env.example infra/compose/.env, then a value for every variable marked secret. Generate them; none of them is shared with any other environment.
The whole stack
docker compose -f infra/compose/compose.yaml --env-file infra/compose/.env up -d --build --wait
--wait returns when every service is healthy: PostgreSQL, Redis and Mailpit, Kratos after its migration, the kit's migration and message storage setup, then the api, the worker and the web.
| Open | For |
|---|---|
http://localhost:3000 | the web: sign up, and the codes arrive in Mailpit |
http://localhost:8025 | Mailpit: every email the stack sends |
http://localhost:5080/scalar | the API's reference, in Development |
http://localhost:5090/hangfire | the job dashboard, with Jobs__DashboardPassword from .env |
down stops the stack and keeps the data; down -v deletes the data too. After a change to a host's code, up -d --build --wait rebuilds only the images whose sources changed.
One part from source
Start the infrastructure without the kit's own containers, then run the part you change from source:
docker compose -f infra/compose/compose.yaml --env-file infra/compose/.env --profile services up -d --wait
That starts PostgreSQL, Redis, Mailpit, Kratos and its migration. Apply the kit's migrations once against it, from source (ConnectionStrings__Postgres as below):
dotnet run --project apps/api/Colden.Api -- migrate
The API or the worker
appsettings.Development.json points both hosts at the Compose services. The secrets come from dotnet user-secrets, with the values of infra/compose/.env:
dotnet user-secrets set "ConnectionStrings:Postgres" "Host=localhost;Port=5432;Database=colden;Username=colden;Password=<COLDEN_DB_PASSWORD>" --project apps/api/Colden.Api
dotnet user-secrets set "Ory:WebhookSecret" "<Ory__WebhookSecret>" --project apps/api/Colden.Api
dotnet user-secrets set "ConnectionStrings:Postgres" "<the same connection string>" --project apps/worker/Colden.Worker
dotnet run --project apps/api/Colden.Api # http://localhost:5080, the internal port 5081
dotnet run --project apps/worker/Colden.Worker # http://localhost:5090
Kratos sends its registration and settings hooks to the api's internal port. For an api run from source, set KRATOS_HOOK_TARGET=http://host.docker.internal:5081 in infra/compose/.env and recreate the identity server (up -d kratos with the same -f and --env-file). In Development the hosts build the message tables themselves (Messaging:AutoBuildStorage), so setup-messaging is not needed.
The web
pnpm --filter @colden/web dev # http://localhost:3000
apps/web/.env.example lists the web's variables: the API's address (http://localhost:5080 for an api from source, the Compose api otherwise), the identity server's public address, and WEB_COOKIE_SECRET. The development server adds the component gallery at /dev/kit and the system states at /dev/states. Stop the Compose web container first if it holds port 3000.
The hosts' commands
The api binary carries the administrative commands. In Compose they run in a one-off container; from source they run with dotnet run:
docker compose -f infra/compose/compose.yaml --env-file infra/compose/.env run --rm --no-deps api seed-demo
dotnet run --project apps/api/Colden.Api -- make-superadmin [email protected]
| Command | Does |
|---|---|
migrate | applies the kit's migrations, then each product context's |
setup-messaging | creates or updates the message store and the queue tables |
seed-demo | creates the demo organization with its people and data |
make-superadmin <email> | makes a person who signed in once a super-admin, who then enters the super-admin area with a second factor |
Getting started lists their exit codes.
Every email goes to Mailpit, which sends none: the sign-up and sign-in codes, the invitations, the billing emails. In Development the api previews a template with sample data at http://localhost:5080/dev/emails/<template>?culture=ro-RO. To send for real, set Email__Provider=Mailgun and the Mailgun variables; email has the sending domain's records.
Payments
Billing needs a Stripe account in test mode:
- Put its secret key in
Stripe__SecretKeyand its publishable key inStripe__PublishableKey. - Print the forwarder's signing secret with
docker compose -f infra/compose/compose.yaml --env-file infra/compose/.env run --rm --no-deps stripe-cli listen --print-secretand put it inStripe__WebhookSecret. - Set the plan catalog (
Billing__Plans__*) to that account's prices, as billing describes. - Keep
stripeinCOMPOSE_PROFILES, so thestripe-cliservice forwards the account's test events to the api.
Without a key the kit runs, billing answers 503 and the screens say so.
Tests while you work
dotnet test --project apps/api/tests/Colden.Api.Tests --filter-class "*InvitationTests" # one class
dotnet test --solution Colden.slnx # everything
pnpm -r lint && pnpm -r typecheck && pnpm -r test # the web workspace
pnpm e2e # the journeys, against the stack
The .NET integration tests start their own PostgreSQL, Redis and Kratos containers and do not touch the Compose stack; without Docker they are reported skipped, never passed (testing). The journeys drive the stack's web and read the codes from its Mailpit (tests/e2e/README.md).
Usual problems
| Symptom | Cause and fix |
|---|---|
| A host exits at once with code 64 | A required configuration key is missing; the message names it. Set it in .env or dotnet user-secrets. |
| Sign-up works but the organization never appears | Kratos cannot reach the api's hooks: KRATOS_HOOK_TARGET points at the wrong api, or Ory__WebhookSecret differs between the two. |
| No email arrives | The worker sends them: check that it runs (ps) and look in Mailpit at http://localhost:8025. |
A changed password in .env does not work | The database roles are created on the first start of the volume only: down -v, or ALTER ROLE by hand. |
/health/ready answers 503 | A required check fails (PostgreSQL, the migrations, the message store); the JSON names it. Run migrate or setup-messaging when the tables are missing. |
| Billing answers 503 | No Stripe key, or no plan catalog; see Payments. |