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>
78 lines
4.7 KiB
Markdown
78 lines
4.7 KiB
Markdown
# ADR-0007: Wiring OpenZaak → Open Notificaties (NRC) for notifications
|
|
|
|
- **Status:** Accepted
|
|
- **Date:** 2026-06-29
|
|
- **Deciders:** Respellion engineering
|
|
- **Relates to:** S-01-c (#56); completes S-01 (#2); unblocks the Event Subscriber (#7); builds on ADR-0002 (catalogus/seed) and ADR-0006 (runner-safe container harnesses)
|
|
|
|
## Context
|
|
|
|
S-01 brought OpenZaak + Open Notificaties (NRC) up in compose but **deferred the
|
|
notification wiring**: OpenZaak ran with `NOTIFICATIONS_DISABLED=true` and NRC's
|
|
`setup_configuration` was empty. The walking skeleton (PRD §12) needs the upstream
|
|
event path — a zaak created in OpenZaak must publish a notification NRC fans out to
|
|
subscribers — before the Event Subscriber (#7) can consume it.
|
|
|
|
The OpenZaak↔NRC handshake is intricate and several details are non-obvious; they
|
|
were nailed down by iterating `setup_configuration` against the running stack.
|
|
|
|
## Decision
|
|
|
|
**Provision both sides declaratively via `setup_configuration`, authenticate with the
|
|
existing `big-reference-seed` client, and run NRC's celery-beat so deliveries happen.**
|
|
|
|
- **OpenZaak** (`infra/openzaak/setup_configuration/data.yaml`): a `zgw_consumers`
|
|
service `nrc` (api_type `nrc`, the NRC API root) plus `notifications_config` naming
|
|
it. `NOTIFICATIONS_DISABLED` is flipped to `false` **only when NRC is present** —
|
|
the full stack and the local twin set it; OpenZaak-only bring-ups (`openzaak-up`,
|
|
the ACL integration test) default it back to `true` via `OZ_NOTIFICATIONS_DISABLED`
|
|
so they don't 500 publishing to an absent NRC.
|
|
- **NRC** (`infra/opennotificaties/setup_configuration/data.yaml`): the
|
|
`big-reference-seed` JWT credential (to verify OpenZaak's token), a `zgw_consumers`
|
|
`ac` service pointing at **OpenZaak's Autorisaties API**, the `autorisaties_api`
|
|
step delegating authorization to that AC, and the `zaken` kanaal. NRC's init
|
|
container switches from `migrate` to `/setup_configuration.sh`; its data.yaml is
|
|
delivered through the `rr-nrc-config` external volume by `infra/seed-config.sh`
|
|
(the same `docker cp` pattern as OpenZaak — bind mounts don't reach the CI runner's
|
|
daemon).
|
|
- **celery-beat is required.** NRC accepts a notification and writes a
|
|
`ScheduledNotification`; a periodic `execute_notifications` task (celery-beat,
|
|
every `NOTIFICATION_SEC_INTERVAL`s) drains it to the worker for delivery. The lean
|
|
S-01 stack dropped beat — so notifications were accepted but never delivered. An
|
|
`nrc-beat` service is added to every compose; the interval is lowered to 5s.
|
|
|
|
Verification is a runner-safe smoke (`infra/run-notification-check.sh`): it 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 (ADR-0006). Locally it runs via `make verify-notifications`
|
|
(a throwaway oz+nrc stack); in CI it runs as the `verify-nrc` step of the consolidated
|
|
`verify-stack` job (one shared full-stack bring-up — issue #58).
|
|
|
|
## Consequences
|
|
|
|
- **Positive:** the walking-skeleton event path works end to end; #7 can consume real
|
|
notifications; the wiring is declarative and reproducible from a fresh `make`.
|
|
- **Gotchas captured (see gitea-actions-gotchas.md):**
|
|
- **Single-label hosts aren't URL-valid.** OpenZaak/NRC reject `http://openzaak…`
|
|
/`http://nrc-web…` in URLs they validate (Django `URLValidator`); the verify
|
|
harness reaches services and registers the sink callback **by container IP**.
|
|
- **Abonnement callbacks must enforce auth.** NRC probes the callback during
|
|
registration and refuses it (`no-auth-on-callback-url`) unless it returns 401
|
|
without the configured `Authorization`; the sink enforces a bearer token.
|
|
- **Cost:** an extra long-running service (`nrc-beat`) per stack, and the verify job
|
|
needs egress (base images + `selectielijst.openzaak.nl`, since the published
|
|
zaaktype the check creates a zaak against depends on it — ADR-0006).
|
|
- **Dev-only credentials** reused (`big-reference-seed` / its secret) across publish,
|
|
AC lookup, and seeding — acceptable for the reference app, not production.
|
|
|
|
## Alternatives considered
|
|
|
|
- **NRC with its own (non-AC) authorization** — rejected: delegating to OpenZaak's
|
|
Autorisaties API is the upstream-intended model and reuses the applicatie that
|
|
already grants `heeft_alle_autorisaties`.
|
|
- **Keep beat out, deliver synchronously** — not an option: Open Notificaties 1.16
|
|
delivers via scheduled notifications drained by beat; there is no sync path.
|
|
- **A persistent abonnement in `setup_configuration`** instead of registering one in
|
|
the verify harness — deferred: the real subscriber is #7; the harness's sink
|
|
abonnement is throwaway and IP-specific.
|