fix(infra): address Objecten by a dotted host so NRC accepts its notifications (refs #152)
CI / build (pull_request) Successful in 1m9s
CI / lint (pull_request) Successful in 1m24s
CI / unit (pull_request) Successful in 1m32s
CI / frontend (pull_request) Successful in 3m13s
CI / mutation (pull_request) Successful in 6m25s
CI / verify-stack (pull_request) Failing after 8m11s

The worker published and NRC answered 400 on every message:

  {"hoofdObject":["Voer een geldige URL in."],"resourceUrl":["Voer een geldige URL in."]}

NRC types both as DRF `URLField`, and Django's URLValidator refuses a single-label host.
Objecten fills them from the object `url` DRF built with `request.build_absolute_uri` —
the Host the *caller* used — so `SITE_DOMAIN` never entered into it. Dropped that env pair;
it was a wrong guess at the mechanism.

The fix is on the caller side: keep the `objecten.local` network alias and point every
writer whose writes must be notified at it — the ACL, the gateway integration tests, and
this slice's verify driver. Readers keep the plain service name.

ADR-0029 updated with the real mechanism and the ceiling it leaves: a new writer using
`objecten:8000` gets a 201 and silently no notification.
This commit is contained in:
not
2026-08-28 11:31:31 +02:00
parent 448896206f
commit d76abf2df2
5 changed files with 50 additions and 34 deletions
@@ -47,21 +47,36 @@ The kanaal name is **not ours to choose**: the Objects API sends
slice reported first. Its filter set (`object_type`) matches the kenmerken the Objects API
sends, so an abonnement can narrow to one objecttype instead of receiving every write.
### `SITE_DOMAIN: objecten.local:8000` — NRC rejects single-label hosts
### Writers address Objecten as `objecten.local` — NRC rejects single-label hosts
NRC validates a notification's `hoofdObject`/`resourceUrl` with Django's `URLValidator`,
which refuses a **single-label** host. Objecten builds those URLs from `objects.utils.get_domain`
(i.e. `SITE_DOMAIN`), so with the compose service name they read `http://objecten:8000/...`
and every publish is refused with `"Voer een geldige URL in."`
NRC types a notification's `hoofdObject` and `resourceUrl` as DRF `URLField`s, so Django's
`URLValidator` runs on them — and it refuses a **single-label** host. Objecten fills both
from the object `url` that DRF built with `request.build_absolute_uri`, i.e. **the Host the
caller used**. Write to `http://objecten:8000` and NRC answers every publish with
`SITE_DOMAIN` is therefore set to a dotted host, and the `objecten` service carries an
`objecten.local` network alias so that host still **resolves in-network** — a subscriber
that follows `resourceUrl` reaches the record it points at (which S-19b-2 will do). A
dotted name that didn't resolve would trade one broken link for a quieter one.
```
{"hoofdObject":["Voer een geldige URL in."],"resourceUrl":["Voer een geldige URL in."]}
```
which `objecten-celery` then retries with exponential backoff, forever, in the background —
the write itself having returned 201.
`SITE_DOMAIN` does **not** fix this; it is not what builds those URLs. The fix is on the
caller side: the `objecten` service carries an `objecten.local` network alias, and every
component whose writes must be notified — the ACL (`Acl__Objecten__BaseUrl`), the gateway
integration tests, this slice's verify check — addresses it there. An alias rather than a
plain dotted `SITE_DOMAIN` so the host still **resolves in-network**: a subscriber that
follows `resourceUrl` reaches the record it points at, which S-19b-2 will do. Readers are
unaffected and keep using the plain service name.
This is the same class of constraint as ADR-0028's "the ACL's Objecttypen base URL must
match Objecten's configured `api_root`": these modules put request-derived hosts into data
that another module then validates or dereferences.
another module then validates or dereferences.
- ponytail ceiling: nothing *enforces* that a new writer uses the alias — it would get a 201
and silently no notification.
- Upgrade path: if a second writer ever appears, rename the compose service to `objecten.local`
so the plain name stops working, rather than adding a lint.
### A worker, not a synchronous send
@@ -93,8 +108,8 @@ config.
- One more long-running container (`objecten-celery`) on an already memory-tight CI runner.
- A second publisher on the shared `big-reference-seed` credential — a credential rotation
now touches two modules.
- One more hand-kept host constant: `SITE_DOMAIN` and the `objecten.local` alias must stay
in step, in both compose files.
- Objecten now has two in-network names, and which one a caller uses silently decides
whether its writes are notified (ceiling above).
- ponytail ceiling: notification delivery has no dead-letter or alerting — a failed publish
is visible only in the worker log.
- Upgrade path: if undelivered register events start mattering, subscribe an audit sink or