Skip to content

Local Development

  • Go — the version pinned in go.mod (go 1.25.0).
  • Bun — no version is pinned; CI installs the latest via oven-sh/setup-bun. Used for web/, desktop/, sdk/, and automations/.

The root Makefile covers the Go binaries and the web build:

Terminal window
make build # build-server + build-runner + build-web
make build-server # ./dist/nexul-server
make build-runner # ./dist/nexul-runner
make build-web # bun run --cwd web build
make build-single # single-binary server, web assets embedded via go:embed
make test # go test ./...
make vet # go vet ./...
make coverage # go test -race with the 80% gate
make sqlc # regenerate internal/platform/storage/sqlcgen from queries/*.sql
make sqlc-check # sqlc vet + sqlc diff — fails if generated code is stale

Outside the debug stack, run the server directly against Go:

Terminal window
go run ./server/cmd

The server generates its auth secret and reads its config from environment variables (see .env.example — nothing is required for a local run; every variable there is an override).

The web app has its own dev server:

Terminal window
bun install
bun run --cwd web dev

docker-compose.debug.yml runs the server, web (Vite, not nginx), runner, automations host, and an OpenObserve instance for logs together, with Delve attached to the Go binaries (:2345 server, :2346 runner):

Terminal window
docker compose -f docker-compose.debug.yml up

Web (:5173), server HTTP/WS/MCP (:8080/:8081/:8082), and OpenObserve (:5080) are all reachable on the host.

Named node_modules volumes. web-node-modules and automations-node-modules are named Docker volumes, not bind mounts — they exist so the container’s installed dependencies don’t get shadowed by whatever (or nothing) is in your host checkout’s node_modules. That means a host-side bun install doesn’t reach the container: after changing package.json in web/ or automations/, install inside the running container instead:

Terminal window
docker compose -f docker-compose.debug.yml exec web bun install
docker compose -f docker-compose.debug.yml exec automations bun install

Never run docker compose -f docker-compose.debug.yml down -v to pick up a dependency change — that also wipes the debug database and OpenObserve volumes. exec ... bun install is the fix; down -v is not.

Terminal window
go test ./... # Go, all packages
bun run --cwd web test # web, Vitest
bun run --cwd desktop test # desktop, Vitest
bun run --cwd sdk test # sdk, Vitest
bun run --cwd automations test # automations, bun test

make coverage is the enforced gate, not a suggestion:

Terminal window
make coverage

It runs go test -coverprofile=coverage.out -covermode=atomic ./..., drops exempt paths from the denominator (cmd/*, testutil/, node_modules/, generated code, sqlcgen/), computes the percentage over what’s left, and fails below the threshold. It writes coverage.filtered.out and coverage.html — the same artifacts CI uploads. See Coding Standards for what the gate expects beyond the number.