4 Commits
Author SHA1 Message Date
not 1dd8bd4e1b S-24/#25 · Helm chart + Kubernetes deployment, and Caddy for the portals (#166) (#167)
CI / lint (push) Successful in 1m17s
CI / build (push) Successful in 1m12s
CI / unit (push) Successful in 1m26s
CI / frontend (push) Successful in 2m58s
CI / mutation (push) Successful in 9m1s
CI / verify-stack (push) Successful in 8m53s
## What & why

Two changes, made and verified together on a real cluster.

**S-24 / #25 — a Helm chart for the platform.** One chart, `infra/helm/big-reference`,
whose `values.yaml` is a near-literal transcription of `infra/docker-compose.yml`, rendered
by three generic templates (Deployment, Job, Service) over a `workloads` map. Adding a
service is a values edit. `make k8s-lint` renders and schema-checks the whole stack without
a cluster. The issue asked for a *sketch*; this is deployed and verified end to end (see
below), which is more than it asked for — the part it asked for that is **not** here is the
production-posture write-up (HA, secrets, backup), see Known gaps.

**#166 — Caddy replaces nginx in the portals.** nginx resolves a variable `proxy_pass`
upstream itself, using only the `resolver` directive and never `/etc/resolv.conf`'s search
domains. That had cost two workarounds in one script: rewriting the resolver address for
rootless podman, and injecting a full FQDN so the bare `bff` name could resolve on
Kubernetes. Caddy dials per request through the system resolver, so `reverse_proxy
bff:8080` works on every engine unchanged; `apps/portal-nginx-resolver.sh` and the chart's
`BFF_HOST` env are deleted.

Closes #25
Closes #166

## Definition of Done

- [x] Linked Gitea issue (above).
- [x] Failing test committed before the implementation — twice: the Caddyfile contract test
      before the Caddyfiles, `make k8s-lint` before the chart.
- [x] Implementation makes the test pass.
- [x] Conventional Commits referencing the issues (`refs #25` / `refs #166`).
- [ ] CI green — awaiting the run on this PR (`make k8s-lint`, `dotnet format` and the new
      unit self-check pass locally; the compose e2e and mutation lanes are CI's).
- [ ] `docker compose up` from a fresh clone reaches green health checks within 3 minutes —
      the portal images were rebuilt and verified standalone, but a full `make up` run has
      not been done on this branch. Please confirm in review or let CI's smoke test speak.
- [x] Docs updated — `docs/runbooks/kubernetes-talos.md` (new), `frontend-decisions.md`,
      `demo-script.md`, and the docs that named nginx.
- [x] ADR added — ADR-0033 (chart) and ADR-0034 (Caddy).
- [ ] Demo note in `docs/demo-script.md` — not added: the deployment target is not a
      user-visible slice, and the Caddy swap is invisible to the demo script beyond the
      wording fix included here.

## How it was verified

Brought up from scratch on a single-node Talos v1.14.0 VM (6 vCPU / 10 GB, virtio disk)
under virt-manager: **29 pods ready and four bootstrap Jobs complete in under three
minutes, zero restarts**, using ~4.4 GB of the VM's 10 GB.

- Full Common Ground path: portal Caddy → BFF → domain → Flowable → ACL → OpenZaak +
  Objecten → NRC → event-subscriber → projection → public register (`INGEDIEND`, reference
  matching the submitted registration).
- Werkbak read with an MFA'd medewerker token → 200.
- The browser flow driven with Playwright against `http://localhost:30140`: secure context,
  `crypto.subtle` present, Keycloak form reached, login completed, **no console errors**.
- Routing checked against a stub BFF: SPA fallback serves deep links, each portal proxies
  its own groups, and a portal does *not* proxy a neighbour's group.

## Notes for reviewers

Three bugs this shook out, each fixed at the cause rather than the symptom:

1. **`command` vs `args`.** Compose's `command:` replaces the image CMD; Kubernetes'
   replaces the ENTRYPOINT. Transcribing one to the other broke every upstream image that
   relies on its entrypoint — postgres refused to run as root, Keycloak tried to exec
   `start-dev`. The chart now `fail`s at render time on `command`.
2. **Concurrent migrations.** Both `/setup_configuration.sh` and `/start.sh` run
   `manage.py migrate`; compose serialises them with `depends_on`, Kubernetes has no such
   edge, so the init Job and its web pod raced (`relation "zgw_consumers_service" already
   exists`). The four Django services now do both steps in order in the web pod — which
   also deletes four workloads.
3. **`emptyDir` databases are wiped by any pod-template change.** `make k8s-reseed` now
   also restarts `event-subscriber` and `projection-api`, which create the projection
   schema on start and otherwise keep writing to a schema-less database.

Known gaps / follow-ups:

- **Secrets.** `values.yaml` carries the dev credentials in plain text (`admin/admin`, the
  ZGW client secret, the two Objecten tokens) and the chart has no `Secret` objects. Fine
  for a laptop demo, and exactly what #25's "production posture" ADR should address — I
  suggest a follow-up issue rather than stretching this PR.
- **No CI gate for the chart yet.** `make k8s-lint` exists but is not wired into
  `.gitea/workflows/ci.yaml`, and nothing enforces that the chart and the compose file stay
  in step. Worth a small follow-up.
- **This is two slices in one PR.** They were built and verified together and the diff is
  entangled (the chart was written against Caddy from the start), so splitting now would
  mean re-creating an nginx-shaped chart to throw away. Happy to split if you'd rather.
- **Rebased onto #161** (merged as #165) rather than merged, to keep the history linear.
  One conflict, in the `unit:` target where both branches add a self-check line — resolved
  by keeping both. #161's `infra/host-browser.yml` arrived with
  `/usr/share/nginx/html/config.json` and is fixed to `/usr/share/caddy/` inside the
  `feat(portals)` commit, so no commit on this branch leaves that overlay pointing at a
  path the images no longer have.Reviewed-on: #167
2026-09-10 08:53:58 +00:00
not 0494730223 feat(portal-beheer): ACL default-fill configuration editor (closes #131) (#138)
CI / build (push) Successful in 1m34s
CI / lint (push) Successful in 1m36s
CI / unit (push) Successful in 1m45s
CI / frontend (push) Successful in 3m37s
CI / mutation (push) Successful in 6m25s
CI / verify-stack (push) Successful in 7m56s
## What & why

S-15b, second of the S-15 (#16) split, on top of S-15a (#133). A beheerder edits the ACL's ZGW **default-fill** values from the beheer portal, and the next zaak is stamped with the new values — no restart.

Closes #131

### The vertical

portal → BFF `GET/PUT /beheer/default-fill` (medewerker realm + `beheerder` role) → ACL `GET/PUT /default-fill` → a runtime-mutable in-memory store the ACL reads **per zaak**.

- **ACL**: `IDefaultFillStore` / `InMemoryDefaultFillStore` (thread-safe, seeded from `Acl:Defaults`); `AclService` reads `fill.Current` per zaak (not cached at construction); `GET`/`PUT /default-fill` with required-field validation.
- **BFF**: `IAclClient` gains `GetDefaultFillAsync`/`UpdateDefaultFillAsync`; `GET`/`PUT /beheer/default-fill` behind the `beheerder` policy. OpenAPI + generated client regenerated.
- **Frontend**: a *Default-fill* editor page in the beheer app (load → edit → save, with saved/failure states) + nav between Catalogus and Default-fill.

### Scope decision → ADR-0026

Only the **three ZGW fill fields** (bronorganisatie, verantwoordelijke organisatie, vertrouwelijkheidaanduiding) are editable. The S-27 catalog-resolution keys stay **static config** — editing them would desync the zaaktype-URL cache (ADR-0021), and they're catalogus wiring, not "default fill". The store is **in-memory** (seeded from config): an edit reverts on restart. That's the reference-app-appropriate ceiling (no DB added to the stateless ACL); upgrade path documented. Recorded in **ADR-0026**.

## Verified locally

lint (`dotnet format`) ✓ · .NET unit — acl 60 / bff 45 / domain 152 / event-subscriber 19 / acceptance 17 ✓ · frontend lint+test (8 projects) ✓ · beheer build ✓. Clean full-solution build (caught + fixed the acceptance `AclService` ctor drift). TDD red→green per layer (ACL store, ACL endpoints, BFF, frontend).

## Definition of Done

- [x] Failing test committed before each implementation (red→green per layer).
- [x] Conventional Commits referencing #131.
- [ ] CI green — see note below.
- [x] Docs: ADR-0026 + S-15b demo note.
- [x] Demo note in `docs/demo-script.md`.

## Note on CI

The bulk validates in the fast jobs (lint/build/unit/frontend/mutation). The **verify-stack e2e** (incl. the new `default-fill.spec.ts`) can't go green until the pre-existing **verify-stack bring-up failure on the 1.27/2.0.0 runner** is resolved (that fails on plain `main` too — unrelated to this PR). Additive change; no existing e2e touched.

🤖 Generated with [Claude Code](https://claude.com/claude-code)Reviewed-on: #138
2026-07-24 14:22:22 +00:00
not fff88ca23d ci: richer step reports via Gitea 1.27 job summaries (closes #136) (#137)
CI / build (push) Successful in 1m16s
CI / lint (push) Successful in 1m29s
CI / unit (push) Successful in 1m44s
CI / frontend (push) Successful in 3m47s
CI / mutation (push) Successful in 6m40s
CI / verify-stack (push) Successful in 8m15s
## What & why

Use the standard `$GITHUB_STEP_SUMMARY` (Gitea 1.27 + act_runner 2.0.0) to surface on the run page what was previously buried in logs or download-only artifacts. All five quick wins from #136, **reporting-only** — no job's pass/fail gating changes.

Closes #136

### Items

1. **Mutation scores** — added the `markdown` reporter to each `stryker-config.json`; the `mutation` job concatenates each service's `mutation-report.md` into the summary (`if: always()`). Also reveals where `make mutation` stopped on a ratchet break.
2. **Per-frontend tests** — the 4 apps' `test` targets emit vitest JSON to `test-output/{projectName}.json` (Nx token interpolation); `infra/vitest-summary.py` renders a per-frontend table.
3. **Per-service unit tests** — `make unit` now writes TRX; `infra/trx-summary.py` renders a per-service table (service name derived from the `services/<name>/` path, so `domain` shows, not `big.tests`).
4. **e2e per-spec results** — Playwright writes `playwright-report.json`; `run-e2e-check.sh` copies it out of the container (capturing the exit code first); `infra/playwright-summary.py` renders a per-spec table. Turns a red e2e into a one-glance "which spec".
5. **verify-stack check table** — each live-stack check has an `id`; a final `if: always()` step tabulates each check's //⏭️.

Docs: `gitea-actions-gotchas.md` §8 (version requirement + `$GITHUB_STEP_SUMMARY` guard + step-level `always()` note).

### Notes

- Every summary write is guarded with `[ -n "${GITHUB_STEP_SUMMARY:-}" ]`, so it no-ops on an unsupported runner / locally.
- New helper scripts are stdlib-only Python, matching the existing `infra/*.py` check scripts (no new dependency — a few lines of parsing rather than a test-logger package).
- `TestResults/` and `test-output/` gitignored.
- This is also the first PR-run exercising the #135 verify-stack fix end to end.

## Verified locally

`make unit` (TRX) ✓ · 4 apps' vitest JSON ✓ · ACL Stryker markdown report ✓ · all four parsers + the two summary shell blocks ✓ · `ci.yaml` + `run-e2e-check.sh` syntax ✓. The rendered summaries themselves only appear on the run page — this PR's CI run is the end-to-end check.

## Definition of Done

- [x] Each item writes to `$GITHUB_STEP_SUMMARY` (guarded), renders on the run page.
- [x] No change to any job's pass/fail gating.
- [x] Conventional Commits referencing #136 (one per item + docs).
- [ ] CI green; summaries visible on the run.
- [x] Runbook note (gotchas §8).

🤖 Generated with [Claude Code](https://claude.com/claude-code)Reviewed-on: #137
2026-07-24 13:27:53 +00:00
not 4698c869f3 feat(portal-beheer): beheer portal + read-only catalogus viewer (closes #130) (#133)
CI / build (push) Successful in 4m31s
CI / lint (push) Successful in 4m48s
CI / unit (push) Successful in 1m50s
CI / frontend (push) Successful in 4m44s
CI / mutation (push) Successful in 7m12s
CI / verify-stack (push) Canceled after 0s
## What & why

S-15a, the first of the S-15 (#16) split. A new **beheer** portal (medewerker realm, like behandel) shows the ZTC catalogus — the published zaaktypen — **read-only**. A beheerder logs in and sees the seeded BIG-REGISTRATIE zaaktype.

Closes #130

### The vertical

portal → BFF `GET /beheer/catalogi/zaaktypen` (medewerker realm + `beheerder` role) → ACL `GET /catalogi/zaaktypen` → ZGW Catalogi API.

- **ACL**: new read-only `GET /catalogi/zaaktypen` listing published zaaktypen (reuses the ADR-0021 Catalogi client; public-safe `identificatie`/`omschrijving`).
- **BFF**: new typed `IAclClient` + `Downstream:Acl:BaseUrl`, and `GET /beheer/catalogi/zaaktypen` behind a new `beheerder` policy (reuses the medewerker bearer scheme + realm-role lifting). OpenAPI spec + generated Angular client regenerated.
- **Keycloak**: `beheerder` realm role + `bram-beheerder` test user in the medewerker realm.
- **Frontend**: new `apps/beheer` Angular app (copied from behandel) with a read-only catalogus page; `SECURE_API_ROUTES=['/beheer/']`.
- **Infra**: `beheer` compose service (port 8143), added to `WAIT_SVCS` + CI log-dump; a Playwright e2e (beheerder login → catalogus shows BIG-REGISTRATIE).

### New boundary → ADR-0025

The BFF now reaches the **ACL directly** for the catalogus read — a new service-to-service edge (§14). The catalogus is neither a domain nor a projection concern, and §8.1 means only the ACL may read ZGW; routing through the domain would pollute it with a non-domain passthrough. §8.1/§8.3 stay intact. Recorded in **ADR-0025**.

## Definition of Done

- [x] Failing test committed before each implementation (red→green per layer: ACL, BFF, frontend).
- [x] Conventional Commits referencing #130.
- [ ] CI green — pending Gitea Actions run.
- [x] `docker compose up` brings up `beheer` (health-gated in `WAIT_SVCS`).
- [x] Docs — ADR-0025 + demo-script S-15a note.
- [x] Demo note in `docs/demo-script.md`.

## Verified locally

lint (`dotnet format`) ✓ · .NET unit (Acl 57 / Big 152 / EventSubscriber 19 / Bff 40) ✓ · frontend lint+test (8 projects) ✓ · frontend build (4 apps) ✓. Mutation ratchet: added a gateway unit test for the new `ListZaaktypenAsync` mapping so the ACL score holds. verify-stack (compose smoke + e2e) runs in CI.

## Notes for reviewers

- The BFF drops the ZGW URL from `BeheerZaaktype` (public-safe: identificatie + omschrijving only).
- The catalogus e2e asserts on the stable seeded `BIG-REGISTRATIE` (not a per-test reference), safe on the shared verify stack.
- Follow-ups: **S-15b** (#131) default-fill CRUD, **S-15c** (#132) medewerker-realm MFA.

🤖 Generated with [Claude Code](https://claude.com/claude-code)Reviewed-on: #133
2026-07-24 11:08:17 +00:00