Repository navigation
The public passport response omits a field its own schema marks required #378
Description
api/components/schemas/passport/PassportResponse.yaml lists retentionLocked under required. The redacted views strip it, so the response served by the public routes does not validate against the schema those routes publish.
Where it bites
PassportResponse is the response schema for both the authenticated full read and three redacted doors:
GET /vault/public/dpp/{dppId}GET /dpp/{dppId}(resolver)GET /vault/credential/dpp/{dppId}
retentionLocked is classified Professional by ProductGroupAccessPolicy::passport_default, so it is removed for Audience::Public. Checking a real Public view against the required list, it is the only required property missing — the other ten (id, productName, productGroup, manufacturer, materials, status, schemaVersion, createdAt, updatedAt, version) are all present, so this is one field rather than a systematic mismatch.
Why nothing caught it
The OpenAPI contract test compares the status codes a handler can emit against the codes the spec documents. It does not validate a served body against the response schema, and no other gate does either. A consumer that generates a client from the published spec and validates responses gets a failure on every public read; one that does not validate never notices.
Pre-existing. This is not a consequence of #195 / #374 — the redaction has always stripped the field, and the schema has always required it. Found while checking whether #374's change to productGroupData contradicted the spec (it did not: that property is not in required, and its prose is corrected in #374).
The decision
- Drop
retentionLockedfromrequired. Truthful and one line. Weakens the contract for the authenticated read, where the field genuinely is always present. - Split the schema — a full
PassportResponseand aRedactedPassportResponsewhoserequiredis the intersection that survives Public redaction. Honest about there being two shapes, at the cost of a second schema and the drift risk that brings. - Derive the redacted schema's
requiredfrom the disclosure policy, so it cannot drift. Most work; the only option where adding a Professional classification to a currently-public field cannot silently invalidate the spec again.
I lean towards 1 for now and 3 when the spec is next generated rather than hand-maintained — the table in the route docs already carries a warning that hand-maintained API surface here drifts, and this is the same failure in a different file.
Worth pairing with
Whatever is chosen, the gap that let it through is the absence of any response-body-against-schema validation. One contract test that validates a public read's body against PassportResponse would have caught this and would catch the next one.
Activity