docs: ADR-0027 + demo note — public-safe RegisterRecord objecttype schema (refs #141)
CI / lint (pull_request) Successful in 1m50s
CI / build (pull_request) Successful in 1m43s
CI / unit (pull_request) Successful in 2m20s
CI / frontend (pull_request) Successful in 5m4s
CI / mutation (pull_request) Successful in 21m0s
CI / verify-stack (pull_request) Canceled after 31m0s
CI / lint (pull_request) Successful in 1m50s
CI / build (pull_request) Successful in 1m43s
CI / unit (pull_request) Successful in 2m20s
CI / frontend (pull_request) Successful in 5m4s
CI / mutation (pull_request) Successful in 21m0s
CI / verify-stack (pull_request) Canceled after 31m0s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -285,7 +285,7 @@ Split into independently deployable sub-slices (CLAUDE.md §13):
|
|||||||
|
|
||||||
- **S-18a** (#139, ✅) · Objecttypen API up in compose (own DB + seeded config + health + static token).
|
- **S-18a** (#139, ✅) · Objecttypen API up in compose (own DB + seeded config + health + static token).
|
||||||
- **S-18b** (#140, ✅) · Objecten API up in compose, wired to Objecttypen. Depends on S-18a.
|
- **S-18b** (#140, ✅) · Objecten API up in compose, wired to Objecttypen. Depends on S-18a.
|
||||||
- **S-18c** (#141) · RegisterRecord objecttype defined + registered (public-safe JSON schema). Depends on S-18a/b.
|
- **S-18c** (#141, ✅) · RegisterRecord objecttype defined + registered (public-safe JSON schema). Depends on S-18a/b.
|
||||||
|
|
||||||
### S-19 · ACL extension: write register-record to Objecten on approval
|
### S-19 · ACL extension: write register-record to Objecten on approval
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
# ADR-0027: The RegisterRecord objecttype is public-safe by construction
|
||||||
|
|
||||||
|
- **Status:** Accepted
|
||||||
|
- **Date:** 2026-07-27
|
||||||
|
- **Deciders:** Respellion engineering
|
||||||
|
- **Slice:** S-18c (#141), third of the S-18 (#19) split
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
S-18 stands up Objecttypen (S-18a) and Objecten (S-18b) as the authoritative
|
||||||
|
register-record store (PRD §"Objecten as the authoritative register record store").
|
||||||
|
S-19 (#20) will, on approval, write the canonical register record to the Objecten API
|
||||||
|
instead of OpenZaak zaak-eigenschappen, and the openbaar (public) register will read it.
|
||||||
|
|
||||||
|
Objecten validates every object against a **objecttype version's JSON schema**. So the
|
||||||
|
schema is a contract: it fixes which fields a register record may carry. The register is
|
||||||
|
read **anonymously** by the openbaar portal (ADR-0010), so the schema is also a
|
||||||
|
disclosure boundary — anything the schema allows can end up public.
|
||||||
|
|
||||||
|
Two questions: **which fields** the schema defines, and **how** the objecttype gets into
|
||||||
|
the Objecttypen API (which has no declarative objecttype step).
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
**Define a `RegisterRecord` objecttype whose published schema carries exactly the
|
||||||
|
public-safe fields — `id`, `status`, `reference` — and register it over the API at
|
||||||
|
startup with a one-shot, idempotently.**
|
||||||
|
|
||||||
|
### The schema mirrors the BFF's public projection, not the internal one
|
||||||
|
|
||||||
|
The public-safe field set already exists: the BFF's `OpenbaarEntry`
|
||||||
|
(`services/bff/Bff.Api/DownstreamClients.cs`) — `id`, `status`, `reference` — is what
|
||||||
|
`OpenbaarProjection.PublicView` narrows every row down to, dropping `bsn` and
|
||||||
|
`naamPlaceholder` at the boundary (S-09). The RegisterRecord schema mirrors that record,
|
||||||
|
**not** the internal `RegisterEntry` / `RegisterEntryRow` (which carry bsn/naam):
|
||||||
|
|
||||||
|
| field | type | notes |
|
||||||
|
|-------|------|-------|
|
||||||
|
| `id` | string (required) | zaak id — the entry's stable key |
|
||||||
|
| `status` | string (required) | enum `INGEDIEND` \| `INGESCHREVEN` (`RegistrationStatus`) |
|
||||||
|
| `reference` | string \| null | citizen-facing zaak identificatie (ADR-0012) |
|
||||||
|
|
||||||
|
`additionalProperties: false` so a record can't smuggle a field the schema didn't
|
||||||
|
sanction, and `dataClassification: "open"` records the intent that this objecttype is
|
||||||
|
public. **`bsn` and `naamPlaceholder` are deliberately absent** — public-safe by
|
||||||
|
construction, so S-19 cannot write a personal-data field into the public register even by
|
||||||
|
mistake.
|
||||||
|
|
||||||
|
### Registered over the API by a one-shot, not setup_configuration
|
||||||
|
|
||||||
|
The Objecttypen API's `setup_configuration` (3.4.2) provisions only tokens — it has no
|
||||||
|
declarative step to create an objecttype with a schema. So a `registerrecord-init`
|
||||||
|
compose one-shot (stdlib Python, on the stack network) creates the objecttype + a
|
||||||
|
**published** version over the API once Objecttypen is healthy, following the ADR-0020
|
||||||
|
self-seed pattern. It is **idempotent**: if a `RegisterRecord` with a version already
|
||||||
|
exists it is a no-op, so it is safe on every `up`.
|
||||||
|
|
||||||
|
- ponytail ceiling: no schema-migration/versioning story — a schema change means editing
|
||||||
|
`registerrecord.schema.json` and bumping the version by hand; the one-shot only ever
|
||||||
|
adds v1 if none exists.
|
||||||
|
- Upgrade path: if the schema evolves, have the one-shot diff the published schema and
|
||||||
|
POST a new version, or move to a declarative step once the upstream supports one.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
**Positive**
|
||||||
|
|
||||||
|
- The public register's disclosure surface is fixed in one reviewed artifact
|
||||||
|
(`registerrecord.schema.json`) and enforced by Objecten's own validation.
|
||||||
|
- Self-seeds on a fresh `make up` / bare local compose; no manual step, no built image.
|
||||||
|
|
||||||
|
**Negative / costs**
|
||||||
|
|
||||||
|
- The public-safe field set now lives in two places — the BFF's `OpenbaarEntry` and this
|
||||||
|
schema — that must be kept in sync by hand (a drift check is a candidate for later).
|
||||||
|
- Hand-managed schema version (ceiling above).
|
||||||
|
|
||||||
|
## Coupling rules touched (CLAUDE.md §8)
|
||||||
|
|
||||||
|
None new. Registration talks to the Objecttypen API over its documented API. S-19 will
|
||||||
|
write records via the ACL (§8.1) — this ADR only fixes the schema they conform to.
|
||||||
@@ -5,6 +5,35 @@ copy-pasteable walkthrough against a local `make up` stack.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## S-18c — RegisterRecord objecttype defined + registered (#141, ADR-0027)
|
||||||
|
|
||||||
|
**Outcome:** a **RegisterRecord** objecttype with a **published** JSON schema is registered in the
|
||||||
|
Objecttypen API at startup. The schema is public-safe by construction — `id`, `status`, `reference`
|
||||||
|
only, mirroring the BFF's `OpenbaarEntry` (no `bsn`/`naam`), `dataClassification: open`. This is the
|
||||||
|
schema S-19 writes register records against on approval. A `registerrecord-init` one-shot creates it
|
||||||
|
over the API once Objecttypen is healthy (the Objecttypen `setup_configuration` has no objecttype
|
||||||
|
step), idempotently.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make up
|
||||||
|
# The RegisterRecord objecttype exists with a published version:
|
||||||
|
curl -s -H "Authorization: Token 0123456789abcdef0123456789abcdef01234567" \
|
||||||
|
"http://localhost:8020/api/v2/objecttypes" | python3 -c \
|
||||||
|
'import sys,json; o=[x for x in json.load(sys.stdin)["results"] if x["name"]=="RegisterRecord"][0]; print(o["name"], o["dataClassification"], o["versions"])'
|
||||||
|
# → RegisterRecord open ['http://.../objecttypes/<uuid>/versions/1']
|
||||||
|
#
|
||||||
|
# Automated (a CI verify-stack step): asserts the objecttype exists, has a published version, and
|
||||||
|
# that version's schema carries id/status/reference.
|
||||||
|
make verify-registerrecord # → OK — RegisterRecord v1 published, fields=['id', 'reference', 'status']
|
||||||
|
```
|
||||||
|
|
||||||
|
**The path:** `infra/objecttypen-registerrecord/registerrecord.schema.json` (the reviewed public-safe
|
||||||
|
contract) + `register.py` are streamed into an external config volume by `infra/seed-config.sh
|
||||||
|
registerrecord` (bind-mounted locally); the `registerrecord-init` one-shot POSTs the objecttype + a
|
||||||
|
published version. Re-running is a no-op. S-19 (#20) writes records against this schema in Objecten.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## S-18b — Objecten API up in compose, wired to Objecttypen (#140)
|
## S-18b — Objecten API up in compose, wired to Objecttypen (#140)
|
||||||
|
|
||||||
**Outcome:** the upstream Maykin **Objecten API** runs in the stack — own **PostGIS** DB + redis,
|
**Outcome:** the upstream Maykin **Objecten API** runs in the stack — own **PostGIS** DB + redis,
|
||||||
|
|||||||
Reference in New Issue
Block a user