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:8180config.json over the image's baked keycloak:8080 one (infra/local-config/*). openbaar is anonymous, no config.
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.
## 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.
docker-compose.local.yml lacked the domain service and all three portals, and never wired
host-browser OIDC. Add them, give the BFF its Keycloak/downstream env, and pin Keycloak's issuer
to http://localhost:8180 with a dynamic backchannel 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 baked keycloak:8080 one.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What & why
Closes #91.
infra/docker-compose.local.yml(the no-make local stack) was missing thedomainservice and all three portals, and never wired host-browser OIDC — so browsing the behandel portal redirected tohttp://keycloak:8080/…, which a host browser can't resolve.domain,self-service,openbaar,behandel(local now matches the CI-canonicaldocker-compose.ymlservice-for-service).http://localhost:8180(KC_HOSTNAME) withKC_HOSTNAME_BACKCHANNEL_DYNAMIC=true, so a host browser logs in onlocalhost:8180while the BFF still validates in-network viakeycloak:8080.localhost:8180config.jsonover the image's bakedkeycloak:8080one (infra/local-config/*). openbaar is anonymous, no config.How verified
docker compose -f infra/docker-compose.local.yml configvalid; parity check shows nothing missing.localhost:8180):issuer+ all endpoints onlocalhost:8180(what the browser uses).keycloak:8080):issuerstayshttp://localhost:8180/...(matches browser tokens) whilejwks_uri/token_endpointresolve tokeycloak:8080(reachable by the BFF).Notes for reviewers
docs/synthetic-data.md.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.docker-compose.yml(CI-canonical) is untouched.