KRINT Developer Setup
This is what a fresh checkout needs to be productive locally.
Prerequisites
- .NET 10 SDK (the API targets
net10.0) - Docker + Docker Compose (for Postgres and Keycloak)
- Node.js 20+ and Bun 1.3+ (the frontend uses bun as its package manager)
- Apache Maven (only if you want to rebuild the Keycloak theme JAR, see Notes)
- dotnet-ef global tool (only if you'll add EF migrations)
1. Backend secrets
Secrets live in dotnet user-secrets: they are never committed and never written to appsettings.json. The KRINT.API project has a UserSecretsId configured.
Set them once:
# DB connection
dotnet user-secrets --project src/KRINT.API set "ConnectionStrings:KrintDatabase" "Host=localhost;Port=5434;Database=krint-dev;Username=postgres;Password=d4vpas8w0rd13!!!"
# OIDC (consumed by /api/App, which the frontend reads at startup to configure auth)
dotnet user-secrets --project src/KRINT.API set "Oidc:Authority" "http://localhost:8080/realms/krint"
dotnet user-secrets --project src/KRINT.API set "Oidc:RequireHttpsMetadata" "false"
dotnet user-secrets --project src/KRINT.API set "Oidc:ClientId" "krint"
dotnet user-secrets --project src/KRINT.API set "Oidc:RedirectUri" "http://localhost:4200/"
dotnet user-secrets --project src/KRINT.API set "Oidc:PostLogoutRedirectUri" "http://localhost:4200/"
dotnet user-secrets --project src/KRINT.API set "Oidc:Scope" "openid profile email roles"
# CORS: origins allowed to call the API
dotnet user-secrets --project src/KRINT.API set "Cors:AllowedOrigins:0" "http://localhost:4200"
# Vault master key, used by SecretsVaultService to AES-GCM encrypt stored secrets at rest.
# 32 raw bytes, base64-encoded. Generate fresh on bash via `openssl rand -base64 32`.
dotnet user-secrets --project src/KRINT.API set "Vault:MasterKey" "$(openssl rand -base64 32)"Verify with dotnet user-secrets list --project src/KRINT.API.
appsettings.jsonandappsettings.Development.jsononly carry ASP.NET framework defaults (logging, allowed hosts). Application config goes in user-secrets.
Don't rotate
Vault:MasterKeywhile you have encrypted secrets in the DB: anything written with the old key becomes unreadable. There's no key-rotation flow yet.
2. Application config: krint.yaml
Non-secret app config lives in krint.yaml at the repo root. The API loads it via services.AddKrintConfig(env) (see src/KRINT.API/Extensions/KrintConfigExtensions.cs), which walks up from the content root to find the file. Override the path with the KRINT_CONFIG environment variable.
Currently used to declare which host ports each engine is allowed to bind:
krint:
port_ranges:
postgres: 30000-30199
mysql: 30200-30399
mariadb: 30400-30599
mssql: 30600-30799
mongo: 30800-30999Bind into a handler via IOptions<KrintOptions> (in KRINT.Application/Options/). The file is reload-on-change, so edits are picked up without a restart.
3. Dev infrastructure (Postgres + Keycloak)
docker compose -f compose.dev.yml up -d- Postgres →
localhost:5434, dbkrint-dev, userpostgres, passwordd4vpas8w0rd13!!! - Keycloak →
http://localhost:8080, adminadmin/admin. Thekrintrealm is auto-imported on first start fromkeycloak/krint-realm.json.
Stop with docker compose -f compose.dev.yml down. Volumes (postgres-data-dev, keycloak-data-dev) persist across restarts; drop them with -v if you want a clean slate.
4. Backend
dotnet run --project src/KRINT.API --launch-profile httpThe API binds to http://localhost:5165. In Development:
- OpenAPI document:
http://localhost:5165/openapi/v1.json(AllowAnonymous) - Scalar API reference UI:
http://localhost:5165/scalar/v1: click Authenticate to redirect to Keycloak (authorization code + PKCE against thekrintrealm/client). On return, Scalar attaches the bearer token to every request you fire from the UI.
On startup the API:
- Applies any pending EF migrations to the dev DB (
ApplyMigrations()). - Runs seeders (
ApplySeedsAsync(): currently a no-op placeholder). - Talks to the local Docker socket via
DockerService(Windows named pipe / Unix socket auto-detected by Docker.DotNet).
5. Frontend: first run
cd src/KRINT.Frontend
bun installWith the backend running, generate the typed API client:
bun run apigenThis reads openapitools.json, fetches http://localhost:5165/openapi/v1.json, and writes the typescript-angular client into src/app/api/. Rerun any time the backend's contract changes.
Then start the dev server:
bun startFrontend on http://localhost:4200.
6. Tests
dotnet run --project src/KRINT.TestsTUnit 1.x test runner. Coverage:
- Unit:
PingQueryTests,SecretGeneratorServiceTests,SecretsVaultServiceTests. All useMicrosoft.EntityFrameworkCore.InMemory: no Postgres required. - E2E: Browser-driven tests via Microsoft.Playwright. The suite boots its own ephemeral stack each session by driving
e2e/compose.e2e.ymlthrough Ductus.FluentDocker (Postgres +mock-oauth2-server+ the bundled KRINT image built from this repo's Dockerfile). You don't need to run the dev stack manually. Spin it up by hand for debugging withdocker compose -f e2e/compose.e2e.yml up -d --build.
Running the E2E suite
Prereqs: Docker running on the host, plus (one-time) Playwright's Chromium build:
dotnet build src/KRINT.Tests
pwsh src/KRINT.Tests/bin/Debug/net10.0/playwright.ps1 install chromiumThen:
dotnet run --project src/KRINT.TestsWhat happens on first run:
- Image build (~2 min): compose builds
src/KRINT.API/Dockerfile(frontend viabun build+ .NET API publish) into a single distroless Azure Linux image. - Stack boot (~30 s): Postgres 18 on
localhost:15434,mock-oauth2-serveronlocalhost:18080(non-interactive, auto-issues signed JWTs frome2e/mock-oauth2-config.json), KRINT app onlocalhost:18081. - Browser tests: each test opens a fresh browser context. The SPA redirects to the mock IdP, which auto-redirects straight back with a token. No login form, no credentials.
Test files (src/KRINT.Tests/E2E/):
KrintStack.cs: drivese2e/compose.e2e.ymlvia Ductus.FluentDocker, exposes URLsKrintTestFixture.cs: per-test browser contextKrintSessionHooks.cs: TUnit[Before/After(TestSession)]for stack lifecycleWizardHelper.cs: drives the/createwizardNavigationTests,WizardTests,InstanceDialogTests,BackupTests,ActivityLogTests,InstanceLifecycleTests
Headless by default. Set KrintTestFixture.Headless = false in KrintSessionHooks.StartStack for visible browser windows during local debugging.
7. EF migrations
KRINT can store its own data in SQLite (default) or PostgreSQL, selected with Database__Provider (Sqlite or Postgres). The connection string is ConnectionStrings__KrintDatabase; for SQLite it defaults to Data Source=krint.db.
Because EF Core can't keep two providers' migrations in one assembly, there are two migration sets, both auto-applied at API startup via ApplyMigrations() for whichever provider is active:
- Postgres →
src/KRINT.Infrastructure/Migrations/ - SQLite →
src/KRINT.Infrastructure.Migrations.Sqlite/Migrations/
After changing entities, add the migration to both sets (the EF tooling reads the target provider from Database__Provider):
# Postgres
$env:Database__Provider="Postgres"
dotnet ef migrations add <Name> -p src/KRINT.Infrastructure -s src/KRINT.API
# SQLite
$env:Database__Provider="Sqlite"
dotnet ef migrations add <Name> -p src/KRINT.Infrastructure.Migrations.Sqlite -s src/KRINT.APIdotnet ef database update is not needed for local dev. dotnet run does it on startup. Run it manually only if you want to apply migrations without booting the API.
Notes
- Keycloak theme:
keycloak/krint-realm.jsonreferencesloginTheme: krint. The theme source lives inkeycloak/keycloakify/. Build the JAR withbun run build-keycloak-themeinsidekeycloak/keycloakify/(Apache Maven must be onPATH); the dev compose mounts the JAR into Keycloak. Until you build it, dev Keycloak falls back to the default theme. - API exploration: use the Scalar UI at
/scalar/v1, the generatedtypescript-angularclient, or any spec-aware tool against/openapi/v1.json. No Swagger UI is mounted. - Scalar auth wiring:
OAuth2SecuritySchemeTransformerdeclares an OAuth2 authorization-code scheme on the OpenAPI doc (derived fromOidc:Authority);MapScalarApiReferencepre-fillsClientId+ PKCE. ThekrintKeycloak client whitelistshttp://localhost:5165/scalar/v1/oauth2-redirect.htmlas a valid redirect URI. If you add another API origin or path, updatekeycloak/krint-realm.jsonand re-import the realm (drop thekeycloak-data-devvolume). - Docker socket:
DockerServiceconnects to the local Docker daemon. On Windows this is the Docker Desktop named pipenpipe://./pipe/docker_engine; on Linux it'sunix:///var/run/docker.sock. When the API runs inside a container (prod compose), the host's socket must be mounted into it (/var/run/docker.sock:/var/run/docker.sock). - Architecture quick map:
KRINT.Domain: entities (Secret,BaseEntity), no dependenciesKRINT.Application: Mediator queries/commands + DTOsKRINT.Infrastructure: EF DbContext + migrations +Services/(DockerService, SecretGeneratorService, SecretsVaultService) +Interfaces/KRINT.API: ASP.NET Core entry point, JWT bearer auth, CORS, OpenAPI, controllers, DI wiring viaAddDocker()/AddSecrets()/ etc.KRINT.Frontend: Angular 21 + Tailwind + Spartan UI, OIDC viaangular-auth-oidc-client, typed API client generated by openapi-generator-cli.