feat(infra): wire OpenZaak → Open Notificaties notifications (closes #56) #57

Merged
not merged 4 commits from feat/56-nrc-notification-wiring into main 2026-06-30 12:29:44 +00:00
Contributor

What & why

S-01-c — the OpenZaak → Open Notificaties (NRC) notification wiring deferred by S-01 (#2). With this, a zaak created in OpenZaak is published to NRC and fanned out to subscribers — the upstream half of the event path the Event Subscriber (#7) will consume.

Closes #56

How it works (ADR-0007)

  • OpenZaak publishes to NRC: a zgw_consumers nrc service + notifications_config (via setup_configuration), authenticating as the existing big-reference-seed client. NOTIFICATIONS_DISABLED stays true for OpenZaak-only bring-ups (so the ACL integration test doesn't 500 against an absent NRC) and is set false by the full/local stacks and make stack-up.
  • NRC accepts it: the JWT credential, an ac service + autorisaties_api delegation to OpenZaak's Autorisaties API, and the zaken kanaal. nrc-init now runs setup_configuration (data delivered via the rr-nrc-config volume, like OpenZaak).
  • celery-beat (nrc-beat) added to every stack: NRC writes a ScheduledNotification on publish and a periodic execute_notifications task drains it for delivery. The lean S-01 stack dropped beat, so notifications were accepted but never delivered — this was the key missing piece.

Verification

make verify-notifications (+ a notifications CI job): brings the stack up, seeds a published BIG zaaktype, registers an abonnement to a webhook sink, creates a zaak, and asserts the sink receives the zaken/create notification — all from containers inside the compose network (runner-safe, per ADR-0006). Verified green locally.

Two non-obvious findings (documented in gitea-actions-gotchas.md §6)

  • Single-label hosts aren't URL-valid for OpenZaak/NRC (Django URLValidator) — the harness reaches services and registers the callback by container IP.
  • Abonnement callbacks must enforce auth — NRC probes the callback and refuses it (no-auth-on-callback-url) unless it returns 401 without the configured Authorization; the sink enforces a bearer token.

Definition of Done

  • Linked Gitea issue (#56).
  • Verification check committed (make verify-notifications + CI job).
  • Conventional Commits referencing the issue (refs #56).
  • CI: new notifications job; compose-smoke still green (full stack health unaffected — smoke creates no zaken).
  • docker compose up (full + local twin) brings the stack up healthy with notifications enabled.
  • Docs: ADR-0007, gitea-actions-gotchas §6, CI runbook.
  • ADR added — ADR-0007.
  • [N/A] Demo note — not user-visible (infra slice).

Notes for reviewers

  • Wiring applied consistently across the standalone (openzaak/opennotificaties), full (infra/docker-compose.yml), and local-bind-mount (docker-compose.local.yml) composes.
  • The notifications CI job needs egress to selectielijst.openzaak.nl (the published zaaktype the check creates a zaak against — same dependency as the ACL integration job, ADR-0006).
  • Unblocks #7 (Event Subscriber + Read Projection), which can now subscribe to the zaken kanaal.

Also: consolidated the live-stack CI checks (Closes #58)

On the single self-hosted runner, jobs run sequentially, so each make-up of OpenZaak was paid once per job. This PR also replaces the integration + notifications + compose-smoke jobs with one verify-stack job that boots the full stack once and runs clearly-named steps: health (verify-up) → ACL ↔ OpenZaak (verify-acl) → OpenZaak → NRC (verify-nrc) → teardown. The check logic moved into stack-agnostic runners (run-acl-integration.sh, run-notification-check.sh); the local make integration / make verify-notifications keep working by delegating to them. Verified locally via make verify.

## What & why S-01-c — the OpenZaak → Open Notificaties (NRC) **notification wiring** deferred by S-01 (#2). With this, a zaak created in OpenZaak is published to NRC and fanned out to subscribers — the upstream half of the event path the Event Subscriber (#7) will consume. Closes #56 ## How it works (ADR-0007) - **OpenZaak** publishes to NRC: a `zgw_consumers` `nrc` service + `notifications_config` (via `setup_configuration`), authenticating as the existing `big-reference-seed` client. `NOTIFICATIONS_DISABLED` stays `true` for OpenZaak-only bring-ups (so the ACL integration test doesn't 500 against an absent NRC) and is set `false` by the full/local stacks and `make stack-up`. - **NRC** accepts it: the JWT credential, an `ac` service + `autorisaties_api` delegation to OpenZaak's Autorisaties API, and the `zaken` kanaal. `nrc-init` now runs `setup_configuration` (data delivered via the `rr-nrc-config` volume, like OpenZaak). - **celery-beat (`nrc-beat`)** added to every stack: NRC writes a `ScheduledNotification` on publish and a periodic `execute_notifications` task drains it for delivery. The lean S-01 stack dropped beat, so notifications were accepted but never delivered — this was the key missing piece. ## Verification `make verify-notifications` (+ a `notifications` CI job): brings the stack up, seeds a published BIG zaaktype, registers an abonnement to a webhook sink, creates a zaak, and asserts the sink receives the `zaken`/`create` notification — all from containers **inside** the compose network (runner-safe, per ADR-0006). Verified green locally. ## Two non-obvious findings (documented in gitea-actions-gotchas.md §6) - **Single-label hosts aren't URL-valid** for OpenZaak/NRC (Django `URLValidator`) — the harness reaches services and registers the callback **by container IP**. - **Abonnement callbacks must enforce auth** — NRC probes the callback and refuses it (`no-auth-on-callback-url`) unless it returns 401 without the configured `Authorization`; the sink enforces a bearer token. ## Definition of Done - [x] Linked Gitea issue (#56). - [x] Verification check committed (`make verify-notifications` + CI job). - [x] Conventional Commits referencing the issue (`refs #56`). - [x] CI: new `notifications` job; `compose-smoke` still green (full stack health unaffected — smoke creates no zaken). - [x] `docker compose up` (full + local twin) brings the stack up healthy with notifications enabled. - [x] Docs: ADR-0007, gitea-actions-gotchas §6, CI runbook. - [x] ADR added — ADR-0007. - [N/A] Demo note — not user-visible (infra slice). ## Notes for reviewers - Wiring applied consistently across the standalone (`openzaak`/`opennotificaties`), full (`infra/docker-compose.yml`), and local-bind-mount (`docker-compose.local.yml`) composes. - The `notifications` CI job needs egress to `selectielijst.openzaak.nl` (the published zaaktype the check creates a zaak against — same dependency as the ACL integration job, ADR-0006). - Unblocks #7 (Event Subscriber + Read Projection), which can now subscribe to the `zaken` kanaal. --- ## Also: consolidated the live-stack CI checks (Closes #58) On the single self-hosted runner, jobs run **sequentially**, so each `make`-up of OpenZaak was paid once per job. This PR also replaces the `integration` + `notifications` + `compose-smoke` jobs with **one `verify-stack` job** that boots the full stack once and runs clearly-named steps: health (`verify-up`) → ACL ↔ OpenZaak (`verify-acl`) → OpenZaak → NRC (`verify-nrc`) → teardown. The check logic moved into stack-agnostic runners (`run-acl-integration.sh`, `run-notification-check.sh`); the local `make integration` / `make verify-notifications` keep working by delegating to them. Verified locally via `make verify`.
not added this to the Iteration 1 — Walking Skeleton milestone 2026-06-29 12:29:56 +00:00
not added 3 commits 2026-06-29 12:29:57 +00:00
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>
make verify-notifications brings the stack up, seeds a published BIG zaaktype, and
asserts a zaak-create notification is delivered to a webhook-sink abonnement. The
sink + driver run as containers inside the compose network and reach OpenZaak/NRC by
container IP (the runner can't reach published ports, and a single-label host isn't
URL-valid). The sink enforces a bearer token because NRC refuses an unauthenticated
callback. New 'notifications' Gitea Actions job runs it (Docker-only).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
arch(infra): ADR-0007 + runbooks for the OZ→NRC notification wiring (refs #56)
All checks were successful
CI / lint (pull_request) Successful in 52s
CI / build (pull_request) Successful in 40s
CI / unit (pull_request) Successful in 49s
CI / mutation (pull_request) Successful in 1m35s
CI / integration (pull_request) Successful in 3m24s
CI / notifications (pull_request) Successful in 3m14s
CI / compose-smoke (pull_request) Successful in 4m2s
a256db1a23
Records the wiring decision (AC-delegated auth, required celery-beat) and the two
non-obvious gotchas: single-label hosts aren't URL-valid (reach services by IP) and
abonnement callbacks must enforce auth. Documents the new notifications CI job.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
not added the type:slicearea:infra labels 2026-06-29 12:30:08 +00:00
not added 1 commit 2026-06-29 14:48:42 +00:00
refactor(ci): one verify-stack stage for all live-stack checks (closes #58) (refs #46 #56)
All checks were successful
CI / lint (pull_request) Successful in 51s
CI / build (pull_request) Successful in 40s
CI / unit (pull_request) Successful in 48s
CI / mutation (pull_request) Successful in 1m36s
CI / verify-stack (pull_request) Successful in 4m37s
d49443353e
On the single self-hosted runner CI jobs run sequentially, so booting OpenZaak once
beats once-per-job. Replace the integration + notifications + compose-smoke jobs with
one verify-stack job that brings the full stack up once and runs, as clearly-named
steps: health (make verify-up, the DoD smoke) → ACL ↔ OpenZaak (verify-acl) →
OpenZaak → NRC delivery (verify-nrc) → teardown (always) + log dump on failure.

The check logic moves into stack-agnostic runners (run-acl-integration.sh,
run-notification-check.sh) that operate on whatever stack is already up, reaching
services by container IP. The local single-concern wrappers (make integration oz-only,
make verify-notifications oz+nrc) keep working by delegating to the same runners, so
nothing is duplicated. make ci now runs the consolidated 'verify' stage.

Verified locally: make verify boots the full stack once, ACL integration passes and
the NRC notification is delivered, then tears down.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
not merged commit 32c98f00db into main 2026-06-30 12:29:44 +00:00
Sign in to join this conversation.