Run locally¶
This is the shortest path to bring up just the platform (API + web) and look at the screens. For the full process of actually installing a stack on a cluster, see the E2E guide.
The shortest path¶
./scripts/runbook_local.sh preflight # Check tools
./scripts/runbook_local.sh up # Infrastructure + migrations + API + frontend
When it finishes, check the result.
The platform is up properly only when db shows connected.
| Address | What |
|---|---|
http://localhost:8090 |
API |
http://localhost:5173 |
Web (Vite) |
http://localhost:8180 |
Keycloak |
The frontend does not take the API address from an environment variable. It sends requests to the
relative path /api/v1, and Vite forwards /api and /ws to localhost:8090.
The manual path¶
make dev # Start Docker Compose + apply migrations
make run # Build bin/api and run it → http://localhost:8090
make web-dev # Vite dev server → http://localhost:5173
make dev starts four containers — PostgreSQL, object storage, Redis, and Keycloak.
The onboarding doc leaves out Keycloak and says three
make dev-up brings up every service without naming any, so Keycloak starts as well.
The object storage service is named minio, but the implementation is SILO
It uses an AGPL fork of MinIO (pgsty/silo). It also keeps the MINIO_ROOT_* environment
variables (for compatibility), so the names make it easy to mistake it for MinIO. The banner
that make dev prints still says "MinIO", too.
Common make targets¶
| Target | What it does |
|---|---|
dev-up / dev-down / dev-clean |
Start Compose / stop it / stop it and delete the volumes too |
dev-status / dev-logs |
Show status / follow logs |
dev |
dev-up + migrate-up |
build |
Builds bin/api and bin/nullus-bootstrap |
run, run-dev |
Runs the API, reading .env.dev |
test |
go test ./... — untagged tests only |
test-integration |
go test -tags integration ./e2e/ (starts Postgres itself with testcontainers) |
test-golden-path |
60-minute E2E that actually deploys to kind nullus-platform |
test-cover |
coverage.out and coverage.html |
lint |
golangci-lint run ./... — Go only |
migrate-up, migrate-down, migrate-status |
Migrations |
db-shell |
Connects with psql |
web-dev, web-build, web-test |
Frontend |
clean |
Deletes bin/, coverage.*, and web/dist |
Four things that are easy to mix up
make buildbuilds only the Go binaries —bin/apiandbin/nullus-bootstrap. The frontend build output (web/dist) comes frommake web-build. No target buildscmd/nullus(the CLI)make lintchecks Go only. For the frontend, runcd web && npm run lintmake testruns without build tags, so theintegrationande2etests are left out. For those, usemake test-integrationmake test-golden-pathis a 60-minute E2E that actually deploys to a kind cluster
Frontend commands¶
cd web
npm run dev # Vite dev server
npm run build # tsc -b && vite build
npm run lint # eslint .
npx vitest run # Unit tests
npm run e2e # Playwright (chromium)
There is no test script
Run unit tests with npx vitest run (for watch mode, npx vitest with no arguments).
There is no lint:fix either, so auto-fix is npx eslint . --fix. Prettier is not a
dependency; you fetch and run it with npx prettier --write . — the format check in CI uses
its own prettier.
Other available scripts: preview, e2e:visual, e2e:visual:update,
e2e:headed, e2e:report, e2e:stack-critical, e2e:shots,
inventory[:check], theme:generate[:check], responsive:audit[:check]
Database¶
| Item | Value |
|---|---|
| Tool | golang-migrate (v4.20.1 in the image; installed automatically with go install locally) |
| Location | db/migrations/ — 96 up/down pairs, numbered 000001–000098 |
| Apply locally | make migrate-up |
| Connection string (local default) | postgres://nullus:nullus_dev@localhost:5433/nullus?sslmode=disable |
When you deploy to Kubernetes, the API does not run the migrations itself. A pre-upgrade hook
Job in the chart runs migrate with the API image (migration.enabled defaults to true).
The Compose Postgres does not run migrations
It mounts db/migrations into a subdirectory of /docker-entrypoint-initdb.d, and
the Postgres init step does not look into subdirectories.
migrate always applies the migrations.
Changing environment variables¶
Environment variable names follow the config keys directly — database.host becomes
NULLUS_DATABASE_HOST. .env.example, .env.dev, and the Makefile use these names.
The old NULLUS_DB_* names have no effect
Shortened names such as NULLUS_DB_HOST match no config key, so they are silently
ignored. If the .env you have uses those names, changing the port or password has no
effect — switch to NULLUS_DATABASE_*.
(internal/shared/config/database_env_test.go pins down both cases.)
The three lines NULLUS_MINIO_ENDPOINT, _ACCESS_KEY, and _SECRET_KEY are not used at
all, because the config structure has no matching fields.
The correct names are listed in Environment variables.
Also, make run/run-dev read .env.dev, not .env.
Backend hot reload is not set up
The setup guide tells you to install Air, but the repository has no .air.toml and no
script calls air.