Local Development¶
This runbook reflects the current Docker Compose setup in this repository.
Prerequisites¶
- Docker with Compose support
- Python 3.12+ if you want to run backend tasks outside containers
- Node.js 20+ if you want to run UI tasks outside containers
Fast Path¶
Bring the full stack up:
make preflight
make up
make ps
If you switched branches and migrations or seed data changed, reset volumes first:
make down-v
make preflight
make up
make preflight runs three checks before startup:
- tooling checks (
docker,docker compose, compose wrapper) - NPM startup decision (
NPM_MODE=auto|on|off) - env consistency checks for auth hostname/issuer/CORS alignment
Use NPM_MODE=auto to reuse external Nginx Proxy Manager when available.
Force behavior when needed:
make up NPM_MODE=off
make up NPM_MODE=on
Default Local Endpoints¶
- UI:
http://localhost:18080 - UI HTTPS:
https://localhost:18443 - Backend API:
http://localhost:18011 - Keycloak:
http://localhost:18081 - Nginx Proxy Manager Admin:
http://localhost:81(only if local NPM is started) - PostgreSQL:
localhost:18432 - Redis:
localhost:18433 - Neo4j Browser:
http://localhost:7474 - Neo4j Bolt:
localhost:7687 - NATS client:
localhost:18004 - OPA:
http://localhost:18007 - PgAdmin:
http://localhost:5050 - Redis Commander:
http://localhost:18083
Health Checks¶
Use these after bootstrap:
make ps
curl http://localhost:18011/api/v1/config
curl http://localhost:18081/realms/substrate/.well-known/openid-configuration
curl -I http://localhost:18080
Expected local auth behavior:
- browser-facing issuer matches
SUBSTRATE_OIDC_ISSUER_URL - backend discovery resolves Keycloak on the Docker network
Environment Model¶
The local stack intentionally splits public URLs from internal service URLs.
Browser-facing values¶
These are the URLs a browser or external tool should see:
SUBSTRATE_VITE_OAUTH_ISSUER_URLSUBSTRATE_OIDC_ISSUER_URLSUBSTRATE_KEYCLOAK_HOSTNAMESUBSTRATE_KEYCLOAK_UI_BASE_URLSUBSTRATE_CORS_ALLOW_ORIGINS- Keycloak client redirect URLs in
infra/keycloak/substrate-realm.json
Internal container values¶
These are Docker-network addresses used between services:
SUBSTRATE_OIDC_DISCOVERY_URLSUBSTRATE_DATABASE_URLSUBSTRATE_REDIS_URLSUBSTRATE_NATS_URLSUBSTRATE_NEO4J_URISUBSTRATE_OPA_URL
For local Docker, the important split is:
- public issuer:
https://auth.invariantcontinuum.com/realms/substrate(default in.env.example) - internal discovery:
http://keycloak:8080/realms/substrate/.well-known/openid-configuration
Default Variable Source¶
Defaults live in .env.example.
maketargets read.env; if missing, it is created from.env.example.- Use
make preflight,make up,make down,make ps,make logs,make config. - Use
make render-infra-configif you only want to re-render infra config files. - Override any value ad-hoc by prefixing the command, for example:
SUBSTRATE_KEYCLOAK_PORT=28081 SUBSTRATE_UI_PORT=28080 make up NPM_MODE=off
- Keep
backend/.env.backendandui/.env.uifor optional local overrides only.
Infra Template Rendering¶
Some infra config files are generated from templates and controlled through Makefile defaults/overrides:
infra/keycloak/substrate-realm.json←infra/keycloak/substrate-realm.json.templateinfra/pgadmin/servers.json←infra/pgadmin/servers.json.templateinfra/pgadmin/pgpass←infra/pgadmin/pgpass.template
All make compose targets render every infra/**/*.template file first automatically.
Edit template files, not generated output files.
If you only want to refresh generated infra configs, run:
make render-infra-config
Running Individual Workflows¶
Backend tests¶
cd backend
uv run pytest
UI build or tests¶
cd ui
npm run build
npm run test:run
Regenerate API contracts and UI types¶
./api/generate-types.sh
Troubleshooting¶
Keycloak login redirects are wrong¶
Check:
docker-compose.ymlinfra/keycloak/docker-compose.ymlinfra/keycloak/substrate-realm.jsonbackend/.env.backendui/entrypoint.sh
The usual failure is mixing the public issuer URL with the internal Docker hostname.
Backend starts but auth fails¶
Check the config endpoint:
curl http://localhost:18011/api/v1/config
The reported issuer should match SUBSTRATE_OIDC_ISSUER_URL.
Branch switch caused migration failures¶
Reset the local state:
make down-v
make preflight
make up
Frontend boots but API calls fail¶
Check the UI container env defaults and backend config:
ui/entrypoint.shui/dockerfilebackend/app/settings.pybackend/app/core/security.py