dpp-domain
Core domain types, port traits, and schema validation for the Odal Node Digital Product Passport system.
This is the foundational crate: any other dpp-* crate may depend on it, and
several do. It contains everything that changes when EU regulations change — and
nothing else.
When to use this crate
- You need the DPP data model:
Passport,ProductGroupData,TransferChain. - You need to know what law reaches a product group:
InstrumentCatalogholds one manifest per act, with aPassportObligationand oneInstrumentBindingper (act, product group) pair. Obligations accumulate — ESPR Art. 5(7) lets acts overlap and sets no precedence rule between them — so this answers with a set, and a determination is always made under a named act. - You are implementing a platform adapter (database, HTTP layer) and need the
port trait interfaces:
PassportRepository,IdentityPort,PluginHost, etc. - You want to validate passport data against embedded JSON schemas.
Example
use ;
use ProductGroupCatalog;
use Audience;
use json;
// Product groups are data, not code — one embedded manifest each. The descriptor
// carries identity, scope, schema versions, disclosure and plugin binding, and no
// law at all: status, legal basis, passport obligation, dates, retention and
// granularity are properties of an (act, product group) pair and live on
// `InstrumentBinding` in `catalog::InstrumentCatalog`.
let catalog = new;
let battery = catalog.get.expect;
assert_eq!;
// Disclosure classes come from the schema *version* a passport declares,
// not from one unversioned map. That is what lets a published passport keep
// the classification its signatures were taken under: reclassifying a field
// is a new schema version, and an older passport goes on being filtered by
// the version it was validated against.
let policy = for_schema_version
.expect;
// A passport: envelope fields, with the product group's own data nested under
// `productGroupData`. The nesting is not decoration — a product group's schema
// classifies its own payload and has no authority over the envelope around it.
let full = json!;
let public = filter_by_audience;
// State of health is Annex XIII point 4 — data about one individual battery,
// reserved to holders of a legitimate interest and withheld from the public.
assert_eq!;
assert!;
To filter a bare product-group payload — a document already inside
productGroupData, which is how a two-pass caller handles the two halves
separately — use filter_by_audience_in_scope and pass
DocumentScope::ProductGroupData. Filtering a payload as an envelope applies
none of the product group's classes, so every restricted field in it would be
served.
Relationship to other crates
| Crate | Role |
|---|---|
dpp-crypto |
JWS signing and the keystore — no dependency in either direction |
dpp-vc |
Credentials, did:web and status lists — depends on this crate |
dpp-digital-link |
GS1 Digital Link parsing — depends on this crate |
dpp-aas |
AAS submodel mapping — depends on this crate |
dpp-registry |
EU Central Registry connector — depends on this crate |
dpp-plugin-traits |
Wasm plugin ABI — standalone, no dependency on this crate |
Minimum Rust version
1.96 (MSRV is enforced in CI)
License
Apache-2.0 — see LICENSE