## What & why
Closes#91. `infra/docker-compose.local.yml` (the no-make local stack) was missing the `domain` service and all three portals, and never wired host-browser OIDC — so browsing the behandel portal redirected to `http://keycloak:8080/…`, which a host browser can't resolve.
- **Parity**: add `domain`, `self-service`, `openbaar`, `behandel` (local now matches the CI-canonical `docker-compose.yml` service-for-service).
- **BFF**: give it the Keycloak + downstream env it was missing (it previously fell back to appsettings and couldn't reach Keycloak).
- **Host-browser OIDC**: pin Keycloak's frontend/issuer URL to `http://localhost:8180` (`KC_HOSTNAME`) with `KC_HOSTNAME_BACKCHANNEL_DYNAMIC=true`, so a host browser logs in on `localhost:8180` while the BFF still validates in-network via `keycloak:8080`.
- **Portals**: bind-mount a `localhost:8180` `config.json` over the image's baked `keycloak:8080` one (`infra/local-config/*`). openbaar is anonymous, no config.
## How verified
- `docker compose -f infra/docker-compose.local.yml config` valid; parity check shows nothing missing.
- Started Keycloak from the local compose and confirmed the discovery document:
- **host view** (`localhost:8180`): `issuer` + all endpoints on `localhost:8180` (what the browser uses).
- **in-network view** (`keycloak:8080`): `issuer` stays `http://localhost:8180/...` (matches browser tokens) while `jwks_uri`/`token_endpoint` resolve to `keycloak:8080` (reachable by the BFF).
## Notes for reviewers
- The full portal→BFF→Keycloak login round-trip should get a quick browser smoke test on a real engine (I validated the Keycloak issuer/backchannel split and compose validity, but can't drive a browser here). Ports: self-service :8140, openbaar :8141, behandel :8142; users in `docs/synthetic-data.md`.
- On rootless podman the portal→BFF nginx proxy (`resolver 127.0.0.11`) may 502 (a separate known podman-vs-docker DNS quirk); login is a browser redirect and is unaffected. Works on Docker Desktop.
- No app-code change; `docker-compose.yml` (CI-canonical) is untouched.
Reviewed-on: #92
Add projection-db + the two services to both compose files (host ports 8110/8120), their
Dockerfiles (repo-root context — they share Projection.ReadModel), and a runner-safe
verify-projection check (infra/run-projection-check.sh) that registers the abonnement at the
real subscriber, creates a zaak and asserts projection-api serves an INGEDIEND row. Wire it
into make (verify-projection, verify, WAIT_SVCS) and the CI verify-stack job, and run the
event-subscriber Stryker ratchet in `make mutation` + upload its report.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes the S-01-c wiring so a zaak created in OpenZaak is published to NRC:
- OpenZaak: a zgw_consumers 'nrc' service + notifications_config (setup_configuration),
publishing as big-reference-seed. NOTIFICATIONS_DISABLED stays true for OpenZaak-only
bring-ups (OZ_NOTIFICATIONS_DISABLED) so the ACL integration test doesn't 500; the
full/local stacks and stack-up set it false.
- NRC: the JWT credential, an 'ac' service + autorisaties_api delegation to OpenZaak's
Autorisaties API, and the 'zaken' kanaal. nrc-init now runs setup_configuration; its
data.yaml is delivered via the rr-nrc-config volume (seed-config.sh nrc), mirroring oz.
- nrc-beat added to every stack: NRC accepts a notification then drains it via a
scheduled execute_notifications task — without beat, nothing is delivered. Interval 5s.
Applied across the standalone, full, and local-bind-mount composes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds infra/docker-compose.local.yml: the same full stack as the canonical
infra/docker-compose.yml, but the three config inputs (OpenZaak data.yaml,
Keycloak realms, Flowable BPMN) are bind-mounted from the repo instead of
streamed into external volumes by seed-config.sh.
Bind mounts are valid here because a local daemon (Docker Desktop on Windows/
macOS, or rootless Podman on Linux) can see the working directory — the seed
dance only exists for the containerized CI runner, where it can't. So this file
runs with a plain `docker compose up`: no make, no seed step, no bash.
docker compose -f infra/docker-compose.local.yml up -d --build
docker compose -f infra/docker-compose.local.yml up -d --build --wait # Docker Desktop
Linux/macOS convenience wrappers `make local` / `make local-down` added too.
Verified on podman: Keycloak boots from this file and imports the bind-mounted
realms (digid realm returns 200). docs/runbooks/ci.md documents the Windows path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>