Development Environment

How the local stack runs: infrastructure in Docker and application services natively with hot reload.

Architecture

The development stack runs infrastructure in Docker and application services natively with hot reload:

┌─────────────────────────────────────────────────┐ │ Native (hot reload) │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ Editor :3000 │ │Admin API:8000│ │ │ │ (Vite) │ │(cargo watch) │ │ │ └──────────────┘ └──────────────┘ │ ├─────────────────────────────────────────────────┤ │ Docker │ │ ┌──────────┐ ┌────────────┐ ┌───────┐ │ │ │PostgreSQL│ │ Prometheus │ │Grafana│ │ │ │ :5433 │ │ :9090 │ │ :3002 │ │ │ └──────────┘ └────────────┘ └───────┘ │ └─────────────────────────────────────────────────┘

One-Time Setup (build speed)

Run once per machine after cloning:

just dev-setup

It installs the mold linker (a hard requirement; the dev recipes won’t link without it) plus sccache, and points every git worktree at a single shared cargo target-dir so a new worktree reuses the already-built dependency graph instead of cold-building from scratch. When the repo is on a slow mount (9p/NFS/SMB, e.g. a WSL2 or Docker-Desktop dev container backed by a Windows drive), it relocates that target dir to fast local storage under ~/.cache/regelrecht/, which is usually the biggest build-time win. sccache is installed but left off locally (it disables incremental compilation, which hurts the hot-reload loop); CI uses both.

Starting the Dev Stack

just dev

This command:

  1. Checks prerequisites (cargo, node, docker, cargo-watch, mold)
  2. Starts infrastructure containers (PostgreSQL, Prometheus, Grafana)
  3. Waits for PostgreSQL to be ready
  4. Installs frontend dependencies if needed
  5. Starts all application services with hot reload

Frontend-Focused Dev Stack

When you only need to work on a frontend, just dev-frontend starts just the components that frontend needs (its backend, PostgreSQL, the engine WASM, and the Vite dev server with HMR) and skips Grafana, Prometheus, and the workers.

just dev-frontend # all frontends at once (default) just dev-frontend editor # just the editor just dev-frontend admin # just the admin dashboard just dev-frontend lawmaking # just the lawmaking UI (no backend) just dev-down # stop it (shared with `just dev`)
AppURLBackendDBNotes
editorhttp://localhost:7300editor-api :8000yesreal SSO, needs .env.sso-local; hosts the Corpusinwinning section
harvester-adminAPI only (UI is the editor’s Corpusinwinning section)admin API :8000 (:8001 when all run together)yesin all, editor-api proxies /api/harvest-admin/* here
lawmakinghttp://localhost:7500nonenostatic, no backend

Notes:

  • Backends run once via cargo run (not cargo watch); Vite keeps HMR for the frontend. Restarts after the first build are near-instant because the Rust artifacts are reused (see One-Time Setup).
  • The editor uses real SSO against the central Keycloak, so it needs .env.sso-local (copy .env.sso-local.example and fill in the values, see Auth and roles). Use Chrome or Firefox: the session cookie is Secure and only those send it over http://localhost. The default port 7300 (and 7500) are the redirect URIs already registered on the regelrecht-local Keycloak client. Override ports with EDITOR_PORT / LAWMAKING_PORT.
  • just dev-frontend and just dev are mutually exclusive: they share .dev-pids and ports, so run one at a time. just dev-down stops either.
  • In a dev container where the native backend can’t reach Postgres on localhost, set DB_HOST=host.docker.internal in .env (admin / just dev paths); the editor takes that host from DATABASE_URL in .env.sso-local.

Stopping

just dev-down

Logs

tail -f .dev-admin.log # Admin (harvester) API log tail -f .dev-editor.log # Editor log (hosts the Corpusinwinning section) just dev-logs # Infrastructure logs

Database Access

just dev-psql

Full Docker Stack

For running everything in Docker without hot reload:

just local # Start just local-down # Stop just local-logs # Logs just local-psql # Database access

Environment Variables

Create a .env file in the project root:

# Optional overrides POSTGRES_PORT=5433 GRAFANA_PORT=3002 PROMETHEUS_PORT=9090 RUST_LOG=info

Logging

Five binaries read these variables: editor-api, admin, and the three pipeline binaries (harvest worker, enrich worker, pipeline API). The harvester CLI builds its own subscriber and reads only RUST_LOG.

VariableValuesDefaultEffect
RUST_LOGtracing filterinfo (harvester CLI: warn)Which events are emitted
LOG_FORMATtext (plain), jsontextOutput format
LOG_SPAN_EVENTSnone, close, new, active, fullper service: close for editor-api, none elsewherePer-span timing lines

LOG_FORMAT=json writes one JSON object per event. The event’s own fields are flattened to the top level; the enclosing spans are added as nested span and spans keys, so a log backend can search per field. Set it per deployment in ZAD; locally the text lines read better, so leave the variable unset. An unrecognized value falls back to text and warns on stderr, so a typo never silences logging.

Pre-commit Hooks

Install pre-commit hooks:

pre-commit install

Hooks run automatically on commit:

  • Trailing whitespace, end-of-file fixes
  • YAML linting
  • Rust formatting (just format)
  • Rust linting (just lint)
  • Schema validation (just validate)

RegelRecht

An exploration by Bureau Architectuur of the Dutch Ministry of Economic Affairs and Climate Policy into the possibilities of transparent, executable legislation.

Links

GitHub repository
How it works
Stay informed
Roadmap (Dutch)
Documentation
Research

Contact

regelrecht@minbzk.nl

Part of

Bureau Architectuur
Ministry of Economic Affairs and Climate Policy