Skip to content
Merged
24 changes: 19 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ That's it. Everything compiles and tests with nothing else running.

```
dpp-core/
Cargo.toml # Workspace root — 9 member crates + benches
Cargo.toml # Workspace root — 12 member crates + benches
LICENSE # Apache-2.0
crates/
dpp-domain/ # Domain types, port traits, ProductGroupCatalog, VersionedSchemaRegistry
schemas/ # Versioned JSON schemas, embedded via include_str! (the product):
# aluminium, battery (v1+v2), construction, detergent,
# electronics, furniture, steel, textile (v1+v2),
# unsold-goods, toy, tyre — 11 product groups
# aluminium, battery, construction, detergent, electronics,
# furniture, mattress, steel, textile, toy, tyre,
# unsold-goods — 12 product groups, 30 versions
dpp-rules/ # Pure no_std, zero-dep cross-field regulatory rules
dpp-crypto/ # Ed25519, AES-GCM, JWS, encrypted keystore
dpp-vc/ # W3C VCs, did:web, status lists, LocalIdentityService, JSON-LD
Expand All @@ -73,7 +73,8 @@ dpp-core/
dpp-plugin-traits/ # Wasm plugin ABI (no_std)
dpp-plugin-sdk/ # Guest-side SDK: export_plugin! macro + Validator
dpp-registry/ # EU registry interface types (wasm32-safe)
dpp-tests/ # Cross-crate integration tests (publish = false)
dpp-vocab/ # External vocabulary authorities, one file per authority
dpp-tests/ # Cross-crate integration tests and structural tripwires (publish = false)
benches/ # Criterion benchmarks (workspace member)
plugins/ # 10 product group Wasm plugins (excluded from workspace)
product-group-battery/ product-group-textile/ product-group-steel/ product-group-electronics/
Expand Down Expand Up @@ -106,6 +107,19 @@ dpp-tests -> dpp-domain, dpp-crypto, dpp-digital-link, dpp-aas (dev only

## 4. Coding Conventions

### Where a file goes

[`docs/architecture/CODE-LAYOUT.md`](docs/architecture/CODE-LAYOUT.md) is the
standard: one public type per file, `mod.rs` is a pure index, tests are siblings
rather than inline, every file opens with a `//!` doc, only `lib.rs` sits at a
crate's `src/` root.

Most of those rules are enforced by tripwires in `crates/dpp-tests/tests/`, so
`just check` will tell you before a reviewer does. Each carries a baseline of
files that already violate it; those are being worked through. **Do not add to a
baseline to go green** — fix the file, or mark it with a
`// LAYOUT-DEVIATION: <reason>` comment, which is greppable and has to state why.

### Pure Domain Code

Every module in this workspace must compile without I/O crates. If you are importing `axum`, `sqlx`, or `async-nats`, that code belongs downstream, not here.
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Succeeds with zero infrastructure running. No DB, no Redis, no env vars. If it n
dpp-core/
crates/
dpp-domain .......... Domain types, port traits, VersionedSchemaRegistry, JSON Schema validation
schemas/ .......... Versioned JSON Schemas for 11 product groups (battery, textile, electronics, …), embedded via include_str!
schemas/ .......... Versioned JSON Schemas for 12 product groups (battery, textile, electronics, …), embedded via include_str!
dpp-crypto .......... Ed25519 keys, AES-256-GCM, JWS sign/verify, JAdES
dpp-digital-link .... GS1 Digital Link parser and link-type negotiation
dpp-aas ............. Asset Administration Shell (AAS) shells and submodels
Expand All @@ -51,7 +51,8 @@ dpp-core/
dpp-rules ........... Pure no_std cross-field regulatory rules, shared by dpp-domain and plugins
dpp-registry ........ EU Central Registry interface types (wasm32-safe)
dpp-calc ............ EU-methodology calculators (CO2e, repairability), pure functions
dpp-tests ........... Cross-crate integration tests (domain, crypto, digital-link, aas, vc)
dpp-vocab ........... External vocabulary authorities, one file per authority, with what we verified
dpp-tests ........... Cross-crate integration tests and the structural tripwires
plugins/ .............. 10 Wasm product group plugins (wasm32-wasip1, excluded from workspace)
```

Expand Down
17 changes: 13 additions & 4 deletions crates/dpp-domain/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@
Core domain types, port traits, and schema validation for the
[Odal Node](https://odal-node.io) Digital Product Passport system.

This is the foundational crate. All other `dpp-*` crates depend on it.
It contains everything that changes when EU regulations change — and nothing else.
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**: `InstrumentCatalog`
holds one manifest per act, with a `PassportObligation` and one
`InstrumentBinding` per (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.
Expand All @@ -25,8 +31,11 @@ use dpp_domain::catalog::ProductGroupCatalog;
use dpp_domain::Audience;
use serde_json::json;

// Product group metadata is data, not code: regime, status and retention all come
// from the catalog manifests.
// 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 = ProductGroupCatalog::new();
let battery = catalog.get("battery").expect("battery is in the catalog");
assert_eq!(battery.key, "battery");
Expand Down
2 changes: 1 addition & 1 deletion crates/dpp-domain/src/access/policy.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! ProductGroup access policy types and disclosure-class lookup.
//! ProductGroup access policy types and disclosure-class lookup.

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
Expand Down
2 changes: 1 addition & 1 deletion crates/dpp-domain/src/domain/passport/view.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! [`PassportView`] — an audience-filtered, serialisable view of a passport.
//! [`PassportView`] — an audience-filtered, serialisable view of a passport.

/// An audience-filtered, serialisable view of a
/// [`Passport`](crate::domain::passport::Passport).
Expand Down
7 changes: 5 additions & 2 deletions crates/dpp-domain/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
//! `dpp-domain` — EU Digital Product Passport domain types and port traits.
//!
//! This crate is the dependency root of the DPP workspace. Every other crate
//! depends on this one. It depends only on `dpp-rules` (pure regulatory rules).
//! The dependency root of the DPP workspace: any crate here may depend on it,
//! and it depends only on `dpp-rules` (pure regulatory rules). Not every crate
//! does — `dpp-rules`, `dpp-crypto`, `dpp-calc`, `dpp-vocab`, `dpp-plugin-traits`
//! and `dpp-plugin-sdk` stand on their own, which is why a Wasm product-group
//! plugin never links this crate.
//!
//! No I/O, no async, no HTTP, no database drivers — pure domain logic only.

Expand Down
13 changes: 13 additions & 0 deletions crates/dpp-domain/src/ports/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@
//! withdrawal by the economic operator. A copy of the DPP must be hosted by
//! an independent third-party digital service provider.
//!
//! The obligation is **Art. 10(4)**: the economic operator "shall make available
//! a back-up copy of the digital product passport through a digital product
//! passport service provider", which **Art. 2(32)** defines as "an independent
//! third-party authorised by the economic operator". The period is **Annex
//! III(i)** — "at least the expected lifetime of a specific product" — delegated
//! per product group. **Annex III(l)** makes the provider's reference a passport
//! data element.
//!
//! Two consequences worth stating, because both have been got wrong before.
//! *Independent third party* means an operator's own storage does not discharge
//! this, however durable. And the article is **not Art. 13**, which establishes
//! the registry and is a different duty entirely.
//!
//! This port defines the contract that platform adapters implement to
//! replicate published passport data to an independent archive.

Expand Down
13 changes: 9 additions & 4 deletions crates/dpp-tests/tests/domain_concerns.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
//! Drift tripwire: the concern inventory in `docs/architecture/ARCHITECTURE.md`
//! must exactly match the public modules declared in `dpp-domain`'s `lib.rs`.
//!
//! `dpp-domain` is the largest crate in the workspace — roughly three and a half
//! times the next one — and is the hub every other crate depends on. That makes
//! it the crate most able to absorb a new capability without anyone noticing,
//! and "it has room" is exactly how a hub becomes a bag.
//! `dpp-domain` is by a wide margin the largest crate in the workspace, and the
//! hub of the crates that depend on anything at all. That makes it the crate
//! most able to absorb a new capability without anyone noticing, and "it has
//! room" is exactly how a hub becomes a bag.
//!
//! Deliberately no ratio here. An earlier version of this comment said "roughly
//! three and a half times the next one", which had drifted by the time anyone
//! checked — in a file whose whole subject is that prose counts go stale with
//! nothing watching them.
//!
//! The rule this enforces is that **growing a top-level concern is a deliberate
//! act**: adding one means editing `lib.rs` *and* the inventory, which is the
Expand Down
Loading
Loading