Runbook β€” GIW POC Identity Platform

Status: POC Β· Date: 2026-09-21


1. Start, stop, reset

cd poc/identity

docker compose up -d --build     # start (first build ~3-5 min)
docker compose ps                # health of every service
docker compose logs -f keycloak  # follow one service
docker compose down              # stop, keep the database
docker compose down -v           # stop and wipe β€” realm re-imports on next start

Rebuild one service after a code change:

docker compose up -d --build entitlement-service

Changing the Java authenticator or the login theme requires a Keycloak rebuild:

docker compose build keycloak && docker compose up -d keycloak

2. Verify

node tests/e2e.mjs                                    # 39 tests
cd api && npx @redocly/cli lint giw-poc               # OpenAPI
cd api/collection && npx @usebruno/cli run "01 HR Verification" --env local \
  --env-var hrApiKey=giw-poc-hr-key-change-me

3. Trace one journey

Every request carries X-Correlation-ID. To follow one end to end:

CORR=corr-<uuid>
for c in giw-wifi-portal giw-keycloak giw-mock-hr-api giw-entitlement giw-session; do
  echo "── $c"; docker logs $c 2>&1 | grep "$CORR"
done

The portal prints the correlation id on the Access Granted page, so a passenger can read it to support staff.

4. Inject failures

# HR times out
curl -X POST localhost:3001/admin/fault -H 'Content-Type: application/json' \
  -d '{"mode":"timeout","delayMs":4000}'

# HR returns 503
curl -X POST localhost:3001/admin/fault -H 'Content-Type: application/json' \
  -d '{"mode":"unavailable"}'

# back to normal
curl -X POST localhost:3001/admin/fault -H 'Content-Type: application/json' \
  -d '{"mode":"none"}'

curl -s localhost:3001/health   # shows the active fault mode

Stop a service outright to test a hard outage:

docker compose stop mock-hr-api    # employee flow must fail closed
docker compose stop entitlement-service  # both flows must fail closed
docker compose start mock-hr-api entitlement-service

5. Keycloak admin

http://localhost:8080 β€” admin / admin (from .env).

Task Where
See the employee flow Authentication β†’ Flows β†’ employee-browser
Change the HR endpoint or timeout Authentication β†’ Flows β†’ employee-browser β†’ the giw-employee-verify execution β†’ gear icon
Confirm the flow binding Clients β†’ wifi-portal-employee β†’ Advanced β†’ Authentication flow overrides β†’ Browser Flow
Inspect a provisioned employee Users β†’ search emp- β†’ Attributes
Watch events live Realm settings β†’ Sessions, and Events β†’ User events
Check the claim mappers Client scopes β†’ giw-identity β†’ Mappers

6. Troubleshooting

Symptom Cause Fix
dependency failed to start: container giw-keycloak is unhealthy Keycloak still booting, or the health probe is wrong docker logs giw-keycloak. Health lives on port 9000, not 8080.
Tokens have no profile/email claims A clientScopes array in a full realm import replaces Keycloak's built-ins Do not put clientScopes in realm-export.json. bootstrap creates giw-identity over the Admin API for exactly this reason.
Employee flow shows the ordinary password form The flow binding override did not apply docker logs giw-bootstrap β€” look for flow.binding.set. Re-run with docker compose up -d --force-recreate bootstrap.
500 on the employee page FreeMarker error in employee-verify.ftl `docker logs giw-keycloak
UNAUTHORIZED from Entitlement with a fresh token iss mismatch The token's iss is the browser-facing hostname (KC_HOSTNAME), while JWKS is fetched over the container network. OIDC_ISSUER and OIDC_JWKS_URI are separate variables on purpose.
ENTITLEMENT_DENIED Β· COMPANY_NOT_ELIGIBLE Working as designed The company is not in EMPLOYEE_COMPANIES.
Employee authenticates but gets BASIC giw-identity scope not attached to the client Client scopes β†’ wifi-portal-employee β†’ Client scopes tab.
RATE_LIMITED on a correct login 5 failed guesses against that employee record inside 60 s Wait, or curl -X POST localhost:3001/admin/reset-limits (test harness only). Successful logins are never counted.
DMN_UNAVAILABLE Decision engine down β€” fails closed by design docker compose ps dmn-service, docker logs giw-dmn. POLICY_FALLBACK=local enables the built-in table instead.
Rule change has no effect The engine still holds the previous model curl -X POST localhost:3005/api/v1/decisions/reload, then GET /api/v1/decisions to confirm the file it loaded.
DMN_EVALUATION_ERROR The .dmn loaded but cannot evaluate A modelling bug, not a denial. docker logs giw-dmn shows the engine messages.
IDENTITY_LINK_CONFLICT Two users share one employee_ref Manual data fix. The flow refuses to guess.
Portal session lost after a restart In-memory store Expected. Log in again.
Ports already in use 3000-3003, 5432, 8080, 9000 lsof -ti tcp:8080
Maven build is slow or fails First build resolves the Keycloak SPI tree Needs network access to repo1.maven.org. The layer caches afterwards.

7. What breaks when

Service down Customer flow Employee flow
dmn-service fails closed β€” no rules, no access fails closed
mock-hr-api unaffected fails closed at verification
keycloak fails fails
entitlement-service authenticates, then 502 β€” no access same
session-service entitlement issued, then 502 β€” no access same
postgres Keycloak fails Keycloak fails

No path grants access when a dependency is down. That is the property worth re-testing after any change.

8. Health

for p in 3000 3001 3002 3003; do echo -n "$p: "; curl -s localhost:$p/health; echo; done
curl -s localhost:9000/health/ready    # Keycloak

9. Before this leaves a laptop

Standing instruction: it does not leave the laptop. No cloud, no test environment, no shared server, no production, no external hosting. The list below is what a future deployment would have to satisfy β€” it is not a to-do list for now. See ENVIRONMENTS.md Β§3.

  • Replace every secret in .env
  • DEBUG_EXPOSE_TOKEN=0
  • Remove POST /admin/fault and POST /admin/reset-limits from mock-hr-api and mock-loyalty-api
  • Remove the service landing pages (GET / on ports 3001–3003, 3005–3006) β€” they list the endpoint surface
  • Authenticate POST /api/v1/decisions/reload, and put a rule change behind an approval workflow
  • TLS: sslRequired: external, https KC_HOSTNAME, Secure on the sid cookie
  • Replace both X-API-Key legs with mTLS or service tokens
  • Replace the in-memory entitlement and session stores
  • Add rate limiting to Entitlement and Session
  • Narrow the accepted audience β€” stop accepting account
  • Replace Direct Grant with browser OIDC / federation β€” Mode B in AUTH-STRATEGY.md. Direct Grant is POC only.
  • Close decision D7 (Controller/Processor) before pointing at real HR data
  • Capture a consent artefact for the CCCD path, or remove CCCD per ADR-003

GIW POC Identity Platform Β· local demo Β· not production Β· generated from the repository