Passports & versions
A Passport (DPP) is a versioned product-information artifact with defined disclosure and lifecycle. It is generated from a product, then published — and once published, a version is never mutated, only superseded.
States — three vocabularies, do not conflate
- Product DPP-journey status (PRD F4 canonical 11-state vocabulary, what generation drives):
draft → validated → ready_for_dpp → dpp_pending → dpp_issued → published, plus the terminal/exception statessuspended,expired,withdrawn,recalled,recycled(andvoidedfor serialized-item tombstones). - Passport command-machine state (what passport commands drive):
draft | submitted | rejected | approved | published | suspended | revoked | archived | recycled. A draft is publicly indistinguishable from nonexistent (404 "not found or not published"). - EU Registry status (per passport, ADR-069):
NOT_REQUIRED | PENDING | REGISTERED | FAILED | SYNCED | REVOKED— the passport's standing against the ESPR Art. 13 EU registry, independent of lifecycle state.
"Superseded" is not a lifecycle state — it is the version-level attribute of v(N) after a republish mints v(N+1).
Immutable versioning
Every publish mints an immutable passport_version: version number, a previous_version_id hash-chain link, and a current flag. Republishing mints v(N+1); v(N) is superseded, never edited. Read the chain at GET /passports/{id}/history.
as-of queries are live (PRD F4): GET /passports/{id}/as-of?date=<ISO 8601> returns the passport projection that was valid at the given instant — a pre-update timestamp returns the old value, a post-update timestamp the new one. A date before the first version returns a typed 404 NOT_FOUND ("No passport version was active at …").Publishing
Three publish-family routes with distinct jobs — do not conflate them (PRD F7):
Publishing returns the public passportUid and, from publish-dpp, the verifiable credential plus a merkleRoot for later verification.
Lifecycle guards
Guards are real: an EDIT on a published passport returns 403 GUARD_REJECTED. Replaying a command with the same idempotencyKey returns the first result. See Passport commands.