Files
register-referentie/docs/architecture/adr-0007-notification-wiring.md
Niek Otten a256db1a23
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
arch(infra): ADR-0007 + runbooks for the OZ→NRC notification wiring (refs #56)
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>
2026-06-29 14:29:12 +02:00

4.6 KiB

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_INTERVALs) 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 (make verify-notifications, infra/verify-notifications.sh + a notifications CI job): it 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 (ADR-0006).

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.