Skip to content
Firewatch
Get started
Site

Development guide

Install the following tools on the development host:

  • .NET 10 SDK;
  • Node.js 24 or another version satisfying the root engines field, with the npm version recorded by packageManager;
  • Docker Engine with the Compose v2 plugin;
  • Git; and
  • optionally psql for database inspection.

From the repository root, run the reproducible setup command:

Terminal window
dotnet tool restore && dotnet restore Firewatch.sln && npm ci

Re-run the setup command whenever the lock files, tool manifest, or project graph changes.

Terminal window
docker compose up --build

Open http://localhost:3000. The API is also published at http://localhost:8080. Compose waits for PostgreSQL readiness, starts the API and its local migration step, waits for API readiness, then starts the independent worker and web processes.

The base stack bootstraps the first self-hosted administrator without an email round trip and leaves email delivery disabled. To test verification and SMTP delivery with Mailpit at http://localhost:8025, start the explicit development override:

Terminal window
docker compose -f docker-compose.yml -f docker-compose.mailpit.yml up --build

Useful commands:

Terminal window
docker compose ps
docker compose logs --follow api worker
docker compose down

docker compose down preserves the PostgreSQL named volume. Use docker compose down --volumes only when you intentionally want to erase local Firewatch data.

Keep infrastructure in Docker:

Terminal window
docker compose -f docker-compose.yml -f docker-compose.mailpit.yml up -d postgres mailpit

In one terminal, configure a host-reachable database and run the API:

Terminal window
export ConnectionStrings__Firewatch='Host=localhost;Port=5432;Database=firewatch;Username=firewatch;Password=change-me-for-non-local-use'
export Database__MigrateOnStartup=true
export Auth__PublicBaseUrl=http://localhost:5173
export Email__Enabled=true
export Email__Smtp__Host=localhost
export Email__Smtp__Port=1025
export Email__Smtp__Security=None
dotnet run --project src/Firewatch.Api

In a second terminal, export the same connection string, select the local queue provider, then run:

Terminal window
export ConnectionStrings__Firewatch='Host=localhost;Port=5432;Database=firewatch;Username=firewatch;Password=change-me-for-non-local-use'
export Queueing__Provider=Postgres
dotnet run --project src/Firewatch.Worker

In a third terminal, start Vite:

Terminal window
npm run dev

Open http://localhost:5173 for the direct Vite workflow.

Vite is the only direct development server. The React application still uses the HTTP API through its same-origin development proxy; it never imports backend implementation code. VITE_DEV_API_PROXY_TARGET changes the proxy target from its http://localhost:8080 default while preserving same-origin browser calls. Set VITE_API_BASE_URL only when intentionally bypassing that proxy for a different public API origin.

With the API and worker running:

Terminal window
curl --include \
--request POST \
--header 'Content-Type: application/json' \
--data '{"name":"documentation-smoke-test"}' \
http://localhost:8080/api/v1/system/test-jobs

The response is 202 Accepted and includes an identifier and status URL. Replace <id> below with the returned identifier:

Terminal window
curl http://localhost:8080/api/v1/system/test-jobs/<id>
docker compose logs worker

The status eventually becomes Completed, and the worker log includes the stable message identifier. Retrying delivery does not create a second completion.

Run backend checks:

Terminal window
dotnet format Firewatch.sln --verify-no-changes --no-restore
dotnet build Firewatch.sln --configuration Release
dotnet test Firewatch.sln --configuration Release --no-build

Integration tests use Testcontainers with real PostgreSQL behavior. Docker must be running; SQLite is intentionally not used as a substitute.

Run frontend checks:

Terminal window
npm run frontend:check

Or run the constituent commands:

Terminal window
npm run lint
npm run format:check
npm run typecheck
npm test
npm run build
npm run api:check

The committed openapi/firewatch.v1.json document is the language-neutral API contract. After changing API routes or contracts, first regenerate the document from the ASP.NET Core project using the repository’s deterministic OpenAPI generation command, then generate the TypeScript types:

Terminal window
dotnet build src/Firewatch.Api/Firewatch.Api.csproj --configuration Release
npm run api:generate
npm run api:check

openapi-typescript is used because it is maintained, produces small static types without a proprietary runtime, and composes with the maintained openapi-fetch client. Generated files under packages/api-client/src/generated must not be manually edited. The handwritten package wrapper is the stable import surface consumed by apps/web.

npm run api:check regenerates to a temporary file and compares bytes, so CI fails when the checked-in client does not match the checked-in OpenAPI document. Both files must be committed with an API change.

A future iOS repository can feed the same OpenAPI file to Apple’s Swift OpenAPI Generator (with versions pinned by that repository). No iOS application or Swift-specific API is created here.

Restore the pinned local dotnet-ef tool before migration work:

The design-time fallback matches the unchanged .env.example database. Export ConnectionStrings__Firewatch first when using different credentials or a different database.

Terminal window
dotnet tool restore

Create a migration after changing the EF model:

Terminal window
dotnet ef migrations add <MigrationName> \
--project src/Firewatch.Infrastructure \
--startup-project src/Firewatch.Api \
--output-dir Persistence/Migrations

Apply it to the configured database:

Terminal window
dotnet ef database update \
--project src/Firewatch.Infrastructure \
--startup-project src/Firewatch.Api

Verify that the model and migration snapshot agree, as backend CI does:

Terminal window
dotnet ef migrations has-pending-model-changes \
--project src/Firewatch.Infrastructure \
--startup-project src/Firewatch.Api

Containerized deployments should use the API image’s one-off migration mode:

Terminal window
docker compose run --rm api --migrate

Do not enable automatic startup migrations for a multi-replica or managed deployment. See the cloud deployment contract for the expand/contract policy.

  • Readiness returns 503: inspect docker compose logs postgres api and verify ConnectionStrings__Firewatch and the PostgreSQL health check.
  • Integration tests cannot start: verify docker info works from the same terminal and that the Docker daemon is running.
  • The web page loads but API calls fail: for Vite, check its /api proxy or an explicitly set VITE_API_BASE_URL; for the web container, check FIREWATCH_API_UPSTREAM and API readiness.
  • Registration says email setup is required: the API is configured to require verification. Start the Mailpit override or configure Email__Enabled, the SMTP settings, and Auth__PublicBaseUrl.
  • Generated client check fails: regenerate with npm run api:generate, review the API diff, and commit the generated file together with the schema change.