From a6611ab8e1ffc3a984a8476a1e7031f0f9a175eb Mon Sep 17 00:00:00 2001 From: chillin_since_26 Date: Fri, 30 Oct 2026 22:21:28 +0100 Subject: [PATCH 01/12] chore: release v0.15.0 --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ Cargo.lock | 26 +++++++++++++------------- Cargo.toml | 2 +- api/openapi.bundled.json | 2 +- api/openapi.bundled.yaml | 2 +- api/openapi.yaml | 2 +- 6 files changed, 43 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25af7064..cf323521 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ under the pre-1.0 conventions in [VERSIONING.md](docs/governance/VERSIONING.md): ## [Unreleased] +## [0.15.0] - 2026-10-30 + ### Breaking - **`dpp-core` 0.22.0: a key is named by its own thumbprint, and a token @@ -1095,6 +1097,30 @@ under the pre-1.0 conventions in [VERSIONING.md](docs/governance/VERSIONING.md): status. It proves a citation has a status somebody read, not that the status is current, so re-reading the register is now a step of the release checklist. +- **No tool had read the workflows, and two kinds of finding sat in all of + them.** `workflow-lint.yml` now runs zizmor (security) and actionlint + (correctness) on every pull request, on `main`, weekly and on demand, each + downloaded at a pinned version and checked against a recorded SHA-256. What + they found is fixed: every `actions/checkout` step kept the job's token in the + checkout, and none needs it, so each now sets `persist-credentials: false`; + `release.yml` granted `packages: write` to the whole workflow and now grants it + to its one publishing job; `dtolnay/rust-toolchain` was pinned to a commit on a + branch that has diverged from upstream's `master`, and `Swatinem/rust-cache` to + a commit one past its last release, so the first now pins `master` and the + second the release, and each version comment beside a pin names the exact + release. Dependabot waits seven days before proposing an update. No job's + effective permissions widened. + +### Known limitations + +- **Two transfers started at the same moment on one passport keep only one in + its chain.** `initiate_transfer` reads the transfer chain without a lock and + writes the whole chain back, so two initiations that overlap both pass the + check for a pending transfer, both return a `TransferRecord`, and both write + an audit entry and a `dpp.passport.transferred` event, while the chain holds + only the second. Transfers initiated one at a time are not affected. The fix + is scheduled for 0.16.0 (#535). + ## [0.14.0] - 2026-09-24 ### Breaking diff --git a/Cargo.lock b/Cargo.lock index 89b4ff0e..c2c81a7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2315,7 +2315,7 @@ dependencies = [ [[package]] name = "dpp-cli" -version = "0.14.0" +version = "0.15.0" dependencies = [ "anyhow", "base64 0.23.1", @@ -2343,7 +2343,7 @@ dependencies = [ [[package]] name = "dpp-common" -version = "0.14.0" +version = "0.15.0" dependencies = [ "anyhow", "async-trait", @@ -2399,7 +2399,7 @@ dependencies = [ [[package]] name = "dpp-dal" -version = "0.14.0" +version = "0.15.0" dependencies = [ "anyhow", "async-trait", @@ -2456,7 +2456,7 @@ dependencies = [ [[package]] name = "dpp-factor-data" -version = "0.14.0" +version = "0.15.0" dependencies = [ "chrono", "dpp-calc", @@ -2469,7 +2469,7 @@ dependencies = [ [[package]] name = "dpp-identity" -version = "0.14.0" +version = "0.15.0" dependencies = [ "anyhow", "axum", @@ -2496,7 +2496,7 @@ dependencies = [ [[package]] name = "dpp-integrator" -version = "0.14.0" +version = "0.15.0" dependencies = [ "anyhow", "async-trait", @@ -2532,7 +2532,7 @@ dependencies = [ [[package]] name = "dpp-node" -version = "0.14.0" +version = "0.15.0" dependencies = [ "anyhow", "async-nats", @@ -2591,7 +2591,7 @@ dependencies = [ [[package]] name = "dpp-plugin-host" -version = "0.14.0" +version = "0.15.0" dependencies = [ "anyhow", "base64 0.23.1", @@ -2645,7 +2645,7 @@ dependencies = [ [[package]] name = "dpp-render" -version = "0.14.0" +version = "0.15.0" dependencies = [ "chrono", "criterion", @@ -2658,7 +2658,7 @@ dependencies = [ [[package]] name = "dpp-resolver" -version = "0.14.0" +version = "0.15.0" dependencies = [ "anyhow", "axum", @@ -2711,7 +2711,7 @@ dependencies = [ [[package]] name = "dpp-seal" -version = "0.14.0" +version = "0.15.0" dependencies = [ "async-trait", "axum", @@ -2747,7 +2747,7 @@ dependencies = [ [[package]] name = "dpp-types" -version = "0.14.0" +version = "0.15.0" dependencies = [ "async-trait", "chrono", @@ -2764,7 +2764,7 @@ dependencies = [ [[package]] name = "dpp-vault" -version = "0.14.0" +version = "0.15.0" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index d8f2190a..dbaeeb3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ members = [ ] [workspace.package] -version = "0.14.0" +version = "0.15.0" edition = "2024" authors = ["Odal Node "] license = "BSL-1.1" diff --git a/api/openapi.bundled.json b/api/openapi.bundled.json index db5818dc..a43c2c6c 100644 --- a/api/openapi.bundled.json +++ b/api/openapi.bundled.json @@ -2,7 +2,7 @@ "openapi": "3.1.0", "info": { "title": "Odal Node API", - "version": "0.14.0", + "version": "0.15.0", "description": "The HTTP API of a self-hosted Odal Node: create, validate, sign, publish and\nserve EU Digital Product Passports. Pre-production: the API can still change\nbetween minor versions.\n\nTwo deployables serve it:\n\n- **Node** (`http://localhost:8001`): one process with three route groups,\n `/vault/*` (passport lifecycle), `/identity/*` (`did:web` document) and\n `/integrator/*` (CSV/XLSX import).\n- **Resolver** (`http://localhost:8003`): the public, unauthenticated reader\n that GS1 Digital Link scans land on.\n\nAuthenticated vault routes are under `/vault/api/v1/`. Send an API key as\n`Authorization: Bearer odal_sk_...`; each key has the scope `read`, `write`\nor `admin`. The local admin credential (HTTP Basic) exists to mint the first\nkey. Errors are RFC 9457 problem documents.\n\nRegulations implemented: Regulation (EU) 2024/1781 (ESPR) and Regulation\n(EU) 2023/1542 (batteries). Identity follows W3C DID Core 1.0 and the W3C\nVerifiable Credentials Data Model 2.0.\n", "contact": { "name": "Odal Support", diff --git a/api/openapi.bundled.yaml b/api/openapi.bundled.yaml index c50f8467..dfc252ce 100644 --- a/api/openapi.bundled.yaml +++ b/api/openapi.bundled.yaml @@ -1,7 +1,7 @@ openapi: 3.1.0 info: title: Odal Node API - version: 0.14.0 + version: 0.15.0 description: | The HTTP API of a self-hosted Odal Node: create, validate, sign, publish and serve EU Digital Product Passports. Pre-production: the API can still change diff --git a/api/openapi.yaml b/api/openapi.yaml index f26faee7..bf1e2390 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.1.0 info: title: Odal Node API - version: 0.14.0 + version: 0.15.0 description: | The HTTP API of a self-hosted Odal Node: create, validate, sign, publish and serve EU Digital Product Passports. Pre-production: the API can still change From 1a94fad802834e810fdad5b1feb5fdf04641261c Mon Sep 17 00:00:00 2001 From: chillin_since_26 Date: Mon, 2 Nov 2026 17:50:50 +0100 Subject: [PATCH 02/12] chore: prepare the dpp-core 0.23.0 repin --- CHANGELOG.md | 62 ++++ .../schemas/passport/PublishReadiness.yaml | 8 +- api/openapi.bundled.json | 2 +- api/openapi.bundled.yaml | 8 +- crates/dpp-dal/src/pg/repo_operator_config.rs | 4 + .../passport_docs/battery/v2.10.0.json | 179 ++++++++++ .../passport_docs/electronics/v1.5.0.json | 79 +++++ crates/dpp-dal/tests/pg_integration.rs | 2 + .../src/domain/battery_template.rs | 35 +- .../src/domain/validate/battery.rs | 4 + .../dpp-integrator/src/handlers/templates.rs | 10 +- crates/dpp-node/src/infra/registry/client.rs | 6 + crates/dpp-node/src/infra/registry/mapping.rs | 87 ++++- crates/dpp-node/src/infra/registry/tests.rs | 323 +++++++++++++++--- crates/dpp-node/src/infra/transfer_drain.rs | 161 ++++++--- crates/dpp-node/tests/openapi_contract.rs | 29 +- crates/dpp-node/tests/registry_outbox.rs | 5 +- crates/dpp-node/tests/seal_outbox.rs | 1 + crates/dpp-node/tests/transfer_outbox.rs | 1 + crates/dpp-plugin-host/tests/integration.rs | 2 + .../src/jws_verification_tests.rs | 23 +- crates/dpp-resolver/tests/resolver_e2e.rs | 8 +- crates/dpp-vault/src/domain/verify/engine.rs | 2 + .../src/domain/verify/transfer_chain.rs | 1 + crates/dpp-vault/src/handlers/create.rs | 1 + crates/dpp-vault/tests/battery.rs | 13 +- crates/dpp-vault/tests/continuity_snapshot.rs | 1 + .../dpp-vault/tests/demo_passports_publish.rs | 35 +- crates/dpp-vault/tests/evidence_dossier.rs | 2 + crates/dpp-vault/tests/seal_route.rs | 1 + 30 files changed, 955 insertions(+), 140 deletions(-) create mode 100644 crates/dpp-dal/tests/fixtures/passport_docs/battery/v2.10.0.json create mode 100644 crates/dpp-dal/tests/fixtures/passport_docs/electronics/v1.5.0.json diff --git a/CHANGELOG.md b/CHANGELOG.md index cf323521..6eb8d781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,68 @@ under the pre-1.0 conventions in [VERSIONING.md](docs/governance/VERSIONING.md): ## [Unreleased] +### Breaking + +- **`dpp-core` 0.23.0: a registration refuses an operator identifier with no + EN 18219 clause 6 route.** *(Breaking for an operator whose primary identifier + is a VAT or EORI number.)* Core now accepts an operator or facility identifier + on a registration only under `lei`, `duns` or `gln`: an EU-wide VAT number has + no ICD, none was found for an EORI number, and a DID is scheme 8 only with a + credential a register has issued. The adapter fails closed on that check as on + every other, so each attempt at such a registration is refused before it is + sent, and the row backs off and counts toward the stalled rollup. Facilities + are added under a GLN and are not affected. + + **Migration:** make an LEI or a D-U-N-S number the primary operator identifier + (`POST /vault/api/v1/operator-identifiers/{id}/primary`). A draft stamped before + the change, and a registration already queued, keep the identifier they were + given. + +- **`dpp-core` 0.23.0: an EV, LMT or industrial battery publishes only with its + placing date, and from 18 August 2027 with its due diligence declaration.** + Core's content table answers the due diligence report (Annex XIII point 1(d)) + as of the battery's facts: from 18 August 2027 it is owed unless Art. 47 + exempts the battery. So the publish gate refuses such a battery that states no + `placedOnMarketDate`, on the envelope or in `productGroupData`, unless it is + second-life, and from that date one that does not carry + `dueDiligenceOperatorScope` (battery schema 2.10.0). `PublishReadiness` says the + count of mandatory data points grows by one from that date, and a test holds + the sentence to the gate. + + **Migration:** send `placedOnMarketDate` on create, or in the import column of + that name. The battery CSV templates have no column for `dueDiligenceUrl` or + `dueDiligenceOperatorScope` yet, so a battery placed from 18 August 2027 cannot + be imported into a passport that publishes. + +- **`dpp-core` 0.23.0: an anonymous reader no longer receives a critical raw + material's weight or country of origin** from a battery passport written at + schema 2.9.0 or later, or an electronics passport written at 1.5.0 or later. + Core labels both fields `restricted` from those versions. A passport written + against an earlier version keeps its classes and the bytes its signatures + cover; a new one is written against the new version. + +### Fixed + +- **A transfer notification is sent under a stable key, and only an answer that + says the registry took it closes it.** `dpp-core` 0.23.0's registry port + answers a transfer notification with a `TransferReport`, whether the registry + took it, rather than a record's status. The notification now carries the + transfer's own identifier as its `Idempotency-Key`, which is what makes sending + it again the same notification: a reply that is still processing, or a state + this node has no name for, keeps it open and sends it again, and a conflict + naming the replayed key is read as already held and closes it. That answer says + the registry holds the notification, not that it applied the handover, and no + route to ask has been seen. A refusal is recorded with the registry's reasons. + Before, a reply reporting the record suspended or deactivated closed the row as + notified. The transfer route's reply is unobserved, so only `registered` is read + as an acceptance. + +- **The resolver's JSON-LD defines a term for every product group field.** It + carries `dpp-core` 0.23.0's context, so a JSON-LD processor no longer drops a + product group's data when it expands the document. The context names withheld + fields too, as terms; their values are not served, and the tests that scan the + body for a withheld field now read it without its `@context`. + ## [0.15.0] - 2026-10-30 ### Breaking diff --git a/api/components/schemas/passport/PublishReadiness.yaml b/api/components/schemas/passport/PublishReadiness.yaml index 0ebfb05c..fb7c8ca6 100644 --- a/api/components/schemas/passport/PublishReadiness.yaml +++ b/api/components/schemas/passport/PublishReadiness.yaml @@ -11,9 +11,11 @@ description: | The mandatory-content half is the reason this exists. It is the gate that most often refuses a battery (41 data points for an electric-vehicle one, 40 for - an LMT battery, 33 for an industrial one) and it could not previously be - previewed anywhere: it is reachable only from a stored passport, and the only - dry-run the API offered takes an unsaved request body. + an LMT battery, 33 for an industrial one, and one more for each placed from + 18 August 2027, when the due diligence report falls due unless Art. 47 exempts + the battery) and it could not previously be previewed anywhere: it is + reachable only from a stored passport, and the only dry-run the API offered + takes an unsaved request body. required: - ready - blockers diff --git a/api/openapi.bundled.json b/api/openapi.bundled.json index a43c2c6c..a80fadd6 100644 --- a/api/openapi.bundled.json +++ b/api/openapi.bundled.json @@ -9799,7 +9799,7 @@ }, "PublishReadiness": { "type": "object", - "description": "Whether the passport would clear the publish gates that can be answered\nwithout attempting the transition: the category **mandatory-content** gate and\nthe product-group data/schema gates.\n\nDeliberately not the whole of publish. The registry-identity requirement is\noperator state rather than passport state, and the binding-compliance gate\nneeds a determination this route does not run, so `ready: true` means \"these\ngates pass\", not \"publish will certainly succeed\".\n\nThe mandatory-content half is the reason this exists. It is the gate that most\noften refuses a battery (41 data points for an electric-vehicle one, 40 for\nan LMT battery, 33 for an industrial one) and it could not previously be\npreviewed anywhere: it is reachable only from a stored passport, and the only\ndry-run the API offered takes an unsaved request body.\n", + "description": "Whether the passport would clear the publish gates that can be answered\nwithout attempting the transition: the category **mandatory-content** gate and\nthe product-group data/schema gates.\n\nDeliberately not the whole of publish. The registry-identity requirement is\noperator state rather than passport state, and the binding-compliance gate\nneeds a determination this route does not run, so `ready: true` means \"these\ngates pass\", not \"publish will certainly succeed\".\n\nThe mandatory-content half is the reason this exists. It is the gate that most\noften refuses a battery (41 data points for an electric-vehicle one, 40 for\nan LMT battery, 33 for an industrial one, and one more for each placed from\n18 August 2027, when the due diligence report falls due unless Art. 47 exempts\nthe battery) and it could not previously be previewed anywhere: it is\nreachable only from a stored passport, and the only dry-run the API offered\ntakes an unsaved request body.\n", "required": [ "ready", "blockers", diff --git a/api/openapi.bundled.yaml b/api/openapi.bundled.yaml index dfc252ce..8ea9629e 100644 --- a/api/openapi.bundled.yaml +++ b/api/openapi.bundled.yaml @@ -8430,9 +8430,11 @@ components: The mandatory-content half is the reason this exists. It is the gate that most often refuses a battery (41 data points for an electric-vehicle one, 40 for - an LMT battery, 33 for an industrial one) and it could not previously be - previewed anywhere: it is reachable only from a stored passport, and the only - dry-run the API offered takes an unsaved request body. + an LMT battery, 33 for an industrial one, and one more for each placed from + 18 August 2027, when the due diligence report falls due unless Art. 47 exempts + the battery) and it could not previously be previewed anywhere: it is + reachable only from a stored passport, and the only dry-run the API offered + takes an unsaved request body. required: - ready - blockers diff --git a/crates/dpp-dal/src/pg/repo_operator_config.rs b/crates/dpp-dal/src/pg/repo_operator_config.rs index 66ad5512..626dcc20 100644 --- a/crates/dpp-dal/src/pg/repo_operator_config.rs +++ b/crates/dpp-dal/src/pg/repo_operator_config.rs @@ -88,6 +88,10 @@ impl OperatorConfigRepository for PgOperatorConfigRepo { name: r.get("name"), country: r.get::("country"), address: r.get("address"), + // Not typed here: `actor_identifier()` reads the scheme and value + // above when this is unset, and leaving it unset keeps the snapshot's + // bytes as they were. + identifier: None, })) } diff --git a/crates/dpp-dal/tests/fixtures/passport_docs/battery/v2.10.0.json b/crates/dpp-dal/tests/fixtures/passport_docs/battery/v2.10.0.json new file mode 100644 index 00000000..fb4b6afc --- /dev/null +++ b/crates/dpp-dal/tests/fixtures/passport_docs/battery/v2.10.0.json @@ -0,0 +1,179 @@ +{ + "applicableInstruments": [ + { + "instrument": "battery-reg-2023-1542", + "recorded": "catalog" + } + ], + "batchId": "LOT-2026-08", + "co2ePerUnit": { + "valueKg": 39.1 + }, + "commodityCode": "85076000", + "complianceResult": { + "assessedAt": "2026-11-02T16:34:48.989879074Z", + "co2eScore": 39.1, + "complianceStatus": "PASSTHROUGH_NO_VALIDATION", + "recycledContentPct": null, + "repairabilityIndex": null + }, + "createdAt": "2026-11-02T16:34:48.985607004Z", + "disclosureSignatures": { + "public+restricted+conformity": "test-header.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJMT1QtMjAyNi0wOCIsImNvMmVQZXJVbml0Ijp7InZhbHVlS2ciOjM5LjF9LCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjQ4Ljk4OTg3OTA3NFoiLCJjbzJlU2NvcmUiOjM5LjEsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGx9LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjQ4Ljk4NTYwNzAwNFoiLCJmYWNpbGl0eSI6eyJjb3VudHJ5IjoiREUiLCJuYW1lIjoiU2VlZCBQbGFudCIsInNjaGVtZSI6ImdsbiIsInZhbHVlIjoiNDAxMjM0NTAwMDAwOSJ9LCJpZCI6IjAxYTE5ZDFkLTBjMTktNzI2My1iZDZmLTMzZWMwYmI5OGNlNiIsImxpbnRSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDJUMTY6MzQ6NDguOTg5ODgxMjQ3WiIsImZpbmRpbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5tYXRlcmlhbF9jb21wb3NpdGlvbl9zdW0iLCJmaWVsZCI6ImNhdGhvZGVNYXRlcmlhbCIsIm1lc3NhZ2UiOiJjYXRob2RlTWF0ZXJpYWwgd2VpZ2h0UGN0IGVudHJpZXMgc3VtIHRvIDMyLjAlLCBleHBlY3RlZCB-MTAwJSDigJQgaW50ZW5kZWQ_Iiwic2V2ZXJpdHkiOiJXQVJOSU5HIn0seyJjb2RlIjoiYmF0dGVyeS5tYXRlcmlhbF9jb21wb3NpdGlvbl9zdW0iLCJmaWVsZCI6ImFub2RlTWF0ZXJpYWwiLCJtZXNzYWdlIjoiYW5vZGVNYXRlcmlhbCB3ZWlnaHRQY3QgZW50cmllcyBzdW0gdG8gMTguMCUsIGV4cGVjdGVkIH4xMDAlIOKAlCBpbnRlbmRlZD8iLCJzZXZlcml0eSI6IldBUk5JTkcifSx7ImNvZGUiOiJiYXR0ZXJ5Lm1hdGVyaWFsX2NvbXBvc2l0aW9uX3N1bSIsImZpZWxkIjoiZWxlY3Ryb2x5dGVNYXRlcmlhbCIsIm1lc3NhZ2UiOiJlbGVjdHJvbHl0ZU1hdGVyaWFsIHdlaWdodFBjdCBlbnRyaWVzIHN1bSB0byAxMS4wJSwgZXhwZWN0ZWQgfjEwMCUg4oCUIGludGVuZGVkPyIsInNldmVyaXR5IjoiV0FSTklORyJ9XSwicGFja1ZlcnNpb24iOiIxLjEuMCJ9LCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IkluZHVzdHJpZXN0cmFzc2UgNCwgMTAxMTUgQmVybGluLCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vYmF0dGVyaWVzLmV4YW1wbGUuY29tLy53ZWxsLWtub3duL2RpZC5qc29uIiwibmFtZSI6IkV4YW1wbGUgQmF0dGVyaWVzIEdtYkgifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOiJDTCIsIm5hbWUiOiJMaXRoaXVtIiwicmVjeWNsZWRQY3QiOjEyLjUsIndlaWdodEtnIjowLjh9LHsiY291bnRyeU9mT3JpZ2luIjoiQ0QiLCJuYW1lIjoiQ29iYWx0IiwicmVjeWNsZWRQY3QiOjE2LjAsIndlaWdodEtnIjowLjN9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJhbm9kZU1hdGVyaWFsIjpbeyJuYW1lIjoiR3JhcGhpdGUiLCJ3ZWlnaHRQY3QiOjE4LjB9XSwiYmF0dGVyeUNoZW1pc3RyeSI6Ik5NQyIsImJhdHRlcnlNb2RlbElkIjoiSU5ELTQ4LzEwMC1BIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiQlAtMjAyNi0wMDAxMjMiLCJiYXR0ZXJ5VHlwZSI6ImluZHVzdHJpYWwiLCJiYXR0ZXJ5V2VpZ2h0S2ciOjI0LjUsImNhdGhvZGVNYXRlcmlhbCI6W3sibmFtZSI6IkxpTmlNbkNvTzIiLCJ3ZWlnaHRQY3QiOjMyLjB9XSwiY28yZVBlclVuaXRLZyI6MzkuMSwiY29tcG9uZW50UGFydE51bWJlcnMiOlsiQ0VMTC0yMTcwMC1BIiwiQk1TLTQ4MjAiXSwiY3JpdGljYWxSYXdNYXRlcmlhbHMiOlt7ImNhc051bWJlciI6Ijc0NDAtNDgtNCIsIm5hbWUiOiJDb2JhbHQiLCJ3ZWlnaHRHcmFtcyI6MzAwLjB9LHsiY2FzTnVtYmVyIjoiNzQzOS05My0yIiwibmFtZSI6IkxpdGhpdW0iLCJ3ZWlnaHRHcmFtcyI6ODAwLjB9XSwiZGlzYXNzZW1ibHlJbnN0cnVjdGlvbnNVcmwiOiJodHRwczovL2JhdHRlcmllcy5leGFtcGxlLmNvbS9kaXNhc3NlbWJseS9pbmQtNDgiLCJlbGVjdHJvbHl0ZU1hdGVyaWFsIjpbeyJuYW1lIjoiTGlQRjYgaW4gRUMvRE1DIiwid2VpZ2h0UGN0IjoxMS4wfV0sImV1RGVjbGFyYXRpb25PZkNvbmZvcm1pdHkiOiJodHRwczovL2JhdHRlcmllcy5leGFtcGxlLmNvbS9kb2MvaW5kLTQ4LnBkZiIsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjM1MDAsImV4cGVjdGVkTGlmZXRpbWVSZWZlcmVuY2VUZXN0IjoiSUVDIDYxNDI3LTI6MjAxNSBjeWNsZSBlbmR1cmFuY2UiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiIyMTMyNC00MC0zIiwiY29uY2VudHJhdGlvblBjdCI6MS4yLCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIn1dLCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6MS40LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6MTIuMCwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTA2LTE1VDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkJlcmxpbiwgREUiLCJtYXJraW5nSW5mb3JtYXRpb24iOiJDRSwgc2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wsIGNhcGFjaXR5IGxhYmVsIiwibWF4aW11bVZvbHRhZ2VWIjo1NC42LCJtaW5pbWFsVm9sdGFnZVYiOjQwLjAsIm5vbWluYWxDYXBhY2l0eUFoIjoxMDAuMCwibm9taW5hbFZvbHRhZ2VWIjo0OC4wLCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NjAuMCwibWluQyI6LTIwLjB9LCJub3RJblVzZVRlbXBlcmF0dXJlUmVmZXJlbmNlVGVzdCI6IklFQyA2MjY2MC0xOjIwMTggc3RvcmFnZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NDgwMC4wLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA3LTE1IiwicG93ZXJMaW1pdE1heFciOjUyMDAuMCwicG93ZXJMaW1pdE1pblciOjIwMC4wLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTA2MDAwMTM0MzUyIiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOm51bGwsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6MTYuMCwicmVjeWNsZWRDb250ZW50TGVhZFBjdCI6MC4wLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjoxMi41LCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOjYuMCwicmVuZXdhYmxlQ29udGVudFBjdCI6OC4wLCJzYWZldHlNZWFzdXJlcyI6Iklzb2xhdGUgYmVmb3JlIHNlcnZpY2luZzsgZG8gbm90IHB1bmN0dXJlIiwic3BhcmVQYXJ0c0NvbnRhY3RzIjoicGFydHNAYmF0dGVyaWVzLmV4YW1wbGUuY29tIiwidGVzdFJlcG9ydFJlc3VsdHMiOiJJRUMgNjI2MTk6MjAyMiDigJQgcGFzcyIsInVzYWJsZUV4dGluZ3Vpc2hpbmdBZ2VudCI6IldhdGVyLCBDTzIsIEFCQyBkcnkgcG93ZGVyIiwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJSZXR1cm4gdG8gYW4gYXV0aG9yaXNlZCBjb2xsZWN0aW9uIHBvaW50In0sInByb2R1Y3ROYW1lIjoiR3JpZENlbGwgSU5ELTQ4LzEwMCIsInB1Ymxpc2hlZEF0IjoiMjAyNi0xMS0wMlQxNjozNDo0OS4wMjM2MjE3MjJaIiwicXJDb2RlVXJsIjoiaHR0cHM6Ly9yZXNvbHZlci5leGFtcGxlLmNvbS8wMS8wOTUwNjAwMDEzNDM1Mi8yMS83MjYzYmQ2ZjMzZWMwYmI5OGNlNiIsInJlcGFpcmFiaWxpdHlTY29yZSI6bnVsbCwicmV0ZW50aW9uTG9ja2VkIjp0cnVlLCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDJUMTY6MzQ6NDkuMDIzNjIxNzIyWiIsInNjaGVtYVZlcnNpb24iOiIyLjEwLjAiLCJzdGF0dXMiOiJhY3RpdmUiLCJ1cGRhdGVkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjQ5LjAyMzYyMTcyMloiLCJ2ZXJzaW9uIjoxfQ.test-sig", + "public+restricted+individual": "test-header.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJMT1QtMjAyNi0wOCIsImNvMmVQZXJVbml0Ijp7InZhbHVlS2ciOjM5LjF9LCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjQ4Ljk4OTg3OTA3NFoiLCJjbzJlU2NvcmUiOjM5LjEsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGx9LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjQ4Ljk4NTYwNzAwNFoiLCJmYWNpbGl0eSI6eyJjb3VudHJ5IjoiREUiLCJuYW1lIjoiU2VlZCBQbGFudCIsInNjaGVtZSI6ImdsbiIsInZhbHVlIjoiNDAxMjM0NTAwMDAwOSJ9LCJpZCI6IjAxYTE5ZDFkLTBjMTktNzI2My1iZDZmLTMzZWMwYmI5OGNlNiIsImxpbnRSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDJUMTY6MzQ6NDguOTg5ODgxMjQ3WiIsImZpbmRpbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5tYXRlcmlhbF9jb21wb3NpdGlvbl9zdW0iLCJmaWVsZCI6ImNhdGhvZGVNYXRlcmlhbCIsIm1lc3NhZ2UiOiJjYXRob2RlTWF0ZXJpYWwgd2VpZ2h0UGN0IGVudHJpZXMgc3VtIHRvIDMyLjAlLCBleHBlY3RlZCB-MTAwJSDigJQgaW50ZW5kZWQ_Iiwic2V2ZXJpdHkiOiJXQVJOSU5HIn0seyJjb2RlIjoiYmF0dGVyeS5tYXRlcmlhbF9jb21wb3NpdGlvbl9zdW0iLCJmaWVsZCI6ImFub2RlTWF0ZXJpYWwiLCJtZXNzYWdlIjoiYW5vZGVNYXRlcmlhbCB3ZWlnaHRQY3QgZW50cmllcyBzdW0gdG8gMTguMCUsIGV4cGVjdGVkIH4xMDAlIOKAlCBpbnRlbmRlZD8iLCJzZXZlcml0eSI6IldBUk5JTkcifSx7ImNvZGUiOiJiYXR0ZXJ5Lm1hdGVyaWFsX2NvbXBvc2l0aW9uX3N1bSIsImZpZWxkIjoiZWxlY3Ryb2x5dGVNYXRlcmlhbCIsIm1lc3NhZ2UiOiJlbGVjdHJvbHl0ZU1hdGVyaWFsIHdlaWdodFBjdCBlbnRyaWVzIHN1bSB0byAxMS4wJSwgZXhwZWN0ZWQgfjEwMCUg4oCUIGludGVuZGVkPyIsInNldmVyaXR5IjoiV0FSTklORyJ9XSwicGFja1ZlcnNpb24iOiIxLjEuMCJ9LCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IkluZHVzdHJpZXN0cmFzc2UgNCwgMTAxMTUgQmVybGluLCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vYmF0dGVyaWVzLmV4YW1wbGUuY29tLy53ZWxsLWtub3duL2RpZC5qc29uIiwibmFtZSI6IkV4YW1wbGUgQmF0dGVyaWVzIEdtYkgifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOiJDTCIsIm5hbWUiOiJMaXRoaXVtIiwicmVjeWNsZWRQY3QiOjEyLjUsIndlaWdodEtnIjowLjh9LHsiY291bnRyeU9mT3JpZ2luIjoiQ0QiLCJuYW1lIjoiQ29iYWx0IiwicmVjeWNsZWRQY3QiOjE2LjAsIndlaWdodEtnIjowLjN9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJhbm9kZU1hdGVyaWFsIjpbeyJuYW1lIjoiR3JhcGhpdGUiLCJ3ZWlnaHRQY3QiOjE4LjB9XSwiYmF0dGVyeUNoZW1pc3RyeSI6Ik5NQyIsImJhdHRlcnlNb2RlbElkIjoiSU5ELTQ4LzEwMC1BIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiQlAtMjAyNi0wMDAxMjMiLCJiYXR0ZXJ5U3RhdHVzIjoib3JpZ2luYWwiLCJiYXR0ZXJ5VHlwZSI6ImluZHVzdHJpYWwiLCJiYXR0ZXJ5V2VpZ2h0S2ciOjI0LjUsImNhdGhvZGVNYXRlcmlhbCI6W3sibmFtZSI6IkxpTmlNbkNvTzIiLCJ3ZWlnaHRQY3QiOjMyLjB9XSwiY28yZVBlclVuaXRLZyI6MzkuMSwiY29tcG9uZW50UGFydE51bWJlcnMiOlsiQ0VMTC0yMTcwMC1BIiwiQk1TLTQ4MjAiXSwiY3JpdGljYWxSYXdNYXRlcmlhbHMiOlt7ImNhc051bWJlciI6Ijc0NDAtNDgtNCIsIm5hbWUiOiJDb2JhbHQiLCJ3ZWlnaHRHcmFtcyI6MzAwLjB9LHsiY2FzTnVtYmVyIjoiNzQzOS05My0yIiwibmFtZSI6IkxpdGhpdW0iLCJ3ZWlnaHRHcmFtcyI6ODAwLjB9XSwiZGlzYXNzZW1ibHlJbnN0cnVjdGlvbnNVcmwiOiJodHRwczovL2JhdHRlcmllcy5leGFtcGxlLmNvbS9kaXNhc3NlbWJseS9pbmQtNDgiLCJlbGVjdHJvbHl0ZU1hdGVyaWFsIjpbeyJuYW1lIjoiTGlQRjYgaW4gRUMvRE1DIiwid2VpZ2h0UGN0IjoxMS4wfV0sImV1RGVjbGFyYXRpb25PZkNvbmZvcm1pdHkiOiJodHRwczovL2JhdHRlcmllcy5leGFtcGxlLmNvbS9kb2MvaW5kLTQ4LnBkZiIsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjM1MDAsImV4cGVjdGVkTGlmZXRpbWVSZWZlcmVuY2VUZXN0IjoiSUVDIDYxNDI3LTI6MjAxNSBjeWNsZSBlbmR1cmFuY2UiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiIyMTMyNC00MC0zIiwiY29uY2VudHJhdGlvblBjdCI6MS4yLCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIn1dLCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6MS40LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6MTIuMCwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTA2LTE1VDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkJlcmxpbiwgREUiLCJtYXJraW5nSW5mb3JtYXRpb24iOiJDRSwgc2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wsIGNhcGFjaXR5IGxhYmVsIiwibWF4aW11bVZvbHRhZ2VWIjo1NC42LCJtaW5pbWFsVm9sdGFnZVYiOjQwLjAsIm5vbWluYWxDYXBhY2l0eUFoIjoxMDAuMCwibm9taW5hbFZvbHRhZ2VWIjo0OC4wLCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NjAuMCwibWluQyI6LTIwLjB9LCJub3RJblVzZVRlbXBlcmF0dXJlUmVmZXJlbmNlVGVzdCI6IklFQyA2MjY2MC0xOjIwMTggc3RvcmFnZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NDgwMC4wLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA3LTE1IiwicG93ZXJMaW1pdE1heFciOjUyMDAuMCwicG93ZXJMaW1pdE1pblciOjIwMC4wLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTA2MDAwMTM0MzUyIiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOm51bGwsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6MTYuMCwicmVjeWNsZWRDb250ZW50TGVhZFBjdCI6MC4wLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjoxMi41LCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOjYuMCwicmVuZXdhYmxlQ29udGVudFBjdCI6OC4wLCJzYWZldHlNZWFzdXJlcyI6Iklzb2xhdGUgYmVmb3JlIHNlcnZpY2luZzsgZG8gbm90IHB1bmN0dXJlIiwic3BhcmVQYXJ0c0NvbnRhY3RzIjoicGFydHNAYmF0dGVyaWVzLmV4YW1wbGUuY29tIiwic3RhdGVPZkhlYWx0aFBjdCI6bnVsbCwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIsIENPMiwgQUJDIGRyeSBwb3dkZXIiLCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IlJldHVybiB0byBhbiBhdXRob3Jpc2VkIGNvbGxlY3Rpb24gcG9pbnQifSwicHJvZHVjdE5hbWUiOiJHcmlkQ2VsbCBJTkQtNDgvMTAwIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjQ5LjAyMzYyMTcyMloiLCJxckNvZGVVcmwiOiJodHRwczovL3Jlc29sdmVyLmV4YW1wbGUuY29tLzAxLzA5NTA2MDAwMTM0MzUyLzIxLzcyNjNiZDZmMzNlYzBiYjk4Y2U2IiwicmVwYWlyYWJpbGl0eVNjb3JlIjpudWxsLCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDJUMTY6MzQ6NDkuMDIzNjIxNzIyWiIsInNjaGVtYVZlcnNpb24iOiIyLjEwLjAiLCJzdGF0dXMiOiJhY3RpdmUiLCJ1cGRhdGVkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjQ5LjAyMzYyMTcyMloiLCJ2ZXJzaW9uIjoxfQ.test-sig" + }, + "facility": { + "country": "DE", + "name": "Seed Plant", + "scheme": "gln", + "value": "4012345000009" + }, + "id": "01a19d1d-0c19-7263-bd6f-33ec0bb98ce6", + "jwsSignature": "test-header.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJMT1QtMjAyNi0wOCIsImNvMmVQZXJVbml0Ijp7InZhbHVlS2ciOjM5LjF9LCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjQ4Ljk4OTg3OTA3NFoiLCJjbzJlU2NvcmUiOjM5LjEsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGx9LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjQ4Ljk4NTYwNzAwNFoiLCJmYWNpbGl0eSI6eyJjb3VudHJ5IjoiREUiLCJuYW1lIjoiU2VlZCBQbGFudCIsInNjaGVtZSI6ImdsbiIsInZhbHVlIjoiNDAxMjM0NTAwMDAwOSJ9LCJpZCI6IjAxYTE5ZDFkLTBjMTktNzI2My1iZDZmLTMzZWMwYmI5OGNlNiIsImp3c1NpZ25hdHVyZSI6bnVsbCwibGludFJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wMlQxNjozNDo0OC45ODk4ODEyNDdaIiwiZmluZGluZ3MiOlt7ImNvZGUiOiJiYXR0ZXJ5Lm1hdGVyaWFsX2NvbXBvc2l0aW9uX3N1bSIsImZpZWxkIjoiY2F0aG9kZU1hdGVyaWFsIiwibWVzc2FnZSI6ImNhdGhvZGVNYXRlcmlhbCB3ZWlnaHRQY3QgZW50cmllcyBzdW0gdG8gMzIuMCUsIGV4cGVjdGVkIH4xMDAlIOKAlCBpbnRlbmRlZD8iLCJzZXZlcml0eSI6IldBUk5JTkcifSx7ImNvZGUiOiJiYXR0ZXJ5Lm1hdGVyaWFsX2NvbXBvc2l0aW9uX3N1bSIsImZpZWxkIjoiYW5vZGVNYXRlcmlhbCIsIm1lc3NhZ2UiOiJhbm9kZU1hdGVyaWFsIHdlaWdodFBjdCBlbnRyaWVzIHN1bSB0byAxOC4wJSwgZXhwZWN0ZWQgfjEwMCUg4oCUIGludGVuZGVkPyIsInNldmVyaXR5IjoiV0FSTklORyJ9LHsiY29kZSI6ImJhdHRlcnkubWF0ZXJpYWxfY29tcG9zaXRpb25fc3VtIiwiZmllbGQiOiJlbGVjdHJvbHl0ZU1hdGVyaWFsIiwibWVzc2FnZSI6ImVsZWN0cm9seXRlTWF0ZXJpYWwgd2VpZ2h0UGN0IGVudHJpZXMgc3VtIHRvIDExLjAlLCBleHBlY3RlZCB-MTAwJSDigJQgaW50ZW5kZWQ_Iiwic2V2ZXJpdHkiOiJXQVJOSU5HIn1dLCJwYWNrVmVyc2lvbiI6IjEuMS4wIn0sIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiSW5kdXN0cmllc3RyYXNzZSA0LCAxMDExNSBCZXJsaW4sIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly9iYXR0ZXJpZXMuZXhhbXBsZS5jb20vLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiRXhhbXBsZSBCYXR0ZXJpZXMgR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkNMIiwibmFtZSI6IkxpdGhpdW0iLCJyZWN5Y2xlZFBjdCI6MTIuNSwid2VpZ2h0S2ciOjAuOH0seyJjb3VudHJ5T2ZPcmlnaW4iOiJDRCIsIm5hbWUiOiJDb2JhbHQiLCJyZWN5Y2xlZFBjdCI6MTYuMCwid2VpZ2h0S2ciOjAuM31dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImFub2RlTWF0ZXJpYWwiOlt7Im5hbWUiOiJHcmFwaGl0ZSIsIndlaWdodFBjdCI6MTguMH1dLCJiYXR0ZXJ5Q2hlbWlzdHJ5IjoiTk1DIiwiYmF0dGVyeU1vZGVsSWQiOiJJTkQtNDgvMTAwLUEiLCJiYXR0ZXJ5UGFzc3BvcnROdW1iZXIiOiJCUC0yMDI2LTAwMDEyMyIsImJhdHRlcnlTdGF0dXMiOiJvcmlnaW5hbCIsImJhdHRlcnlUeXBlIjoiaW5kdXN0cmlhbCIsImJhdHRlcnlXZWlnaHRLZyI6MjQuNSwiY2F0aG9kZU1hdGVyaWFsIjpbeyJuYW1lIjoiTGlOaU1uQ29PMiIsIndlaWdodFBjdCI6MzIuMH1dLCJjbzJlUGVyVW5pdEtnIjozOS4xLCJjb21wb25lbnRQYXJ0TnVtYmVycyI6WyJDRUxMLTIxNzAwLUEiLCJCTVMtNDgyMCJdLCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQ0MC00OC00IiwibmFtZSI6IkNvYmFsdCIsIndlaWdodEdyYW1zIjozMDAuMH0seyJjYXNOdW1iZXIiOiI3NDM5LTkzLTIiLCJuYW1lIjoiTGl0aGl1bSIsIndlaWdodEdyYW1zIjo4MDAuMH1dLCJkaXNhc3NlbWJseUluc3RydWN0aW9uc1VybCI6Imh0dHBzOi8vYmF0dGVyaWVzLmV4YW1wbGUuY29tL2Rpc2Fzc2VtYmx5L2luZC00OCIsImVsZWN0cm9seXRlTWF0ZXJpYWwiOlt7Im5hbWUiOiJMaVBGNiBpbiBFQy9ETUMiLCJ3ZWlnaHRQY3QiOjExLjB9XSwiZXVEZWNsYXJhdGlvbk9mQ29uZm9ybWl0eSI6Imh0dHBzOi8vYmF0dGVyaWVzLmV4YW1wbGUuY29tL2RvYy9pbmQtNDgucGRmIiwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6MzUwMCwiZXhwZWN0ZWRMaWZldGltZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjE0MjctMjoyMDE1IGN5Y2xlIGVuZHVyYW5jZSIsImhhemFyZG91c1N1YnN0YW5jZXMiOlt7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJjb25jZW50cmF0aW9uUGN0IjoxLjIsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUifV0sImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjoxLjQsImludGVybmFsUGFja1Jlc2lzdGFuY2VNb2htIjoxMi4wLCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDYtMTVUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiQmVybGluLCBERSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IkNFLCBzZXBhcmF0ZS1jb2xsZWN0aW9uIHN5bWJvbCwgY2FwYWNpdHkgbGFiZWwiLCJtYXhpbXVtVm9sdGFnZVYiOjU0LjYsIm1pbmltYWxWb2x0YWdlViI6NDAuMCwibm9taW5hbENhcGFjaXR5QWgiOjEwMC4wLCJub21pbmFsVm9sdGFnZVYiOjQ4LjAsIm5vdEluVXNlVGVtcGVyYXR1cmVSYW5nZSI6eyJtYXhDIjo2MC4wLCJtaW5DIjotMjAuMH0sIm5vdEluVXNlVGVtcGVyYXR1cmVSZWZlcmVuY2VUZXN0IjoiSUVDIDYyNjYwLTE6MjAxOCBzdG9yYWdlIiwib3JpZ2luYWxQb3dlckNhcGFiaWxpdHlXIjo0ODAwLjAsInBsYWNlZE9uTWFya2V0RGF0ZSI6IjIwMjYtMDctMTUiLCJwb3dlckxpbWl0TWF4VyI6NTIwMC4wLCJwb3dlckxpbWl0TWluVyI6MjAwLjAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1MDYwMDAxMzQzNTIiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6bnVsbCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjoxNi4wLCJyZWN5Y2xlZENvbnRlbnRMZWFkUGN0IjowLjAsInJlY3ljbGVkQ29udGVudExpdGhpdW1QY3QiOjEyLjUsInJlY3ljbGVkQ29udGVudE5pY2tlbFBjdCI6Ni4wLCJyZW5ld2FibGVDb250ZW50UGN0Ijo4LjAsInNhZmV0eU1lYXN1cmVzIjoiSXNvbGF0ZSBiZWZvcmUgc2VydmljaW5nOyBkbyBub3QgcHVuY3R1cmUiLCJzcGFyZVBhcnRzQ29udGFjdHMiOiJwYXJ0c0BiYXR0ZXJpZXMuZXhhbXBsZS5jb20iLCJzdGF0ZU9mSGVhbHRoUGN0IjpudWxsLCJ0ZXN0UmVwb3J0UmVzdWx0cyI6IklFQyA2MjYxOToyMDIyIOKAlCBwYXNzIiwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIsIENPMiwgQUJDIGRyeSBwb3dkZXIiLCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IlJldHVybiB0byBhbiBhdXRob3Jpc2VkIGNvbGxlY3Rpb24gcG9pbnQifSwicHJvZHVjdE5hbWUiOiJHcmlkQ2VsbCBJTkQtNDgvMTAwIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjQ5LjAyMzYyMTcyMloiLCJxckNvZGVVcmwiOiJodHRwczovL3Jlc29sdmVyLmV4YW1wbGUuY29tLzAxLzA5NTA2MDAwMTM0MzUyLzIxLzcyNjNiZDZmMzNlYzBiYjk4Y2U2IiwicmVwYWlyYWJpbGl0eVNjb3JlIjpudWxsLCJyZXRlbnRpb25Mb2NrZWQiOnRydWUsInJldGVudGlvblVudGlsIjoiMjAzNi0xMS0wMlQxNjozNDo0OS4wMjM2MjE3MjJaIiwic2NoZW1hVmVyc2lvbiI6IjIuMTAuMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDJUMTY6MzQ6NDkuMDIzNjIxNzIyWiIsInZlcnNpb24iOjF9.test-sig", + "lintResult": { + "assessedAt": "2026-11-02T16:34:48.989881247Z", + "findings": [ + { + "code": "battery.material_composition_sum", + "field": "cathodeMaterial", + "message": "cathodeMaterial weightPct entries sum to 32.0%, expected ~100% — intended?", + "severity": "WARNING" + }, + { + "code": "battery.material_composition_sum", + "field": "anodeMaterial", + "message": "anodeMaterial weightPct entries sum to 18.0%, expected ~100% — intended?", + "severity": "WARNING" + }, + { + "code": "battery.material_composition_sum", + "field": "electrolyteMaterial", + "message": "electrolyteMaterial weightPct entries sum to 11.0%, expected ~100% — intended?", + "severity": "WARNING" + } + ], + "packVersion": "1.1.0" + }, + "manufacturer": { + "address": "Industriestrasse 4, 10115 Berlin, DE", + "country": null, + "didWebUrl": "https://batteries.example.com/.well-known/did.json", + "name": "Example Batteries GmbH" + }, + "materials": [ + { + "countryOfOrigin": "CL", + "name": "Lithium", + "recycledPct": 12.5, + "weightKg": 0.8 + }, + { + "countryOfOrigin": "CD", + "name": "Cobalt", + "recycledPct": 16.0, + "weightKg": 0.3 + } + ], + "operatorIdentifier": "DE123456789", + "productGroup": "battery", + "productGroupData": { + "anodeMaterial": [ + { + "name": "Graphite", + "weightPct": 18.0 + } + ], + "batteryChemistry": "NMC", + "batteryModelId": "IND-48/100-A", + "batteryPassportNumber": "BP-2026-000123", + "batteryStatus": "original", + "batteryType": "industrial", + "batteryWeightKg": 24.5, + "cathodeMaterial": [ + { + "name": "LiNiMnCoO2", + "weightPct": 32.0 + } + ], + "co2ePerUnitKg": 39.1, + "componentPartNumbers": [ + "CELL-21700-A", + "BMS-4820" + ], + "criticalRawMaterials": [ + { + "casNumber": "7440-48-4", + "name": "Cobalt", + "weightGrams": 300.0 + }, + { + "casNumber": "7439-93-2", + "name": "Lithium", + "weightGrams": 800.0 + } + ], + "disassemblyInstructionsUrl": "https://batteries.example.com/disassembly/ind-48", + "electrolyteMaterial": [ + { + "name": "LiPF6 in EC/DMC", + "weightPct": 11.0 + } + ], + "euDeclarationOfConformity": "https://batteries.example.com/doc/ind-48.pdf", + "expectedLifetimeCycles": 3500, + "expectedLifetimeReferenceTest": "IEC 61427-2:2015 cycle endurance", + "hazardousSubstances": [ + { + "casNumber": "21324-40-3", + "concentrationPct": 1.2, + "name": "Lithium hexafluorophosphate" + } + ], + "internalCellResistanceMohm": 1.4, + "internalPackResistanceMohm": 12.0, + "manufacturingDate": "2026-06-15T00:00:00Z", + "manufacturingPlace": "Berlin, DE", + "markingInformation": "CE, separate-collection symbol, capacity label", + "maximumVoltageV": 54.6, + "minimalVoltageV": 40.0, + "nominalCapacityAh": 100.0, + "nominalVoltageV": 48.0, + "notInUseTemperatureRange": { + "maxC": 60.0, + "minC": -20.0 + }, + "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage", + "originalPowerCapabilityW": 4800.0, + "placedOnMarketDate": "2026-07-15", + "powerLimitMaxW": 5200.0, + "powerLimitMinW": 200.0, + "productGroup": "battery", + "productIdentifier": { + "gtin": "09506000134352", + "scheme": "gs1" + }, + "ratedCapacityKwh": null, + "recycledContentCobaltPct": 16.0, + "recycledContentLeadPct": 0.0, + "recycledContentLithiumPct": 12.5, + "recycledContentNickelPct": 6.0, + "renewableContentPct": 8.0, + "safetyMeasures": "Isolate before servicing; do not puncture", + "sparePartsContacts": "parts@batteries.example.com", + "stateOfHealthPct": null, + "testReportResults": "IEC 62619:2022 — pass", + "usableExtinguishingAgent": "Water, CO2, ABC dry powder", + "wasteBatteryInformation": "Return to an authorised collection point" + }, + "productName": "GridCell IND-48/100", + "publicJwsSignature": "test-header.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJMT1QtMjAyNi0wOCIsImNvMmVQZXJVbml0Ijp7InZhbHVlS2ciOjM5LjF9LCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjQ4Ljk4OTg3OTA3NFoiLCJjbzJlU2NvcmUiOjM5LjEsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGx9LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjQ4Ljk4NTYwNzAwNFoiLCJmYWNpbGl0eSI6eyJjb3VudHJ5IjoiREUiLCJuYW1lIjoiU2VlZCBQbGFudCIsInNjaGVtZSI6ImdsbiIsInZhbHVlIjoiNDAxMjM0NTAwMDAwOSJ9LCJpZCI6IjAxYTE5ZDFkLTBjMTktNzI2My1iZDZmLTMzZWMwYmI5OGNlNiIsIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiSW5kdXN0cmllc3RyYXNzZSA0LCAxMDExNSBCZXJsaW4sIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly9iYXR0ZXJpZXMuZXhhbXBsZS5jb20vLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiRXhhbXBsZSBCYXR0ZXJpZXMgR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkNMIiwibmFtZSI6IkxpdGhpdW0iLCJyZWN5Y2xlZFBjdCI6MTIuNSwid2VpZ2h0S2ciOjAuOH0seyJjb3VudHJ5T2ZPcmlnaW4iOiJDRCIsIm5hbWUiOiJDb2JhbHQiLCJyZWN5Y2xlZFBjdCI6MTYuMCwid2VpZ2h0S2ciOjAuM31dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImJhdHRlcnlDaGVtaXN0cnkiOiJOTUMiLCJiYXR0ZXJ5TW9kZWxJZCI6IklORC00OC8xMDAtQSIsImJhdHRlcnlQYXNzcG9ydE51bWJlciI6IkJQLTIwMjYtMDAwMTIzIiwiYmF0dGVyeVR5cGUiOiJpbmR1c3RyaWFsIiwiYmF0dGVyeVdlaWdodEtnIjoyNC41LCJjbzJlUGVyVW5pdEtnIjozOS4xLCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQ0MC00OC00IiwibmFtZSI6IkNvYmFsdCJ9LHsiY2FzTnVtYmVyIjoiNzQzOS05My0yIiwibmFtZSI6IkxpdGhpdW0ifV0sImV1RGVjbGFyYXRpb25PZkNvbmZvcm1pdHkiOiJodHRwczovL2JhdHRlcmllcy5leGFtcGxlLmNvbS9kb2MvaW5kLTQ4LnBkZiIsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjM1MDAsImV4cGVjdGVkTGlmZXRpbWVSZWZlcmVuY2VUZXN0IjoiSUVDIDYxNDI3LTI6MjAxNSBjeWNsZSBlbmR1cmFuY2UiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiIyMTMyNC00MC0zIiwiY29uY2VudHJhdGlvblBjdCI6MS4yLCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIn1dLCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6MS40LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6MTIuMCwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTA2LTE1VDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkJlcmxpbiwgREUiLCJtYXJraW5nSW5mb3JtYXRpb24iOiJDRSwgc2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wsIGNhcGFjaXR5IGxhYmVsIiwibWF4aW11bVZvbHRhZ2VWIjo1NC42LCJtaW5pbWFsVm9sdGFnZVYiOjQwLjAsIm5vbWluYWxDYXBhY2l0eUFoIjoxMDAuMCwibm9taW5hbFZvbHRhZ2VWIjo0OC4wLCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NjAuMCwibWluQyI6LTIwLjB9LCJub3RJblVzZVRlbXBlcmF0dXJlUmVmZXJlbmNlVGVzdCI6IklFQyA2MjY2MC0xOjIwMTggc3RvcmFnZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NDgwMC4wLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA3LTE1IiwicG93ZXJMaW1pdE1heFciOjUyMDAuMCwicG93ZXJMaW1pdE1pblciOjIwMC4wLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTA2MDAwMTM0MzUyIiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOm51bGwsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6MTYuMCwicmVjeWNsZWRDb250ZW50TGVhZFBjdCI6MC4wLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjoxMi41LCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOjYuMCwicmVuZXdhYmxlQ29udGVudFBjdCI6OC4wLCJ1c2FibGVFeHRpbmd1aXNoaW5nQWdlbnQiOiJXYXRlciwgQ08yLCBBQkMgZHJ5IHBvd2RlciIsIndhc3RlQmF0dGVyeUluZm9ybWF0aW9uIjoiUmV0dXJuIHRvIGFuIGF1dGhvcmlzZWQgY29sbGVjdGlvbiBwb2ludCJ9LCJwcm9kdWN0TmFtZSI6IkdyaWRDZWxsIElORC00OC8xMDAiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDJUMTY6MzQ6NDkuMDIzNjIxNzIyWiIsInFyQ29kZVVybCI6Imh0dHBzOi8vcmVzb2x2ZXIuZXhhbXBsZS5jb20vMDEvMDk1MDYwMDAxMzQzNTIvMjEvNzI2M2JkNmYzM2VjMGJiOThjZTYiLCJyZXBhaXJhYmlsaXR5U2NvcmUiOm51bGwsInJldGVudGlvblVudGlsIjoiMjAzNi0xMS0wMlQxNjozNDo0OS4wMjM2MjE3MjJaIiwic2NoZW1hVmVyc2lvbiI6IjIuMTAuMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDJUMTY6MzQ6NDkuMDIzNjIxNzIyWiIsInZlcnNpb24iOjF9.test-sig", + "publishedAt": "2026-11-02T16:34:49.023621722Z", + "qrCodeUrl": "https://resolver.example.com/01/09506000134352/21/7263bd6f33ec0bb98ce6", + "repairabilityScore": null, + "retentionLocked": true, + "retentionUntil": "2036-11-02T16:34:49.023621722Z", + "schemaVersion": "2.10.0", + "status": "active", + "updatedAt": "2026-11-02T16:34:49.023621722Z", + "version": 1 +} diff --git a/crates/dpp-dal/tests/fixtures/passport_docs/electronics/v1.5.0.json b/crates/dpp-dal/tests/fixtures/passport_docs/electronics/v1.5.0.json new file mode 100644 index 00000000..026744f3 --- /dev/null +++ b/crates/dpp-dal/tests/fixtures/passport_docs/electronics/v1.5.0.json @@ -0,0 +1,79 @@ +{ + "applicableInstruments": [ + { + "instrument": "ecodesign-energy-labelling-mobile", + "recorded": "catalog" + } + ], + "batchId": null, + "co2ePerUnit": null, + "complianceResult": { + "assessedAt": "2026-11-02T16:34:54.475056927Z", + "co2eScore": null, + "complianceStatus": "PASSTHROUGH_NO_VALIDATION", + "recycledContentPct": null, + "repairabilityIndex": null + }, + "createdAt": "2026-11-02T16:34:54.471910631Z", + "disclosureSignatures": { + "public+restricted+conformity": "test-header.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJlY29kZXNpZ24tZW5lcmd5LWxhYmVsbGluZy1tb2JpbGUiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOm51bGwsImNvMmVQZXJVbml0IjpudWxsLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjU0LjQ3NTA1NjkyN1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGx9LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjU0LjQ3MTkxMDYzMVoiLCJmYWNpbGl0eSI6eyJjb3VudHJ5IjoiREUiLCJuYW1lIjoiU2VlZCBQbGFudCIsInNjaGVtZSI6ImdsbiIsInZhbHVlIjoiNDAxMjM0NTAwMDAwOSJ9LCJpZCI6IjAxYTE5ZDFkLTIxODctNzMwNy04ZDg5LTkzYjI2MWJhMDc2MSIsImxpbnRSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDJUMTY6MzQ6NTQuNDc1MDU5Mjk2WiIsInBhY2tWZXJzaW9uIjoiMS4xLjAifSwibWFudWZhY3R1cmVyIjp7ImFkZHJlc3MiOiJLZWl6ZXJzZ3JhY2h0IDEwMCwgMTAxNSBBbXN0ZXJkYW0sIE5MIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjpudWxsLCJuYW1lIjoiQXVyb3JhIERldmljZXMgQlYifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOm51bGwsIm5hbWUiOiJBbHVtaW5pdW0iLCJyZWN5Y2xlZFBjdCI6ODAuMCwid2VpZ2h0S2ciOjAuMDZ9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwcm9kdWN0R3JvdXAiOiJlbGVjdHJvbmljcyIsInByb2R1Y3RHcm91cERhdGEiOnsiY28yZVBlclVuaXRLZyI6NTIuNCwiZGlzYXNzZW1ibHlJbnN0cnVjdGlvbnNVcmwiOiJodHRwczovL2F1cm9yYS5leGFtcGxlLmNvbS9kaXNhc3NlbWJseS9zMSIsImVuZXJneUVmZmljaWVuY3lDbGFzcyI6IkIiLCJleHBlY3RlZExpZmV0aW1lWWVhcnMiOjcsImZpcm13YXJlVXBkYXRlVW50aWwiOiIyMDMzLTA5LTAxVDAwOjAwOjAwWiIsInByb2R1Y3RDYXRlZ29yeSI6InNtYXJ0cGhvbmUiLCJwcm9kdWN0R3JvdXAiOiJlbGVjdHJvbmljcyIsInByb2R1Y3RJZGVudGlmaWVyIjp7Imd0aW4iOiIwOTUwNjAwMDEzNDM3NiIsInNjaGVtZSI6ImdzMSJ9LCJyZWN5Y2xlZENvbnRlbnRQY3QiOjIyLjAsInJlcGFpck1hbnVhbFVybCI6Imh0dHBzOi8vYXVyb3JhLmV4YW1wbGUuY29tL3JlcGFpci9zMSIsInJvaHNDb21wbGlhbnQiOnRydWUsInNwYXJlUGFydHNBdmFpbGFibGUiOnRydWUsInN0YW5kYnlQb3dlclciOjAuMTV9LCJwcm9kdWN0TmFtZSI6IkF1cm9yYSBTMSBTbWFydHBob25lIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjU0LjUwMTkwOTgzNVoiLCJxckNvZGVVcmwiOiJodHRwczovL3Jlc29sdmVyLmV4YW1wbGUuY29tLzAxLzA5NTA2MDAwMTM0Mzc2LzIxLzczMDc4ZDg5OTNiMjYxYmEwNzYxIiwicmVwYWlyYWJpbGl0eVNjb3JlIjpudWxsLCJyZXRlbnRpb25Mb2NrZWQiOnRydWUsInJldGVudGlvblVudGlsIjoiMjAzNi0xMS0wMlQxNjozNDo1NC41MDE5MDk4MzVaIiwic2NoZW1hVmVyc2lvbiI6IjEuNS4wIiwic3RhdHVzIjoiYWN0aXZlIiwidXBkYXRlZEF0IjoiMjAyNi0xMS0wMlQxNjozNDo1NC41MDE5MDk4MzVaIiwidmVyc2lvbiI6MX0.test-sig", + "public+restricted+individual": "test-header.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJlY29kZXNpZ24tZW5lcmd5LWxhYmVsbGluZy1tb2JpbGUiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOm51bGwsImNvMmVQZXJVbml0IjpudWxsLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjU0LjQ3NTA1NjkyN1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGx9LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjU0LjQ3MTkxMDYzMVoiLCJmYWNpbGl0eSI6eyJjb3VudHJ5IjoiREUiLCJuYW1lIjoiU2VlZCBQbGFudCIsInNjaGVtZSI6ImdsbiIsInZhbHVlIjoiNDAxMjM0NTAwMDAwOSJ9LCJpZCI6IjAxYTE5ZDFkLTIxODctNzMwNy04ZDg5LTkzYjI2MWJhMDc2MSIsImxpbnRSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDJUMTY6MzQ6NTQuNDc1MDU5Mjk2WiIsInBhY2tWZXJzaW9uIjoiMS4xLjAifSwibWFudWZhY3R1cmVyIjp7ImFkZHJlc3MiOiJLZWl6ZXJzZ3JhY2h0IDEwMCwgMTAxNSBBbXN0ZXJkYW0sIE5MIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjpudWxsLCJuYW1lIjoiQXVyb3JhIERldmljZXMgQlYifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOm51bGwsIm5hbWUiOiJBbHVtaW5pdW0iLCJyZWN5Y2xlZFBjdCI6ODAuMCwid2VpZ2h0S2ciOjAuMDZ9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwcm9kdWN0R3JvdXAiOiJlbGVjdHJvbmljcyIsInByb2R1Y3RHcm91cERhdGEiOnsiY28yZVBlclVuaXRLZyI6NTIuNCwiZGlzYXNzZW1ibHlJbnN0cnVjdGlvbnNVcmwiOiJodHRwczovL2F1cm9yYS5leGFtcGxlLmNvbS9kaXNhc3NlbWJseS9zMSIsImVuZXJneUVmZmljaWVuY3lDbGFzcyI6IkIiLCJleHBlY3RlZExpZmV0aW1lWWVhcnMiOjcsImZpcm13YXJlVXBkYXRlVW50aWwiOiIyMDMzLTA5LTAxVDAwOjAwOjAwWiIsInByb2R1Y3RDYXRlZ29yeSI6InNtYXJ0cGhvbmUiLCJwcm9kdWN0R3JvdXAiOiJlbGVjdHJvbmljcyIsInByb2R1Y3RJZGVudGlmaWVyIjp7Imd0aW4iOiIwOTUwNjAwMDEzNDM3NiIsInNjaGVtZSI6ImdzMSJ9LCJyZWN5Y2xlZENvbnRlbnRQY3QiOjIyLjAsInJlcGFpck1hbnVhbFVybCI6Imh0dHBzOi8vYXVyb3JhLmV4YW1wbGUuY29tL3JlcGFpci9zMSIsInJvaHNDb21wbGlhbnQiOnRydWUsInNwYXJlUGFydHNBdmFpbGFibGUiOnRydWUsInN0YW5kYnlQb3dlclciOjAuMTV9LCJwcm9kdWN0TmFtZSI6IkF1cm9yYSBTMSBTbWFydHBob25lIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjU0LjUwMTkwOTgzNVoiLCJxckNvZGVVcmwiOiJodHRwczovL3Jlc29sdmVyLmV4YW1wbGUuY29tLzAxLzA5NTA2MDAwMTM0Mzc2LzIxLzczMDc4ZDg5OTNiMjYxYmEwNzYxIiwicmVwYWlyYWJpbGl0eVNjb3JlIjpudWxsLCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDJUMTY6MzQ6NTQuNTAxOTA5ODM1WiIsInNjaGVtYVZlcnNpb24iOiIxLjUuMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDJUMTY6MzQ6NTQuNTAxOTA5ODM1WiIsInZlcnNpb24iOjF9.test-sig" + }, + "facility": { + "country": "DE", + "name": "Seed Plant", + "scheme": "gln", + "value": "4012345000009" + }, + "id": "01a19d1d-2187-7307-8d89-93b261ba0761", + "jwsSignature": "test-header.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJlY29kZXNpZ24tZW5lcmd5LWxhYmVsbGluZy1tb2JpbGUiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOm51bGwsImNvMmVQZXJVbml0IjpudWxsLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjU0LjQ3NTA1NjkyN1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGx9LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjU0LjQ3MTkxMDYzMVoiLCJmYWNpbGl0eSI6eyJjb3VudHJ5IjoiREUiLCJuYW1lIjoiU2VlZCBQbGFudCIsInNjaGVtZSI6ImdsbiIsInZhbHVlIjoiNDAxMjM0NTAwMDAwOSJ9LCJpZCI6IjAxYTE5ZDFkLTIxODctNzMwNy04ZDg5LTkzYjI2MWJhMDc2MSIsImp3c1NpZ25hdHVyZSI6bnVsbCwibGludFJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wMlQxNjozNDo1NC40NzUwNTkyOTZaIiwicGFja1ZlcnNpb24iOiIxLjEuMCJ9LCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IktlaXplcnNncmFjaHQgMTAwLCAxMDE1IEFtc3RlcmRhbSwgTkwiLCJjb3VudHJ5IjpudWxsLCJkaWRXZWJVcmwiOm51bGwsIm5hbWUiOiJBdXJvcmEgRGV2aWNlcyBCViJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6bnVsbCwibmFtZSI6IkFsdW1pbml1bSIsInJlY3ljbGVkUGN0Ijo4MC4wLCJ3ZWlnaHRLZyI6MC4wNn1dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInByb2R1Y3RHcm91cCI6ImVsZWN0cm9uaWNzIiwicHJvZHVjdEdyb3VwRGF0YSI6eyJjbzJlUGVyVW5pdEtnIjo1Mi40LCJkaXNhc3NlbWJseUluc3RydWN0aW9uc1VybCI6Imh0dHBzOi8vYXVyb3JhLmV4YW1wbGUuY29tL2Rpc2Fzc2VtYmx5L3MxIiwiZW5lcmd5RWZmaWNpZW5jeUNsYXNzIjoiQiIsImV4cGVjdGVkTGlmZXRpbWVZZWFycyI6NywiZmlybXdhcmVVcGRhdGVVbnRpbCI6IjIwMzMtMDktMDFUMDA6MDA6MDBaIiwicHJvZHVjdENhdGVnb3J5Ijoic21hcnRwaG9uZSIsInByb2R1Y3RHcm91cCI6ImVsZWN0cm9uaWNzIiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTA2MDAwMTM0Mzc2Iiwic2NoZW1lIjoiZ3MxIn0sInJlY3ljbGVkQ29udGVudFBjdCI6MjIuMCwicmVwYWlyTWFudWFsVXJsIjoiaHR0cHM6Ly9hdXJvcmEuZXhhbXBsZS5jb20vcmVwYWlyL3MxIiwicm9oc0NvbXBsaWFudCI6dHJ1ZSwic3BhcmVQYXJ0c0F2YWlsYWJsZSI6dHJ1ZSwic3RhbmRieVBvd2VyVyI6MC4xNX0sInByb2R1Y3ROYW1lIjoiQXVyb3JhIFMxIFNtYXJ0cGhvbmUiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDJUMTY6MzQ6NTQuNTAxOTA5ODM1WiIsInFyQ29kZVVybCI6Imh0dHBzOi8vcmVzb2x2ZXIuZXhhbXBsZS5jb20vMDEvMDk1MDYwMDAxMzQzNzYvMjEvNzMwNzhkODk5M2IyNjFiYTA3NjEiLCJyZXBhaXJhYmlsaXR5U2NvcmUiOm51bGwsInJldGVudGlvbkxvY2tlZCI6dHJ1ZSwicmV0ZW50aW9uVW50aWwiOiIyMDM2LTExLTAyVDE2OjM0OjU0LjUwMTkwOTgzNVoiLCJzY2hlbWFWZXJzaW9uIjoiMS41LjAiLCJzdGF0dXMiOiJhY3RpdmUiLCJ1cGRhdGVkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjU0LjUwMTkwOTgzNVoiLCJ2ZXJzaW9uIjoxfQ.test-sig", + "lintResult": { + "assessedAt": "2026-11-02T16:34:54.475059296Z", + "packVersion": "1.1.0" + }, + "manufacturer": { + "address": "Keizersgracht 100, 1015 Amsterdam, NL", + "country": null, + "didWebUrl": null, + "name": "Aurora Devices BV" + }, + "materials": [ + { + "countryOfOrigin": null, + "name": "Aluminium", + "recycledPct": 80.0, + "weightKg": 0.06 + } + ], + "operatorIdentifier": "DE123456789", + "productGroup": "electronics", + "productGroupData": { + "co2ePerUnitKg": 52.4, + "disassemblyInstructionsUrl": "https://aurora.example.com/disassembly/s1", + "energyEfficiencyClass": "B", + "expectedLifetimeYears": 7, + "firmwareUpdateUntil": "2033-09-01T00:00:00Z", + "productCategory": "smartphone", + "productGroup": "electronics", + "productIdentifier": { + "gtin": "09506000134376", + "scheme": "gs1" + }, + "recycledContentPct": 22.0, + "repairManualUrl": "https://aurora.example.com/repair/s1", + "rohsCompliant": true, + "sparePartsAvailable": true, + "standbyPowerW": 0.15 + }, + "productName": "Aurora S1 Smartphone", + "publicJwsSignature": "test-header.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJlY29kZXNpZ24tZW5lcmd5LWxhYmVsbGluZy1tb2JpbGUiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImNvMmVQZXJVbml0IjpudWxsLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjU0LjQ3NTA1NjkyN1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGx9LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjU0LjQ3MTkxMDYzMVoiLCJmYWNpbGl0eSI6eyJjb3VudHJ5IjoiREUiLCJuYW1lIjoiU2VlZCBQbGFudCIsInNjaGVtZSI6ImdsbiIsInZhbHVlIjoiNDAxMjM0NTAwMDAwOSJ9LCJpZCI6IjAxYTE5ZDFkLTIxODctNzMwNy04ZDg5LTkzYjI2MWJhMDc2MSIsIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiS2VpemVyc2dyYWNodCAxMDAsIDEwMTUgQW1zdGVyZGFtLCBOTCIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6bnVsbCwibmFtZSI6IkF1cm9yYSBEZXZpY2VzIEJWIn0sIm1hdGVyaWFscyI6W3siY291bnRyeU9mT3JpZ2luIjpudWxsLCJuYW1lIjoiQWx1bWluaXVtIiwicmVjeWNsZWRQY3QiOjgwLjAsIndlaWdodEtnIjowLjA2fV0sIm9wZXJhdG9ySWRlbnRpZmllciI6IkRFMTIzNDU2Nzg5IiwicHJvZHVjdEdyb3VwIjoiZWxlY3Ryb25pY3MiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImNvMmVQZXJVbml0S2ciOjUyLjQsImVuZXJneUVmZmljaWVuY3lDbGFzcyI6IkIiLCJleHBlY3RlZExpZmV0aW1lWWVhcnMiOjcsImZpcm13YXJlVXBkYXRlVW50aWwiOiIyMDMzLTA5LTAxVDAwOjAwOjAwWiIsInByb2R1Y3RDYXRlZ29yeSI6InNtYXJ0cGhvbmUiLCJwcm9kdWN0R3JvdXAiOiJlbGVjdHJvbmljcyIsInByb2R1Y3RJZGVudGlmaWVyIjp7Imd0aW4iOiIwOTUwNjAwMDEzNDM3NiIsInNjaGVtZSI6ImdzMSJ9LCJyZWN5Y2xlZENvbnRlbnRQY3QiOjIyLjAsInJvaHNDb21wbGlhbnQiOnRydWUsInNwYXJlUGFydHNBdmFpbGFibGUiOnRydWUsInN0YW5kYnlQb3dlclciOjAuMTV9LCJwcm9kdWN0TmFtZSI6IkF1cm9yYSBTMSBTbWFydHBob25lIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAyVDE2OjM0OjU0LjUwMTkwOTgzNVoiLCJxckNvZGVVcmwiOiJodHRwczovL3Jlc29sdmVyLmV4YW1wbGUuY29tLzAxLzA5NTA2MDAwMTM0Mzc2LzIxLzczMDc4ZDg5OTNiMjYxYmEwNzYxIiwicmVwYWlyYWJpbGl0eVNjb3JlIjpudWxsLCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDJUMTY6MzQ6NTQuNTAxOTA5ODM1WiIsInNjaGVtYVZlcnNpb24iOiIxLjUuMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDJUMTY6MzQ6NTQuNTAxOTA5ODM1WiIsInZlcnNpb24iOjF9.test-sig", + "publishedAt": "2026-11-02T16:34:54.501909835Z", + "qrCodeUrl": "https://resolver.example.com/01/09506000134376/21/73078d8993b261ba0761", + "repairabilityScore": null, + "retentionLocked": true, + "retentionUntil": "2036-11-02T16:34:54.501909835Z", + "schemaVersion": "1.5.0", + "status": "active", + "updatedAt": "2026-11-02T16:34:54.501909835Z", + "version": 1 +} diff --git a/crates/dpp-dal/tests/pg_integration.rs b/crates/dpp-dal/tests/pg_integration.rs index fc0a1cb4..a0480b67 100644 --- a/crates/dpp-dal/tests/pg_integration.rs +++ b/crates/dpp-dal/tests/pg_integration.rs @@ -180,6 +180,7 @@ fn battery_passport_with(gtin: &str, batch: Option<&str>, status: PassportStatus usage_history: None, voltage_temperature_range: None, waste_battery_information: None, + due_diligence_operator_scope: None, }))); p } @@ -193,6 +194,7 @@ fn facility_with_value(value: &str) -> FacilitySnapshot { name: "Test Facility".into(), country: "DE".into(), address: None, + identifier: None, } } diff --git a/crates/dpp-integrator/src/domain/battery_template.rs b/crates/dpp-integrator/src/domain/battery_template.rs index cd671948..020194d3 100644 --- a/crates/dpp-integrator/src/domain/battery_template.rs +++ b/crates/dpp-integrator/src/domain/battery_template.rs @@ -607,6 +607,26 @@ fn csv_cell(value: &str) -> String { } } +/// The facts of an original battery placed on the market the day Art. 77(1) +/// begins, 18 February 2027. +/// +/// The template gates ask the content table with these rather than with +/// `BatteryFacts::default()`, under which the due diligence row answers +/// `Undetermined` instead of the `NotApplicable` it is for such a battery. +/// +/// 🚨 They are the obligations of a battery placed before 18 August 2027. From +/// that date the due diligence report (`dueDiligenceUrl`) is owed unless Art. 47 +/// exempts the battery, and no template has a column for it or for the +/// operator's declaration (`dueDiligenceOperatorScope`), so a battery placed +/// from then cannot be imported into a passport that publishes. +#[cfg(test)] +pub(crate) fn facts_before_due_diligence() -> dpp_rules::batteries::battery_facts::BatteryFacts { + dpp_rules::batteries::battery_facts::BatteryFacts { + placed_on_market: Some(dpp_rules::common::date::CalendarDate::new(2027, 2, 18)), + ..dpp_rules::batteries::battery_facts::BatteryFacts::default() + } +} + #[cfg(test)] mod tests { use super::*; @@ -630,10 +650,12 @@ mod tests { fn every_mandatory_field_has_a_column() { for category in BatteryCategory::ALL { let covered = fields_covered(*category); - let missing: Vec<&str> = - dpp_rules::batteries::passport_content::mandatory_fields(category.as_str()) - .filter(|f| !covered.contains(f)) - .collect(); + let missing: Vec<&str> = dpp_rules::batteries::passport_content::mandatory_fields( + category.as_str(), + facts_before_due_diligence(), + ) + .filter(|f| !covered.contains(f)) + .collect(); assert!( missing.is_empty(), "the {} template has no column for {} mandatory data point(s): {missing:?}", @@ -660,7 +682,10 @@ mod tests { for category in BatteryCategory::ALL { let refused: Vec<&str> = fields_covered(*category) .into_iter() - .filter(|f| !annex_xiii_requirement(f, category.as_str()).permits_presence()) + .filter(|f| { + !annex_xiii_requirement(f, category.as_str(), facts_before_due_diligence()) + .permits_presence() + }) .collect(); assert!( refused.is_empty(), diff --git a/crates/dpp-integrator/src/domain/validate/battery.rs b/crates/dpp-integrator/src/domain/validate/battery.rs index 1e70ede6..467e157c 100644 --- a/crates/dpp-integrator/src/domain/validate/battery.rs +++ b/crates/dpp-integrator/src/domain/validate/battery.rs @@ -276,6 +276,10 @@ pub fn validate_battery_row( // for any category, and a column an operator cannot answer is a column // they will fill with something. placed_on_market_date: None, + // No column yet: core reads the declaration only for a battery placed on + // the market from 18 August 2027, and refuses that battery's first + // publish until it is declared. + due_diligence_operator_scope: None, carbon_footprint_class_ruleset_id: None, carbon_footprint_class_ruleset_version: None, recycled_content_reporting_year: None, diff --git a/crates/dpp-integrator/src/handlers/templates.rs b/crates/dpp-integrator/src/handlers/templates.rs index 076b862c..f04bc589 100644 --- a/crates/dpp-integrator/src/handlers/templates.rs +++ b/crates/dpp-integrator/src/handlers/templates.rs @@ -325,10 +325,12 @@ mod template_publishes { }; let json = serde_json::to_value(&*data).expect("battery data serialises"); - let missing: Vec<&str> = - dpp_rules::batteries::passport_content::mandatory_fields(category.as_str()) - .filter(|f| json.get(*f).is_none_or(serde_json::Value::is_null)) - .collect(); + let missing: Vec<&str> = dpp_rules::batteries::passport_content::mandatory_fields( + category.as_str(), + crate::domain::battery_template::facts_before_due_diligence(), + ) + .filter(|f| json.get(*f).is_none_or(serde_json::Value::is_null)) + .collect(); assert!( missing.is_empty(), "the {} template imports a passport still missing {} mandatory data point(s): \ diff --git a/crates/dpp-node/src/infra/registry/client.rs b/crates/dpp-node/src/infra/registry/client.rs index 9723c81a..165ce22b 100644 --- a/crates/dpp-node/src/infra/registry/client.rs +++ b/crates/dpp-node/src/infra/registry/client.rs @@ -20,6 +20,12 @@ //! the submission exists, and the adapter reports it as pending so the drain //! polls for the verdict instead of posting it a third time. //! +//! A transfer notification carries the transfer's own identifier in the same +//! header. It is what makes sending one again after an answer the node could not +//! settle on (still processing, or a state it has no name for) the same +//! notification rather than a second, and a 409 naming the key is read the same +//! way: the registry already holds it. +//! //! # Unreachable registry //! //! If the registry is unreachable (connection refused, DNS failure) the adapter diff --git a/crates/dpp-node/src/infra/registry/mapping.rs b/crates/dpp-node/src/infra/registry/mapping.rs index ca915832..484c4e5e 100644 --- a/crates/dpp-node/src/infra/registry/mapping.rs +++ b/crates/dpp-node/src/infra/registry/mapping.rs @@ -12,6 +12,7 @@ use dpp_domain::{ ports::registry_sync::{ RegistrationGranularity, RegistrationRequest, RegistryRecord, RegistryStatus, RegistrySyncPort, ServiceProviderRef, SubmissionAcknowledgement, SubmissionReport, + TransferReport, }, transfer::TransferRecord, }; @@ -113,17 +114,51 @@ fn record_status(code: &RegistryStatusCode) -> RegistryStatus { } } -impl EuRegistrySync { - /// Map a bridge `EuRegistryResponse` to a domain `RegistryRecord`. - pub(super) fn response_to_record(resp: &EuRegistryResponse) -> RegistryRecord { - RegistryRecord { - registration_id: resp.registry_id.clone(), - status: record_status(&resp.status), - registered_at: resp.updated_at, - updated_at: resp.updated_at, - } +/// What the registry's reply to a transfer notification says, in the port's +/// words. +/// +/// The reply is a record's status because that is the one shape the registry is +/// known to answer in, but it is not asked as one: whether the notification was +/// taken is the question, so each status reads as an answer to it. +/// `registered` is the record staying in service with the handover applied; +/// `pending` is received and not yet applied. `rejected` is a refusal. A record +/// reported suspended or deactivated, or in a state nobody has named, is not +/// something this node can read as taken or refused, and is reported as it came. +fn transfer_report_for(resp: &EuRegistryResponse) -> TransferReport { + match &resp.status { + RegistryStatusCode::Registered => TransferReport::Accepted, + RegistryStatusCode::Pending => TransferReport::Processing, + RegistryStatusCode::Rejected => TransferReport::Refused { + reason: refusal_reason(resp), + }, + other => TransferReport::Unrecognised { + state: other.wire_str().to_owned(), + }, + } +} + +/// Why the registry refused a notification, in its own words, or that it gave no +/// reason: a refusal with the reason blank would read as nothing having +/// happened. +fn refusal_reason(resp: &EuRegistryResponse) -> String { + let reasons = resp + .rejection_reasons + .iter() + .flatten() + .map(|reason| reason.trim()) + .filter(|reason| !reason.is_empty()) + .collect::>() + .join("; "); + if !reasons.is_empty() { + return reasons; } + match resp.message.as_deref().map(str::trim) { + Some(message) if !message.is_empty() => message.to_owned(), + _ => "the registry refused the notification and gave no reason".to_owned(), + } +} +impl EuRegistrySync { /// Map a bridge `StatusResponse` to a domain `RegistryRecord`. pub(super) fn status_to_record(resp: &StatusResponse) -> RegistryRecord { RegistryRecord { @@ -563,7 +598,7 @@ impl RegistrySyncPort for EuRegistrySync { &self, record: &TransferRecord, registry_id: &str, - ) -> Result { + ) -> Result { let base_url = &self.config.endpoint.base_url; let passport_id = record.passport_id; @@ -624,9 +659,16 @@ impl RegistrySyncPort for EuRegistrySync { // The route names the registered record, so it is built from the // registration identifier, never from this node's passport id. let url = url_for(base_url, TRANSFER_PATH_TEMPLATE, registry_id)?; + // The handover's own identity, which is stable across every attempt at + // telling the registry about it. Sending it as the idempotency key is what + // makes a notification sent again, after an answer this node could not + // settle on, the same notification and not a second: a replay is answered + // with the conflict below, which is read as "the registry already holds it". + let idempotency_key = record.transfer_id.to_string(); self.with_retry(|| { let url = url.clone(); let notification = notification.clone(); + let idempotency_key = idempotency_key.clone(); async move { let token = self .get_token() @@ -637,6 +679,7 @@ impl RegistrySyncPort for EuRegistrySync { .client .post(&url) .bearer_auth(&token) + .header(IDEMPOTENCY_KEY_HEADER, idempotency_key) .json(¬ification) .send() .await @@ -661,6 +704,22 @@ impl RegistrySyncPort for EuRegistrySync { } if !resp.status().is_success() { let body = resp.text().await.unwrap_or_default(); + // A replayed key is not a failure: an earlier attempt reached + // the registry and its answer was lost, or was one this node + // could not settle on. The notification is held, and sending + // it a third time would be wrong, as it is for a submission. + if status_code == STATUS_IDEMPOTENCY_KEY_REUSED + && serde_json::from_str::(&body) + .is_ok_and(|b| b.is_idempotency_key_reused()) + { + tracing::info!( + passport_id = %passport_id, + detail = %refusal_text(&body), + "EU registry already holds this transfer notification \ + (idempotency key replayed) — not sending it again" + ); + return Ok(TransferReport::AlreadyHeld); + } return Err(RetryableError::Fatal(format!( "transfer notification failed {status_code}: {}", refusal_text(&body) @@ -668,10 +727,14 @@ impl RegistrySyncPort for EuRegistrySync { } let eu_resp: EuRegistryResponse = resp.json().await.map_err(|e| { - RetryableError::Fatal(format!("invalid transfer response: {e}")) + RetryableError::Fatal(format!( + "invalid transfer response: {e}; the notification may have been taken, \ + and the retry carries the same Idempotency-Key, so it is recognised, \ + not sent twice" + )) })?; - Ok(Self::response_to_record(&eu_resp)) + Ok(transfer_report_for(&eu_resp)) } }) .await diff --git a/crates/dpp-node/src/infra/registry/tests.rs b/crates/dpp-node/src/infra/registry/tests.rs index ae1f5a1b..7c7371ba 100644 --- a/crates/dpp-node/src/infra/registry/tests.rs +++ b/crates/dpp-node/src/infra/registry/tests.rs @@ -4,14 +4,14 @@ use std::time::{Duration, Instant}; use chrono::Utc; use dpp_domain::passport::PassportId; -use dpp_registry::{EuRegistryResponse, RegistryStatusCode}; +use dpp_registry::RegistryStatusCode; use uuid::Uuid; use dpp_domain::DppError; use dpp_domain::operator::{OperatorRole, ResponsibleOperator}; use dpp_domain::ports::registry_sync::{ RegistrationGranularity, RegistrationRequest, RegistryStatus, RegistrySyncPort, - SubmissionAcknowledgement, SubmissionReport, + SubmissionAcknowledgement, SubmissionReport, TransferReport, }; use dpp_domain::transfer::{TransferReason, TransferRecord}; @@ -43,16 +43,14 @@ fn production_config_requires_mtls() { } #[test] -fn response_to_record_maps_status_correctly() { - let resp = EuRegistryResponse { +fn a_status_reply_of_registered_is_a_registered_record() { + let resp = StatusResponse { registry_id: "EU-REG-2026-00001".into(), - passport_id: Uuid::nil(), status: RegistryStatusCode::Registered, - message: None, - rejection_reasons: None, updated_at: Utc::now(), + message: None, }; - let record = EuRegistrySync::response_to_record(&resp); + let record = EuRegistrySync::status_to_record(&resp); assert_eq!(record.status, RegistryStatus::Registered); assert_eq!(record.registration_id, "EU-REG-2026-00001"); } @@ -62,16 +60,14 @@ fn response_to_record_maps_status_correctly() { /// the port has no name for, and carries it as the registry said it rather than /// reading it as one of the three it does name. #[test] -fn a_record_reply_of_rejected_is_carried_as_the_registry_said_it() { - let resp = EuRegistryResponse { +fn a_status_reply_of_rejected_is_carried_as_the_registry_said_it() { + let resp = StatusResponse { registry_id: "EU-REG-2026-00002".into(), - passport_id: Uuid::nil(), status: RegistryStatusCode::Rejected, - message: Some("invalid data".into()), - rejection_reasons: Some(vec!["bad GTIN".into()]), updated_at: Utc::now(), + message: Some("invalid data".into()), }; - let record = EuRegistrySync::response_to_record(&resp); + let record = EuRegistrySync::status_to_record(&resp); assert_eq!( record.status, RegistryStatus::Unrecognised("rejected".into()) @@ -105,8 +101,10 @@ fn request_with_facility(facility: Option) -> Regi RegistrationRequest { request_id: Uuid::now_v7(), passport_id: PassportId::new(), - operator_identifier: "did:web:test.example".into(), - operator_identifier_scheme: "did".into(), + // An LEI, one of the three schemes with an EN 18219 clause 6 route, + // which is what a registration now has to carry. + operator_identifier: "529900T8BM49AURSDO55".into(), + operator_identifier_scheme: "lei".into(), operator_name: "Test Operator GmbH".into(), facility_identifier: "LEGACY-FAC".into(), facility, @@ -137,6 +135,7 @@ fn valid_request() -> RegistrationRequest { name: "Default Plant".into(), country: "DE".into(), address: Some("1 Allee, Berlin".into()), + identifier: None, })) } @@ -157,6 +156,7 @@ fn facility_identifier_prefers_full_snapshot() { name: "Default Plant".into(), country: "DE".into(), address: Some("1 Allee, Berlin".into()), + identifier: None, })); let fid = facility_identifier_for(&request); assert_eq!(fid.scheme, "gln"); @@ -286,6 +286,9 @@ mod mock_server { pub(super) task_results_read: Mutex>, pub(super) task_pages_asked: Mutex>, pub(super) transfer_queue: Mutex>, + /// The `Idempotency-Key` header of each transfer notification, `None` + /// when the request carried none. + pub(super) transfer_keys: Mutex>>, /// Bodies the registry actually received on `/transfer`. Asserting on /// the wire body is the only way to catch a field the adapter silently /// drops — a status-code assertion passes either way. @@ -362,8 +365,15 @@ mod mock_server { async fn transfer_handler( State(state): State>, Path(_id): Path, + headers: HeaderMap, Json(body): Json, ) -> Response { + state.transfer_keys.lock().await.push_back( + headers + .get(IDEMPOTENCY_KEY_HEADER) + .and_then(|v| v.to_str().ok()) + .map(str::to_owned), + ); state.transfer_bodies.lock().await.push_back(body); pop_or_500(&state.transfer_queue).await } @@ -962,12 +972,210 @@ async fn notify_transfer_success() { let base_url = mock_server::spawn(state.clone()).await; let sync = EuRegistrySync::new(mock_config(&base_url)).unwrap(); - let record = sync + let report = sync .notify_transfer(&completed_transfer(), "EU-REG-4") .await .expect("notify_transfer should succeed"); - assert_eq!(record.status, RegistryStatus::Registered); + // The record stays in service with the handover applied: that is the + // registry taking the notification, not a status to be passed on. + assert_eq!(report, TransferReport::Accepted); +} + +/// 🚨 The notification is sent under the handover's own identity. Three of the +/// answers end in the drain sending it again, and the key is what makes that the +/// same notification and not a second: it must be the transfer's, and the same +/// on every attempt. +#[tokio::test] +async fn a_transfer_notification_carries_the_transfers_id_as_its_idempotency_key() { + let state = Arc::new(MockState::default()); + { + let mut queue = state.transfer_queue.lock().await; + queue.push_back((axum::http::StatusCode::OK, registered_response("EU-REG-K"))); + queue.push_back((axum::http::StatusCode::OK, registered_response("EU-REG-K"))); + } + let base_url = mock_server::spawn(state.clone()).await; + let sync = EuRegistrySync::new(mock_config(&base_url)).unwrap(); + let transfer = completed_transfer(); + + sync.notify_transfer(&transfer, "EU-REG-K").await.unwrap(); + sync.notify_transfer(&transfer, "EU-REG-K").await.unwrap(); + + let expected = transfer.transfer_id.to_string(); + assert_eq!( + state + .transfer_keys + .lock() + .await + .iter() + .cloned() + .collect::>(), + vec![Some(expected.clone()), Some(expected)], + "every attempt presents the same key, and it is the transfer's" + ); +} + +/// A different handover is a different notification, so it is a different key. +#[tokio::test] +async fn two_transfers_do_not_share_a_key() { + let state = Arc::new(MockState::default()); + { + let mut queue = state.transfer_queue.lock().await; + queue.push_back((axum::http::StatusCode::OK, registered_response("EU-REG-K"))); + queue.push_back((axum::http::StatusCode::OK, registered_response("EU-REG-K"))); + } + let base_url = mock_server::spawn(state.clone()).await; + let sync = EuRegistrySync::new(mock_config(&base_url)).unwrap(); + + sync.notify_transfer(&completed_transfer(), "EU-REG-K") + .await + .unwrap(); + sync.notify_transfer(&completed_transfer(), "EU-REG-K") + .await + .unwrap(); + + let keys = state.transfer_keys.lock().await; + assert_eq!(keys.len(), 2); + assert_ne!(keys[0], keys[1]); +} + +/// The registry's answer to a notification is read as an answer to it: whether +/// it took the notification, and not where a registration stands. Each status is +/// its own report, a state nobody has named is carried as it came, and none is +/// read as an acceptance it did not say. +#[tokio::test] +async fn the_registrys_answer_to_a_notification_is_read_as_one() { + fn reply(status: &str, extra: serde_json::Value) -> serde_json::Value { + let mut body = serde_json::json!({ + "registryId": "EU-REG-R", + "passportId": Uuid::now_v7(), + "status": status, + "updatedAt": Utc::now().to_rfc3339(), + }); + body.as_object_mut() + .unwrap() + .extend(extra.as_object().unwrap().clone()); + body + } + + let cases = [ + ( + reply("registered", serde_json::json!({})), + TransferReport::Accepted, + ), + ( + reply("pending", serde_json::json!({})), + TransferReport::Processing, + ), + ( + reply( + "rejected", + serde_json::json!({"rejectionReasons": ["no such record", " ", "operator not verified"]}), + ), + TransferReport::Refused { + reason: "no such record; operator not verified".into(), + }, + ), + ( + reply( + "rejected", + serde_json::json!({"message": "refused by the registry"}), + ), + TransferReport::Refused { + reason: "refused by the registry".into(), + }, + ), + ( + reply("rejected", serde_json::json!({})), + TransferReport::Refused { + reason: "the registry refused the notification and gave no reason".into(), + }, + ), + // A record the registry reports out of service is not a notification + // taken, or refused: it is a state to show as it came. + ( + reply("suspended_by_authority", serde_json::json!({})), + TransferReport::Unrecognised { + state: "suspended_by_authority".into(), + }, + ), + ( + reply("deactivated", serde_json::json!({})), + TransferReport::Unrecognised { + state: "deactivated".into(), + }, + ), + ( + reply("UNDER_REVIEW", serde_json::json!({})), + TransferReport::Unrecognised { + state: "UNDER_REVIEW".into(), + }, + ), + ]; + + let state = Arc::new(MockState::default()); + { + let mut queue = state.transfer_queue.lock().await; + for (body, _) in &cases { + queue.push_back((axum::http::StatusCode::OK, body.clone())); + } + } + let base_url = mock_server::spawn(state.clone()).await; + let sync = EuRegistrySync::new(mock_config(&base_url)).unwrap(); + + for (body, expected) in cases { + let report = sync + .notify_transfer(&completed_transfer(), "EU-REG-R") + .await + .expect("every reply is an answer"); + assert_eq!(report, expected, "for {body}"); + } +} + +/// A replayed key is the registry saying it already holds the notification: an +/// earlier attempt got through and its answer was lost, or was one this node +/// could not settle on. Neither a failure nor grounds to send it a third time. +#[tokio::test] +async fn a_replayed_transfer_key_means_the_registry_already_holds_it() { + let state = Arc::new(MockState::default()); + state + .transfer_queue + .lock() + .await + .push_back((axum::http::StatusCode::CONFLICT, key_reused_body())); + let base_url = mock_server::spawn(state.clone()).await; + let sync = EuRegistrySync::new(mock_config(&base_url)).unwrap(); + + let report = sync + .notify_transfer(&completed_transfer(), "EU-REG-K") + .await + .expect("a replayed key is not a failure"); + + assert_eq!(report, TransferReport::AlreadyHeld); + assert_eq!( + state.transfer_bodies.lock().await.len(), + 1, + "a replayed key is not retried" + ); +} + +/// Only the one `subCode` is read as "already done". Any other conflict says +/// nothing about whether the notification was taken. +#[tokio::test] +async fn a_transfer_conflict_that_is_not_a_replayed_key_is_still_an_error() { + let state = Arc::new(MockState::default()); + state.transfer_queue.lock().await.push_back(( + axum::http::StatusCode::CONFLICT, + serde_json::json!({"subCode": "SOME_OTHER_CONFLICT", "traceId": "trace-x"}), + )); + let base_url = mock_server::spawn(state.clone()).await; + let sync = EuRegistrySync::new(mock_config(&base_url)).unwrap(); + + let err = sync + .notify_transfer(&completed_transfer(), "EU-REG-K") + .await + .expect_err("another conflict is a refusal to be seen, not an acceptance"); + assert!(err.to_string().contains("409"), "{err}"); } // ── Payload validation is fail-closed ─────────────────────────────────────── @@ -1177,6 +1385,7 @@ fn responsible(did: &str, name: &str, country: &str) -> ResponsibleOperator { postal_address: None, electronic_address: None, country: country.to_owned(), + identifier: None, } } @@ -1305,24 +1514,19 @@ async fn a_pending_transfer_reports_its_initiation_time() { /// a false statement `validate` cannot catch, because `did` is the one scheme /// accepted without structural verification. #[tokio::test] -async fn a_vat_operator_is_not_submitted_as_a_did() { +async fn an_lei_operator_is_not_submitted_as_a_did() { let state = Arc::new(MockState::default()); state .register_queue .lock() .await - .push_back((axum::http::StatusCode::OK, accepted_response("CORR-V"))); + .push_back((axum::http::StatusCode::OK, accepted_response("CORR-L"))); let base_url = mock_server::spawn(state.clone()).await; let sync = EuRegistrySync::new(mock_config(&base_url)).unwrap(); - let request = RegistrationRequest { - operator_identifier: "DE811234567".into(), - operator_identifier_scheme: "vat".into(), - ..valid_request() - }; - sync.register(request) + sync.register(valid_request()) .await - .expect("a VAT-scheme operator must register"); + .expect("an LEI-scheme operator must register"); let sent = state .register_bodies @@ -1331,15 +1535,45 @@ async fn a_vat_operator_is_not_submitted_as_a_did() { .pop_front() .expect("the registry must have received a payload"); let operator = &sent["submission"][0]["operatorId"]; - assert_eq!(operator["scheme"], "vat"); - assert_eq!(operator["value"], "DE811234567"); + assert_eq!(operator["scheme"], "lei"); + assert_eq!(operator["value"], "529900T8BM49AURSDO55"); assert!( operator.get("did").is_none(), - "a VAT number is not a DID and must not be sent as one: {operator}" + "an LEI is not a DID and must not be sent as one: {operator}" ); } -/// A genuine DID still travels in the `did` field. +/// 🚨 A VAT number and a DID are well formed and have no EN 18219 clause 6 route: +/// an EU-wide VAT number has no ICD, and a DID is scheme 8 only with a credential +/// a register has issued. dpp-core 0.23.0 refuses such a registration, and the +/// adapter fails closed on that, so nothing reaches the registry. +#[tokio::test] +async fn an_operator_scheme_with_no_clause_6_route_is_refused_before_it_is_sent() { + let state = Arc::new(MockState::default()); + let base_url = mock_server::spawn(state.clone()).await; + let sync = EuRegistrySync::new(mock_config(&base_url)).unwrap(); + + for (scheme, value) in [("vat", "DE811234567"), ("did", "did:web:test.example")] { + let request = RegistrationRequest { + operator_identifier: value.into(), + operator_identifier_scheme: scheme.into(), + ..valid_request() + }; + let err = sync + .register(request) + .await + .expect_err("a scheme with no clause 6 route must be refused"); + assert!( + matches!(&err, DppError::Validation(_)) && err.to_string().contains("clause 6"), + "a '{scheme}' operator must be refused for its scheme, got: {err:?}" + ); + } + assert_eq!(state.register_hits.load(Ordering::SeqCst), 0); +} + +/// A genuine DID still travels in the `did` field, where an operator has chosen +/// to submit a payload that fails validation (`allow_invalid_payloads`), which is +/// now the only way a DID reaches the registry. #[tokio::test] async fn a_did_operator_keeps_its_did_field() { let state = Arc::new(MockState::default()); @@ -1349,11 +1583,20 @@ async fn a_did_operator_keeps_its_did_field() { .await .push_back((axum::http::StatusCode::OK, accepted_response("CORR-D"))); let base_url = mock_server::spawn(state.clone()).await; - let sync = EuRegistrySync::new(mock_config(&base_url)).unwrap(); + let sync = EuRegistrySync::new(EuRegistrySyncConfig { + allow_invalid_payloads: true, + ..mock_config(&base_url) + }) + .unwrap(); - sync.register(valid_request()) + let request = RegistrationRequest { + operator_identifier: "did:web:test.example".into(), + operator_identifier_scheme: "did".into(), + ..valid_request() + }; + sync.register(request) .await - .expect("a did-scheme operator must register"); + .expect("a did-scheme operator is submitted when the operator allows it"); let sent = state.register_bodies.lock().await.pop_front().unwrap(); let operator = &sent["submission"][0]["operatorId"]; @@ -1429,18 +1672,6 @@ fn a_counterparty_without_an_eu_identifier_falls_back_to_its_did() { /// service, which resubmitting does not fix. #[test] fn a_deactivated_record_is_not_reported_as_rejected() { - let resp = EuRegistryResponse { - registry_id: "EU-REG-D".into(), - passport_id: Uuid::nil(), - status: RegistryStatusCode::Deactivated, - message: None, - rejection_reasons: None, - updated_at: Utc::now(), - }; - let mapped = EuRegistrySync::response_to_record(&resp).status; - assert_eq!(mapped, RegistryStatus::Deactivated); - assert_ne!(mapped, RegistryStatus::Unrecognised("rejected".into())); - let status = StatusResponse { registry_id: "EU-REG-D".into(), status: RegistryStatusCode::Deactivated, diff --git a/crates/dpp-node/src/infra/transfer_drain.rs b/crates/dpp-node/src/infra/transfer_drain.rs index 096cb507..19039536 100644 --- a/crates/dpp-node/src/infra/transfer_drain.rs +++ b/crates/dpp-node/src/infra/transfer_drain.rs @@ -11,7 +11,7 @@ use std::sync::Arc; -use dpp_domain::ports::registry_sync::{RegistryStatus, RegistrySyncPort}; +use dpp_domain::ports::registry_sync::{RegistrySyncPort, TransferReport}; use dpp_domain::transfer::TransferRecord; use dpp_types::{RegistrySyncOutbox, RegistrySyncStatus, RegistryTransferOutbox}; @@ -31,18 +31,6 @@ pub struct TransferDrainStats { pub deferred: u32, } -/// Whether the registry answered a transfer notification with a refusal. -/// -/// A record has no `rejected` state: that was a state of a submission, and the -/// port reports it as a [`SubmissionReport`](dpp_domain::ports::registry_sync::SubmissionReport). -/// The registry's `rejected` reply to a *notification* is still a refusal of it, -/// and arrives as the state the port has no name for, carrying the registry's -/// word. Read as anything else it would be retried for as long as the registry -/// keeps saying no. -fn is_rejection(status: &RegistryStatus) -> bool { - matches!(status, RegistryStatus::Unrecognised(state) if state.eq_ignore_ascii_case("rejected")) -} - /// Drain up to `batch` due rows once. /// /// Never panics and never propagates: a per-row failure is recorded on the row @@ -119,42 +107,64 @@ pub async fn drain_once( metrics::histogram!("registry_transfer_drain_seconds") .record(started.elapsed().as_secs_f64()); match outcome { - Ok(rec) if is_rejection(&rec.status) => { + // Taken, or already held: an earlier attempt under the same key got + // through. Either way the registry has the notification and sending + // it again would be wrong. Already held does not say the handover was + // applied, and no route that would say so has been seen, so the row + // closes on the registry's word that it holds the notification. + Ok(TransferReport::Accepted | TransferReport::AlreadyHeld) => { + let _ = outbox.mark_notified(tid, registry_id.clone()).await; + stats.notified += 1; + } + Ok(TransferReport::Refused { reason }) => { tracing::warn!( passport_id = %row.passport_id, transfer_id = %tid, + reason = %reason, "registry rejected transfer notification" ); metrics::counter!("registry_transfer_rejected_total").increment(1); let _ = outbox - .mark_rejected(tid, "registry rejected transfer notification".into()) + .mark_rejected( + tid, + format!("registry rejected transfer notification: {reason}"), + ) .await; stats.rejected += 1; } - // A state the port has no name for, other than the refusal above, is - // not a verdict. Closing the row as notified would record a - // notification the registry may not have accepted, which the - // registration drain declines to do for the same reason. It is a - // failed attempt: the row backs off, stays pending, and counts toward - // the stalled rollup. The notification route carries no idempotency - // key, so the retry sends it again. - Ok(rec) if matches!(rec.status, RegistryStatus::Unrecognised(_)) => { + // Received and not yet applied, or a state this node has no name for: + // neither is a verdict, and closing the row on either would record a + // handover the registry may not have taken, which its record never + // learns of. The notification stays open as a failed attempt: it backs + // off, counts toward the stalled rollup, and is sent again under the + // same idempotency key, which is what makes that the same notification + // and not a second. When the registry answers a replay is unobserved: an + // answer naming the replayed key closes the row as already held, above, + // and one that never settles is for a person. + Ok(TransferReport::Processing) => { + let _ = outbox + .mark_attempt_failed( + tid, + "the registry has the notification and has not applied it yet — sending \ + it again under the same key" + .into(), + ) + .await; + stats.retried += 1; + } + Ok(other) => { let _ = outbox .mark_attempt_failed( tid, format!( "the registry answered a transfer notification with a state this \ - node has no name for: {:?}", - rec.status + node has no name for ({other:?}) — sending it again under the \ + same key" ), ) .await; stats.retried += 1; } - Ok(rec) => { - let _ = outbox.mark_notified(tid, rec.registration_id).await; - stats.notified += 1; - } Err(e) => { // Transient/unreachable — back off and retry. The row stays. let _ = outbox.mark_attempt_failed(tid, e.to_string()).await; @@ -196,6 +206,7 @@ mod tests { postal_address: None, electronic_address: None, country: "DE".to_owned(), + identifier: None, } } @@ -373,10 +384,15 @@ mod tests { } enum Outcome { + /// The registry took the notification. Notified, + /// A replayed key: an earlier attempt got through. + AlreadyHeld, + /// Received and not yet applied. + Processing, + /// Refused. Rejected, - /// A reply carrying a state the port has no name for that is not the - /// registry's `rejected`. + /// A state the port has no name for. Unrecognised, Transient, } @@ -406,29 +422,23 @@ mod tests { &self, record: &TransferRecord, registry_id: &str, - ) -> Result { + ) -> Result { self.seen .lock() .unwrap() .push((record.clone(), registry_id.to_owned())); - let status = match self.outcome { - // A notified transfer leaves the registration `Registered` — the - // registry amends the record, it does not restatus it. - Outcome::Notified => RegistryStatus::Registered, - // A record has no `rejected` state; the registry's refusal of a - // notification arrives as the state the port has no name for. - Outcome::Rejected => RegistryStatus::Unrecognised("rejected".into()), - Outcome::Unrecognised => RegistryStatus::Unrecognised("UNDER_REVIEW".into()), - Outcome::Transient => { - return Err(DppError::Internal("registry unreachable".into())); - } - }; - Ok(RegistryRecord { - registration_id: "EU-REG-T".into(), - status, - registered_at: Utc::now(), - updated_at: Utc::now(), - }) + match self.outcome { + Outcome::Notified => Ok(TransferReport::Accepted), + Outcome::AlreadyHeld => Ok(TransferReport::AlreadyHeld), + Outcome::Processing => Ok(TransferReport::Processing), + Outcome::Rejected => Ok(TransferReport::Refused { + reason: "no such record".into(), + }), + Outcome::Unrecognised => Ok(TransferReport::Unrecognised { + state: "UNDER_REVIEW".into(), + }), + Outcome::Transient => Err(DppError::Internal("registry unreachable".into())), + } } } @@ -583,9 +593,9 @@ mod tests { assert!(outbox.notified.lock().unwrap().is_empty()); } - /// 🚨 A state this node has no name for, other than `rejected`, is not an - /// acceptance. Recording it as one would close the row for good on a - /// notification the registry may not have taken. + /// 🚨 A state this node has no name for is not an acceptance. Recording it as + /// one would close the row for good on a notification the registry may not + /// have taken. #[tokio::test] async fn an_unrecognised_reply_is_not_recorded_as_a_notification() { let (outbox, port) = fakes(Outcome::Unrecognised, vec![row_from(&record())]); @@ -603,6 +613,51 @@ mod tests { assert!(outbox.rejected.lock().unwrap().is_empty()); } + /// `Processing` is the registry saying it has the notification, so it is not + /// closed (it is not applied) and not refused. It stays open and is sent again + /// as the *same* notification: the record the port is handed carries the same + /// transfer identity each time, which is what the adapter keys the request on. + #[tokio::test] + async fn a_notification_still_being_processed_is_sent_again_under_the_same_identity() { + let rec = record(); + let (outbox, port) = fakes(Outcome::Processing, vec![row_from(&rec)]); + + let first = run(&outbox, &port).await; + let second = run(&outbox, &port).await; + + assert_eq!((first.retried, second.retried), (1, 1)); + assert!(outbox.notified.lock().unwrap().is_empty()); + assert!(outbox.rejected.lock().unwrap().is_empty()); + assert_eq!( + outbox.failed.lock().unwrap().len(), + 2, + "each pass is counted" + ); + + let seen = port.seen.lock().unwrap(); + assert_eq!(seen.len(), 2, "sent again, not dropped"); + assert!( + seen.iter() + .all(|(sent, _)| sent.transfer_id == rec.transfer_id), + "always the same transfer, so a key derived from it is stable" + ); + } + + /// A replay answered as already held is how a notification whose first answer + /// was lost or unsettled is finally closed, without a second one reaching the + /// registry. + #[tokio::test] + async fn a_notification_the_registry_already_holds_is_closed_as_notified() { + let (outbox, port) = fakes(Outcome::AlreadyHeld, vec![row_from(&record())]); + + let stats = run(&outbox, &port).await; + + assert_eq!(stats.notified, 1); + assert_eq!((stats.rejected, stats.retried), (0, 0)); + assert_eq!(outbox.notified.lock().unwrap().len(), 1); + assert!(outbox.failed.lock().unwrap().is_empty()); + } + /// An unreachable registry must not lose the notification: the row backs off /// and stays pending. #[tokio::test] diff --git a/crates/dpp-node/tests/openapi_contract.rs b/crates/dpp-node/tests/openapi_contract.rs index aff25e40..2209a685 100644 --- a/crates/dpp-node/tests/openapi_contract.rs +++ b/crates/dpp-node/tests/openapi_contract.rs @@ -2224,14 +2224,39 @@ fn publish_readiness_quotes_the_real_mandatory_field_counts() { .as_str() .expect("PublishReadiness carries a description"); + use dpp_rules::batteries::battery_facts::BatteryFacts; + use dpp_rules::batteries::due_diligence::{ + DUE_DILIGENCE_REQUIRED_FROM, DueDiligenceSecondLife, OperatorScope, + }; + use dpp_rules::common::date::CalendarDate; + + // The quoted counts are for a battery placed before the due diligence report + // falls due; the sentence says one more is owed from that date, and the + // second count pins that claim. + let before = BatteryFacts { + placed_on_market: Some(CalendarDate::new(2027, 2, 18)), + ..BatteryFacts::default() + }; + let owing = BatteryFacts { + placed_on_market: Some(DUE_DILIGENCE_REQUIRED_FROM), + second_life: DueDiligenceSecondLife::None, + operator_scope: Some(OperatorScope::Subject), + }; for category in ["ev", "lmt", "industrial"] { - let count = dpp_rules::batteries::passport_content::mandatory_fields(category).count(); + let count = + dpp_rules::batteries::passport_content::mandatory_fields(category, before).count(); assert!( description.contains(&count.to_string()), "PublishReadiness quotes mandatory-field counts and does not name {count}, \ which is what the gate requires of a '{category}' battery. The description \ reads:\n{description}" ); + assert_eq!( + dpp_rules::batteries::passport_content::mandatory_fields(category, owing).count(), + count + 1, + "PublishReadiness says a '{category}' battery owes one more data point from \ + 18 August 2027, and the gate no longer agrees" + ); } } @@ -2957,6 +2982,7 @@ mod fixtures { name: "Werk Nord".into(), country: "DE".into(), address: Some("Werkstrasse 4, 21079 Hamburg, DE".into()), + identifier: None, } } @@ -2989,6 +3015,7 @@ mod fixtures { postal_address: Some("Hauptstrasse 1, 10115 Berlin, DE".into()), electronic_address: Some("compliance@nordwerk.example".into()), country: "DE".into(), + identifier: None, } } diff --git a/crates/dpp-node/tests/registry_outbox.rs b/crates/dpp-node/tests/registry_outbox.rs index e1e90fcb..735f3066 100644 --- a/crates/dpp-node/tests/registry_outbox.rs +++ b/crates/dpp-node/tests/registry_outbox.rs @@ -39,7 +39,7 @@ use dpp_domain::{ ports::passport_repo::PassportRepository, ports::registry_sync::{ RegisteringOperator, RegistrationGranularity, RegistrationRequest, RegistryRecord, - RegistrySyncPort, SubmissionAcknowledgement, SubmissionReport, + RegistrySyncPort, SubmissionAcknowledgement, SubmissionReport, TransferReport, }, product_group::{ProductGroup, ProductGroupData}, status::PassportStatus, @@ -123,6 +123,7 @@ fn draft_passport() -> Passport { name: "Outbox Plant".into(), country: "DE".into(), address: None, + identifier: None, }), seal: None, carrier_serial: None, @@ -249,7 +250,7 @@ impl RegistrySyncPort for MockPort { &self, _record: &dpp_domain::transfer::TransferRecord, _registry_id: &str, - ) -> Result { + ) -> Result { unimplemented!("not exercised") } } diff --git a/crates/dpp-node/tests/seal_outbox.rs b/crates/dpp-node/tests/seal_outbox.rs index ea4c5ec9..e961f3eb 100644 --- a/crates/dpp-node/tests/seal_outbox.rs +++ b/crates/dpp-node/tests/seal_outbox.rs @@ -234,6 +234,7 @@ fn draft_passport() -> Passport { name: "Odal Simulation Plant".into(), country: "MK".into(), address: Some("Skopje, MK".into()), + identifier: None, }), seal: None, carrier_serial: None, diff --git a/crates/dpp-node/tests/transfer_outbox.rs b/crates/dpp-node/tests/transfer_outbox.rs index 6c61b361..45638e15 100644 --- a/crates/dpp-node/tests/transfer_outbox.rs +++ b/crates/dpp-node/tests/transfer_outbox.rs @@ -98,6 +98,7 @@ fn operator(did: &str, name: &str) -> ResponsibleOperator { postal_address: None, electronic_address: None, country: "DE".to_owned(), + identifier: None, } } diff --git a/crates/dpp-plugin-host/tests/integration.rs b/crates/dpp-plugin-host/tests/integration.rs index cc7a9d14..7683f94f 100644 --- a/crates/dpp-plugin-host/tests/integration.rs +++ b/crates/dpp-plugin-host/tests/integration.rs @@ -213,6 +213,8 @@ fn battery_product_group_data() -> ProductGroupData { usage_history: None, voltage_temperature_range: None, waste_battery_information: None, + // The Art. 47 declaration, added in dpp-core 0.23.0. + due_diligence_operator_scope: None, })) } diff --git a/crates/dpp-resolver/src/jws_verification_tests.rs b/crates/dpp-resolver/src/jws_verification_tests.rs index cc9127c5..7c5e4969 100644 --- a/crates/dpp-resolver/src/jws_verification_tests.rs +++ b/crates/dpp-resolver/src/jws_verification_tests.rs @@ -438,7 +438,7 @@ async fn access_tier_header_cannot_unlock_restricted_fields() { let body = axum::body::to_bytes(resp.into_body(), usize::MAX) .await .unwrap(); - let s = String::from_utf8_lossy(&body); + let s = without_context(&body); assert!( !s.contains("svhcSubstances") && !s.contains("disassemblyInstructions"), "tier header '{tier}' must not unlock restricted fields, got: {s}" @@ -446,6 +446,19 @@ async fn access_tier_header_cannot_unlock_restricted_fields() { } } +/// A JSON-LD body as text, without its top-level `@context`. +/// +/// The inline context defines a term for every field the product group can +/// carry, served or withheld, so a field's name in it says nothing about what was +/// served. A scan for a withheld field reads the document without it. +fn without_context(body: &[u8]) -> String { + let mut doc: serde_json::Value = serde_json::from_slice(body).expect("a JSON-LD body"); + if let Some(object) = doc.as_object_mut() { + object.remove("@context"); + } + doc.to_string() +} + // ─── AAS door ───────────────────────────────────────────────────────────────── /// A published battery passport carrying restricted and individual-tier fields. @@ -775,11 +788,15 @@ async fn the_aas_door_projects_the_signed_payload_not_the_live_row() { // ─── cross-door agreement ───────────────────────────────────────────────────── -/// Every object key in `value`, at any depth. +/// Every object key in `value`, at any depth, outside a JSON-LD `@context`. +/// +/// A context's term definitions are keyed by field name and name every field the +/// product group can carry, served or withheld, so they are not the document's +/// fields. fn keys_at_any_depth(value: &serde_json::Value, out: &mut std::collections::BTreeSet) { match value { serde_json::Value::Object(map) => { - for (key, child) in map { + for (key, child) in map.iter().filter(|(key, _)| key.as_str() != "@context") { out.insert(key.clone()); keys_at_any_depth(child, out); } diff --git a/crates/dpp-resolver/tests/resolver_e2e.rs b/crates/dpp-resolver/tests/resolver_e2e.rs index 7fc13d64..2202a896 100644 --- a/crates/dpp-resolver/tests/resolver_e2e.rs +++ b/crates/dpp-resolver/tests/resolver_e2e.rs @@ -986,7 +986,13 @@ async fn a_restricted_product_group_field_is_absent_from_the_public_view() { let body = axum::body::to_bytes(resp.into_body(), usize::MAX) .await .expect("read body"); - let body = String::from_utf8(body.to_vec()).expect("utf-8"); + // The inline `@context` defines a term for every field the product group can + // carry, served or withheld, so the scan reads the document without it. + let mut doc: serde_json::Value = serde_json::from_slice(&body).expect("a JSON-LD body"); + if let Some(object) = doc.as_object_mut() { + object.remove("@context"); + } + let body = doc.to_string(); assert!( !body.contains("cathodeMaterial"), diff --git a/crates/dpp-vault/src/domain/verify/engine.rs b/crates/dpp-vault/src/domain/verify/engine.rs index 698ccafa..9659eae3 100644 --- a/crates/dpp-vault/src/domain/verify/engine.rs +++ b/crates/dpp-vault/src/domain/verify/engine.rs @@ -744,6 +744,7 @@ mod tests { postal_address: None, electronic_address: None, country: "DE".into(), + identifier: None, }; let mut record = TransferRecord { transfer_id: Uuid::now_v7(), @@ -872,6 +873,7 @@ mod tests { postal_address: None, electronic_address: None, country: "DE".into(), + identifier: None, }; let record = TransferRecord { transfer_id: Uuid::now_v7(), diff --git a/crates/dpp-vault/src/domain/verify/transfer_chain.rs b/crates/dpp-vault/src/domain/verify/transfer_chain.rs index 529875e8..cd23016e 100644 --- a/crates/dpp-vault/src/domain/verify/transfer_chain.rs +++ b/crates/dpp-vault/src/domain/verify/transfer_chain.rs @@ -222,6 +222,7 @@ mod tests { postal_address: None, electronic_address: None, country: "DE".into(), + identifier: None, } } diff --git a/crates/dpp-vault/src/handlers/create.rs b/crates/dpp-vault/src/handlers/create.rs index 8ce9446d..cf4b5e3c 100644 --- a/crates/dpp-vault/src/handlers/create.rs +++ b/crates/dpp-vault/src/handlers/create.rs @@ -472,6 +472,7 @@ mod schema_validation { usage_history: None, voltage_temperature_range: None, waste_battery_information: None, + due_diligence_operator_scope: None, })) } diff --git a/crates/dpp-vault/tests/battery.rs b/crates/dpp-vault/tests/battery.rs index 18bee255..176ec67a 100644 --- a/crates/dpp-vault/tests/battery.rs +++ b/crates/dpp-vault/tests/battery.rs @@ -28,6 +28,9 @@ async fn test_battery_create_publish_resolve() { "materials": [ {"name": "Lithium Iron Phosphate", "weightKg": 1.2} ], + // An industrial battery publishes only with its placing date: the due + // diligence row is answered as of it, and the gate refuses without it. + "placedOnMarketDate": "2026-07-15", // The complete content the Battery Regulation makes mandatory for an // industrial battery — 38 fields, not the six that merely satisfy the // schema. This is the one test whose subject *is* a battery passport, so @@ -186,8 +189,14 @@ async fn an_incomplete_industrial_battery_cannot_be_published() { "the refusal must say what is wrong and for which category: {body}" ); // Reported together, not one per attempt: an operator fixing these one at a - // time would need a publish round-trip per field. - for field in ["batteryModelId", "manufacturingPlace", "cathodeMaterial"] { + // time would need a publish round-trip per field. The missing placing date is + // among them, since the due diligence row cannot be answered without it. + for field in [ + "batteryModelId", + "manufacturingPlace", + "cathodeMaterial", + "placedOnMarketDate", + ] { assert!( body.contains(field), "every missing field is named at once; `{field}` is absent from: {body}" diff --git a/crates/dpp-vault/tests/continuity_snapshot.rs b/crates/dpp-vault/tests/continuity_snapshot.rs index 12740e3b..d5276fde 100644 --- a/crates/dpp-vault/tests/continuity_snapshot.rs +++ b/crates/dpp-vault/tests/continuity_snapshot.rs @@ -281,6 +281,7 @@ fn draft_passport() -> Passport { name: "Test Facility".into(), country: "DE".into(), address: None, + identifier: None, }), seal: None, carrier_serial: None, diff --git a/crates/dpp-vault/tests/demo_passports_publish.rs b/crates/dpp-vault/tests/demo_passports_publish.rs index 325737c2..90a69e91 100644 --- a/crates/dpp-vault/tests/demo_passports_publish.rs +++ b/crates/dpp-vault/tests/demo_passports_publish.rs @@ -19,8 +19,11 @@ use std::fs; use std::path::{Path, PathBuf}; +use chrono::Datelike; use dpp_domain::ProductGroupData; +use dpp_rules::batteries::battery_facts::BatteryFacts; use dpp_rules::batteries::passport_content; +use dpp_rules::common::date::CalendarDate; fn demo_passport_files() -> Vec { let dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../ops/demo/passports"); @@ -61,6 +64,28 @@ fn product_group_data(path: &Path) -> ProductGroupData { }) } +/// The facts the content table reads for one demo file: its placing date, from +/// the envelope, as the publish gate takes it. The second-life limb and the +/// operator's declaration stay at their defaults, which only a battery placed +/// from 18 August 2027 is asked about. +fn battery_facts(path: &Path) -> BatteryFacts { + let raw = fs::read_to_string(path).expect("read demo passport"); + let body: serde_json::Value = serde_json::from_str(&raw).expect("demo passport is JSON"); + let placed_on_market = body + .get("placedOnMarketDate") + .and_then(serde_json::Value::as_str) + .map(|date| { + let date = chrono::NaiveDate::parse_from_str(date, "%Y-%m-%d").unwrap_or_else(|e| { + panic!("{}'s placedOnMarketDate is not a date: {e}", path.display()) + }); + CalendarDate::new(date.year(), date.month() as u8, date.day() as u8) + }); + BatteryFacts { + placed_on_market, + ..BatteryFacts::default() + } +} + /// Every demo file must land on its typed variant, not the open `Other` one. /// /// `Other` is deliberately forgiving — a product group this build has no @@ -109,9 +134,10 @@ fn every_demo_passport_satisfies_the_mandatory_content_gate() { // Present-but-null is absent: `skip_serializing_if` means a `None` never // reaches the wire, so an explicit null carries no value either way. - let missing: Vec<&str> = passport_content::mandatory_fields(battery_type) - .filter(|f| value.get(*f).is_none_or(serde_json::Value::is_null)) - .collect(); + let missing: Vec<&str> = + passport_content::mandatory_fields(battery_type, battery_facts(&path)) + .filter(|f| value.get(*f).is_none_or(serde_json::Value::is_null)) + .collect(); if !missing.is_empty() { broken.push(format!( @@ -159,7 +185,8 @@ fn no_demo_passport_carries_content_its_category_may_not() { .map(|(k, _)| k.as_str()) .collect(); let barred: Vec<&str> = - passport_content::fields_not_applicable(&present, battery_type).collect(); + passport_content::fields_not_applicable(&present, battery_type, battery_facts(&path)) + .collect(); if !barred.is_empty() { broken.push(format!( diff --git a/crates/dpp-vault/tests/evidence_dossier.rs b/crates/dpp-vault/tests/evidence_dossier.rs index 4ff31102..3fac9c3b 100644 --- a/crates/dpp-vault/tests/evidence_dossier.rs +++ b/crates/dpp-vault/tests/evidence_dossier.rs @@ -265,6 +265,7 @@ fn draft_passport() -> Passport { name: "Test Facility".into(), country: "DE".into(), address: None, + identifier: None, }), seal: None, carrier_serial: None, @@ -319,6 +320,7 @@ async fn publish_transfer_eol_then_generate_verifies_and_persists() { postal_address: None, electronic_address: None, country: "DE".into(), + identifier: None, }; service .initiate_transfer( diff --git a/crates/dpp-vault/tests/seal_route.rs b/crates/dpp-vault/tests/seal_route.rs index d76306c5..f1988823 100644 --- a/crates/dpp-vault/tests/seal_route.rs +++ b/crates/dpp-vault/tests/seal_route.rs @@ -421,6 +421,7 @@ async fn a_completed_handover_says_responsibility_may_have_moved() { postal_address: None, electronic_address: None, country: "DE".to_owned(), + identifier: None, }; let from = operator("did:web:acme.example", "Acme GmbH"); let mut chain = TransferChain::new(id, from.clone()); From 9fb00732aeeda96ab7592416b4fe0fe513f63f88 Mon Sep 17 00:00:00 2001 From: chillin_since_26 Date: Mon, 2 Nov 2026 18:13:40 +0100 Subject: [PATCH 03/12] fix: refuse an unregistrable identity at publish --- CHANGELOG.md | 27 +++- crates/dpp-node/src/infra/registry_drain.rs | 14 +- crates/dpp-node/tests/registry_outbox.rs | 42 +++++ crates/dpp-node/tests/smoke.rs | 8 +- .../dpp-vault/src/domain/service/publish.rs | 84 +++++++--- .../tests/facility_operator_stamping.rs | 153 ++++++++++++++++++ crates/dpp-vault/tests/helpers/mod.rs | 7 +- .../tests/registry_identity_governance.rs | 6 +- .../dpp-vault/tests/registry_status_route.rs | 2 +- docs/ops/METRICS-PLAN.md | 2 +- 10 files changed, 308 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6eb8d781..b91c184c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,15 +17,20 @@ under the pre-1.0 conventions in [VERSIONING.md](docs/governance/VERSIONING.md): is a VAT or EORI number.)* Core now accepts an operator or facility identifier on a registration only under `lei`, `duns` or `gln`: an EU-wide VAT number has no ICD, none was found for an EORI number, and a DID is scheme 8 only with a - credential a register has issued. The adapter fails closed on that check as on - every other, so each attempt at such a registration is refused before it is - sent, and the row backs off and counts toward the stalled rollup. Facilities - are added under a GLN and are not affected. + credential a register has issued. Publish now asks the same question, through + core's `ActorIdentifier::from_legacy`, before it queues a registration: where + the product group's obligation is live it refuses with `422` and the reason + `unregistrable_identity`, naming the scheme and the fix, and elsewhere it + publishes without a registration, as it does when the identity is missing. + Queued, such a registration would have been refused on every attempt, because + its payload is fixed at publish. A VAT or EORI number is still accepted and + stamped as the operator identifier. Facilities are added under a GLN and are + not affected. **Migration:** make an LEI or a D-U-N-S number the primary operator identifier - (`POST /vault/api/v1/operator-identifiers/{id}/primary`). A draft stamped before - the change, and a registration already queued, keep the identifier they were - given. + (`POST /vault/api/v1/operator-identifiers/{id}/primary`). A registration already + queued under a VAT or EORI number is rejected by the drain the first time it is + tried (see **Fixed**). - **`dpp-core` 0.23.0: an EV, LMT or industrial battery publishes only with its placing date, and from 18 August 2027 with its due diligence declaration.** @@ -52,6 +57,14 @@ under the pre-1.0 conventions in [VERSIONING.md](docs/governance/VERSIONING.md): ### Fixed +- **A registration the adapter refuses before sending is rejected, not retried + for ever.** The registration drain read every error as transient, so a payload + the adapter's own checks refused backed off and was tried again with no end. + It can never pass: the payload is fixed when the passport is published. Such a + row is now marked rejected, with the reason, as a corrupt payload already was. + Only the adapter's checks report a `Validation` error; a registry that cannot be + reached, or that answers with a failure, is still retried. (#562) + - **A transfer notification is sent under a stable key, and only an answer that says the registry took it closes it.** `dpp-core` 0.23.0's registry port answers a transfer notification with a `TransferReport`, whether the registry diff --git a/crates/dpp-node/src/infra/registry_drain.rs b/crates/dpp-node/src/infra/registry_drain.rs index 6fc42301..4e85f464 100644 --- a/crates/dpp-node/src/infra/registry_drain.rs +++ b/crates/dpp-node/src/infra/registry_drain.rs @@ -38,7 +38,8 @@ pub struct DrainStats { pub rejected: u32, /// Rows that failed transiently and were backed off for retry. pub retried: u32, - /// Rows dropped from draining because their payload was missing/corrupt. + /// Rows dropped from draining because their payload was missing, corrupt, or + /// refused by the adapter's own checks before it was sent. pub skipped: u32, } @@ -181,6 +182,17 @@ async fn apply_acknowledgement( .await; stats.retried += 1; } + // The adapter's own checks refused the payload before sending it. The + // payload was fixed when the passport was published, so sending it again + // cannot change that answer: it is rejected with the reason, as a corrupt + // payload is. Every failure to reach or hear from the registry arrives as + // another variant and is retried below. + Err(dpp_domain::DppError::Validation(e)) => { + let _ = outbox + .mark_rejected(pid, format!("refused before sending: {e}")) + .await; + stats.skipped += 1; + } Err(e) => { // Transient/unreachable — back off and retry. The row stays. let _ = outbox.mark_attempt_failed(pid, e.to_string()).await; diff --git a/crates/dpp-node/tests/registry_outbox.rs b/crates/dpp-node/tests/registry_outbox.rs index 735f3066..d4efe75d 100644 --- a/crates/dpp-node/tests/registry_outbox.rs +++ b/crates/dpp-node/tests/registry_outbox.rs @@ -175,6 +175,8 @@ enum Outcome { Accepted, /// A replayed submission: the registry holds it and gave no handle for it. Held, + /// The adapter's own checks refused the payload before anything was sent. + Invalid, /// The registry could not be reached. Transient, /// Poll: the registry ruled and generated a registration identifier. @@ -216,6 +218,11 @@ impl RegistrySyncPort for MockPort { correlation_id: CORRELATION_ID.into(), }), Outcome::Held => Ok(SubmissionAcknowledgement::AlreadyHeld), + Outcome::Invalid => Err(DppError::Validation( + "EU registry payload failed validation: the operator identifier scheme 'vat' \ + has no EN 18219 clause 6 route in this build" + .into(), + )), Outcome::Transient => Err(DppError::Internal("EU registry transient failure".into())), other => panic!("not an answer to a submission: {}", other as u8), } @@ -406,6 +413,41 @@ async fn drain_backs_off_on_transient_and_marks_terminal_rejection() { ); } +/// 🚨 A payload the adapter refuses before sending is refused for good. It was +/// fixed when the passport was published, so sending it again cannot change the +/// answer, and retrying it would leave the row backing off for ever with nobody +/// told why. It is rejected with the reason, and never offered to the port again. +#[tokio::test(flavor = "multi_thread")] +async fn a_payload_the_adapter_refuses_is_rejected_not_retried() { + let _c = start_pg().await; + let dal = _c.dal.clone(); + let outbox: Arc = Arc::new(PgRegistrySyncRepo::new(dal.clone())); + + let id = create_and_publish(&dal, &outbox).await; + let (port, calls) = mock(Outcome::Invalid); + let stats = drain_once(&outbox, &port, None, 50).await; + assert_eq!(stats.skipped, 1, "{stats:?}"); + assert_eq!(stats.retried, 0, "a refusal is not a transient failure"); + + let row = outbox.pending_for(id).await.unwrap().unwrap(); + assert_eq!(row.status, RegistrySyncStatus::Rejected); + assert!( + row.message + .as_deref() + .is_some_and(|m| m.contains("clause 6")), + "the reason reaches the row for a human: {:?}", + row.message + ); + + make_due(&dal, id).await; + drain_once(&outbox, &port, None, 50).await; + assert_eq!( + calls.load(Ordering::SeqCst), + 1, + "never offered to the port again" + ); +} + // ─── 0024 heals rows the old write path already clobbered ───────────────────── /// Bring a fresh database up to the pre-0024 schema by applying every migration diff --git a/crates/dpp-node/tests/smoke.rs b/crates/dpp-node/tests/smoke.rs index 1518c1c2..b0aa7ae1 100644 --- a/crates/dpp-node/tests/smoke.rs +++ b/crates/dpp-node/tests/smoke.rs @@ -637,7 +637,7 @@ async fn route_inventory_matches_assembled_router() { let operator_id_resp = client .post(format!("{base}/vault/api/v1/operator-identifiers")) .bearer_auth(&token) - .json(&serde_json::json!({ "scheme": "vat", "value": "DE123456789", "isPrimary": true })) + .json(&serde_json::json!({ "scheme": "lei", "value": "529900T8BM49AURSDO55", "isPrimary": true })) .send() .await .expect("operator-identifier seed request failed"); @@ -922,7 +922,7 @@ async fn second_life_successor_verifies_against_pinned_parent() { let op_id_resp = client .post(format!("{base}/vault/api/v1/operator-identifiers")) .bearer_auth(&token) - .json(&serde_json::json!({ "scheme": "vat", "value": "DE123456789", "isPrimary": true })) + .json(&serde_json::json!({ "scheme": "lei", "value": "529900T8BM49AURSDO55", "isPrimary": true })) .send() .await .expect("operator-identifier seed request failed"); @@ -1243,7 +1243,7 @@ async fn the_quick_start_battery_sample_creates_and_publishes() { let operator_id = client .post(format!("{base}/vault/api/v1/operator-identifiers")) .bearer_auth(&token) - .json(&serde_json::json!({ "scheme": "vat", "value": "DE123456789", "isPrimary": true })) + .json(&serde_json::json!({ "scheme": "lei", "value": "529900T8BM49AURSDO55", "isPrimary": true })) .send() .await .expect("operator-identifier request failed"); @@ -2336,7 +2336,7 @@ async fn publish_layered_battery(base: &str, token: &str, client: &reqwest::Clie let resp = client .post(format!("{base}/vault/api/v1/operator-identifiers")) .bearer_auth(token) - .json(&serde_json::json!({ "scheme": "vat", "value": "DE123456789", "isPrimary": true })) + .json(&serde_json::json!({ "scheme": "lei", "value": "529900T8BM49AURSDO55", "isPrimary": true })) .send() .await .expect("operator-identifier seed request failed"); diff --git a/crates/dpp-vault/src/domain/service/publish.rs b/crates/dpp-vault/src/domain/service/publish.rs index 549ed324..f8b73e8f 100644 --- a/crates/dpp-vault/src/domain/service/publish.rs +++ b/crates/dpp-vault/src/domain/service/publish.rs @@ -6,7 +6,7 @@ use chrono::Utc; use dpp_common::{event, event_codes}; use dpp_digital_link::build_qr_url; use dpp_domain::{ - ComplianceError, ComplianceErrorKind, ComplianceResult, + ActorIdentifier, ActorPosition, ComplianceError, ComplianceErrorKind, ComplianceResult, error::DppError, passport::{Passport, PassportId}, ports::registry_sync::{ @@ -33,6 +33,10 @@ use super::{catalog, schema_registry}; mod reason { pub const INVALID_TRANSITION: &str = "invalid_transition"; pub const MISSING_REGISTRY_IDENTITY: &str = "missing_registry_identity"; + /// The operator or facility identifier is there, under a scheme with no + /// EN 18219 clause 6 route, so the registration this publish owes would be + /// refused. Distinct from `missing_registry_identity`, where it is absent. + pub const UNREGISTRABLE_IDENTITY: &str = "unregistrable_identity"; pub const SECTOR_DATA_INVALID: &str = "product_group_data_invalid"; pub const SCHEMA_INVALID: &str = "schema_invalid"; pub const COMPLIANCE_VIOLATIONS: &str = "compliance_violations"; @@ -69,6 +73,7 @@ use reason::{ MISSING_REGISTRY_IDENTITY as REASON_MISSING_REGISTRY_IDENTITY, SCHEMA_INVALID as REASON_SCHEMA_INVALID, SECTOR_DATA_INVALID as REASON_SECTOR_DATA_INVALID, SIGNING_FAILED as REASON_SIGNING_FAILED, + UNREGISTRABLE_IDENTITY as REASON_UNREGISTRABLE_IDENTITY, }; /// Count a publish rejection, then return its error unchanged. @@ -463,6 +468,9 @@ impl PassportService { // product group data mandatory at publish for every group — a // decision deferred above, not one to take as a side effect of how // registrations are built. + // + // An identifier the registration cannot carry is treated the same way, + // under its own reason: see `unregistrable_identity`. let registration = match &self.registry_outbox { None => None, Some(_) => { @@ -477,18 +485,23 @@ impl PassportService { .filter(|(_, value)| Some(value) == passport.operator_identifier.as_ref()) .map(|(scheme, _)| scheme.as_str()) .unwrap_or_default(); - match RegistrationRequest::from_published_passport( - &passport, - RegisteringOperator { - legal_name: &self.operator.legal_name, - country: &self.operator.country, - identifier_scheme, - }, - // Item level: the granularity the battery product group is - // defined at, and the only one the registry accepts today. - // Set per product group once further delegated acts land. - RegistrationGranularity::Item, - ) { + let built = match unregistrable_identity(identifier_scheme, &passport) { + Some(why) => Err((REASON_UNREGISTRABLE_IDENTITY, why.into())), + None => RegistrationRequest::from_published_passport( + &passport, + RegisteringOperator { + legal_name: &self.operator.legal_name, + country: &self.operator.country, + identifier_scheme, + }, + // Item level: the granularity the battery product group is + // defined at, and the only one the registry accepts today. + // Set per product group once further delegated acts land. + RegistrationGranularity::Item, + ) + .map_err(|missing| (REASON_MISSING_REGISTRY_IDENTITY, missing)), + }; + match built { Ok(mut reg_req) => { declare_backup( &mut reg_req, @@ -499,17 +512,14 @@ impl PassportService { .map_err(|e| DppError::Serialisation(e.to_string()))?; Some(payload) } - Err(missing) + Err((reason, missing)) if super::passport_obligation_live( passport.product_group.catalog_key(), ) => { - return Err(reject( - REASON_MISSING_REGISTRY_IDENTITY, - DppError::Validation(missing), - )); + return Err(reject(reason, DppError::Validation(missing))); } - Err(missing) => { + Err((_, missing)) => { tracing::warn!( passport_id = %id, missing = %missing, @@ -654,6 +664,42 @@ fn validate_schema_for_publish(product_group_data: &ProductGroupData) -> Result< .map_err(DppError::from) } +/// Why the registration this passport owes would be refused for its operator or +/// facility identifier, if it would. +/// +/// dpp-core accepts an identifier on a registration only under a scheme with an +/// EN 18219 clause 6 route, and the registry adapter refuses one without before +/// anything is sent. A queued registration is fixed, so that refusal would come +/// on every attempt and reach nobody who could act on it. This asks the same +/// question here, through [`ActorIdentifier::from_legacy`], core's one reading of +/// a scheme and value, so the two cannot answer differently. +/// +/// `None` when an identifier is absent, which building the registration reports +/// under its own reason, and when the operator's scheme is empty because the +/// stamped value is no longer the primary one, which this cannot read. +fn unregistrable_identity(operator_scheme: &str, passport: &Passport) -> Option { + if let Some(value) = passport.operator_identifier.as_deref() + && !operator_scheme.is_empty() + && let Err(e) = + ActorIdentifier::from_legacy(operator_scheme, value, ActorPosition::Operator) + { + return Some(format!( + "cannot publish: the operator identifier cannot be registered with the EU \ + registry: {e}. A registration carries an LEI or a D-U-N-S number; make one the \ + primary operator identifier (`odal operator-id`)." + )); + } + if let Some(facility) = &passport.facility + && let Err(e) = facility.actor_identifier() + { + return Some(format!( + "cannot publish: the facility identifier cannot be registered with the EU \ + registry: {e}. A registration carries a GLN, an LEI or a D-U-N-S number." + )); + } + None +} + /// The independently-hosted back-up URL declared to the EU registry. /// /// The path is [`snapshot_json_key`], which is the same definition the snapshot diff --git a/crates/dpp-vault/tests/facility_operator_stamping.rs b/crates/dpp-vault/tests/facility_operator_stamping.rs index 121372ba..4ab89a6b 100644 --- a/crates/dpp-vault/tests/facility_operator_stamping.rs +++ b/crates/dpp-vault/tests/facility_operator_stamping.rs @@ -134,3 +134,156 @@ async fn duplicate_facility_returns_422_not_500() { let dup = client.post_json("/api/v1/facilities", body).await; assert_eq!(dup.status(), 422, "a duplicate facility must return 422"); } + +/// 🚨 A VAT number is stamped as the operator identifier, and is refused as the +/// identifier of a registration. dpp-core accepts only a scheme with an EN 18219 +/// clause 6 route there (an LEI, a D-U-N-S number or a GLN), and a queued +/// registration is fixed, so one built from a VAT number would be refused on +/// every attempt. Publish says so instead, where the operator can act on it, +/// and queues nothing. +/// +/// A product group with no live registration duty publishes under the same +/// identifier, with no registration, as it does when the identity is missing. +#[tokio::test(flavor = "multi_thread")] +async fn a_vat_primary_identifier_is_refused_at_publish_where_a_registration_is_owed() { + use dpp_dal::pg::PgRegistrySyncRepo; + use dpp_domain::passport::PassportId; + use dpp_types::RegistrySyncOutbox; + + let pg = start_postgres().await; + seed_operator_config(&pg.dal).await; + let vault_url = start_vault(pg.dal.clone()).await; + let token = make_jwt("00000000-0000-0000-0000-000000000001"); + let client = TestClient::new(&vault_url, &token); + let outbox = PgRegistrySyncRepo::new(pg.dal.clone()); + + let resp = client + .post_json( + "/api/v1/facilities", + serde_json::json!({ + "name": "Default Plant", + "identifierScheme": "gln", + "identifierValue": "4012345000009", + "country": "DE", + "isDefault": true + }), + ) + .await; + assert_eq!(resp.status(), 201); + let resp = client + .post_json( + "/api/v1/operator-identifiers", + serde_json::json!({ "scheme": "vat", "value": "DE123456789", "isPrimary": true }), + ) + .await; + assert_eq!(resp.status(), 201); + + // A battery owes a registration. + let resp = client + .post_json( + "/api/v1/dpp", + serde_json::json!({ + "productName": "Portable Cell", + "manufacturer": { "name": "Example Batteries GmbH", "address": "Berlin, DE" }, + "productGroupData": { + "productGroup": "battery", + "productIdentifier": { "scheme": "gs1", "gtin": "09506000134352" }, + "batteryChemistry": "LFP", + "batteryType": "portable", + "nominalVoltageV": 3.7, + "nominalCapacityAh": 2.5, + "co2ePerUnitKg": 1.8 + } + }), + ) + .await; + assert_eq!(resp.status(), 201); + let battery = resp.json::().await.unwrap()["id"] + .as_str() + .expect("created passport has an id") + .to_owned(); + + let resp = client + .post_json( + &format!("/api/v1/dpp/{battery}/publish"), + serde_json::json!({}), + ) + .await; + assert_eq!( + resp.status(), + 422, + "a registration it cannot carry is refused" + ); + let body = resp.text().await.unwrap_or_default(); + assert!( + body.contains("operator identifier cannot be registered") + && body.contains("'vat'") + && body.contains("LEI"), + "the refusal names the scheme and the fix: {body}" + ); + let after: serde_json::Value = client + .get(&format!("/api/v1/dpp/{battery}")) + .await + .json() + .await + .unwrap(); + assert_eq!(after["status"], "draft", "a refused publish leaves a draft"); + let battery_id = PassportId(uuid::Uuid::parse_str(&battery).unwrap()); + assert!( + outbox.pending_for(battery_id).await.unwrap().is_none(), + "nothing is queued for a registration the adapter would refuse" + ); + + // A textile passport owes none yet, so it publishes under the same identifier. + let resp = client + .post_json( + "/api/v1/dpp", + serde_json::json!({ + "productName": "Organic Cotton Tee", + "manufacturer": { "name": "Example Textiles SpA", "address": "Milano, IT" }, + "materials": [{"name": "Cotton", "weightKg": 0.22}], + "productGroupData": { + "productGroup": "textile", + "productIdentifier": { "scheme": "gs1", "gtin": "09506000134369" }, + "carbonFootprintKgCo2e": 4.2, + "careInstructions": "Machine wash cold, line dry", + "chemicalComplianceStandard": "OEKO-TEX 100", + "countryOfOrigin": "IT", + "countryOfRawMaterialOrigin": "IN", + "disassemblyInstructions": "Remove label and neck tape before recycling", + "endOfLifeInstructions": "Return to a textile collection point", + "expectedWashCycles": 50, + "fibreComposition": [{"fibre": "cotton", "pct": 100}], + "microplasticSheddingMgPerWash": 12, + "productWeightGrams": 220, + "recyclabilityClass": "B", + "recycledContentPct": 30, + "sparePartsAvailable": false, + "waterUseLitres": 1800 + } + }), + ) + .await; + assert_eq!(resp.status(), 201, "a textile passport is created"); + let textile = resp.json::().await.unwrap()["id"] + .as_str() + .expect("created passport has an id") + .to_owned(); + let resp = client + .post_json( + &format!("/api/v1/dpp/{textile}/publish"), + serde_json::json!({}), + ) + .await; + assert_eq!( + resp.status(), + 200, + "no registration is owed, so none is refused: {}", + resp.text().await.unwrap_or_default() + ); + let textile_id = PassportId(uuid::Uuid::parse_str(&textile).unwrap()); + assert!( + outbox.pending_for(textile_id).await.unwrap().is_none(), + "and none is queued" + ); +} diff --git a/crates/dpp-vault/tests/helpers/mod.rs b/crates/dpp-vault/tests/helpers/mod.rs index 52c7ffda..97888cd6 100644 --- a/crates/dpp-vault/tests/helpers/mod.rs +++ b/crates/dpp-vault/tests/helpers/mod.rs @@ -578,8 +578,11 @@ pub async fn seed_complete_operator(dal: &PgDal) { STANDALONE_OPERATOR_ID, OperatorIdentifier { id: uuid::Uuid::now_v7(), - scheme: "vat".into(), - value: "DE123456789".into(), + // An LEI: a registration carries only a scheme with an EN 18219 + // clause 6 route, so a VAT number here would refuse every publish + // that owes one. + scheme: "lei".into(), + value: "529900T8BM49AURSDO55".into(), label: None, is_primary: true, created_at: chrono::Utc::now(), diff --git a/crates/dpp-vault/tests/registry_identity_governance.rs b/crates/dpp-vault/tests/registry_identity_governance.rs index 5dc9fb9d..7c3d4603 100644 --- a/crates/dpp-vault/tests/registry_identity_governance.rs +++ b/crates/dpp-vault/tests/registry_identity_governance.rs @@ -39,10 +39,12 @@ async fn add_default_facility(client: &TestClient) { ) .await; assert_eq!(resp.status(), 201); + // An LEI: a battery owes a registration, which carries only a scheme with an + // EN 18219 clause 6 route. let resp = client .post_json( "/api/v1/operator-identifiers", - json!({ "scheme": "vat", "value": "DE123456789", "isPrimary": true }), + json!({ "scheme": "lei", "value": "529900T8BM49AURSDO55", "isPrimary": true }), ) .await; assert_eq!(resp.status(), 201); @@ -92,7 +94,7 @@ async fn publish_is_blocked_without_annexiii_identity_then_backfills() { .await .unwrap(); assert_eq!(dpp["facility"]["value"], "4012345000009"); - assert_eq!(dpp["operatorIdentifier"], "DE123456789"); + assert_eq!(dpp["operatorIdentifier"], "529900T8BM49AURSDO55"); } #[tokio::test(flavor = "multi_thread")] diff --git a/crates/dpp-vault/tests/registry_status_route.rs b/crates/dpp-vault/tests/registry_status_route.rs index b1146e83..b60337e4 100644 --- a/crates/dpp-vault/tests/registry_status_route.rs +++ b/crates/dpp-vault/tests/registry_status_route.rs @@ -43,7 +43,7 @@ async fn a_published_passport_reports_its_registration() { let resp = client .post_json( "/api/v1/operator-identifiers", - serde_json::json!({ "scheme": "vat", "value": "DE123456789", "isPrimary": true }), + serde_json::json!({ "scheme": "lei", "value": "529900T8BM49AURSDO55", "isPrimary": true }), ) .await; assert_eq!( diff --git a/docs/ops/METRICS-PLAN.md b/docs/ops/METRICS-PLAN.md index 7f1063d8..c9ef3d31 100644 --- a/docs/ops/METRICS-PLAN.md +++ b/docs/ops/METRICS-PLAN.md @@ -64,7 +64,7 @@ path template (for example `/vault/api/v1/dpp/{dppId}`), never the requested URL | Metric | Type | Labels | |---|---|---| | `passport_publish_total` | counter | `outcome` = `success`, `error` | -| `passport_publish_rejected_total` | counter | `reason` = `invalid_transition`, `missing_registry_identity`, `product_group_data_invalid`, `schema_invalid`, `mandatory_content`, `compliance_violations`, `compliance_unavailable`, `carrier_invalid`, `carrier_taken`, `signing_failed` | +| `passport_publish_rejected_total` | counter | `reason` = `invalid_transition`, `missing_registry_identity`, `unregistrable_identity`, `product_group_data_invalid`, `schema_invalid`, `mandatory_content`, `compliance_violations`, `compliance_unavailable`, `carrier_invalid`, `carrier_taken`, `signing_failed` | | `publish_schema_unresolved_total` | counter | `productGroup` | | `signing_failures_total` | counter | | | `auth_failures_total` | counter | `reason` = `missing`, `invalid`, `suspended` | From fb7b0b802ac680c80a8d371a592060f58e2923e2 Mon Sep 17 00:00:00 2001 From: chillin_since_26 Date: Tue, 3 Nov 2026 21:12:02 +0100 Subject: [PATCH 04/12] test(node): capture the v0.15.0 upgrade fixture --- .../tests/fixtures/upgrade/v0.15.0/dump.sql | 1785 +++++++++++++++++ .../fixtures/upgrade/v0.15.0/public.json | 8 + 2 files changed, 1793 insertions(+) create mode 100644 crates/dpp-node/tests/fixtures/upgrade/v0.15.0/dump.sql create mode 100644 crates/dpp-node/tests/fixtures/upgrade/v0.15.0/public.json diff --git a/crates/dpp-node/tests/fixtures/upgrade/v0.15.0/dump.sql b/crates/dpp-node/tests/fixtures/upgrade/v0.15.0/dump.sql new file mode 100644 index 00000000..3ec2fbc0 --- /dev/null +++ b/crates/dpp-node/tests/fixtures/upgrade/v0.15.0/dump.sql @@ -0,0 +1,1785 @@ +-- dpp-engine v0.15.0 (6733e9f8), captured by +-- scripts/capture-upgrade-fixture.sh on 2026-11-03. Do not edit: re-capture. +-- Written by dpp-node sha256:fdc108d3bbf4878eba5f260574079ba5c94a6850645565d86aaa6eb449c98283 +-- +-- PostgreSQL database dump +-- + + +-- Dumped from database version 17.11 (Debian 17.11-1.pgdg13+2) +-- Dumped by pg_dump version 17.11 (Debian 17.11-1.pgdg13+2) + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET transaction_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Name: identity; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA identity; + + +-- +-- Name: odal; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA odal; + + +-- +-- Name: pg_trgm; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA public; + + +-- +-- Name: EXTENSION pg_trgm; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_trgm IS 'text similarity measurement and index searching based on trigrams'; + + +-- +-- Name: audit_append_only(); Type: FUNCTION; Schema: odal; Owner: - +-- + +CREATE FUNCTION odal.audit_append_only() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + RAISE EXCEPTION 'ODAL_AUDIT: audit entries are append-only'; +END $$; + + +-- +-- Name: evidence_append_only(); Type: FUNCTION; Schema: odal; Owner: - +-- + +CREATE FUNCTION odal.evidence_append_only() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + RAISE EXCEPTION 'ODAL_EVIDENCE: evidence dossiers are append-only'; +END $$; + + +-- +-- Name: passport_retention_guard(); Type: FUNCTION; Schema: odal; Owner: - +-- + +CREATE FUNCTION odal.passport_retention_guard() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + mutable_keys TEXT[] := ARRAY['status','jwsSignature','publicJwsSignature', + 'disclosureSignatures','seal', + 'qrCodeUrl','publishedAt','retentionLocked', + 'updatedAt','lintResult']; +BEGIN + IF TG_OP = 'DELETE' THEN + RAISE EXCEPTION 'ODAL_RETENTION: passports are never deleted (ESPR retention)'; + END IF; + IF OLD.retention_locked + AND (OLD.doc - mutable_keys) IS DISTINCT FROM (NEW.doc - mutable_keys) THEN + RAISE EXCEPTION 'ODAL_RETENTION: retention-locked passport content is immutable'; + END IF; + NEW.updated_at := now(); + RETURN NEW; +END $$; + + +-- +-- Name: passport_version_append_only(); Type: FUNCTION; Schema: odal; Owner: - +-- + +CREATE FUNCTION odal.passport_version_append_only() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + RAISE EXCEPTION 'ODAL_VERSION: archived passport versions are append-only'; +END $$; + + +-- +-- Name: personal_data_record_guard(); Type: FUNCTION; Schema: odal; Owner: - +-- + +CREATE FUNCTION odal.personal_data_record_guard() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF OLD.erased_at IS NOT NULL THEN + RAISE EXCEPTION 'ODAL_PERSONAL_DATA: an erased record is a tombstone and cannot change'; + END IF; + IF NEW.content IS NOT NULL + OR NEW.id <> OLD.id + OR NEW.passport_id <> OLD.passport_id + OR NEW.field <> OLD.field + OR NEW.stored_at <> OLD.stored_at THEN + RAISE EXCEPTION 'ODAL_PERSONAL_DATA: a record can only be erased, not rewritten'; + END IF; + RETURN NEW; +END; +$$; + + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: did_document; Type: TABLE; Schema: identity; Owner: - +-- + +CREATE TABLE identity.did_document ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + operator_id text NOT NULL, + did text NOT NULL, + document jsonb NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: key_pair; Type: TABLE; Schema: identity; Owner: - +-- + +CREATE TABLE identity.key_pair ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + operator_id text NOT NULL, + key_id text NOT NULL, + public_key_jwk jsonb NOT NULL, + private_key_enc text NOT NULL, + algorithm text DEFAULT 'EdDSA'::text NOT NULL, + is_active boolean DEFAULT true NOT NULL, + rotated_at timestamp with time zone, + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT key_pair_algorithm_check CHECK ((algorithm = 'EdDSA'::text)) +); + + +-- +-- Name: api_key; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.api_key ( + id uuid NOT NULL, + name text NOT NULL, + key_hash text NOT NULL, + key_prefix text NOT NULL, + scopes text[], + is_active boolean DEFAULT true NOT NULL, + created_by uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL, + last_used_at timestamp with time zone, + expires_at timestamp with time zone +); + + +-- +-- Name: evidence_dossier; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.evidence_dossier ( + id uuid NOT NULL, + passport_id uuid NOT NULL, + actor text NOT NULL, + doc_hash text NOT NULL, + doc jsonb NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: facility; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.facility ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + operator_id text NOT NULL, + name text NOT NULL, + identifier_scheme text NOT NULL, + identifier_value text NOT NULL, + country character(2) NOT NULL, + address text, + is_default boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + retired_at timestamp with time zone +); + + +-- +-- Name: idempotency_key; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.idempotency_key ( + principal text NOT NULL, + method text NOT NULL, + path text NOT NULL, + idem_key text NOT NULL, + fingerprint text NOT NULL, + state text DEFAULT 'in_flight'::text NOT NULL, + response_status smallint, + response_body bytea, + content_type text, + lease_expires_at timestamp with time zone NOT NULL, + expires_at timestamp with time zone NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + completed_at timestamp with time zone, + claim_token uuid DEFAULT gen_random_uuid() NOT NULL, + CONSTRAINT completed_rows_can_be_replayed CHECK (((state <> 'completed'::text) OR ((response_status IS NOT NULL) AND (completed_at IS NOT NULL)))), + CONSTRAINT idempotency_key_fingerprint_check CHECK ((fingerprint ~ '^[0-9a-f]{64}$'::text)), + CONSTRAINT idempotency_key_response_status_check CHECK (((response_status >= 100) AND (response_status <= 599))), + CONSTRAINT idempotency_key_state_check CHECK ((state = ANY (ARRAY['in_flight'::text, 'completed'::text]))) +); + + +-- +-- Name: import_job; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.import_job ( + id uuid NOT NULL, + status text DEFAULT 'queued'::text NOT NULL, + fail_reason text, + total_rows integer DEFAULT 0 NOT NULL, + processed integer DEFAULT 0 NOT NULL, + result jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + report jsonb, + parent_job_id uuid, + CONSTRAINT import_job_status_check CHECK ((status = ANY (ARRAY['queued'::text, 'processing'::text, 'completed'::text, 'failed'::text]))), + CONSTRAINT import_job_total_rows_check CHECK ((total_rows >= 0)) +); + + +-- +-- Name: operator_config; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.operator_config ( + operator_id text NOT NULL, + legal_name text NOT NULL, + trade_name text, + address text NOT NULL, + country character(2) NOT NULL, + contact_email text NOT NULL, + did_web_url text, + product_categories jsonb, + brand_primary text, + brand_secondary text, + brand_logo_url text, + custom_domain text, + data_residency text DEFAULT 'EU'::text NOT NULL, + retention_policy_days integer DEFAULT 3650 NOT NULL, + feature_flags jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + registry_verified_at timestamp with time zone, + CONSTRAINT operator_config_data_residency_check CHECK ((data_residency = ANY (ARRAY['EU'::text, 'GLOBAL'::text]))), + CONSTRAINT operator_config_retention_policy_days_check CHECK ((retention_policy_days >= 365)) +); + + +-- +-- Name: COLUMN operator_config.registry_verified_at; Type: COMMENT; Schema: odal; Owner: - +-- + +COMMENT ON COLUMN odal.operator_config.registry_verified_at IS 'When this operator completed EU registry identity verification. Verified status expires when the eID means used expire, and at most three years from this date — whichever comes first. NULL = never verified.'; + + +-- +-- Name: operator_identifier; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.operator_identifier ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + operator_id text NOT NULL, + scheme text NOT NULL, + value text NOT NULL, + label text, + is_primary boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + retired_at timestamp with time zone +); + + +-- +-- Name: passport; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.passport ( + id uuid NOT NULL, + product_group text NOT NULL, + status text DEFAULT 'draft'::text NOT NULL, + retention_locked boolean DEFAULT false NOT NULL, + schema_version text NOT NULL, + granularity text, + serial_number text, + version integer DEFAULT 1 NOT NULL, + supersedes_id uuid, + ruleset_version text, + assessed_at timestamp with time zone, + retention_until timestamp with time zone, + product_id uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + published_at timestamp with time zone, + doc jsonb NOT NULL, + CONSTRAINT passport_granularity_check CHECK ((granularity = ANY (ARRAY['model'::text, 'batch'::text, 'item'::text]))), + CONSTRAINT passport_status_check CHECK ((status = ANY (ARRAY['draft'::text, 'active'::text, 'suspended'::text, 'retired'::text, 'superseded'::text, 'deactivated'::text]))) +); + + +-- +-- Name: passport_audit; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.passport_audit ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + passport_id text NOT NULL, + actor text NOT NULL, + actor_user_id uuid, + action text NOT NULL, + previous_status text, + new_status text, + request_id text, + changes jsonb, + metadata jsonb, + ts timestamp with time zone DEFAULT now() NOT NULL, + prev_hash text, + entry_hash text, + CONSTRAINT passport_audit_action_check CHECK ((action = ANY (ARRAY['created'::text, 'updated'::text, 'published'::text, 'suspended'::text, 'archived'::text, 'retired'::text, 'deactivated'::text, 'transferred'::text, 'credentialed_read'::text, 'superseded'::text]))) +); + + +-- +-- Name: passport_transfer; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.passport_transfer ( + passport_id uuid NOT NULL, + chain jsonb NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: passport_version; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.passport_version ( + id uuid NOT NULL, + passport_id uuid NOT NULL, + doc jsonb NOT NULL, + superseded_at timestamp with time zone NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: personal_data_record; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.personal_data_record ( + id uuid NOT NULL, + passport_id uuid NOT NULL, + field text NOT NULL, + content jsonb, + stored_at timestamp with time zone DEFAULT now() NOT NULL, + erased_at timestamp with time zone, + CONSTRAINT personal_data_record_field_check CHECK ((field <> ''::text)), + CONSTRAINT personal_data_record_one_state CHECK ((((content IS NOT NULL) AND (erased_at IS NULL)) OR ((content IS NULL) AND (erased_at IS NOT NULL)))) +); + + +-- +-- Name: qr_render; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.qr_render ( + dpp_id uuid NOT NULL, + day date NOT NULL, + count bigint DEFAULT 0 NOT NULL +); + + +-- +-- Name: registry_identity_audit; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.registry_identity_audit ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + operator_id text NOT NULL, + entity_type text NOT NULL, + entity_id uuid NOT NULL, + action text NOT NULL, + actor text NOT NULL, + snapshot jsonb, + ts timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT registry_identity_audit_action_check CHECK ((action = ANY (ARRAY['added'::text, 'retired'::text, 'set_default'::text, 'set_primary'::text]))), + CONSTRAINT registry_identity_audit_entity_type_check CHECK ((entity_type = ANY (ARRAY['facility'::text, 'operator_identifier'::text]))) +); + + +-- +-- Name: registry_sync; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.registry_sync ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + passport_id uuid NOT NULL, + registry_id text, + status text DEFAULT 'pending'::text NOT NULL, + payload jsonb, + message text, + attempts integer DEFAULT 0 NOT NULL, + next_attempt_at timestamp with time zone DEFAULT now() NOT NULL, + last_attempt_at timestamp with time zone, + registered_at timestamp with time zone, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + status_intent text, + submitted_at timestamp with time zone, + CONSTRAINT registry_sync_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'submitted'::text, 'registered'::text, 'rejected'::text, 'suspended'::text, 'deactivated'::text]))), + CONSTRAINT registry_sync_status_intent_check CHECK ((status_intent = ANY (ARRAY['suspended'::text, 'deactivated'::text]))) +); + + +-- +-- Name: registry_transfer; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.registry_transfer ( + transfer_id uuid NOT NULL, + passport_id uuid NOT NULL, + status text DEFAULT 'pending'::text NOT NULL, + payload jsonb NOT NULL, + registry_id text, + message text, + attempts integer DEFAULT 0 NOT NULL, + next_attempt_at timestamp with time zone DEFAULT now() NOT NULL, + last_attempt_at timestamp with time zone, + notified_at timestamp with time zone, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT registry_transfer_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'notified'::text, 'rejected'::text]))) +); + + +-- +-- Name: scan_telemetry; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.scan_telemetry ( + dpp_id uuid NOT NULL, + day date NOT NULL, + variant text NOT NULL, + count bigint DEFAULT 0 NOT NULL, + CONSTRAINT scan_telemetry_variant_check CHECK ((variant = ANY (ARRAY['html'::text, 'json'::text]))) +); + + +-- +-- Name: seal_audit_state; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.seal_audit_state ( + id smallint DEFAULT 1 NOT NULL, + progress jsonb, + report jsonb, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT seal_audit_state_id_check CHECK ((id = 1)) +); + + +-- +-- Name: seal_outbox; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.seal_outbox ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + passport_id uuid NOT NULL, + payload_hash text NOT NULL, + status text DEFAULT 'pending'::text NOT NULL, + attempts integer DEFAULT 0 NOT NULL, + next_attempt_at timestamp with time zone DEFAULT now() NOT NULL, + last_attempt_at timestamp with time zone, + message text, + sealed_at timestamp with time zone, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT seal_outbox_payload_hash_check CHECK ((payload_hash ~ '^[0-9a-f]{64}$'::text)), + CONSTRAINT seal_outbox_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'sealed'::text, 'exhausted'::text]))) +); + + +-- +-- Name: snapshot_outbox; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.snapshot_outbox ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + passport_id uuid NOT NULL, + status text DEFAULT 'pending'::text NOT NULL, + attempts integer DEFAULT 0 NOT NULL, + next_attempt_at timestamp with time zone DEFAULT now() NOT NULL, + last_attempt_at timestamp with time zone, + message text, + reconciled_at timestamp with time zone, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT snapshot_outbox_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'reconciled'::text, 'exhausted'::text]))) +); + + +-- +-- Name: trusted_list_cache; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.trusted_list_cache ( + territory text NOT NULL, + content jsonb, + signed_by text, + verified_at timestamp with time zone, + unavailable_reason text, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT trusted_list_cache_one_state CHECK ((((content IS NOT NULL) AND (signed_by IS NOT NULL) AND (verified_at IS NOT NULL) AND (unavailable_reason IS NULL)) OR ((content IS NULL) AND (signed_by IS NULL) AND (verified_at IS NULL) AND (unavailable_reason IS NOT NULL)))) +); + + +-- +-- Name: unsold_goods_report; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.unsold_goods_report ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + kind text DEFAULT 'textile_unsold'::text NOT NULL, + operator_id text NOT NULL, + operator_name text, + reporting_period text NOT NULL, + volume_kg double precision NOT NULL, + product_category text NOT NULL, + reason text NOT NULL, + destination text NOT NULL, + destruction_justification text, + country_of_disposal character(2) NOT NULL, + doc jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + unit_count bigint, + CONSTRAINT unsold_goods_report_destination_check CHECK ((destination = ANY (ARRAY['donation'::text, 'recycling'::text, 'repurposing'::text, 'supplierReturn'::text, 'exemptDestruction'::text]))), + CONSTRAINT unsold_goods_report_product_category_check CHECK ((product_category = ANY (ARRAY['apparel'::text, 'footwear'::text, 'homeTextile'::text, 'accessories'::text, 'other'::text]))), + CONSTRAINT unsold_goods_report_reason_check CHECK ((reason = ANY (ARRAY['endOfSeason'::text, 'qualityDefect'::text, 'packagingDefect'::text, 'overProduction'::text, 'customerReturn'::text, 'other'::text]))), + CONSTRAINT unsold_goods_report_unit_count_check CHECK (((unit_count IS NULL) OR (unit_count >= 0))) +); + + +-- +-- Name: webhook_delivery; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.webhook_delivery ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + subscription_id uuid NOT NULL, + event_type text NOT NULL, + body text NOT NULL, + status text DEFAULT 'pending'::text NOT NULL, + attempts integer DEFAULT 0 NOT NULL, + next_attempt_at timestamp with time zone DEFAULT now() NOT NULL, + last_attempt_at timestamp with time zone, + message text, + delivered_at timestamp with time zone, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT webhook_delivery_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'delivered'::text, 'exhausted'::text]))) +); + + +-- +-- Name: webhook_subscription; Type: TABLE; Schema: odal; Owner: - +-- + +CREATE TABLE odal.webhook_subscription ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + url text NOT NULL, + secret text NOT NULL, + events text[] NOT NULL, + active boolean DEFAULT true NOT NULL, + description text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: _sqlx_migrations; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public._sqlx_migrations ( + version bigint NOT NULL, + description text NOT NULL, + installed_on timestamp with time zone DEFAULT now() NOT NULL, + success boolean NOT NULL, + checksum bytea NOT NULL, + execution_time bigint NOT NULL +); + + +-- +-- Data for Name: did_document; Type: TABLE DATA; Schema: identity; Owner: - +-- + + + +-- +-- Data for Name: key_pair; Type: TABLE DATA; Schema: identity; Owner: - +-- + + + +-- +-- Data for Name: api_key; Type: TABLE DATA; Schema: odal; Owner: - +-- + +INSERT INTO odal.api_key VALUES ('01a1a307-b42b-75a7-8b19-fc97f10470ad', 'cli-bootstrap', 'eb88080891e7faa8ed37ff7a0344c778f93377e59724def4be7a18c3af2dfbaa', 'odal_sk_pihf', '{admin}', true, NULL, '2026-11-03 20:09:13.515979+00', '2026-11-03 20:09:13.576208+00', NULL); + + +-- +-- Data for Name: evidence_dossier; Type: TABLE DATA; Schema: odal; Owner: - +-- + + + +-- +-- Data for Name: facility; Type: TABLE DATA; Schema: odal; Owner: - +-- + +INSERT INTO odal.facility VALUES ('01a1a307-b491-71ca-99f2-400f95e6a462', 'self_hosted', 'Demo Plant', 'gln', '4012345000009', 'DE', 'Werkstrasse 2, 10115 Berlin', true, '2026-11-03 20:09:13.617033+00', '2026-11-03 20:09:13.62954+00', NULL); +INSERT INTO odal.facility VALUES ('01a1a307-b9f0-7352-8855-da4204d950cf', 'self_hosted', 'Second Plant', 'gln', '4012345000016', 'DE', NULL, false, '2026-11-03 20:09:14.992137+00', '2026-11-03 20:09:14.992797+00', NULL); + + +-- +-- Data for Name: idempotency_key; Type: TABLE DATA; Schema: odal; Owner: - +-- + +INSERT INTO odal.idempotency_key VALUES ('api-key', 'POST', '/vault/api/v1/facilities', 'upgrade-fixture-facility', 'dfd19d9725d8bb9556d4b0a5efa38570118f2b1fc07c3eb2d9c0339ab56e09df', 'completed', 201, '\x7b226964223a2230316131613330372d623966302d373335322d383835352d646134323034643935306366222c226e616d65223a225365636f6e6420506c616e74222c226964656e746966696572536368656d65223a22676c6e222c226964656e74696669657256616c7565223a2234303132333435303030303136222c22636f756e747279223a224445222c22697344656661756c74223a66616c73652c22637265617465644174223a22323032362d31312d30335432303a30393a31342e3939323133375a227d', 'application/json', '2026-11-03 20:10:14.989852+00', '2026-11-04 20:09:14.989852+00', '2026-11-03 20:09:14.989852+00', '2026-11-03 20:09:15.000151+00', '01a1a307-b9ed-71df-b581-4e86443b9a6d'); + + +-- +-- Data for Name: import_job; Type: TABLE DATA; Schema: odal; Owner: - +-- + +INSERT INTO odal.import_job VALUES ('01a1a307-b8c0-7370-adb4-f4e7bccf22a2', 'completed', NULL, 5, 5, '{"errors": [], "created": [{"row": 1, "passportId": "01a1a307-b97c-73a6-99ea-eb041612e7d1"}, {"row": 2, "passportId": "01a1a307-b97c-73a6-99ea-eb06f0520a8f"}, {"row": 3, "passportId": "01a1a307-b97c-73a6-99ea-eb004fceb13a"}, {"row": 4, "passportId": "01a1a307-b97c-73a6-99ea-eb052b807483"}, {"row": 5, "passportId": "01a1a307-b9c9-73a1-94d4-44a02ace7223"}], "updated": []}', '2026-11-03 20:09:14.68815+00', '{"mode": "apply", "rows": [{"row": 1, "valid": true, "action": "create"}, {"row": 2, "valid": true, "action": "create"}, {"row": 3, "valid": true, "action": "create"}, {"row": 4, "valid": true, "action": "create"}, {"row": 5, "valid": true, "action": "create"}], "totalRows": 5}', NULL); + + +-- +-- Data for Name: operator_config; Type: TABLE DATA; Schema: odal; Owner: - +-- + +INSERT INTO odal.operator_config VALUES ('self_hosted', 'Upgrade Fixture GmbH', NULL, 'Musterstrasse 1, 10115 Berlin', 'DE', 'ops@example.com', NULL, NULL, NULL, NULL, NULL, NULL, 'EU', 3650, NULL, '2026-11-03 20:09:13.507723+00', '2026-11-03 20:09:13.507723+00', NULL); + + +-- +-- Data for Name: operator_identifier; Type: TABLE DATA; Schema: odal; Owner: - +-- + +INSERT INTO odal.operator_identifier VALUES ('01a1a307-b4d7-73f2-95bf-3b2ddd1f6473', 'self_hosted', 'vat', 'DE123456789', NULL, true, '2026-11-03 20:09:13.687031+00', NULL); + + +-- +-- Data for Name: passport; Type: TABLE DATA; Schema: odal; Owner: - +-- + +INSERT INTO odal.passport VALUES ('01a1a307-b510-75c3-b5cc-1ccbddeb9787', 'battery', 'active', true, '2.8.0', NULL, NULL, 1, NULL, NULL, '2026-11-03 20:09:13.747932+00', '2036-11-03 20:09:13.789299+00', NULL, '2026-11-03 20:09:13.748378+00', '2026-11-03 20:09:13.82635+00', '2026-11-03 20:09:13.789299+00', '{"id": "01a1a307-b510-75c3-b5cc-1ccbddeb9787", "status": "active", "batchId": "ODL-IND-NMC-2026-Q2-001", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:13.744378034Z", "materials": [{"name": "Lithium Nickel Manganese Cobalt Oxide", "weightKg": 14.2, "recycledPct": 11.0, "countryOfOrigin": "DE"}, {"name": "Graphite", "weightKg": 6.4, "recycledPct": 5.0, "countryOfOrigin": "DE"}, {"name": "Aluminium enclosure", "weightKg": 8.1, "recycledPct": 78.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000014/21/75c3b5cc1ccbddeb9787", "updatedAt": "2026-11-03T20:09:13.789298757Z", "lintResult": {"assessedAt": "2026-11-03T20:09:13.747933990Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Odal Industrial Battery NMC-48", "publishedAt": "2026-11-03T20:09:13.789298757Z", "jwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELU5NQy0yMDI2LVEyLTAwMSIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjc0NzkzMTk5N1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNi0wMS4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy43NDQzNzgwMzRaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjUxMC03NWMzLWI1Y2MtMWNjYmRkZWI5Nzg3IiwiandzU2lnbmF0dXJlIjpudWxsLCJsaW50UmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjc0NzkzMzk5MFoiLCJwYWNrVmVyc2lvbiI6IjEuMS4wIn0sIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiUHJlbnpsYXVlciBCZXJnIDEyLCAxMDQwNSBCZXJsaW4sIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly9iYXR0ZXJpZXMuZXhhbXBsZS8ud2VsbC1rbm93bi9kaWQuanNvbiIsIm5hbWUiOiJFeGFtcGxlIEJhdHRlcmllcyBHbWJIIn0sIm1hdGVyaWFscyI6W3siY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiTGl0aGl1bSBOaWNrZWwgTWFuZ2FuZXNlIENvYmFsdCBPeGlkZSIsInJlY3ljbGVkUGN0IjoxMSwid2VpZ2h0S2ciOjE0LjJ9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiR3JhcGhpdGUiLCJyZWN5Y2xlZFBjdCI6NSwid2VpZ2h0S2ciOjYuNH0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJBbHVtaW5pdW0gZW5jbG9zdXJlIiwicmVjeWNsZWRQY3QiOjc4LCJ3ZWlnaHRLZyI6OC4xfV0sIm9wZXJhdG9ySWRlbnRpZmllciI6IkRFMTIzNDU2Nzg5IiwicGxhY2VkT25NYXJrZXREYXRlIjoiMjAyNi0wNi0wMSIsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImFub2RlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6Ijc3ODItNDItNSIsIm5hbWUiOiJHcmFwaGl0ZSIsIndlaWdodFBjdCI6OTV9LHsiY2FzTnVtYmVyIjoiOTAwMy01NS04IiwibmFtZSI6IlNCUi9DTUMgYmluZGVyIiwid2VpZ2h0UGN0IjozfSx7ImNhc051bWJlciI6IjEzMzMtODYtNCIsIm5hbWUiOiJDb25kdWN0aXZlIGNhcmJvbiBibGFjayIsIndlaWdodFBjdCI6Mn1dLCJiYXR0ZXJ5Q2hlbWlzdHJ5IjoiTk1DIiwiYmF0dGVyeU1vZGVsSWQiOiJHQy1JTkQtTk1DNDgtMzIwIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDE0Ok9ETC1JTkQtTk1DLTIwMjYtUTItMDAxIiwiYmF0dGVyeVN0YXR1cyI6Im9yaWdpbmFsIiwiYmF0dGVyeVR5cGUiOiJpbmR1c3RyaWFsIiwiYmF0dGVyeVdlaWdodEtnIjozMiwiY2F0aG9kZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiIzNDY0MTctOTctOCIsIm5hbWUiOiJMaU5pMC42TW4wLjJDbzAuMk8yIiwid2VpZ2h0UGN0Ijo5Nn0seyJjYXNOdW1iZXIiOiIyNDkzNy03OS05IiwibmFtZSI6IlBWREYgYmluZGVyIiwid2VpZ2h0UGN0IjoyfSx7ImNhc051bWJlciI6IjEzMzMtODYtNCIsIm5hbWUiOiJDb25kdWN0aXZlIGNhcmJvbiBibGFjayIsIndlaWdodFBjdCI6Mn1dLCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjEyMCwiY29tcG9uZW50UGFydE51bWJlcnMiOlsiR0MtQ0VMTC0yMTcwLU5NQyIsIkdDLUJNUy00OC1WMyIsIkdDLVBBQ0stRU5DTC0zMjAiXSwiY3JpdGljYWxSYXdNYXRlcmlhbHMiOlt7ImNhc051bWJlciI6Ijc0NDAtNDgtNCIsImNvdW50cnlPZk9yaWdpbiI6IkNEIiwibmFtZSI6IkNvYmFsdCIsIndlaWdodEdyYW1zIjoxNDUwfSx7ImNhc051bWJlciI6Ijc0MzktOTMtMiIsImNvdW50cnlPZk9yaWdpbiI6IkNMIiwibmFtZSI6IkxpdGhpdW0iLCJ3ZWlnaHRHcmFtcyI6MjEwMH0seyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDTiIsIm5hbWUiOiJOYXR1cmFsIGdyYXBoaXRlIiwid2VpZ2h0R3JhbXMiOjY0MDB9XSwiY3ljbGVMaWZlVGVzdENSYXRlIjowLjUsImRpc2Fzc2VtYmx5SW5zdHJ1Y3Rpb25zVXJsIjoiaHR0cHM6Ly9iYXR0ZXJpZXMuZXhhbXBsZS9zZXJ2aWNlL2luZC1ubWM0OC9kaXNhc3NlbWJseSIsImR5bmFtaWNQZXJmb3JtYW5jZSI6eyJjYXBhY2l0eUZhZGVQY3QiOjAuNywiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6Mjc5MCwiaW50ZXJuYWxSZXNpc3RhbmNlTW9obSI6MTQuMiwicmF0ZWRDYXBhY2l0eUFoIjo4OS40LCJyb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5NC42fSwiZWxlY3Ryb2x5dGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiOTYtNDktMSIsIm5hbWUiOiJFdGh5bGVuZSBjYXJib25hdGUiLCJ3ZWlnaHRQY3QiOjQwfSx7ImNhc051bWJlciI6IjYxNi0zOC02IiwibmFtZSI6IkRpbWV0aHlsIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6NDd9LHsiY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUiLCJ3ZWlnaHRQY3QiOjEzfV0sImV1RGVjbGFyYXRpb25PZkNvbmZvcm1pdHkiOiJFVS1Eb0MgR0MtMjAyNi1JTkQtMDA5MSBkYXRlZCAyMDI2LTA0LTIwLCBwZXIgUmVndWxhdGlvbiAoRVUpIDIwMjMvMTU0MiBBcnQuIDE4IiwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6MjgwMCwiZXhwZWN0ZWRMaWZldGltZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjE0MjctMToyMDEzLCAyNSBkZWdDLCA4MCUgRG9EIiwiaGF6YXJkb3VzU3Vic3RhbmNlcyI6W3siY2FzTnVtYmVyIjoiNzc4Ni04MS00IiwiY29uY2VudHJhdGlvblBjdCI6MS4yLCJuYW1lIjoiTmlja2VsIHN1bGZhdGUifSx7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJjb25jZW50cmF0aW9uUGN0IjowLjgsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUifV0sImluaXRpYWxSb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5NSwiaW50ZXJuYWxDZWxsUmVzaXN0YW5jZU1vaG0iOjAuOSwiaW50ZXJuYWxQYWNrUmVzaXN0YW5jZU1vaG0iOjE0LCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDQtMTVUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiQmVybGluLCBERSAocGxhbnQgQkxOLTEpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgKGNyb3NzZWQtb3V0IHdoZWVsZWQgYmluKSBwZXIgQXJ0LiAxMyg0KTsgY2FwYWNpdHkgOTAgQWggLyA0LjMyIGtXaCIsIm1heGltdW1Wb2x0YWdlViI6NTQuNiwibWluaW1hbFZvbHRhZ2VWIjo0Miwibm9taW5hbENhcGFjaXR5QWgiOjkwLCJub21pbmFsVm9sdGFnZVYiOjQ4LCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NDUsIm1pbkMiOi0yMH0sIm5vdEluVXNlVGVtcGVyYXR1cmVSZWZlcmVuY2VUZXN0IjoiSUVDIDYyNjYwLTE6MjAxOCBzdG9yYWdlIHByb2ZpbGUiLCJvcmlnaW5hbFBvd2VyQ2FwYWJpbGl0eVciOjQ2MDAsInBvd2VyTGltaXRNYXhXIjo2MDAwLCJwb3dlckxpbWl0TWluVyI6MjAwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDE0Iiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOjQuMzIsInJhdGVkRW5lcmd5V2giOjQzMjAsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRSZXBvcnRpbmdZZWFyIjoyMDI2LCJyZW5ld2FibGVDb250ZW50UGN0IjoxOCwicm91bmRUcmlwRWZmaWNpZW5jeUF0SGFsZkN5Y2xlTGlmZVBjdCI6ODksInNhZmV0eU1lYXN1cmVzIjoiRGUtZW5lcmdpc2UgYW5kIHdhaXQgMTUgbWluIGJlZm9yZSBvcGVuaW5nLiBDbGFzcyAwIGdsb3Zlcy4gTm8gY29uZHVjdGl2ZSB0b29scy4iLCJzcGFyZVBhcnRzQ29udGFjdHMiOiJzcGFyZXNAYmF0dGVyaWVzLmV4YW1wbGU7ICs0OSAzMCA1NTUwIDEyMDAiLCJzdGF0ZU9mSGVhbHRoIjp7Im9obWljUmVzaXN0YW5jZU1vaG0iOjE0LjIsInBhcmFtZXRlclNldCI6InN0YXRpb25hcnlPckxtdCIsInJlbWFpbmluZ0NhcGFjaXR5UGN0Ijo5OS4zLCJyZW1haW5pbmdQb3dlckNhcGFiaWxpdHlQY3QiOjk5LCJyZW1haW5pbmdSb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5OC41LCJzZWxmRGlzY2hhcmdlUmF0ZVBjdFBlck1vbnRoIjoyLjF9LCJzdGF0ZU9mSGVhbHRoUGN0IjpudWxsLCJ0ZXN0UmVwb3J0UmVzdWx0cyI6IklFQyA2MjYxOToyMDIyIHBhc3M7IFVOIDM4LjMgcGFzczsgcmVwb3J0IEdDLVRSLTIwMjYtMDQxMiIsInVzYWJsZUV4dGluZ3Vpc2hpbmdBZ2VudCI6IldhdGVyIG1pc3Qgb3IgQUJDIGRyeSBwb3dkZXI7IGRvIG5vdCB1c2UgQ08yIiwidXNhZ2VIaXN0b3J5Ijp7ImNoYXJnZURpc2NoYXJnZUN5Y2xlcyI6MTIsIm5lZ2F0aXZlRXZlbnRzIjpbXX0sIndhc3RlQmF0dGVyeUluZm9ybWF0aW9uIjoiUmV0dXJuIHRvIGFuIGF1dGhvcmlzZWQgY29sbGVjdGlvbiBwb2ludCBvciBpbnN0YWxsZXIuIERvIG5vdCBzaHJlZC4gQ29udGFpbnMgbGl0aGl1bTsgZmlyZSByaXNrIGlmIGRhbWFnZWQuIFByb2R1Y2VyIHJlZ2lzdHJhdGlvbiBERS1CQVRULTAwMTIzLiJ9LCJwcm9kdWN0TmFtZSI6Ik9kYWwgSW5kdXN0cmlhbCBCYXR0ZXJ5IE5NQy00OCIsInB1Ymxpc2hlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy43ODkyOTg3NTdaIiwicXJDb2RlVXJsIjoiaHR0cDovLzEyNy4wLjAuMToxLzAxLzA5NTkwMDAwMDAwMDE0LzIxLzc1YzNiNWNjMWNjYmRkZWI5Nzg3IiwicmVwYWlyYWJpbGl0eVNjb3JlIjp7Im92ZXJhbGwiOjcuNX0sInJldGVudGlvbkxvY2tlZCI6dHJ1ZSwicmV0ZW50aW9uVW50aWwiOiIyMDM2LTExLTAzVDIwOjA5OjEzLjc4OTI5ODc1N1oiLCJzY2hlbWFWZXJzaW9uIjoiMi44LjAiLCJzdGF0dXMiOiJhY3RpdmUiLCJ1cGRhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjc4OTI5ODc1N1oiLCJ2ZXJzaW9uIjoxfQ.dAaMwYQYGxuJl7K3m8A19ZMa1XYXVZrjfI6cpYK2C01nDyOvcG1Rl2qFVH7guz4DhZ_7RvxefF11rwnwnOnEAw", "manufacturer": {"name": "Example Batteries GmbH", "address": "Prenzlauer Berg 12, 10405 Berlin, DE", "country": null, "didWebUrl": "https://batteries.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:13.789298757Z", "retentionLocked": true, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-06-01. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:13.747931997Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "industrial", "productGroup": "battery", "usageHistory": {"negativeEvents": [], "chargeDischargeCycles": 12}, "anodeMaterial": [{"name": "Graphite", "casNumber": "7782-42-5", "weightPct": 95.0}, {"name": "SBR/CMC binder", "casNumber": "9003-55-8", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 2.0}], "batteryStatus": "original", "ratedEnergyWh": 4320.0, "stateOfHealth": {"parameterSet": "stationaryOrLmt", "ohmicResistanceMohm": 14.2, "remainingCapacityPct": 99.3, "remainingPowerCapabilityPct": 99.0, "selfDischargeRatePctPerMonth": 2.1, "remainingRoundTripEfficiencyPct": 98.5}, "batteryModelId": "GC-IND-NMC48-320", "powerLimitMaxW": 6000.0, "powerLimitMinW": 200.0, "safetyMeasures": "De-energise and wait 15 min before opening. Class 0 gloves. No conductive tools.", "batteryWeightKg": 32.0, "cathodeMaterial": [{"name": "LiNi0.6Mn0.2Co0.2O2", "casNumber": "346417-97-8", "weightPct": 96.0}, {"name": "PVDF binder", "casNumber": "24937-79-9", "weightPct": 2.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 2.0}], "maximumVoltageV": 54.6, "minimalVoltageV": 42.0, "nominalVoltageV": 48.0, "batteryChemistry": "NMC", "ratedCapacityKwh": 4.32, "stateOfHealthPct": null, "manufacturingDate": "2026-04-15T00:00:00Z", "nominalCapacityAh": 90.0, "productIdentifier": {"gtin": "09590000000014", "scheme": "gs1"}, "testReportResults": "IEC 62619:2022 pass; UN 38.3 pass; report GC-TR-2026-0412", "cycleLifeTestCRate": 0.5, "dynamicPerformance": {"capacityFadePct": 0.7, "ratedCapacityAh": 89.4, "expectedLifetimeCycles": 2790, "internalResistanceMohm": 14.2, "roundTripEfficiencyPct": 94.6}, "manufacturingPlace": "Berlin, DE (plant BLN-1)", "markingInformation": "Separate-collection symbol (crossed-out wheeled bin) per Art. 13(4); capacity 90 Ah / 4.32 kWh", "sparePartsContacts": "spares@batteries.example; +49 30 5550 1200", "electrolyteMaterial": [{"name": "Ethylene carbonate", "casNumber": "96-49-1", "weightPct": 40.0}, {"name": "Dimethyl carbonate", "casNumber": "616-38-6", "weightPct": 47.0}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "weightPct": 13.0}], "hazardousSubstances": [{"name": "Nickel sulfate", "casNumber": "7786-81-4", "concentrationPct": 1.2}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.8}], "renewableContentPct": 18.0, "componentPartNumbers": ["GC-CELL-2170-NMC", "GC-BMS-48-V3", "GC-PACK-ENCL-320"], "criticalRawMaterials": [{"name": "Cobalt", "casNumber": "7440-48-4", "weightGrams": 1450.0, "countryOfOrigin": "CD"}, {"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 2100.0, "countryOfOrigin": "CL"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 6400.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000014:ODL-IND-NMC-2026-Q2-001", "expectedLifetimeCycles": 2800, "wasteBatteryInformation": "Return to an authorised collection point or installer. Do not shred. Contains lithium; fire risk if damaged. Producer registration DE-BATT-00123.", "notInUseTemperatureRange": {"maxC": 45.0, "minC": -20.0}, "originalPowerCapabilityW": 4600.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Water mist or ABC dry powder; do not use CO2", "euDeclarationOfConformity": "EU-DoC GC-2026-IND-0091 dated 2026-04-20, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://batteries.example/service/ind-nmc48/disassembly", "internalCellResistanceMohm": 0.9, "internalPackResistanceMohm": 14.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "IEC 61427-1:2013, 25 degC, 80% DoD", "initialRoundTripEfficiencyPct": 95.0, "commercialWarrantyPeriodMonths": 120, "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage profile", "roundTripEfficiencyAtHalfCycleLifePct": 89.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-06-01", "publicJwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELU5NQy0yMDI2LVEyLTAwMSIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjc0NzkzMTk5N1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNi0wMS4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy43NDQzNzgwMzRaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjUxMC03NWMzLWI1Y2MtMWNjYmRkZWI5Nzg3IiwibWFudWZhY3R1cmVyIjp7ImFkZHJlc3MiOiJQcmVuemxhdWVyIEJlcmcgMTIsIDEwNDA1IEJlcmxpbiwgREUiLCJjb3VudHJ5IjpudWxsLCJkaWRXZWJVcmwiOiJodHRwczovL2JhdHRlcmllcy5leGFtcGxlLy53ZWxsLWtub3duL2RpZC5qc29uIiwibmFtZSI6IkV4YW1wbGUgQmF0dGVyaWVzIEdtYkgifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJMaXRoaXVtIE5pY2tlbCBNYW5nYW5lc2UgQ29iYWx0IE94aWRlIiwicmVjeWNsZWRQY3QiOjExLCJ3ZWlnaHRLZyI6MTQuMn0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJHcmFwaGl0ZSIsInJlY3ljbGVkUGN0Ijo1LCJ3ZWlnaHRLZyI6Ni40fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkFsdW1pbml1bSBlbmNsb3N1cmUiLCJyZWN5Y2xlZFBjdCI6NzgsIndlaWdodEtnIjo4LjF9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA2LTAxIiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYmF0dGVyeUNoZW1pc3RyeSI6Ik5NQyIsImJhdHRlcnlNb2RlbElkIjoiR0MtSU5ELU5NQzQ4LTMyMCIsImJhdHRlcnlQYXNzcG9ydE51bWJlciI6IlVSTjpPREw6QkFUVDowOTU5MDAwMDAwMDAxNDpPREwtSU5ELU5NQy0yMDI2LVEyLTAwMSIsImJhdHRlcnlUeXBlIjoiaW5kdXN0cmlhbCIsImJhdHRlcnlXZWlnaHRLZyI6MzIsImNvbW1lcmNpYWxXYXJyYW50eVBlcmlvZE1vbnRocyI6MTIwLCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQ0MC00OC00IiwiY291bnRyeU9mT3JpZ2luIjoiQ0QiLCJuYW1lIjoiQ29iYWx0Iiwid2VpZ2h0R3JhbXMiOjE0NTB9LHsiY2FzTnVtYmVyIjoiNzQzOS05My0yIiwiY291bnRyeU9mT3JpZ2luIjoiQ0wiLCJuYW1lIjoiTGl0aGl1bSIsIndlaWdodEdyYW1zIjoyMTAwfSx7ImNhc051bWJlciI6Ijc3ODItNDItNSIsImNvdW50cnlPZk9yaWdpbiI6IkNOIiwibmFtZSI6Ik5hdHVyYWwgZ3JhcGhpdGUiLCJ3ZWlnaHRHcmFtcyI6NjQwMH1dLCJjeWNsZUxpZmVUZXN0Q1JhdGUiOjAuNSwiZXVEZWNsYXJhdGlvbk9mQ29uZm9ybWl0eSI6IkVVLURvQyBHQy0yMDI2LUlORC0wMDkxIGRhdGVkIDIwMjYtMDQtMjAsIHBlciBSZWd1bGF0aW9uIChFVSkgMjAyMy8xNTQyIEFydC4gMTgiLCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjoyODAwLCJleHBlY3RlZExpZmV0aW1lUmVmZXJlbmNlVGVzdCI6IklFQyA2MTQyNy0xOjIwMTMsIDI1IGRlZ0MsIDgwJSBEb0QiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiI3Nzg2LTgxLTQiLCJjb25jZW50cmF0aW9uUGN0IjoxLjIsIm5hbWUiOiJOaWNrZWwgc3VsZmF0ZSJ9LHsiY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsImNvbmNlbnRyYXRpb25QY3QiOjAuOCwibmFtZSI6IkxpdGhpdW0gaGV4YWZsdW9yb3Bob3NwaGF0ZSJ9XSwiaW5pdGlhbFJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk1LCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6MC45LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6MTQsIm1hbnVmYWN0dXJpbmdEYXRlIjoiMjAyNi0wNC0xNVQwMDowMDowMFoiLCJtYW51ZmFjdHVyaW5nUGxhY2UiOiJCZXJsaW4sIERFIChwbGFudCBCTE4tMSkiLCJtYXJraW5nSW5mb3JtYXRpb24iOiJTZXBhcmF0ZS1jb2xsZWN0aW9uIHN5bWJvbCAoY3Jvc3NlZC1vdXQgd2hlZWxlZCBiaW4pIHBlciBBcnQuIDEzKDQpOyBjYXBhY2l0eSA5MCBBaCAvIDQuMzIga1doIiwibWF4aW11bVZvbHRhZ2VWIjo1NC42LCJtaW5pbWFsVm9sdGFnZVYiOjQyLCJub21pbmFsQ2FwYWNpdHlBaCI6OTAsIm5vbWluYWxWb2x0YWdlViI6NDgsIm5vdEluVXNlVGVtcGVyYXR1cmVSYW5nZSI6eyJtYXhDIjo0NSwibWluQyI6LTIwfSwibm90SW5Vc2VUZW1wZXJhdHVyZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjI2NjAtMToyMDE4IHN0b3JhZ2UgcHJvZmlsZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NDYwMCwicG93ZXJMaW1pdE1heFciOjYwMDAsInBvd2VyTGltaXRNaW5XIjoyMDAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwMTQiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6NC4zMiwicmF0ZWRFbmVyZ3lXaCI6NDMyMCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjE4LCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo4OSwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIgbWlzdCBvciBBQkMgZHJ5IHBvd2RlcjsgZG8gbm90IHVzZSBDTzIiLCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IlJldHVybiB0byBhbiBhdXRob3Jpc2VkIGNvbGxlY3Rpb24gcG9pbnQgb3IgaW5zdGFsbGVyLiBEbyBub3Qgc2hyZWQuIENvbnRhaW5zIGxpdGhpdW07IGZpcmUgcmlzayBpZiBkYW1hZ2VkLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDEyMy4ifSwicHJvZHVjdE5hbWUiOiJPZGFsIEluZHVzdHJpYWwgQmF0dGVyeSBOTUMtNDgiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTMuNzg5Mjk4NzU3WiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDAxNC8yMS83NWMzYjVjYzFjY2JkZGViOTc4NyIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjo3LjV9LCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTMuNzg5Mjk4NzU3WiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTMuNzg5Mjk4NzU3WiIsInZlcnNpb24iOjF9.9TX5ElGt90F54ac4WbhRXEisQKqA27m7ovL9k6ptKJwirMiAUmCxK2zQ1WQfAIeQp-XMXKO6e_qsRjVJ4nNPCg", "repairabilityScore": {"overall": 7.5}, "disclosureSignatures": {"public+restricted+conformity": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELU5NQy0yMDI2LVEyLTAwMSIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjc0NzkzMTk5N1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNi0wMS4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy43NDQzNzgwMzRaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjUxMC03NWMzLWI1Y2MtMWNjYmRkZWI5Nzg3IiwibGludFJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy43NDc5MzM5OTBaIiwicGFja1ZlcnNpb24iOiIxLjEuMCJ9LCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IlByZW56bGF1ZXIgQmVyZyAxMiwgMTA0MDUgQmVybGluLCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vYmF0dGVyaWVzLmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiRXhhbXBsZSBCYXR0ZXJpZXMgR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkxpdGhpdW0gTmlja2VsIE1hbmdhbmVzZSBDb2JhbHQgT3hpZGUiLCJyZWN5Y2xlZFBjdCI6MTEsIndlaWdodEtnIjoxNC4yfSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkdyYXBoaXRlIiwicmVjeWNsZWRQY3QiOjUsIndlaWdodEtnIjo2LjR9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiQWx1bWluaXVtIGVuY2xvc3VyZSIsInJlY3ljbGVkUGN0Ijo3OCwid2VpZ2h0S2ciOjguMX1dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInBsYWNlZE9uTWFya2V0RGF0ZSI6IjIwMjYtMDYtMDEiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJhbm9kZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJuYW1lIjoiR3JhcGhpdGUiLCJ3ZWlnaHRQY3QiOjk1fSx7ImNhc051bWJlciI6IjkwMDMtNTUtOCIsIm5hbWUiOiJTQlIvQ01DIGJpbmRlciIsIndlaWdodFBjdCI6M30seyJjYXNOdW1iZXIiOiIxMzMzLTg2LTQiLCJuYW1lIjoiQ29uZHVjdGl2ZSBjYXJib24gYmxhY2siLCJ3ZWlnaHRQY3QiOjJ9XSwiYmF0dGVyeUNoZW1pc3RyeSI6Ik5NQyIsImJhdHRlcnlNb2RlbElkIjoiR0MtSU5ELU5NQzQ4LTMyMCIsImJhdHRlcnlQYXNzcG9ydE51bWJlciI6IlVSTjpPREw6QkFUVDowOTU5MDAwMDAwMDAxNDpPREwtSU5ELU5NQy0yMDI2LVEyLTAwMSIsImJhdHRlcnlUeXBlIjoiaW5kdXN0cmlhbCIsImJhdHRlcnlXZWlnaHRLZyI6MzIsImNhdGhvZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiMzQ2NDE3LTk3LTgiLCJuYW1lIjoiTGlOaTAuNk1uMC4yQ28wLjJPMiIsIndlaWdodFBjdCI6OTZ9LHsiY2FzTnVtYmVyIjoiMjQ5MzctNzktOSIsIm5hbWUiOiJQVkRGIGJpbmRlciIsIndlaWdodFBjdCI6Mn0seyJjYXNOdW1iZXIiOiIxMzMzLTg2LTQiLCJuYW1lIjoiQ29uZHVjdGl2ZSBjYXJib24gYmxhY2siLCJ3ZWlnaHRQY3QiOjJ9XSwiY29tbWVyY2lhbFdhcnJhbnR5UGVyaW9kTW9udGhzIjoxMjAsImNvbXBvbmVudFBhcnROdW1iZXJzIjpbIkdDLUNFTEwtMjE3MC1OTUMiLCJHQy1CTVMtNDgtVjMiLCJHQy1QQUNLLUVOQ0wtMzIwIl0sImNyaXRpY2FsUmF3TWF0ZXJpYWxzIjpbeyJjYXNOdW1iZXIiOiI3NDQwLTQ4LTQiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDRCIsIm5hbWUiOiJDb2JhbHQiLCJ3ZWlnaHRHcmFtcyI6MTQ1MH0seyJjYXNOdW1iZXIiOiI3NDM5LTkzLTIiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDTCIsIm5hbWUiOiJMaXRoaXVtIiwid2VpZ2h0R3JhbXMiOjIxMDB9LHsiY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwiY291bnRyeU9mT3JpZ2luIjoiQ04iLCJuYW1lIjoiTmF0dXJhbCBncmFwaGl0ZSIsIndlaWdodEdyYW1zIjo2NDAwfV0sImN5Y2xlTGlmZVRlc3RDUmF0ZSI6MC41LCJkaXNhc3NlbWJseUluc3RydWN0aW9uc1VybCI6Imh0dHBzOi8vYmF0dGVyaWVzLmV4YW1wbGUvc2VydmljZS9pbmQtbm1jNDgvZGlzYXNzZW1ibHkiLCJlbGVjdHJvbHl0ZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI5Ni00OS0xIiwibmFtZSI6IkV0aHlsZW5lIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6NDB9LHsiY2FzTnVtYmVyIjoiNjE2LTM4LTYiLCJuYW1lIjoiRGltZXRoeWwgY2FyYm9uYXRlIiwid2VpZ2h0UGN0Ijo0N30seyJjYXNOdW1iZXIiOiIyMTMyNC00MC0zIiwibmFtZSI6IkxpdGhpdW0gaGV4YWZsdW9yb3Bob3NwaGF0ZSIsIndlaWdodFBjdCI6MTN9XSwiZXVEZWNsYXJhdGlvbk9mQ29uZm9ybWl0eSI6IkVVLURvQyBHQy0yMDI2LUlORC0wMDkxIGRhdGVkIDIwMjYtMDQtMjAsIHBlciBSZWd1bGF0aW9uIChFVSkgMjAyMy8xNTQyIEFydC4gMTgiLCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjoyODAwLCJleHBlY3RlZExpZmV0aW1lUmVmZXJlbmNlVGVzdCI6IklFQyA2MTQyNy0xOjIwMTMsIDI1IGRlZ0MsIDgwJSBEb0QiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiI3Nzg2LTgxLTQiLCJjb25jZW50cmF0aW9uUGN0IjoxLjIsIm5hbWUiOiJOaWNrZWwgc3VsZmF0ZSJ9LHsiY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsImNvbmNlbnRyYXRpb25QY3QiOjAuOCwibmFtZSI6IkxpdGhpdW0gaGV4YWZsdW9yb3Bob3NwaGF0ZSJ9XSwiaW5pdGlhbFJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk1LCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6MC45LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6MTQsIm1hbnVmYWN0dXJpbmdEYXRlIjoiMjAyNi0wNC0xNVQwMDowMDowMFoiLCJtYW51ZmFjdHVyaW5nUGxhY2UiOiJCZXJsaW4sIERFIChwbGFudCBCTE4tMSkiLCJtYXJraW5nSW5mb3JtYXRpb24iOiJTZXBhcmF0ZS1jb2xsZWN0aW9uIHN5bWJvbCAoY3Jvc3NlZC1vdXQgd2hlZWxlZCBiaW4pIHBlciBBcnQuIDEzKDQpOyBjYXBhY2l0eSA5MCBBaCAvIDQuMzIga1doIiwibWF4aW11bVZvbHRhZ2VWIjo1NC42LCJtaW5pbWFsVm9sdGFnZVYiOjQyLCJub21pbmFsQ2FwYWNpdHlBaCI6OTAsIm5vbWluYWxWb2x0YWdlViI6NDgsIm5vdEluVXNlVGVtcGVyYXR1cmVSYW5nZSI6eyJtYXhDIjo0NSwibWluQyI6LTIwfSwibm90SW5Vc2VUZW1wZXJhdHVyZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjI2NjAtMToyMDE4IHN0b3JhZ2UgcHJvZmlsZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NDYwMCwicG93ZXJMaW1pdE1heFciOjYwMDAsInBvd2VyTGltaXRNaW5XIjoyMDAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwMTQiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6NC4zMiwicmF0ZWRFbmVyZ3lXaCI6NDMyMCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjE4LCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo4OSwic2FmZXR5TWVhc3VyZXMiOiJEZS1lbmVyZ2lzZSBhbmQgd2FpdCAxNSBtaW4gYmVmb3JlIG9wZW5pbmcuIENsYXNzIDAgZ2xvdmVzLiBObyBjb25kdWN0aXZlIHRvb2xzLiIsInNwYXJlUGFydHNDb250YWN0cyI6InNwYXJlc0BiYXR0ZXJpZXMuZXhhbXBsZTsgKzQ5IDMwIDU1NTAgMTIwMCIsInRlc3RSZXBvcnRSZXN1bHRzIjoiSUVDIDYyNjE5OjIwMjIgcGFzczsgVU4gMzguMyBwYXNzOyByZXBvcnQgR0MtVFItMjAyNi0wNDEyIiwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIgbWlzdCBvciBBQkMgZHJ5IHBvd2RlcjsgZG8gbm90IHVzZSBDTzIiLCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IlJldHVybiB0byBhbiBhdXRob3Jpc2VkIGNvbGxlY3Rpb24gcG9pbnQgb3IgaW5zdGFsbGVyLiBEbyBub3Qgc2hyZWQuIENvbnRhaW5zIGxpdGhpdW07IGZpcmUgcmlzayBpZiBkYW1hZ2VkLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDEyMy4ifSwicHJvZHVjdE5hbWUiOiJPZGFsIEluZHVzdHJpYWwgQmF0dGVyeSBOTUMtNDgiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTMuNzg5Mjk4NzU3WiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDAxNC8yMS83NWMzYjVjYzFjY2JkZGViOTc4NyIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjo3LjV9LCJyZXRlbnRpb25Mb2NrZWQiOnRydWUsInJldGVudGlvblVudGlsIjoiMjAzNi0xMS0wM1QyMDowOToxMy43ODkyOTg3NTdaIiwic2NoZW1hVmVyc2lvbiI6IjIuOC4wIiwic3RhdHVzIjoiYWN0aXZlIiwidXBkYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy43ODkyOTg3NTdaIiwidmVyc2lvbiI6MX0.yH3Tbt5tHFzZmxnxugRIMe7OYS1VwDdzEWgSo_w2XKNlNwZlrx140trWHXQd67zH8KcMwTlv56Cfw217zxYSDA", "public+restricted+individual": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELU5NQy0yMDI2LVEyLTAwMSIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjc0NzkzMTk5N1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNi0wMS4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy43NDQzNzgwMzRaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjUxMC03NWMzLWI1Y2MtMWNjYmRkZWI5Nzg3IiwibGludFJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy43NDc5MzM5OTBaIiwicGFja1ZlcnNpb24iOiIxLjEuMCJ9LCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IlByZW56bGF1ZXIgQmVyZyAxMiwgMTA0MDUgQmVybGluLCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vYmF0dGVyaWVzLmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiRXhhbXBsZSBCYXR0ZXJpZXMgR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkxpdGhpdW0gTmlja2VsIE1hbmdhbmVzZSBDb2JhbHQgT3hpZGUiLCJyZWN5Y2xlZFBjdCI6MTEsIndlaWdodEtnIjoxNC4yfSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkdyYXBoaXRlIiwicmVjeWNsZWRQY3QiOjUsIndlaWdodEtnIjo2LjR9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiQWx1bWluaXVtIGVuY2xvc3VyZSIsInJlY3ljbGVkUGN0Ijo3OCwid2VpZ2h0S2ciOjguMX1dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInBsYWNlZE9uTWFya2V0RGF0ZSI6IjIwMjYtMDYtMDEiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJhbm9kZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJuYW1lIjoiR3JhcGhpdGUiLCJ3ZWlnaHRQY3QiOjk1fSx7ImNhc051bWJlciI6IjkwMDMtNTUtOCIsIm5hbWUiOiJTQlIvQ01DIGJpbmRlciIsIndlaWdodFBjdCI6M30seyJjYXNOdW1iZXIiOiIxMzMzLTg2LTQiLCJuYW1lIjoiQ29uZHVjdGl2ZSBjYXJib24gYmxhY2siLCJ3ZWlnaHRQY3QiOjJ9XSwiYmF0dGVyeUNoZW1pc3RyeSI6Ik5NQyIsImJhdHRlcnlNb2RlbElkIjoiR0MtSU5ELU5NQzQ4LTMyMCIsImJhdHRlcnlQYXNzcG9ydE51bWJlciI6IlVSTjpPREw6QkFUVDowOTU5MDAwMDAwMDAxNDpPREwtSU5ELU5NQy0yMDI2LVEyLTAwMSIsImJhdHRlcnlTdGF0dXMiOiJvcmlnaW5hbCIsImJhdHRlcnlUeXBlIjoiaW5kdXN0cmlhbCIsImJhdHRlcnlXZWlnaHRLZyI6MzIsImNhdGhvZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiMzQ2NDE3LTk3LTgiLCJuYW1lIjoiTGlOaTAuNk1uMC4yQ28wLjJPMiIsIndlaWdodFBjdCI6OTZ9LHsiY2FzTnVtYmVyIjoiMjQ5MzctNzktOSIsIm5hbWUiOiJQVkRGIGJpbmRlciIsIndlaWdodFBjdCI6Mn0seyJjYXNOdW1iZXIiOiIxMzMzLTg2LTQiLCJuYW1lIjoiQ29uZHVjdGl2ZSBjYXJib24gYmxhY2siLCJ3ZWlnaHRQY3QiOjJ9XSwiY29tbWVyY2lhbFdhcnJhbnR5UGVyaW9kTW9udGhzIjoxMjAsImNvbXBvbmVudFBhcnROdW1iZXJzIjpbIkdDLUNFTEwtMjE3MC1OTUMiLCJHQy1CTVMtNDgtVjMiLCJHQy1QQUNLLUVOQ0wtMzIwIl0sImNyaXRpY2FsUmF3TWF0ZXJpYWxzIjpbeyJjYXNOdW1iZXIiOiI3NDQwLTQ4LTQiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDRCIsIm5hbWUiOiJDb2JhbHQiLCJ3ZWlnaHRHcmFtcyI6MTQ1MH0seyJjYXNOdW1iZXIiOiI3NDM5LTkzLTIiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDTCIsIm5hbWUiOiJMaXRoaXVtIiwid2VpZ2h0R3JhbXMiOjIxMDB9LHsiY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwiY291bnRyeU9mT3JpZ2luIjoiQ04iLCJuYW1lIjoiTmF0dXJhbCBncmFwaGl0ZSIsIndlaWdodEdyYW1zIjo2NDAwfV0sImN5Y2xlTGlmZVRlc3RDUmF0ZSI6MC41LCJkaXNhc3NlbWJseUluc3RydWN0aW9uc1VybCI6Imh0dHBzOi8vYmF0dGVyaWVzLmV4YW1wbGUvc2VydmljZS9pbmQtbm1jNDgvZGlzYXNzZW1ibHkiLCJkeW5hbWljUGVyZm9ybWFuY2UiOnsiY2FwYWNpdHlGYWRlUGN0IjowLjcsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjI3OTAsImludGVybmFsUmVzaXN0YW5jZU1vaG0iOjE0LjIsInJhdGVkQ2FwYWNpdHlBaCI6ODkuNCwicm91bmRUcmlwRWZmaWNpZW5jeVBjdCI6OTQuNn0sImVsZWN0cm9seXRlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6Ijk2LTQ5LTEiLCJuYW1lIjoiRXRoeWxlbmUgY2FyYm9uYXRlIiwid2VpZ2h0UGN0Ijo0MH0seyJjYXNOdW1iZXIiOiI2MTYtMzgtNiIsIm5hbWUiOiJEaW1ldGh5bCBjYXJib25hdGUiLCJ3ZWlnaHRQY3QiOjQ3fSx7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIiwid2VpZ2h0UGN0IjoxM31dLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIEdDLTIwMjYtSU5ELTAwOTEgZGF0ZWQgMjAyNi0wNC0yMCwgcGVyIFJlZ3VsYXRpb24gKEVVKSAyMDIzLzE1NDIgQXJ0LiAxOCIsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjI4MDAsImV4cGVjdGVkTGlmZXRpbWVSZWZlcmVuY2VUZXN0IjoiSUVDIDYxNDI3LTE6MjAxMywgMjUgZGVnQywgODAlIERvRCIsImhhemFyZG91c1N1YnN0YW5jZXMiOlt7ImNhc051bWJlciI6Ijc3ODYtODEtNCIsImNvbmNlbnRyYXRpb25QY3QiOjEuMiwibmFtZSI6Ik5pY2tlbCBzdWxmYXRlIn0seyJjYXNOdW1iZXIiOiIyMTMyNC00MC0zIiwiY29uY2VudHJhdGlvblBjdCI6MC44LCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIn1dLCJpbml0aWFsUm91bmRUcmlwRWZmaWNpZW5jeVBjdCI6OTUsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjowLjksImludGVybmFsUGFja1Jlc2lzdGFuY2VNb2htIjoxNCwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTA0LTE1VDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkJlcmxpbiwgREUgKHBsYW50IEJMTi0xKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIChjcm9zc2VkLW91dCB3aGVlbGVkIGJpbikgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDkwIEFoIC8gNC4zMiBrV2giLCJtYXhpbXVtVm9sdGFnZVYiOjU0LjYsIm1pbmltYWxWb2x0YWdlViI6NDIsIm5vbWluYWxDYXBhY2l0eUFoIjo5MCwibm9taW5hbFZvbHRhZ2VWIjo0OCwibm90SW5Vc2VUZW1wZXJhdHVyZVJhbmdlIjp7Im1heEMiOjQ1LCJtaW5DIjotMjB9LCJub3RJblVzZVRlbXBlcmF0dXJlUmVmZXJlbmNlVGVzdCI6IklFQyA2MjY2MC0xOjIwMTggc3RvcmFnZSBwcm9maWxlIiwib3JpZ2luYWxQb3dlckNhcGFiaWxpdHlXIjo0NjAwLCJwb3dlckxpbWl0TWF4VyI6NjAwMCwicG93ZXJMaW1pdE1pblciOjIwMCwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RJZGVudGlmaWVyIjp7Imd0aW4iOiIwOTU5MDAwMDAwMDAxNCIsInNjaGVtZSI6ImdzMSJ9LCJyYXRlZENhcGFjaXR5S3doIjo0LjMyLCJyYXRlZEVuZXJneVdoIjo0MzIwLCJyZWN5Y2xlZENvbnRlbnRDb2JhbHRQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudExpdGhpdW1QY3QiOm51bGwsInJlY3ljbGVkQ29udGVudE5pY2tlbFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50UmVwb3J0aW5nWWVhciI6MjAyNiwicmVuZXdhYmxlQ29udGVudFBjdCI6MTgsInJvdW5kVHJpcEVmZmljaWVuY3lBdEhhbGZDeWNsZUxpZmVQY3QiOjg5LCJzYWZldHlNZWFzdXJlcyI6IkRlLWVuZXJnaXNlIGFuZCB3YWl0IDE1IG1pbiBiZWZvcmUgb3BlbmluZy4gQ2xhc3MgMCBnbG92ZXMuIE5vIGNvbmR1Y3RpdmUgdG9vbHMuIiwic3BhcmVQYXJ0c0NvbnRhY3RzIjoic3BhcmVzQGJhdHRlcmllcy5leGFtcGxlOyArNDkgMzAgNTU1MCAxMjAwIiwic3RhdGVPZkhlYWx0aCI6eyJvaG1pY1Jlc2lzdGFuY2VNb2htIjoxNC4yLCJwYXJhbWV0ZXJTZXQiOiJzdGF0aW9uYXJ5T3JMbXQiLCJyZW1haW5pbmdDYXBhY2l0eVBjdCI6OTkuMywicmVtYWluaW5nUG93ZXJDYXBhYmlsaXR5UGN0Ijo5OSwicmVtYWluaW5nUm91bmRUcmlwRWZmaWNpZW5jeVBjdCI6OTguNSwic2VsZkRpc2NoYXJnZVJhdGVQY3RQZXJNb250aCI6Mi4xfSwic3RhdGVPZkhlYWx0aFBjdCI6bnVsbCwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIgbWlzdCBvciBBQkMgZHJ5IHBvd2RlcjsgZG8gbm90IHVzZSBDTzIiLCJ1c2FnZUhpc3RvcnkiOnsiY2hhcmdlRGlzY2hhcmdlQ3ljbGVzIjoxMiwibmVnYXRpdmVFdmVudHMiOltdfSwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJSZXR1cm4gdG8gYW4gYXV0aG9yaXNlZCBjb2xsZWN0aW9uIHBvaW50IG9yIGluc3RhbGxlci4gRG8gbm90IHNocmVkLiBDb250YWlucyBsaXRoaXVtOyBmaXJlIHJpc2sgaWYgZGFtYWdlZC4gUHJvZHVjZXIgcmVnaXN0cmF0aW9uIERFLUJBVFQtMDAxMjMuIn0sInByb2R1Y3ROYW1lIjoiT2RhbCBJbmR1c3RyaWFsIEJhdHRlcnkgTk1DLTQ4IiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjc4OTI5ODc1N1oiLCJxckNvZGVVcmwiOiJodHRwOi8vMTI3LjAuMC4xOjEvMDEvMDk1OTAwMDAwMDAwMTQvMjEvNzVjM2I1Y2MxY2NiZGRlYjk3ODciLCJyZXBhaXJhYmlsaXR5U2NvcmUiOnsib3ZlcmFsbCI6Ny41fSwicmV0ZW50aW9uVW50aWwiOiIyMDM2LTExLTAzVDIwOjA5OjEzLjc4OTI5ODc1N1oiLCJzY2hlbWFWZXJzaW9uIjoiMi44LjAiLCJzdGF0dXMiOiJhY3RpdmUiLCJ1cGRhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjc4OTI5ODc1N1oiLCJ2ZXJzaW9uIjoxfQ.OCPMEwYA6rWYPswiaSviA5fCy67kFNBivAK59AqKuHPq10JBeKrn0z4e5iIqdG8BwogrJAxSfK1uscud75KHCg"}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}'); +INSERT INTO odal.passport VALUES ('01a1a307-b67c-706a-a55e-689712fcd122', 'battery', 'active', true, '2.8.0', NULL, NULL, 1, NULL, NULL, '2026-11-03 20:09:14.112527+00', '2036-11-03 20:09:14.173555+00', NULL, '2026-11-03 20:09:14.113299+00', '2026-11-03 20:09:14.221426+00', '2026-11-03 20:09:14.173555+00', '{"id": "01a1a307-b67c-706a-a55e-689712fcd122", "status": "active", "batchId": "ODL-IND-PB-2026-Q1-007", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.108085427Z", "materials": [{"name": "Lead", "weightKg": 38.0, "recycledPct": 82.0, "countryOfOrigin": "DE"}, {"name": "Sulfuric acid electrolyte", "weightKg": 9.0, "recycledPct": 0.0, "countryOfOrigin": "DE"}, {"name": "Polypropylene case", "weightKg": 3.2, "recycledPct": 45.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000038/21/706aa55e689712fcd122", "updatedAt": "2026-11-03T20:09:14.173554734Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.112531026Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Odal Industrial Standby Battery 12V-200Ah (VRLA)", "publishedAt": "2026-11-03T20:09:14.173554734Z", "jwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELVBCLTIwMjYtUTEtMDA3IiwiY28yZVBlclVuaXQiOm51bGwsImNvbW1vZGl0eUNvZGUiOiI4NTA3MjAwMCIsImNvbXBsaWFuY2VSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMTEyNTI2OTQ1WiIsImNvMmVTY29yZSI6bnVsbCwiY29tcGxpYW5jZVN0YXR1cyI6IlBBU1NUSFJPVUdIX05PX1ZBTElEQVRJT04iLCJyZWN5Y2xlZENvbnRlbnRQY3QiOm51bGwsInJlcGFpcmFiaWxpdHlJbmRleCI6bnVsbCwid2FybmluZ3MiOlt7ImNvZGUiOiJiYXR0ZXJ5LnJlY3ljbGVkX2NvbnRlbnQubm90X3lldF9iaW5kaW5nIiwiZmllbGQiOiIvcGxhY2VkT25NYXJrZXREYXRlIiwibWVzc2FnZSI6Ik5vIEFydC4gOCBtaW5pbXVtIHJlY3ljbGVkIHNoYXJlIGJpbmRzIGEgYmF0dGVyeSBwbGFjZWQgb24gdGhlIEVVIG1hcmtldCBvbiAyMDI2LTAyLTEwLiBSdWxlc2V0ICdiYXR0ZXJ5LXJlY3ljbGVkLWNvbnRlbnQtYXJ0OC0yJyBhcHBsaWVzIGZyb20gMjAzMS0wOC0xOC4ifV19LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjEwODA4NTQyN1oiLCJmYWNpbGl0eSI6eyJhZGRyZXNzIjoiV2Vya3N0cmFzc2UgMiwgMTAxMTUgQmVybGluIiwiY291bnRyeSI6IkRFIiwibmFtZSI6IkRlbW8gUGxhbnQiLCJzY2hlbWUiOiJnbG4iLCJ2YWx1ZSI6IjQwMTIzNDUwMDAwMDkifSwiaWQiOiIwMWExYTMwNy1iNjdjLTcwNmEtYTU1ZS02ODk3MTJmY2QxMjIiLCJqd3NTaWduYXR1cmUiOm51bGwsImxpbnRSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMTEyNTMxMDI2WiIsInBhY2tWZXJzaW9uIjoiMS4xLjAifSwibWFudWZhY3R1cmVyIjp7ImFkZHJlc3MiOiJJbmR1c3RyaWVzdHJhc3NlIDgsIDQ3MDU5IER1aXNidXJnLCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vcmhlaW5wb3dlci5leGFtcGxlLy53ZWxsLWtub3duL2RpZC5qc29uIiwibmFtZSI6IlJoZWlucG93ZXIgQmF0dGVyaWVuIEdtYkgifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJMZWFkIiwicmVjeWNsZWRQY3QiOjgyLCJ3ZWlnaHRLZyI6Mzh9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiU3VsZnVyaWMgYWNpZCBlbGVjdHJvbHl0ZSIsInJlY3ljbGVkUGN0IjowLCJ3ZWlnaHRLZyI6OX0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJQb2x5cHJvcHlsZW5lIGNhc2UiLCJyZWN5Y2xlZFBjdCI6NDUsIndlaWdodEtnIjozLjJ9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTAyLTEwIiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYW5vZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiNzQzOS05Mi0xIiwibmFtZSI6IlNwb25nZSBsZWFkIChuZWdhdGl2ZSBhY3RpdmUgbWF0ZXJpYWwpIiwid2VpZ2h0UGN0IjoxMDB9XSwiYmF0dGVyeUNoZW1pc3RyeSI6ImxlYWQtYWNpZCIsImJhdHRlcnlNb2RlbElkIjoiUlAtVlJMQS0xMi0yMDAiLCJiYXR0ZXJ5UGFzc3BvcnROdW1iZXIiOiJVUk46T0RMOkJBVFQ6MDk1OTAwMDAwMDAwMzg6T0RMLUlORC1QQi0yMDI2LVExLTAwNyIsImJhdHRlcnlTdGF0dXMiOiJvcmlnaW5hbCIsImJhdHRlcnlUeXBlIjoiaW5kdXN0cmlhbCIsImJhdHRlcnlXZWlnaHRLZyI6NTIsImNhdGhvZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiMTMwOS02MC0wIiwibmFtZSI6IkxlYWQgZGlveGlkZSAocG9zaXRpdmUgYWN0aXZlIG1hdGVyaWFsKSIsIndlaWdodFBjdCI6MTAwfV0sImNvbW1lcmNpYWxXYXJyYW50eVBlcmlvZE1vbnRocyI6NjAsImNvbXBvbmVudFBhcnROdW1iZXJzIjpbIlJQLVBMQVRFLVBPUy0yMDAiLCJSUC1QTEFURS1ORUctMjAwIiwiUlAtQ0FTRS0xMi0yMDAiLCJSUC1WQUxWRS1WUkxBIl0sImNyaXRpY2FsUmF3TWF0ZXJpYWxzIjpbeyJjYXNOdW1iZXIiOiI3NDQwLTM2LTAiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDTiIsIm5hbWUiOiJBbnRpbW9ueSIsIndlaWdodEdyYW1zIjozODB9XSwiY3ljbGVMaWZlVGVzdENSYXRlIjowLjEsImRpc2Fzc2VtYmx5SW5zdHJ1Y3Rpb25zVXJsIjoiaHR0cHM6Ly9yaGVpbnBvd2VyLmV4YW1wbGUvc2VydmljZS92cmxhLTEyLTIwMC9kaXNhc3NlbWJseSIsImVsZWN0cm9seXRlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6Ijc2NjQtOTMtOSIsIm5hbWUiOiJTdWxmdXJpYyBhY2lkIChhcXVlb3VzLCB-MzclKSIsIndlaWdodFBjdCI6Mzd9LHsiY2FzTnVtYmVyIjoiNzczMi0xOC01IiwibmFtZSI6IldhdGVyIiwid2VpZ2h0UGN0Ijo2M31dLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIFJQLTIwMjYtSU5ELTAwMTQgZGF0ZWQgMjAyNi0wMS0yMCwgcGVyIFJlZ3VsYXRpb24gKEVVKSAyMDIzLzE1NDIgQXJ0LiAxOCIsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjEyMDAsImV4cGVjdGVkTGlmZXRpbWVSZWZlcmVuY2VUZXN0IjoiSUVDIDYwODk2LTIxLzIyLCAyNSBkZWdDLCA1MCUgRG9EIiwiaGF6YXJkU3ltYm9sIjoibGVhZCIsImhhemFyZG91c1N1YnN0YW5jZXMiOlt7ImNhc051bWJlciI6Ijc0MzktOTItMSIsImNvbmNlbnRyYXRpb25QY3QiOjY4LCJuYW1lIjoiTGVhZCJ9LHsiY2FzTnVtYmVyIjoiNzY2NC05My05IiwiY29uY2VudHJhdGlvblBjdCI6MTcsIm5hbWUiOiJTdWxmdXJpYyBhY2lkIn1dLCJpbml0aWFsUm91bmRUcmlwRWZmaWNpZW5jeVBjdCI6ODIsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjozLjUsImludGVybmFsUGFja1Jlc2lzdGFuY2VNb2htIjo0LjIsIm1hbnVmYWN0dXJpbmdEYXRlIjoiMjAyNi0wMS0wOFQwMDowMDowMFoiLCJtYW51ZmFjdHVyaW5nUGxhY2UiOiJEdWlzYnVyZywgREUgKHBsYW50IERVLTMpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IFBiIHN5bWJvbCBwZXIgQXJ0LiAxMyg1KTsgY2FwYWNpdHkgMjAwIEFoIC8gMTIgViIsIm1heGltdW1Wb2x0YWdlViI6MTQuNCwibWluaW1hbFZvbHRhZ2VWIjoxMC41LCJub21pbmFsQ2FwYWNpdHlBaCI6MjAwLCJub21pbmFsVm9sdGFnZVYiOjEyLCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NDAsIm1pbkMiOi0xNX0sIm5vdEluVXNlVGVtcGVyYXR1cmVSZWZlcmVuY2VUZXN0IjoiSUVDIDYwODk2LTIxOjIwMDQgc3RvcmFnZSBjbGF1c2UiLCJvcmlnaW5hbFBvd2VyQ2FwYWJpbGl0eVciOjE4MDAsInBvd2VyTGltaXRNYXhXIjoyNDAwLCJwb3dlckxpbWl0TWluVyI6NTAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwMzgiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6Mi40LCJyYXRlZEVuZXJneVdoIjoyNDAwLCJyZWN5Y2xlZENvbnRlbnRDb2JhbHRQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudExpdGhpdW1QY3QiOm51bGwsInJlY3ljbGVkQ29udGVudE5pY2tlbFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50UmVwb3J0aW5nWWVhciI6MjAyNiwicmVuZXdhYmxlQ29udGVudFBjdCI6Miwicm91bmRUcmlwRWZmaWNpZW5jeUF0SGFsZkN5Y2xlTGlmZVBjdCI6NzQsInNhZmV0eU1lYXN1cmVzIjoiQWNpZCBoYXphcmQuIEZhY2Ugc2hpZWxkIGFuZCBhY2lkLXJlc2lzdGFudCBnbG92ZXMuIERvIG5vdCB0aXAuIFZlbnQgaHlkcm9nZW4gYmVmb3JlIHNlcnZpY2luZy4iLCJzcGFyZVBhcnRzQ29udGFjdHMiOiJzZXJ2aWNlQHJoZWlucG93ZXIuZXhhbXBsZTsgKzQ5IDIwMyA1NTUwIDgwMCIsInN0YXRlT2ZIZWFsdGhQY3QiOm51bGwsInRlc3RSZXBvcnRSZXN1bHRzIjoiSUVDIDYwODk2LTIxLzIyOjIwMDQgcGFzczsgVU4gMjgwMCAobm9uLXNwaWxsYWJsZSkgcGFzczsgcmVwb3J0IFJQLVRSLTIwMjYtMDAyMSIsInVzYWJsZUV4dGluZ3Vpc2hpbmdBZ2VudCI6IkNPMiwgQUJDIGRyeSBwb3dkZXIsIG9yIHdhdGVyOyBuZXV0cmFsaXNlIGFjaWQgc3BpbGxzIHdpdGggc29kYSBhc2giLCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IkxlYWQtYWNpZCBiYXR0ZXJ5IC0gcmV0dXJuIHRvIHJldGFpbGVyIG9yIGF1dGhvcmlzZWQgY29sbGVjdG9yIGZvciByZWN5Y2xpbmcuIE5ldmVyIGRpc3Bvc2UgaW4gaG91c2Vob2xkIHdhc3RlLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDQ1Ny4ifSwicHJvZHVjdE5hbWUiOiJPZGFsIEluZHVzdHJpYWwgU3RhbmRieSBCYXR0ZXJ5IDEyVi0yMDBBaCAoVlJMQSkiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMTczNTU0NzM0WiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDAzOC8yMS83MDZhYTU1ZTY4OTcxMmZjZDEyMiIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjozfSwicmV0ZW50aW9uTG9ja2VkIjp0cnVlLCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuMTczNTU0NzM0WiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMTczNTU0NzM0WiIsInZlcnNpb24iOjF9.G9Xwozbdcl8A1vQTUkgncQp2iTrIGx_pl1x_Ed74t4i9DqXrfM6JYyaTQdCohp1T2LK-73yC2IxW3vW2FfPmCQ", "manufacturer": {"name": "Rheinpower Batterien GmbH", "address": "Industriestrasse 8, 47059 Duisburg, DE", "country": null, "didWebUrl": "https://rheinpower.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85072000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:14.173554734Z", "retentionLocked": true, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-02-10. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:14.112526945Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "industrial", "hazardSymbol": "lead", "productGroup": "battery", "anodeMaterial": [{"name": "Sponge lead (negative active material)", "casNumber": "7439-92-1", "weightPct": 100.0}], "batteryStatus": "original", "ratedEnergyWh": 2400.0, "batteryModelId": "RP-VRLA-12-200", "powerLimitMaxW": 2400.0, "powerLimitMinW": 50.0, "safetyMeasures": "Acid hazard. Face shield and acid-resistant gloves. Do not tip. Vent hydrogen before servicing.", "batteryWeightKg": 52.0, "cathodeMaterial": [{"name": "Lead dioxide (positive active material)", "casNumber": "1309-60-0", "weightPct": 100.0}], "maximumVoltageV": 14.4, "minimalVoltageV": 10.5, "nominalVoltageV": 12.0, "batteryChemistry": "lead-acid", "ratedCapacityKwh": 2.4, "stateOfHealthPct": null, "manufacturingDate": "2026-01-08T00:00:00Z", "nominalCapacityAh": 200.0, "productIdentifier": {"gtin": "09590000000038", "scheme": "gs1"}, "testReportResults": "IEC 60896-21/22:2004 pass; UN 2800 (non-spillable) pass; report RP-TR-2026-0021", "cycleLifeTestCRate": 0.1, "manufacturingPlace": "Duisburg, DE (plant DU-3)", "markingInformation": "Separate-collection symbol per Art. 13(4); Pb symbol per Art. 13(5); capacity 200 Ah / 12 V", "sparePartsContacts": "service@rheinpower.example; +49 203 5550 800", "electrolyteMaterial": [{"name": "Sulfuric acid (aqueous, ~37%)", "casNumber": "7664-93-9", "weightPct": 37.0}, {"name": "Water", "casNumber": "7732-18-5", "weightPct": 63.0}], "hazardousSubstances": [{"name": "Lead", "casNumber": "7439-92-1", "concentrationPct": 68.0}, {"name": "Sulfuric acid", "casNumber": "7664-93-9", "concentrationPct": 17.0}], "renewableContentPct": 2.0, "componentPartNumbers": ["RP-PLATE-POS-200", "RP-PLATE-NEG-200", "RP-CASE-12-200", "RP-VALVE-VRLA"], "criticalRawMaterials": [{"name": "Antimony", "casNumber": "7440-36-0", "weightGrams": 380.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000038:ODL-IND-PB-2026-Q1-007", "expectedLifetimeCycles": 1200, "wasteBatteryInformation": "Lead-acid battery - return to retailer or authorised collector for recycling. Never dispose in household waste. Producer registration DE-BATT-00457.", "notInUseTemperatureRange": {"maxC": 40.0, "minC": -15.0}, "originalPowerCapabilityW": 1800.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "CO2, ABC dry powder, or water; neutralise acid spills with soda ash", "euDeclarationOfConformity": "EU-DoC RP-2026-IND-0014 dated 2026-01-20, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://rheinpower.example/service/vrla-12-200/disassembly", "internalCellResistanceMohm": 3.5, "internalPackResistanceMohm": 4.2, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "IEC 60896-21/22, 25 degC, 50% DoD", "initialRoundTripEfficiencyPct": 82.0, "commercialWarrantyPeriodMonths": 60, "notInUseTemperatureReferenceTest": "IEC 60896-21:2004 storage clause", "roundTripEfficiencyAtHalfCycleLifePct": 74.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-02-10", "publicJwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELVBCLTIwMjYtUTEtMDA3IiwiY28yZVBlclVuaXQiOm51bGwsImNvbW1vZGl0eUNvZGUiOiI4NTA3MjAwMCIsImNvbXBsaWFuY2VSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMTEyNTI2OTQ1WiIsImNvMmVTY29yZSI6bnVsbCwiY29tcGxpYW5jZVN0YXR1cyI6IlBBU1NUSFJPVUdIX05PX1ZBTElEQVRJT04iLCJyZWN5Y2xlZENvbnRlbnRQY3QiOm51bGwsInJlcGFpcmFiaWxpdHlJbmRleCI6bnVsbCwid2FybmluZ3MiOlt7ImNvZGUiOiJiYXR0ZXJ5LnJlY3ljbGVkX2NvbnRlbnQubm90X3lldF9iaW5kaW5nIiwiZmllbGQiOiIvcGxhY2VkT25NYXJrZXREYXRlIiwibWVzc2FnZSI6Ik5vIEFydC4gOCBtaW5pbXVtIHJlY3ljbGVkIHNoYXJlIGJpbmRzIGEgYmF0dGVyeSBwbGFjZWQgb24gdGhlIEVVIG1hcmtldCBvbiAyMDI2LTAyLTEwLiBSdWxlc2V0ICdiYXR0ZXJ5LXJlY3ljbGVkLWNvbnRlbnQtYXJ0OC0yJyBhcHBsaWVzIGZyb20gMjAzMS0wOC0xOC4ifV19LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjEwODA4NTQyN1oiLCJmYWNpbGl0eSI6eyJhZGRyZXNzIjoiV2Vya3N0cmFzc2UgMiwgMTAxMTUgQmVybGluIiwiY291bnRyeSI6IkRFIiwibmFtZSI6IkRlbW8gUGxhbnQiLCJzY2hlbWUiOiJnbG4iLCJ2YWx1ZSI6IjQwMTIzNDUwMDAwMDkifSwiaWQiOiIwMWExYTMwNy1iNjdjLTcwNmEtYTU1ZS02ODk3MTJmY2QxMjIiLCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IkluZHVzdHJpZXN0cmFzc2UgOCwgNDcwNTkgRHVpc2J1cmcsIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly9yaGVpbnBvd2VyLmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiUmhlaW5wb3dlciBCYXR0ZXJpZW4gR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkxlYWQiLCJyZWN5Y2xlZFBjdCI6ODIsIndlaWdodEtnIjozOH0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJTdWxmdXJpYyBhY2lkIGVsZWN0cm9seXRlIiwicmVjeWNsZWRQY3QiOjAsIndlaWdodEtnIjo5fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IlBvbHlwcm9weWxlbmUgY2FzZSIsInJlY3ljbGVkUGN0Ijo0NSwid2VpZ2h0S2ciOjMuMn1dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInBsYWNlZE9uTWFya2V0RGF0ZSI6IjIwMjYtMDItMTAiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJiYXR0ZXJ5Q2hlbWlzdHJ5IjoibGVhZC1hY2lkIiwiYmF0dGVyeU1vZGVsSWQiOiJSUC1WUkxBLTEyLTIwMCIsImJhdHRlcnlQYXNzcG9ydE51bWJlciI6IlVSTjpPREw6QkFUVDowOTU5MDAwMDAwMDAzODpPREwtSU5ELVBCLTIwMjYtUTEtMDA3IiwiYmF0dGVyeVR5cGUiOiJpbmR1c3RyaWFsIiwiYmF0dGVyeVdlaWdodEtnIjo1MiwiY29tbWVyY2lhbFdhcnJhbnR5UGVyaW9kTW9udGhzIjo2MCwiY3JpdGljYWxSYXdNYXRlcmlhbHMiOlt7ImNhc051bWJlciI6Ijc0NDAtMzYtMCIsImNvdW50cnlPZk9yaWdpbiI6IkNOIiwibmFtZSI6IkFudGltb255Iiwid2VpZ2h0R3JhbXMiOjM4MH1dLCJjeWNsZUxpZmVUZXN0Q1JhdGUiOjAuMSwiZXVEZWNsYXJhdGlvbk9mQ29uZm9ybWl0eSI6IkVVLURvQyBSUC0yMDI2LUlORC0wMDE0IGRhdGVkIDIwMjYtMDEtMjAsIHBlciBSZWd1bGF0aW9uIChFVSkgMjAyMy8xNTQyIEFydC4gMTgiLCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjoxMjAwLCJleHBlY3RlZExpZmV0aW1lUmVmZXJlbmNlVGVzdCI6IklFQyA2MDg5Ni0yMS8yMiwgMjUgZGVnQywgNTAlIERvRCIsImhhemFyZFN5bWJvbCI6ImxlYWQiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiI3NDM5LTkyLTEiLCJjb25jZW50cmF0aW9uUGN0Ijo2OCwibmFtZSI6IkxlYWQifSx7ImNhc051bWJlciI6Ijc2NjQtOTMtOSIsImNvbmNlbnRyYXRpb25QY3QiOjE3LCJuYW1lIjoiU3VsZnVyaWMgYWNpZCJ9XSwiaW5pdGlhbFJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjgyLCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6My41LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6NC4yLCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDEtMDhUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiRHVpc2J1cmcsIERFIChwbGFudCBEVS0zKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIHBlciBBcnQuIDEzKDQpOyBQYiBzeW1ib2wgcGVyIEFydC4gMTMoNSk7IGNhcGFjaXR5IDIwMCBBaCAvIDEyIFYiLCJtYXhpbXVtVm9sdGFnZVYiOjE0LjQsIm1pbmltYWxWb2x0YWdlViI6MTAuNSwibm9taW5hbENhcGFjaXR5QWgiOjIwMCwibm9taW5hbFZvbHRhZ2VWIjoxMiwibm90SW5Vc2VUZW1wZXJhdHVyZVJhbmdlIjp7Im1heEMiOjQwLCJtaW5DIjotMTV9LCJub3RJblVzZVRlbXBlcmF0dXJlUmVmZXJlbmNlVGVzdCI6IklFQyA2MDg5Ni0yMToyMDA0IHN0b3JhZ2UgY2xhdXNlIiwib3JpZ2luYWxQb3dlckNhcGFiaWxpdHlXIjoxODAwLCJwb3dlckxpbWl0TWF4VyI6MjQwMCwicG93ZXJMaW1pdE1pblciOjUwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDM4Iiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOjIuNCwicmF0ZWRFbmVyZ3lXaCI6MjQwMCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjIsInJvdW5kVHJpcEVmZmljaWVuY3lBdEhhbGZDeWNsZUxpZmVQY3QiOjc0LCJ1c2FibGVFeHRpbmd1aXNoaW5nQWdlbnQiOiJDTzIsIEFCQyBkcnkgcG93ZGVyLCBvciB3YXRlcjsgbmV1dHJhbGlzZSBhY2lkIHNwaWxscyB3aXRoIHNvZGEgYXNoIiwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJMZWFkLWFjaWQgYmF0dGVyeSAtIHJldHVybiB0byByZXRhaWxlciBvciBhdXRob3Jpc2VkIGNvbGxlY3RvciBmb3IgcmVjeWNsaW5nLiBOZXZlciBkaXNwb3NlIGluIGhvdXNlaG9sZCB3YXN0ZS4gUHJvZHVjZXIgcmVnaXN0cmF0aW9uIERFLUJBVFQtMDA0NTcuIn0sInByb2R1Y3ROYW1lIjoiT2RhbCBJbmR1c3RyaWFsIFN0YW5kYnkgQmF0dGVyeSAxMlYtMjAwQWggKFZSTEEpIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjE3MzU1NDczNFoiLCJxckNvZGVVcmwiOiJodHRwOi8vMTI3LjAuMC4xOjEvMDEvMDk1OTAwMDAwMDAwMzgvMjEvNzA2YWE1NWU2ODk3MTJmY2QxMjIiLCJyZXBhaXJhYmlsaXR5U2NvcmUiOnsib3ZlcmFsbCI6M30sInJldGVudGlvblVudGlsIjoiMjAzNi0xMS0wM1QyMDowOToxNC4xNzM1NTQ3MzRaIiwic2NoZW1hVmVyc2lvbiI6IjIuOC4wIiwic3RhdHVzIjoiYWN0aXZlIiwidXBkYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC4xNzM1NTQ3MzRaIiwidmVyc2lvbiI6MX0.XgFJkY8XptJaEsJoInGkAlWHMfr81JbdNxNQ0LF4PDw7ik2h_m4prtUaaWzqCc05O5McEunL7-tf-J5vZfHzDA", "repairabilityScore": {"overall": 3.0}, "disclosureSignatures": {"public+restricted+conformity": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELVBCLTIwMjYtUTEtMDA3IiwiY28yZVBlclVuaXQiOm51bGwsImNvbW1vZGl0eUNvZGUiOiI4NTA3MjAwMCIsImNvbXBsaWFuY2VSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMTEyNTI2OTQ1WiIsImNvMmVTY29yZSI6bnVsbCwiY29tcGxpYW5jZVN0YXR1cyI6IlBBU1NUSFJPVUdIX05PX1ZBTElEQVRJT04iLCJyZWN5Y2xlZENvbnRlbnRQY3QiOm51bGwsInJlcGFpcmFiaWxpdHlJbmRleCI6bnVsbCwid2FybmluZ3MiOlt7ImNvZGUiOiJiYXR0ZXJ5LnJlY3ljbGVkX2NvbnRlbnQubm90X3lldF9iaW5kaW5nIiwiZmllbGQiOiIvcGxhY2VkT25NYXJrZXREYXRlIiwibWVzc2FnZSI6Ik5vIEFydC4gOCBtaW5pbXVtIHJlY3ljbGVkIHNoYXJlIGJpbmRzIGEgYmF0dGVyeSBwbGFjZWQgb24gdGhlIEVVIG1hcmtldCBvbiAyMDI2LTAyLTEwLiBSdWxlc2V0ICdiYXR0ZXJ5LXJlY3ljbGVkLWNvbnRlbnQtYXJ0OC0yJyBhcHBsaWVzIGZyb20gMjAzMS0wOC0xOC4ifV19LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjEwODA4NTQyN1oiLCJmYWNpbGl0eSI6eyJhZGRyZXNzIjoiV2Vya3N0cmFzc2UgMiwgMTAxMTUgQmVybGluIiwiY291bnRyeSI6IkRFIiwibmFtZSI6IkRlbW8gUGxhbnQiLCJzY2hlbWUiOiJnbG4iLCJ2YWx1ZSI6IjQwMTIzNDUwMDAwMDkifSwiaWQiOiIwMWExYTMwNy1iNjdjLTcwNmEtYTU1ZS02ODk3MTJmY2QxMjIiLCJsaW50UmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjExMjUzMTAyNloiLCJwYWNrVmVyc2lvbiI6IjEuMS4wIn0sIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiSW5kdXN0cmllc3RyYXNzZSA4LCA0NzA1OSBEdWlzYnVyZywgREUiLCJjb3VudHJ5IjpudWxsLCJkaWRXZWJVcmwiOiJodHRwczovL3JoZWlucG93ZXIuZXhhbXBsZS8ud2VsbC1rbm93bi9kaWQuanNvbiIsIm5hbWUiOiJSaGVpbnBvd2VyIEJhdHRlcmllbiBHbWJIIn0sIm1hdGVyaWFscyI6W3siY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiTGVhZCIsInJlY3ljbGVkUGN0Ijo4Miwid2VpZ2h0S2ciOjM4fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IlN1bGZ1cmljIGFjaWQgZWxlY3Ryb2x5dGUiLCJyZWN5Y2xlZFBjdCI6MCwid2VpZ2h0S2ciOjl9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiUG9seXByb3B5bGVuZSBjYXNlIiwicmVjeWNsZWRQY3QiOjQ1LCJ3ZWlnaHRLZyI6My4yfV0sIm9wZXJhdG9ySWRlbnRpZmllciI6IkRFMTIzNDU2Nzg5IiwicGxhY2VkT25NYXJrZXREYXRlIjoiMjAyNi0wMi0xMCIsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImFub2RlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6Ijc0MzktOTItMSIsIm5hbWUiOiJTcG9uZ2UgbGVhZCAobmVnYXRpdmUgYWN0aXZlIG1hdGVyaWFsKSIsIndlaWdodFBjdCI6MTAwfV0sImJhdHRlcnlDaGVtaXN0cnkiOiJsZWFkLWFjaWQiLCJiYXR0ZXJ5TW9kZWxJZCI6IlJQLVZSTEEtMTItMjAwIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDM4Ok9ETC1JTkQtUEItMjAyNi1RMS0wMDciLCJiYXR0ZXJ5VHlwZSI6ImluZHVzdHJpYWwiLCJiYXR0ZXJ5V2VpZ2h0S2ciOjUyLCJjYXRob2RlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6IjEzMDktNjAtMCIsIm5hbWUiOiJMZWFkIGRpb3hpZGUgKHBvc2l0aXZlIGFjdGl2ZSBtYXRlcmlhbCkiLCJ3ZWlnaHRQY3QiOjEwMH1dLCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjYwLCJjb21wb25lbnRQYXJ0TnVtYmVycyI6WyJSUC1QTEFURS1QT1MtMjAwIiwiUlAtUExBVEUtTkVHLTIwMCIsIlJQLUNBU0UtMTItMjAwIiwiUlAtVkFMVkUtVlJMQSJdLCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQ0MC0zNi0wIiwiY291bnRyeU9mT3JpZ2luIjoiQ04iLCJuYW1lIjoiQW50aW1vbnkiLCJ3ZWlnaHRHcmFtcyI6MzgwfV0sImN5Y2xlTGlmZVRlc3RDUmF0ZSI6MC4xLCJkaXNhc3NlbWJseUluc3RydWN0aW9uc1VybCI6Imh0dHBzOi8vcmhlaW5wb3dlci5leGFtcGxlL3NlcnZpY2UvdnJsYS0xMi0yMDAvZGlzYXNzZW1ibHkiLCJlbGVjdHJvbHl0ZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI3NjY0LTkzLTkiLCJuYW1lIjoiU3VsZnVyaWMgYWNpZCAoYXF1ZW91cywgfjM3JSkiLCJ3ZWlnaHRQY3QiOjM3fSx7ImNhc051bWJlciI6Ijc3MzItMTgtNSIsIm5hbWUiOiJXYXRlciIsIndlaWdodFBjdCI6NjN9XSwiZXVEZWNsYXJhdGlvbk9mQ29uZm9ybWl0eSI6IkVVLURvQyBSUC0yMDI2LUlORC0wMDE0IGRhdGVkIDIwMjYtMDEtMjAsIHBlciBSZWd1bGF0aW9uIChFVSkgMjAyMy8xNTQyIEFydC4gMTgiLCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjoxMjAwLCJleHBlY3RlZExpZmV0aW1lUmVmZXJlbmNlVGVzdCI6IklFQyA2MDg5Ni0yMS8yMiwgMjUgZGVnQywgNTAlIERvRCIsImhhemFyZFN5bWJvbCI6ImxlYWQiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiI3NDM5LTkyLTEiLCJjb25jZW50cmF0aW9uUGN0Ijo2OCwibmFtZSI6IkxlYWQifSx7ImNhc051bWJlciI6Ijc2NjQtOTMtOSIsImNvbmNlbnRyYXRpb25QY3QiOjE3LCJuYW1lIjoiU3VsZnVyaWMgYWNpZCJ9XSwiaW5pdGlhbFJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjgyLCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6My41LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6NC4yLCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDEtMDhUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiRHVpc2J1cmcsIERFIChwbGFudCBEVS0zKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIHBlciBBcnQuIDEzKDQpOyBQYiBzeW1ib2wgcGVyIEFydC4gMTMoNSk7IGNhcGFjaXR5IDIwMCBBaCAvIDEyIFYiLCJtYXhpbXVtVm9sdGFnZVYiOjE0LjQsIm1pbmltYWxWb2x0YWdlViI6MTAuNSwibm9taW5hbENhcGFjaXR5QWgiOjIwMCwibm9taW5hbFZvbHRhZ2VWIjoxMiwibm90SW5Vc2VUZW1wZXJhdHVyZVJhbmdlIjp7Im1heEMiOjQwLCJtaW5DIjotMTV9LCJub3RJblVzZVRlbXBlcmF0dXJlUmVmZXJlbmNlVGVzdCI6IklFQyA2MDg5Ni0yMToyMDA0IHN0b3JhZ2UgY2xhdXNlIiwib3JpZ2luYWxQb3dlckNhcGFiaWxpdHlXIjoxODAwLCJwb3dlckxpbWl0TWF4VyI6MjQwMCwicG93ZXJMaW1pdE1pblciOjUwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDM4Iiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOjIuNCwicmF0ZWRFbmVyZ3lXaCI6MjQwMCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjIsInJvdW5kVHJpcEVmZmljaWVuY3lBdEhhbGZDeWNsZUxpZmVQY3QiOjc0LCJzYWZldHlNZWFzdXJlcyI6IkFjaWQgaGF6YXJkLiBGYWNlIHNoaWVsZCBhbmQgYWNpZC1yZXNpc3RhbnQgZ2xvdmVzLiBEbyBub3QgdGlwLiBWZW50IGh5ZHJvZ2VuIGJlZm9yZSBzZXJ2aWNpbmcuIiwic3BhcmVQYXJ0c0NvbnRhY3RzIjoic2VydmljZUByaGVpbnBvd2VyLmV4YW1wbGU7ICs0OSAyMDMgNTU1MCA4MDAiLCJ0ZXN0UmVwb3J0UmVzdWx0cyI6IklFQyA2MDg5Ni0yMS8yMjoyMDA0IHBhc3M7IFVOIDI4MDAgKG5vbi1zcGlsbGFibGUpIHBhc3M7IHJlcG9ydCBSUC1UUi0yMDI2LTAwMjEiLCJ1c2FibGVFeHRpbmd1aXNoaW5nQWdlbnQiOiJDTzIsIEFCQyBkcnkgcG93ZGVyLCBvciB3YXRlcjsgbmV1dHJhbGlzZSBhY2lkIHNwaWxscyB3aXRoIHNvZGEgYXNoIiwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJMZWFkLWFjaWQgYmF0dGVyeSAtIHJldHVybiB0byByZXRhaWxlciBvciBhdXRob3Jpc2VkIGNvbGxlY3RvciBmb3IgcmVjeWNsaW5nLiBOZXZlciBkaXNwb3NlIGluIGhvdXNlaG9sZCB3YXN0ZS4gUHJvZHVjZXIgcmVnaXN0cmF0aW9uIERFLUJBVFQtMDA0NTcuIn0sInByb2R1Y3ROYW1lIjoiT2RhbCBJbmR1c3RyaWFsIFN0YW5kYnkgQmF0dGVyeSAxMlYtMjAwQWggKFZSTEEpIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjE3MzU1NDczNFoiLCJxckNvZGVVcmwiOiJodHRwOi8vMTI3LjAuMC4xOjEvMDEvMDk1OTAwMDAwMDAwMzgvMjEvNzA2YWE1NWU2ODk3MTJmY2QxMjIiLCJyZXBhaXJhYmlsaXR5U2NvcmUiOnsib3ZlcmFsbCI6M30sInJldGVudGlvbkxvY2tlZCI6dHJ1ZSwicmV0ZW50aW9uVW50aWwiOiIyMDM2LTExLTAzVDIwOjA5OjE0LjE3MzU1NDczNFoiLCJzY2hlbWFWZXJzaW9uIjoiMi44LjAiLCJzdGF0dXMiOiJhY3RpdmUiLCJ1cGRhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjE3MzU1NDczNFoiLCJ2ZXJzaW9uIjoxfQ.n_NJvvhDMGqobd6out-na1Ybymh_lP5cq0DDOaMeCBCCUR1BKUeBxym8umP4wPyErcxmI8QqiR25YRjkppl3AA", "public+restricted+individual": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELVBCLTIwMjYtUTEtMDA3IiwiY28yZVBlclVuaXQiOm51bGwsImNvbW1vZGl0eUNvZGUiOiI4NTA3MjAwMCIsImNvbXBsaWFuY2VSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMTEyNTI2OTQ1WiIsImNvMmVTY29yZSI6bnVsbCwiY29tcGxpYW5jZVN0YXR1cyI6IlBBU1NUSFJPVUdIX05PX1ZBTElEQVRJT04iLCJyZWN5Y2xlZENvbnRlbnRQY3QiOm51bGwsInJlcGFpcmFiaWxpdHlJbmRleCI6bnVsbCwid2FybmluZ3MiOlt7ImNvZGUiOiJiYXR0ZXJ5LnJlY3ljbGVkX2NvbnRlbnQubm90X3lldF9iaW5kaW5nIiwiZmllbGQiOiIvcGxhY2VkT25NYXJrZXREYXRlIiwibWVzc2FnZSI6Ik5vIEFydC4gOCBtaW5pbXVtIHJlY3ljbGVkIHNoYXJlIGJpbmRzIGEgYmF0dGVyeSBwbGFjZWQgb24gdGhlIEVVIG1hcmtldCBvbiAyMDI2LTAyLTEwLiBSdWxlc2V0ICdiYXR0ZXJ5LXJlY3ljbGVkLWNvbnRlbnQtYXJ0OC0yJyBhcHBsaWVzIGZyb20gMjAzMS0wOC0xOC4ifV19LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjEwODA4NTQyN1oiLCJmYWNpbGl0eSI6eyJhZGRyZXNzIjoiV2Vya3N0cmFzc2UgMiwgMTAxMTUgQmVybGluIiwiY291bnRyeSI6IkRFIiwibmFtZSI6IkRlbW8gUGxhbnQiLCJzY2hlbWUiOiJnbG4iLCJ2YWx1ZSI6IjQwMTIzNDUwMDAwMDkifSwiaWQiOiIwMWExYTMwNy1iNjdjLTcwNmEtYTU1ZS02ODk3MTJmY2QxMjIiLCJsaW50UmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjExMjUzMTAyNloiLCJwYWNrVmVyc2lvbiI6IjEuMS4wIn0sIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiSW5kdXN0cmllc3RyYXNzZSA4LCA0NzA1OSBEdWlzYnVyZywgREUiLCJjb3VudHJ5IjpudWxsLCJkaWRXZWJVcmwiOiJodHRwczovL3JoZWlucG93ZXIuZXhhbXBsZS8ud2VsbC1rbm93bi9kaWQuanNvbiIsIm5hbWUiOiJSaGVpbnBvd2VyIEJhdHRlcmllbiBHbWJIIn0sIm1hdGVyaWFscyI6W3siY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiTGVhZCIsInJlY3ljbGVkUGN0Ijo4Miwid2VpZ2h0S2ciOjM4fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IlN1bGZ1cmljIGFjaWQgZWxlY3Ryb2x5dGUiLCJyZWN5Y2xlZFBjdCI6MCwid2VpZ2h0S2ciOjl9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiUG9seXByb3B5bGVuZSBjYXNlIiwicmVjeWNsZWRQY3QiOjQ1LCJ3ZWlnaHRLZyI6My4yfV0sIm9wZXJhdG9ySWRlbnRpZmllciI6IkRFMTIzNDU2Nzg5IiwicGxhY2VkT25NYXJrZXREYXRlIjoiMjAyNi0wMi0xMCIsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImFub2RlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6Ijc0MzktOTItMSIsIm5hbWUiOiJTcG9uZ2UgbGVhZCAobmVnYXRpdmUgYWN0aXZlIG1hdGVyaWFsKSIsIndlaWdodFBjdCI6MTAwfV0sImJhdHRlcnlDaGVtaXN0cnkiOiJsZWFkLWFjaWQiLCJiYXR0ZXJ5TW9kZWxJZCI6IlJQLVZSTEEtMTItMjAwIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDM4Ok9ETC1JTkQtUEItMjAyNi1RMS0wMDciLCJiYXR0ZXJ5U3RhdHVzIjoib3JpZ2luYWwiLCJiYXR0ZXJ5VHlwZSI6ImluZHVzdHJpYWwiLCJiYXR0ZXJ5V2VpZ2h0S2ciOjUyLCJjYXRob2RlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6IjEzMDktNjAtMCIsIm5hbWUiOiJMZWFkIGRpb3hpZGUgKHBvc2l0aXZlIGFjdGl2ZSBtYXRlcmlhbCkiLCJ3ZWlnaHRQY3QiOjEwMH1dLCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjYwLCJjb21wb25lbnRQYXJ0TnVtYmVycyI6WyJSUC1QTEFURS1QT1MtMjAwIiwiUlAtUExBVEUtTkVHLTIwMCIsIlJQLUNBU0UtMTItMjAwIiwiUlAtVkFMVkUtVlJMQSJdLCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQ0MC0zNi0wIiwiY291bnRyeU9mT3JpZ2luIjoiQ04iLCJuYW1lIjoiQW50aW1vbnkiLCJ3ZWlnaHRHcmFtcyI6MzgwfV0sImN5Y2xlTGlmZVRlc3RDUmF0ZSI6MC4xLCJkaXNhc3NlbWJseUluc3RydWN0aW9uc1VybCI6Imh0dHBzOi8vcmhlaW5wb3dlci5leGFtcGxlL3NlcnZpY2UvdnJsYS0xMi0yMDAvZGlzYXNzZW1ibHkiLCJlbGVjdHJvbHl0ZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI3NjY0LTkzLTkiLCJuYW1lIjoiU3VsZnVyaWMgYWNpZCAoYXF1ZW91cywgfjM3JSkiLCJ3ZWlnaHRQY3QiOjM3fSx7ImNhc051bWJlciI6Ijc3MzItMTgtNSIsIm5hbWUiOiJXYXRlciIsIndlaWdodFBjdCI6NjN9XSwiZXVEZWNsYXJhdGlvbk9mQ29uZm9ybWl0eSI6IkVVLURvQyBSUC0yMDI2LUlORC0wMDE0IGRhdGVkIDIwMjYtMDEtMjAsIHBlciBSZWd1bGF0aW9uIChFVSkgMjAyMy8xNTQyIEFydC4gMTgiLCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjoxMjAwLCJleHBlY3RlZExpZmV0aW1lUmVmZXJlbmNlVGVzdCI6IklFQyA2MDg5Ni0yMS8yMiwgMjUgZGVnQywgNTAlIERvRCIsImhhemFyZFN5bWJvbCI6ImxlYWQiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiI3NDM5LTkyLTEiLCJjb25jZW50cmF0aW9uUGN0Ijo2OCwibmFtZSI6IkxlYWQifSx7ImNhc051bWJlciI6Ijc2NjQtOTMtOSIsImNvbmNlbnRyYXRpb25QY3QiOjE3LCJuYW1lIjoiU3VsZnVyaWMgYWNpZCJ9XSwiaW5pdGlhbFJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjgyLCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6My41LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6NC4yLCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDEtMDhUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiRHVpc2J1cmcsIERFIChwbGFudCBEVS0zKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIHBlciBBcnQuIDEzKDQpOyBQYiBzeW1ib2wgcGVyIEFydC4gMTMoNSk7IGNhcGFjaXR5IDIwMCBBaCAvIDEyIFYiLCJtYXhpbXVtVm9sdGFnZVYiOjE0LjQsIm1pbmltYWxWb2x0YWdlViI6MTAuNSwibm9taW5hbENhcGFjaXR5QWgiOjIwMCwibm9taW5hbFZvbHRhZ2VWIjoxMiwibm90SW5Vc2VUZW1wZXJhdHVyZVJhbmdlIjp7Im1heEMiOjQwLCJtaW5DIjotMTV9LCJub3RJblVzZVRlbXBlcmF0dXJlUmVmZXJlbmNlVGVzdCI6IklFQyA2MDg5Ni0yMToyMDA0IHN0b3JhZ2UgY2xhdXNlIiwib3JpZ2luYWxQb3dlckNhcGFiaWxpdHlXIjoxODAwLCJwb3dlckxpbWl0TWF4VyI6MjQwMCwicG93ZXJMaW1pdE1pblciOjUwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDM4Iiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOjIuNCwicmF0ZWRFbmVyZ3lXaCI6MjQwMCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjIsInJvdW5kVHJpcEVmZmljaWVuY3lBdEhhbGZDeWNsZUxpZmVQY3QiOjc0LCJzYWZldHlNZWFzdXJlcyI6IkFjaWQgaGF6YXJkLiBGYWNlIHNoaWVsZCBhbmQgYWNpZC1yZXNpc3RhbnQgZ2xvdmVzLiBEbyBub3QgdGlwLiBWZW50IGh5ZHJvZ2VuIGJlZm9yZSBzZXJ2aWNpbmcuIiwic3BhcmVQYXJ0c0NvbnRhY3RzIjoic2VydmljZUByaGVpbnBvd2VyLmV4YW1wbGU7ICs0OSAyMDMgNTU1MCA4MDAiLCJzdGF0ZU9mSGVhbHRoUGN0IjpudWxsLCJ1c2FibGVFeHRpbmd1aXNoaW5nQWdlbnQiOiJDTzIsIEFCQyBkcnkgcG93ZGVyLCBvciB3YXRlcjsgbmV1dHJhbGlzZSBhY2lkIHNwaWxscyB3aXRoIHNvZGEgYXNoIiwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJMZWFkLWFjaWQgYmF0dGVyeSAtIHJldHVybiB0byByZXRhaWxlciBvciBhdXRob3Jpc2VkIGNvbGxlY3RvciBmb3IgcmVjeWNsaW5nLiBOZXZlciBkaXNwb3NlIGluIGhvdXNlaG9sZCB3YXN0ZS4gUHJvZHVjZXIgcmVnaXN0cmF0aW9uIERFLUJBVFQtMDA0NTcuIn0sInByb2R1Y3ROYW1lIjoiT2RhbCBJbmR1c3RyaWFsIFN0YW5kYnkgQmF0dGVyeSAxMlYtMjAwQWggKFZSTEEpIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjE3MzU1NDczNFoiLCJxckNvZGVVcmwiOiJodHRwOi8vMTI3LjAuMC4xOjEvMDEvMDk1OTAwMDAwMDAwMzgvMjEvNzA2YWE1NWU2ODk3MTJmY2QxMjIiLCJyZXBhaXJhYmlsaXR5U2NvcmUiOnsib3ZlcmFsbCI6M30sInJldGVudGlvblVudGlsIjoiMjAzNi0xMS0wM1QyMDowOToxNC4xNzM1NTQ3MzRaIiwic2NoZW1hVmVyc2lvbiI6IjIuOC4wIiwic3RhdHVzIjoiYWN0aXZlIiwidXBkYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC4xNzM1NTQ3MzRaIiwidmVyc2lvbiI6MX0.YS8oT2TunNXW4KNWRLSPHoZkGHXh3bhvlyhPwSgNiiDhceo0kjimlN3keM6YMbRVSV3fdUvFk4Ug5Fg1JFjHCQ"}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}'); +INSERT INTO odal.passport VALUES ('01a1a307-b70c-70c1-b4a3-a616d161e997', 'battery', 'active', true, '2.8.0', NULL, NULL, 1, NULL, NULL, '2026-11-03 20:09:14.255214+00', '2036-11-03 20:09:14.279494+00', NULL, '2026-11-03 20:09:14.255677+00', '2026-11-03 20:09:14.316459+00', '2026-11-03 20:09:14.279494+00', '{"id": "01a1a307-b70c-70c1-b4a3-a616d161e997", "status": "active", "batchId": "VBX-EV-2026-0421", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.252942043Z", "materials": [{"name": "Lithium Nickel Manganese Cobalt Oxide (NMC 811)", "weightKg": 210.0, "recycledPct": 13.0, "countryOfOrigin": "DE"}, {"name": "Graphite/silicon anode", "weightKg": 95.0, "recycledPct": 4.0, "countryOfOrigin": "DE"}, {"name": "Aluminium pack housing", "weightKg": 85.0, "recycledPct": 71.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000045/21/70c1b4a3a616d161e997", "updatedAt": "2026-11-03T20:09:14.279493731Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.255216167Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Voltex EV Traction Battery 75 kWh", "publishedAt": "2026-11-03T20:09:14.279493731Z", "jwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJWQlgtRVYtMjAyNi0wNDIxIiwiY28yZVBlclVuaXQiOm51bGwsImNvbW1vZGl0eUNvZGUiOiI4NTA3NjAwMCIsImNvbXBsaWFuY2VSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMjU1MjE0NDE2WiIsImNvMmVTY29yZSI6bnVsbCwiY29tcGxpYW5jZVN0YXR1cyI6IlBBU1NUSFJPVUdIX05PX1ZBTElEQVRJT04iLCJyZWN5Y2xlZENvbnRlbnRQY3QiOm51bGwsInJlcGFpcmFiaWxpdHlJbmRleCI6bnVsbCwid2FybmluZ3MiOlt7ImNvZGUiOiJiYXR0ZXJ5LnJlY3ljbGVkX2NvbnRlbnQubm90X3lldF9iaW5kaW5nIiwiZmllbGQiOiIvcGxhY2VkT25NYXJrZXREYXRlIiwibWVzc2FnZSI6Ik5vIEFydC4gOCBtaW5pbXVtIHJlY3ljbGVkIHNoYXJlIGJpbmRzIGEgYmF0dGVyeSBwbGFjZWQgb24gdGhlIEVVIG1hcmtldCBvbiAyMDI2LTA3LTAxLiBSdWxlc2V0ICdiYXR0ZXJ5LXJlY3ljbGVkLWNvbnRlbnQtYXJ0OC0yJyBhcHBsaWVzIGZyb20gMjAzMS0wOC0xOC4ifV19LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjI1Mjk0MjA0M1oiLCJmYWNpbGl0eSI6eyJhZGRyZXNzIjoiV2Vya3N0cmFzc2UgMiwgMTAxMTUgQmVybGluIiwiY291bnRyeSI6IkRFIiwibmFtZSI6IkRlbW8gUGxhbnQiLCJzY2hlbWUiOiJnbG4iLCJ2YWx1ZSI6IjQwMTIzNDUwMDAwMDkifSwiaWQiOiIwMWExYTMwNy1iNzBjLTcwYzEtYjRhMy1hNjE2ZDE2MWU5OTciLCJqd3NTaWduYXR1cmUiOm51bGwsImxpbnRSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMjU1MjE2MTY3WiIsInBhY2tWZXJzaW9uIjoiMS4xLjAifSwibWFudWZhY3R1cmVyIjp7ImFkZHJlc3MiOiJXZXJrc3RyYXNzZSA1LCA4NTA1NSBJbmdvbHN0YWR0LCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vdm9sdGV4LmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiVm9sdGV4IEJhdHRlcnkgR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkxpdGhpdW0gTmlja2VsIE1hbmdhbmVzZSBDb2JhbHQgT3hpZGUgKE5NQyA4MTEpIiwicmVjeWNsZWRQY3QiOjEzLCJ3ZWlnaHRLZyI6MjEwfSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkdyYXBoaXRlL3NpbGljb24gYW5vZGUiLCJyZWN5Y2xlZFBjdCI6NCwid2VpZ2h0S2ciOjk1fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkFsdW1pbml1bSBwYWNrIGhvdXNpbmciLCJyZWN5Y2xlZFBjdCI6NzEsIndlaWdodEtnIjo4NX1dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInBsYWNlZE9uTWFya2V0RGF0ZSI6IjIwMjYtMDctMDEiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJhbm9kZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJuYW1lIjoiR3JhcGhpdGUiLCJ3ZWlnaHRQY3QiOjkwfSx7ImNhc051bWJlciI6IjEwMDk3LTI4LTYiLCJuYW1lIjoiU2lsaWNvbiBveGlkZSIsIndlaWdodFBjdCI6Nn0seyJjYXNOdW1iZXIiOiI5MDAzLTU1LTgiLCJuYW1lIjoiU0JSL0NNQyBiaW5kZXIiLCJ3ZWlnaHRQY3QiOjR9XSwiYmF0dGVyeUNoZW1pc3RyeSI6Ik5NQyIsImJhdHRlcnlNb2RlbElkIjoiVkJYLUVWNzUtTk1DODExLVA0Uzk2IiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDQ1OlZCWC1FVi0yMDI2LTA0MjEiLCJiYXR0ZXJ5U3RhdHVzIjoib3JpZ2luYWwiLCJiYXR0ZXJ5VHlwZSI6ImV2IiwiYmF0dGVyeVdlaWdodEtnIjo0NjgsImNhcGFjaXR5VGhyZXNob2xkRm9yRXhoYXVzdGlvblBjdCI6NzAsImNhdGhvZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiMzQ2NDE3LTk3LTgiLCJuYW1lIjoiTGlOaTAuOE1uMC4xQ28wLjFPMiIsIndlaWdodFBjdCI6OTYuNX0seyJjYXNOdW1iZXIiOiIyNDkzNy03OS05IiwibmFtZSI6IlBWREYgYmluZGVyIiwid2VpZ2h0UGN0IjoyfSx7ImNhc051bWJlciI6IjEzMzMtODYtNCIsIm5hbWUiOiJDb25kdWN0aXZlIGNhcmJvbiBibGFjayIsIndlaWdodFBjdCI6MS41fV0sImNvbW1lcmNpYWxXYXJyYW50eVBlcmlvZE1vbnRocyI6OTYsImNvbXBvbmVudFBhcnROdW1iZXJzIjpbIlZCWC1DRUxMLTQ2ODAtTk1DODExIiwiVkJYLU1PRFVMRS0yNFMiLCJWQlgtQk1TLU1BU1RFUi1WNCIsIlZCWC1QQUNLLUhWLTc1Il0sImNyaXRpY2FsUmF3TWF0ZXJpYWxzIjpbeyJjYXNOdW1iZXIiOiI3NDQwLTQ4LTQiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDRCIsIm5hbWUiOiJDb2JhbHQiLCJ3ZWlnaHRHcmFtcyI6NTIwMH0seyJjYXNOdW1iZXIiOiI3NDM5LTkzLTIiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDTCIsIm5hbWUiOiJMaXRoaXVtIiwid2VpZ2h0R3JhbXMiOjgxMDB9LHsiY2FzTnVtYmVyIjoiNzQ0MC0wMi0wIiwiY291bnRyeU9mT3JpZ2luIjoiSUQiLCJuYW1lIjoiTmlja2VsIiwid2VpZ2h0R3JhbXMiOjMyMDAwfSx7ImNhc051bWJlciI6Ijc3ODItNDItNSIsImNvdW50cnlPZk9yaWdpbiI6Ik1aIiwibmFtZSI6Ik5hdHVyYWwgZ3JhcGhpdGUiLCJ3ZWlnaHRHcmFtcyI6NzQwMDB9XSwiY3ljbGVMaWZlVGVzdENSYXRlIjoxLCJkaXNhc3NlbWJseUluc3RydWN0aW9uc1VybCI6Imh0dHBzOi8vdm9sdGV4LmV4YW1wbGUvc2VydmljZS9ldjc1L2Rpc2Fzc2VtYmx5IiwiZHluYW1pY1BlcmZvcm1hbmNlIjp7ImNhcGFjaXR5RmFkZVBjdCI6MC45LCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjoxNzg1LCJleHBlY3RlZExpZmV0aW1lWWVhcnMiOjkuNSwiaW50ZXJuYWxSZXNpc3RhbmNlSW5jcmVhc2VQY3QiOjEsImludGVybmFsUmVzaXN0YW5jZU1vaG0iOjQyLjQsInBvd2VyRmFkZVBjdCI6MC42LCJwb3dlclciOjE0ODAwMCwicmF0ZWRDYXBhY2l0eUFoIjoyMDksInJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjkzLjZ9LCJlbGVjdHJvbHl0ZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI5Ni00OS0xIiwibmFtZSI6IkV0aHlsZW5lIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6MzB9LHsiY2FzTnVtYmVyIjoiNjIzLTUzLTAiLCJuYW1lIjoiRXRoeWwgbWV0aHlsIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6NTV9LHsiY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUiLCJ3ZWlnaHRQY3QiOjEzfSx7ImNhc051bWJlciI6Ijg3Mi0zNi02IiwibmFtZSI6IlZpbnlsZW5lIGNhcmJvbmF0ZSBhZGRpdGl2ZSIsIndlaWdodFBjdCI6Mn1dLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIFZCWC0yMDI2LUVWLTAxNDIgZGF0ZWQgMjAyNi0wNS0xOCwgcGVyIFJlZ3VsYXRpb24gKEVVKSAyMDIzLzE1NDIgQXJ0LiAxOCIsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjE4MDAsImV4cGVjdGVkTGlmZXRpbWVSZWZlcmVuY2VUZXN0IjoiVU5FQ0UgR1RSIDIyIC8gSUVDIDYyNjYwLTE6MjAxOCwgMjUgZGVnQyIsImhhemFyZG91c1N1YnN0YW5jZXMiOlt7ImNhc051bWJlciI6Ijc3ODYtODEtNCIsImNvbmNlbnRyYXRpb25QY3QiOjIuNCwibmFtZSI6Ik5pY2tlbCBzdWxmYXRlIn0seyJjYXNOdW1iZXIiOiIxMDEyNC00My0zIiwiY29uY2VudHJhdGlvblBjdCI6MC45LCJuYW1lIjoiQ29iYWx0IHN1bGZhdGUifSx7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJjb25jZW50cmF0aW9uUGN0IjowLjksIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUifV0sImluaXRpYWxSb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5NCwiaW50ZXJuYWxDZWxsUmVzaXN0YW5jZU1vaG0iOjEuMSwiaW50ZXJuYWxQYWNrUmVzaXN0YW5jZU1vaG0iOjQyLCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDUtMTFUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiSW5nb2xzdGFkdCwgREUgKHBsYW50IElORy0xKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIHBlciBBcnQuIDEzKDQpOyBjYXBhY2l0eSA3NSBrV2g7IEhWIHdhcm5pbmcgbGFiZWwiLCJtYXhpbXVtVm9sdGFnZVYiOjQwMy4yLCJtaW5pbWFsVm9sdGFnZVYiOjI3MCwibm9taW5hbENhcGFjaXR5QWgiOjIxMSwibm9taW5hbFZvbHRhZ2VWIjozNTUsIm5vdEluVXNlVGVtcGVyYXR1cmVSYW5nZSI6eyJtYXhDIjo1NSwibWluQyI6LTMwfSwibm90SW5Vc2VUZW1wZXJhdHVyZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjI2NjAtMToyMDE4IHN0b3JhZ2UgcHJvZmlsZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6MTUwMDAwLCJwb3dlckxpbWl0TWF4VyI6MjIwMDAwLCJwb3dlckxpbWl0TWluVyI6NTAwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDQ1Iiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOjc1LCJyYXRlZEVuZXJneVdoIjo3NTAwMCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjE2LCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo4OCwic2FmZXR5TWVhc3VyZXMiOiJIaWdoLXZvbHRhZ2UgKD42MCBWIERDKS4gSFYtcXVhbGlmaWVkIHBlcnNvbm5lbCBvbmx5LiBTZXJ2aWNlIGRpc2Nvbm5lY3QgcGx1ZyByZW1vdmVkIGFuZCBsb2NrZWQgb3V0LiBJbnN1bGF0ZWQgUFBFIGNsYXNzIDAwLiIsInNwYXJlUGFydHNDb250YWN0cyI6Imh2LXNlcnZpY2VAdm9sdGV4LmV4YW1wbGU7ICs0OSA4NDEgNTU1MCA0MDAgKGNlcnRpZmllZCBIViB3b3Jrc2hvcHMgb25seSkiLCJzdGF0ZU9mSGVhbHRoIjp7InBhcmFtZXRlclNldCI6ImVsZWN0cmljVmVoaWNsZSIsInNvY2VQY3QiOjk5LjF9LCJzdGF0ZU9mSGVhbHRoUGN0IjpudWxsLCJ0ZXN0UmVwb3J0UmVzdWx0cyI6IklFQyA2MjY2MC0yOjIwMTggcGFzczsgSVNPIDY0NjktMToyMDE5IHBhc3M7IFVOIDM4LjMgcGFzczsgcmVwb3J0IFZCWC1UUi0yMDI2LTA1MDciLCJ1c2FibGVFeHRpbmd1aXNoaW5nQWdlbnQiOiJMYXJnZSB2b2x1bWVzIG9mIHdhdGVyIGZvciBjb29saW5nOyBkbyBub3QgdXNlIENPMiBvbiBjZWxsIGZpcmUiLCJ1c2FnZUhpc3RvcnkiOnsiY2hhcmdlRGlzY2hhcmdlQ3ljbGVzIjo4LCJuZWdhdGl2ZUV2ZW50cyI6W10sInN0YXRlT2ZDaGFyZ2UiOlt7InJlY29yZGVkQXQiOiIyMDI2LTA3LTE1VDA5OjAwOjAwWiIsInN0YXRlT2ZDaGFyZ2VQY3QiOjgyfSx7InJlY29yZGVkQXQiOiIyMDI2LTA3LTIyVDA5OjAwOjAwWiIsInN0YXRlT2ZDaGFyZ2VQY3QiOjY0fV19LCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IkVWIGJhdHRlcnkgLSBoYW5kbGVkIGJ5IHRoZSB2ZWhpY2xlIG1hbnVmYWN0dXJlciB0YWtlLWJhY2sgc2NoZW1lLiBEbyBub3Qgb3BlbiBvciBkaXNjaGFyZ2UuIENvbnRhY3QgaHYtc2VydmljZUB2b2x0ZXguZXhhbXBsZS4gUHJvZHVjZXIgcmVnaXN0cmF0aW9uIERFLUJBVFQtMDA5ODEuIn0sInByb2R1Y3ROYW1lIjoiVm9sdGV4IEVWIFRyYWN0aW9uIEJhdHRlcnkgNzUga1doIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjI3OTQ5MzczMVoiLCJxckNvZGVVcmwiOiJodHRwOi8vMTI3LjAuMC4xOjEvMDEvMDk1OTAwMDAwMDAwNDUvMjEvNzBjMWI0YTNhNjE2ZDE2MWU5OTciLCJyZXBhaXJhYmlsaXR5U2NvcmUiOnsib3ZlcmFsbCI6NS41fSwicmV0ZW50aW9uTG9ja2VkIjp0cnVlLCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuMjc5NDkzNzMxWiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMjc5NDkzNzMxWiIsInZlcnNpb24iOjF9.LIfiNtturpbmDIHCfCRaAxGyQUrBS8P8r83iZiR0hWhcKGeimVyYBCYrLWjy0wS6hdZBse-dnrTGsgtdrOQSBg", "manufacturer": {"name": "Voltex Battery GmbH", "address": "Werkstrasse 5, 85055 Ingolstadt, DE", "country": null, "didWebUrl": "https://voltex.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:14.279493731Z", "retentionLocked": true, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-07-01. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:14.255214416Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "ev", "productGroup": "battery", "usageHistory": {"stateOfCharge": [{"recordedAt": "2026-07-15T09:00:00Z", "stateOfChargePct": 82.0}, {"recordedAt": "2026-07-22T09:00:00Z", "stateOfChargePct": 64.0}], "negativeEvents": [], "chargeDischargeCycles": 8}, "anodeMaterial": [{"name": "Graphite", "casNumber": "7782-42-5", "weightPct": 90.0}, {"name": "Silicon oxide", "casNumber": "10097-28-6", "weightPct": 6.0}, {"name": "SBR/CMC binder", "casNumber": "9003-55-8", "weightPct": 4.0}], "batteryStatus": "original", "ratedEnergyWh": 75000.0, "stateOfHealth": {"socePct": 99.1, "parameterSet": "electricVehicle"}, "batteryModelId": "VBX-EV75-NMC811-P4S96", "powerLimitMaxW": 220000.0, "powerLimitMinW": 500.0, "safetyMeasures": "High-voltage (>60 V DC). HV-qualified personnel only. Service disconnect plug removed and locked out. Insulated PPE class 00.", "batteryWeightKg": 468.0, "cathodeMaterial": [{"name": "LiNi0.8Mn0.1Co0.1O2", "casNumber": "346417-97-8", "weightPct": 96.5}, {"name": "PVDF binder", "casNumber": "24937-79-9", "weightPct": 2.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 1.5}], "maximumVoltageV": 403.2, "minimalVoltageV": 270.0, "nominalVoltageV": 355.0, "batteryChemistry": "NMC", "ratedCapacityKwh": 75.0, "stateOfHealthPct": null, "manufacturingDate": "2026-05-11T00:00:00Z", "nominalCapacityAh": 211.0, "productIdentifier": {"gtin": "09590000000045", "scheme": "gs1"}, "testReportResults": "IEC 62660-2:2018 pass; ISO 6469-1:2019 pass; UN 38.3 pass; report VBX-TR-2026-0507", "cycleLifeTestCRate": 1.0, "dynamicPerformance": {"powerW": 148000.0, "powerFadePct": 0.6, "capacityFadePct": 0.9, "ratedCapacityAh": 209.0, "expectedLifetimeYears": 9.5, "expectedLifetimeCycles": 1785, "internalResistanceMohm": 42.4, "roundTripEfficiencyPct": 93.6, "internalResistanceIncreasePct": 1.0}, "manufacturingPlace": "Ingolstadt, DE (plant ING-1)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 75 kWh; HV warning label", "sparePartsContacts": "hv-service@voltex.example; +49 841 5550 400 (certified HV workshops only)", "electrolyteMaterial": [{"name": "Ethylene carbonate", "casNumber": "96-49-1", "weightPct": 30.0}, {"name": "Ethyl methyl carbonate", "casNumber": "623-53-0", "weightPct": 55.0}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "weightPct": 13.0}, {"name": "Vinylene carbonate additive", "casNumber": "872-36-6", "weightPct": 2.0}], "hazardousSubstances": [{"name": "Nickel sulfate", "casNumber": "7786-81-4", "concentrationPct": 2.4}, {"name": "Cobalt sulfate", "casNumber": "10124-43-3", "concentrationPct": 0.9}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.9}], "renewableContentPct": 16.0, "componentPartNumbers": ["VBX-CELL-4680-NMC811", "VBX-MODULE-24S", "VBX-BMS-MASTER-V4", "VBX-PACK-HV-75"], "criticalRawMaterials": [{"name": "Cobalt", "casNumber": "7440-48-4", "weightGrams": 5200.0, "countryOfOrigin": "CD"}, {"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 8100.0, "countryOfOrigin": "CL"}, {"name": "Nickel", "casNumber": "7440-02-0", "weightGrams": 32000.0, "countryOfOrigin": "ID"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 74000.0, "countryOfOrigin": "MZ"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000045:VBX-EV-2026-0421", "expectedLifetimeCycles": 1800, "wasteBatteryInformation": "EV battery - handled by the vehicle manufacturer take-back scheme. Do not open or discharge. Contact hv-service@voltex.example. Producer registration DE-BATT-00981.", "notInUseTemperatureRange": {"maxC": 55.0, "minC": -30.0}, "originalPowerCapabilityW": 150000.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Large volumes of water for cooling; do not use CO2 on cell fire", "euDeclarationOfConformity": "EU-DoC VBX-2026-EV-0142 dated 2026-05-18, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://voltex.example/service/ev75/disassembly", "internalCellResistanceMohm": 1.1, "internalPackResistanceMohm": 42.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "UNECE GTR 22 / IEC 62660-1:2018, 25 degC", "initialRoundTripEfficiencyPct": 94.0, "commercialWarrantyPeriodMonths": 96, "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage profile", "capacityThresholdForExhaustionPct": 70.0, "roundTripEfficiencyAtHalfCycleLifePct": 88.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-07-01", "publicJwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJWQlgtRVYtMjAyNi0wNDIxIiwiY28yZVBlclVuaXQiOm51bGwsImNvbW1vZGl0eUNvZGUiOiI4NTA3NjAwMCIsImNvbXBsaWFuY2VSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMjU1MjE0NDE2WiIsImNvMmVTY29yZSI6bnVsbCwiY29tcGxpYW5jZVN0YXR1cyI6IlBBU1NUSFJPVUdIX05PX1ZBTElEQVRJT04iLCJyZWN5Y2xlZENvbnRlbnRQY3QiOm51bGwsInJlcGFpcmFiaWxpdHlJbmRleCI6bnVsbCwid2FybmluZ3MiOlt7ImNvZGUiOiJiYXR0ZXJ5LnJlY3ljbGVkX2NvbnRlbnQubm90X3lldF9iaW5kaW5nIiwiZmllbGQiOiIvcGxhY2VkT25NYXJrZXREYXRlIiwibWVzc2FnZSI6Ik5vIEFydC4gOCBtaW5pbXVtIHJlY3ljbGVkIHNoYXJlIGJpbmRzIGEgYmF0dGVyeSBwbGFjZWQgb24gdGhlIEVVIG1hcmtldCBvbiAyMDI2LTA3LTAxLiBSdWxlc2V0ICdiYXR0ZXJ5LXJlY3ljbGVkLWNvbnRlbnQtYXJ0OC0yJyBhcHBsaWVzIGZyb20gMjAzMS0wOC0xOC4ifV19LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjI1Mjk0MjA0M1oiLCJmYWNpbGl0eSI6eyJhZGRyZXNzIjoiV2Vya3N0cmFzc2UgMiwgMTAxMTUgQmVybGluIiwiY291bnRyeSI6IkRFIiwibmFtZSI6IkRlbW8gUGxhbnQiLCJzY2hlbWUiOiJnbG4iLCJ2YWx1ZSI6IjQwMTIzNDUwMDAwMDkifSwiaWQiOiIwMWExYTMwNy1iNzBjLTcwYzEtYjRhMy1hNjE2ZDE2MWU5OTciLCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDUsIDg1MDU1IEluZ29sc3RhZHQsIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly92b2x0ZXguZXhhbXBsZS8ud2VsbC1rbm93bi9kaWQuanNvbiIsIm5hbWUiOiJWb2x0ZXggQmF0dGVyeSBHbWJIIn0sIm1hdGVyaWFscyI6W3siY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiTGl0aGl1bSBOaWNrZWwgTWFuZ2FuZXNlIENvYmFsdCBPeGlkZSAoTk1DIDgxMSkiLCJyZWN5Y2xlZFBjdCI6MTMsIndlaWdodEtnIjoyMTB9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiR3JhcGhpdGUvc2lsaWNvbiBhbm9kZSIsInJlY3ljbGVkUGN0Ijo0LCJ3ZWlnaHRLZyI6OTV9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiQWx1bWluaXVtIHBhY2sgaG91c2luZyIsInJlY3ljbGVkUGN0Ijo3MSwid2VpZ2h0S2ciOjg1fV0sIm9wZXJhdG9ySWRlbnRpZmllciI6IkRFMTIzNDU2Nzg5IiwicGxhY2VkT25NYXJrZXREYXRlIjoiMjAyNi0wNy0wMSIsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImJhdHRlcnlDaGVtaXN0cnkiOiJOTUMiLCJiYXR0ZXJ5TW9kZWxJZCI6IlZCWC1FVjc1LU5NQzgxMS1QNFM5NiIsImJhdHRlcnlQYXNzcG9ydE51bWJlciI6IlVSTjpPREw6QkFUVDowOTU5MDAwMDAwMDA0NTpWQlgtRVYtMjAyNi0wNDIxIiwiYmF0dGVyeVR5cGUiOiJldiIsImJhdHRlcnlXZWlnaHRLZyI6NDY4LCJjYXBhY2l0eVRocmVzaG9sZEZvckV4aGF1c3Rpb25QY3QiOjcwLCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjk2LCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQ0MC00OC00IiwiY291bnRyeU9mT3JpZ2luIjoiQ0QiLCJuYW1lIjoiQ29iYWx0Iiwid2VpZ2h0R3JhbXMiOjUyMDB9LHsiY2FzTnVtYmVyIjoiNzQzOS05My0yIiwiY291bnRyeU9mT3JpZ2luIjoiQ0wiLCJuYW1lIjoiTGl0aGl1bSIsIndlaWdodEdyYW1zIjo4MTAwfSx7ImNhc051bWJlciI6Ijc0NDAtMDItMCIsImNvdW50cnlPZk9yaWdpbiI6IklEIiwibmFtZSI6Ik5pY2tlbCIsIndlaWdodEdyYW1zIjozMjAwMH0seyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJjb3VudHJ5T2ZPcmlnaW4iOiJNWiIsIm5hbWUiOiJOYXR1cmFsIGdyYXBoaXRlIiwid2VpZ2h0R3JhbXMiOjc0MDAwfV0sImN5Y2xlTGlmZVRlc3RDUmF0ZSI6MSwiZXVEZWNsYXJhdGlvbk9mQ29uZm9ybWl0eSI6IkVVLURvQyBWQlgtMjAyNi1FVi0wMTQyIGRhdGVkIDIwMjYtMDUtMTgsIHBlciBSZWd1bGF0aW9uIChFVSkgMjAyMy8xNTQyIEFydC4gMTgiLCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjoxODAwLCJleHBlY3RlZExpZmV0aW1lUmVmZXJlbmNlVGVzdCI6IlVORUNFIEdUUiAyMiAvIElFQyA2MjY2MC0xOjIwMTgsIDI1IGRlZ0MiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiI3Nzg2LTgxLTQiLCJjb25jZW50cmF0aW9uUGN0IjoyLjQsIm5hbWUiOiJOaWNrZWwgc3VsZmF0ZSJ9LHsiY2FzTnVtYmVyIjoiMTAxMjQtNDMtMyIsImNvbmNlbnRyYXRpb25QY3QiOjAuOSwibmFtZSI6IkNvYmFsdCBzdWxmYXRlIn0seyJjYXNOdW1iZXIiOiIyMTMyNC00MC0zIiwiY29uY2VudHJhdGlvblBjdCI6MC45LCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIn1dLCJpbml0aWFsUm91bmRUcmlwRWZmaWNpZW5jeVBjdCI6OTQsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjoxLjEsImludGVybmFsUGFja1Jlc2lzdGFuY2VNb2htIjo0MiwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTA1LTExVDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkluZ29sc3RhZHQsIERFIChwbGFudCBJTkctMSkiLCJtYXJraW5nSW5mb3JtYXRpb24iOiJTZXBhcmF0ZS1jb2xsZWN0aW9uIHN5bWJvbCBwZXIgQXJ0LiAxMyg0KTsgY2FwYWNpdHkgNzUga1doOyBIViB3YXJuaW5nIGxhYmVsIiwibWF4aW11bVZvbHRhZ2VWIjo0MDMuMiwibWluaW1hbFZvbHRhZ2VWIjoyNzAsIm5vbWluYWxDYXBhY2l0eUFoIjoyMTEsIm5vbWluYWxWb2x0YWdlViI6MzU1LCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NTUsIm1pbkMiOi0zMH0sIm5vdEluVXNlVGVtcGVyYXR1cmVSZWZlcmVuY2VUZXN0IjoiSUVDIDYyNjYwLTE6MjAxOCBzdG9yYWdlIHByb2ZpbGUiLCJvcmlnaW5hbFBvd2VyQ2FwYWJpbGl0eVciOjE1MDAwMCwicG93ZXJMaW1pdE1heFciOjIyMDAwMCwicG93ZXJMaW1pdE1pblciOjUwMCwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RJZGVudGlmaWVyIjp7Imd0aW4iOiIwOTU5MDAwMDAwMDA0NSIsInNjaGVtZSI6ImdzMSJ9LCJyYXRlZENhcGFjaXR5S3doIjo3NSwicmF0ZWRFbmVyZ3lXaCI6NzUwMDAsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRSZXBvcnRpbmdZZWFyIjoyMDI2LCJyZW5ld2FibGVDb250ZW50UGN0IjoxNiwicm91bmRUcmlwRWZmaWNpZW5jeUF0SGFsZkN5Y2xlTGlmZVBjdCI6ODgsInVzYWJsZUV4dGluZ3Vpc2hpbmdBZ2VudCI6IkxhcmdlIHZvbHVtZXMgb2Ygd2F0ZXIgZm9yIGNvb2xpbmc7IGRvIG5vdCB1c2UgQ08yIG9uIGNlbGwgZmlyZSIsIndhc3RlQmF0dGVyeUluZm9ybWF0aW9uIjoiRVYgYmF0dGVyeSAtIGhhbmRsZWQgYnkgdGhlIHZlaGljbGUgbWFudWZhY3R1cmVyIHRha2UtYmFjayBzY2hlbWUuIERvIG5vdCBvcGVuIG9yIGRpc2NoYXJnZS4gQ29udGFjdCBodi1zZXJ2aWNlQHZvbHRleC5leGFtcGxlLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDk4MS4ifSwicHJvZHVjdE5hbWUiOiJWb2x0ZXggRVYgVHJhY3Rpb24gQmF0dGVyeSA3NSBrV2giLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMjc5NDkzNzMxWiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDA0NS8yMS83MGMxYjRhM2E2MTZkMTYxZTk5NyIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjo1LjV9LCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuMjc5NDkzNzMxWiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMjc5NDkzNzMxWiIsInZlcnNpb24iOjF9.IcMAcCj2y2pu9zIc89MDC0kcO5KrS_l29thCxduNJn5PClbtwiRDLkd1FgNOcqGaxPZwrHrFFrQE7o2aj50KAQ", "repairabilityScore": {"overall": 5.5}, "disclosureSignatures": {"public+restricted+conformity": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJWQlgtRVYtMjAyNi0wNDIxIiwiY28yZVBlclVuaXQiOm51bGwsImNvbW1vZGl0eUNvZGUiOiI4NTA3NjAwMCIsImNvbXBsaWFuY2VSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMjU1MjE0NDE2WiIsImNvMmVTY29yZSI6bnVsbCwiY29tcGxpYW5jZVN0YXR1cyI6IlBBU1NUSFJPVUdIX05PX1ZBTElEQVRJT04iLCJyZWN5Y2xlZENvbnRlbnRQY3QiOm51bGwsInJlcGFpcmFiaWxpdHlJbmRleCI6bnVsbCwid2FybmluZ3MiOlt7ImNvZGUiOiJiYXR0ZXJ5LnJlY3ljbGVkX2NvbnRlbnQubm90X3lldF9iaW5kaW5nIiwiZmllbGQiOiIvcGxhY2VkT25NYXJrZXREYXRlIiwibWVzc2FnZSI6Ik5vIEFydC4gOCBtaW5pbXVtIHJlY3ljbGVkIHNoYXJlIGJpbmRzIGEgYmF0dGVyeSBwbGFjZWQgb24gdGhlIEVVIG1hcmtldCBvbiAyMDI2LTA3LTAxLiBSdWxlc2V0ICdiYXR0ZXJ5LXJlY3ljbGVkLWNvbnRlbnQtYXJ0OC0yJyBhcHBsaWVzIGZyb20gMjAzMS0wOC0xOC4ifV19LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjI1Mjk0MjA0M1oiLCJmYWNpbGl0eSI6eyJhZGRyZXNzIjoiV2Vya3N0cmFzc2UgMiwgMTAxMTUgQmVybGluIiwiY291bnRyeSI6IkRFIiwibmFtZSI6IkRlbW8gUGxhbnQiLCJzY2hlbWUiOiJnbG4iLCJ2YWx1ZSI6IjQwMTIzNDUwMDAwMDkifSwiaWQiOiIwMWExYTMwNy1iNzBjLTcwYzEtYjRhMy1hNjE2ZDE2MWU5OTciLCJsaW50UmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjI1NTIxNjE2N1oiLCJwYWNrVmVyc2lvbiI6IjEuMS4wIn0sIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiV2Vya3N0cmFzc2UgNSwgODUwNTUgSW5nb2xzdGFkdCwgREUiLCJjb3VudHJ5IjpudWxsLCJkaWRXZWJVcmwiOiJodHRwczovL3ZvbHRleC5leGFtcGxlLy53ZWxsLWtub3duL2RpZC5qc29uIiwibmFtZSI6IlZvbHRleCBCYXR0ZXJ5IEdtYkgifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJMaXRoaXVtIE5pY2tlbCBNYW5nYW5lc2UgQ29iYWx0IE94aWRlIChOTUMgODExKSIsInJlY3ljbGVkUGN0IjoxMywid2VpZ2h0S2ciOjIxMH0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJHcmFwaGl0ZS9zaWxpY29uIGFub2RlIiwicmVjeWNsZWRQY3QiOjQsIndlaWdodEtnIjo5NX0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJBbHVtaW5pdW0gcGFjayBob3VzaW5nIiwicmVjeWNsZWRQY3QiOjcxLCJ3ZWlnaHRLZyI6ODV9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA3LTAxIiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYW5vZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwibmFtZSI6IkdyYXBoaXRlIiwid2VpZ2h0UGN0Ijo5MH0seyJjYXNOdW1iZXIiOiIxMDA5Ny0yOC02IiwibmFtZSI6IlNpbGljb24gb3hpZGUiLCJ3ZWlnaHRQY3QiOjZ9LHsiY2FzTnVtYmVyIjoiOTAwMy01NS04IiwibmFtZSI6IlNCUi9DTUMgYmluZGVyIiwid2VpZ2h0UGN0Ijo0fV0sImJhdHRlcnlDaGVtaXN0cnkiOiJOTUMiLCJiYXR0ZXJ5TW9kZWxJZCI6IlZCWC1FVjc1LU5NQzgxMS1QNFM5NiIsImJhdHRlcnlQYXNzcG9ydE51bWJlciI6IlVSTjpPREw6QkFUVDowOTU5MDAwMDAwMDA0NTpWQlgtRVYtMjAyNi0wNDIxIiwiYmF0dGVyeVR5cGUiOiJldiIsImJhdHRlcnlXZWlnaHRLZyI6NDY4LCJjYXBhY2l0eVRocmVzaG9sZEZvckV4aGF1c3Rpb25QY3QiOjcwLCJjYXRob2RlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6IjM0NjQxNy05Ny04IiwibmFtZSI6IkxpTmkwLjhNbjAuMUNvMC4xTzIiLCJ3ZWlnaHRQY3QiOjk2LjV9LHsiY2FzTnVtYmVyIjoiMjQ5MzctNzktOSIsIm5hbWUiOiJQVkRGIGJpbmRlciIsIndlaWdodFBjdCI6Mn0seyJjYXNOdW1iZXIiOiIxMzMzLTg2LTQiLCJuYW1lIjoiQ29uZHVjdGl2ZSBjYXJib24gYmxhY2siLCJ3ZWlnaHRQY3QiOjEuNX1dLCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjk2LCJjb21wb25lbnRQYXJ0TnVtYmVycyI6WyJWQlgtQ0VMTC00NjgwLU5NQzgxMSIsIlZCWC1NT0RVTEUtMjRTIiwiVkJYLUJNUy1NQVNURVItVjQiLCJWQlgtUEFDSy1IVi03NSJdLCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQ0MC00OC00IiwiY291bnRyeU9mT3JpZ2luIjoiQ0QiLCJuYW1lIjoiQ29iYWx0Iiwid2VpZ2h0R3JhbXMiOjUyMDB9LHsiY2FzTnVtYmVyIjoiNzQzOS05My0yIiwiY291bnRyeU9mT3JpZ2luIjoiQ0wiLCJuYW1lIjoiTGl0aGl1bSIsIndlaWdodEdyYW1zIjo4MTAwfSx7ImNhc051bWJlciI6Ijc0NDAtMDItMCIsImNvdW50cnlPZk9yaWdpbiI6IklEIiwibmFtZSI6Ik5pY2tlbCIsIndlaWdodEdyYW1zIjozMjAwMH0seyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJjb3VudHJ5T2ZPcmlnaW4iOiJNWiIsIm5hbWUiOiJOYXR1cmFsIGdyYXBoaXRlIiwid2VpZ2h0R3JhbXMiOjc0MDAwfV0sImN5Y2xlTGlmZVRlc3RDUmF0ZSI6MSwiZGlzYXNzZW1ibHlJbnN0cnVjdGlvbnNVcmwiOiJodHRwczovL3ZvbHRleC5leGFtcGxlL3NlcnZpY2UvZXY3NS9kaXNhc3NlbWJseSIsImVsZWN0cm9seXRlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6Ijk2LTQ5LTEiLCJuYW1lIjoiRXRoeWxlbmUgY2FyYm9uYXRlIiwid2VpZ2h0UGN0IjozMH0seyJjYXNOdW1iZXIiOiI2MjMtNTMtMCIsIm5hbWUiOiJFdGh5bCBtZXRoeWwgY2FyYm9uYXRlIiwid2VpZ2h0UGN0Ijo1NX0seyJjYXNOdW1iZXIiOiIyMTMyNC00MC0zIiwibmFtZSI6IkxpdGhpdW0gaGV4YWZsdW9yb3Bob3NwaGF0ZSIsIndlaWdodFBjdCI6MTN9LHsiY2FzTnVtYmVyIjoiODcyLTM2LTYiLCJuYW1lIjoiVmlueWxlbmUgY2FyYm9uYXRlIGFkZGl0aXZlIiwid2VpZ2h0UGN0IjoyfV0sImV1RGVjbGFyYXRpb25PZkNvbmZvcm1pdHkiOiJFVS1Eb0MgVkJYLTIwMjYtRVYtMDE0MiBkYXRlZCAyMDI2LTA1LTE4LCBwZXIgUmVndWxhdGlvbiAoRVUpIDIwMjMvMTU0MiBBcnQuIDE4IiwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6MTgwMCwiZXhwZWN0ZWRMaWZldGltZVJlZmVyZW5jZVRlc3QiOiJVTkVDRSBHVFIgMjIgLyBJRUMgNjI2NjAtMToyMDE4LCAyNSBkZWdDIiwiaGF6YXJkb3VzU3Vic3RhbmNlcyI6W3siY2FzTnVtYmVyIjoiNzc4Ni04MS00IiwiY29uY2VudHJhdGlvblBjdCI6Mi40LCJuYW1lIjoiTmlja2VsIHN1bGZhdGUifSx7ImNhc051bWJlciI6IjEwMTI0LTQzLTMiLCJjb25jZW50cmF0aW9uUGN0IjowLjksIm5hbWUiOiJDb2JhbHQgc3VsZmF0ZSJ9LHsiY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsImNvbmNlbnRyYXRpb25QY3QiOjAuOSwibmFtZSI6IkxpdGhpdW0gaGV4YWZsdW9yb3Bob3NwaGF0ZSJ9XSwiaW5pdGlhbFJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk0LCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6MS4xLCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6NDIsIm1hbnVmYWN0dXJpbmdEYXRlIjoiMjAyNi0wNS0xMVQwMDowMDowMFoiLCJtYW51ZmFjdHVyaW5nUGxhY2UiOiJJbmdvbHN0YWR0LCBERSAocGxhbnQgSU5HLTEpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDc1IGtXaDsgSFYgd2FybmluZyBsYWJlbCIsIm1heGltdW1Wb2x0YWdlViI6NDAzLjIsIm1pbmltYWxWb2x0YWdlViI6MjcwLCJub21pbmFsQ2FwYWNpdHlBaCI6MjExLCJub21pbmFsVm9sdGFnZVYiOjM1NSwibm90SW5Vc2VUZW1wZXJhdHVyZVJhbmdlIjp7Im1heEMiOjU1LCJtaW5DIjotMzB9LCJub3RJblVzZVRlbXBlcmF0dXJlUmVmZXJlbmNlVGVzdCI6IklFQyA2MjY2MC0xOjIwMTggc3RvcmFnZSBwcm9maWxlIiwib3JpZ2luYWxQb3dlckNhcGFiaWxpdHlXIjoxNTAwMDAsInBvd2VyTGltaXRNYXhXIjoyMjAwMDAsInBvd2VyTGltaXRNaW5XIjo1MDAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwNDUiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6NzUsInJhdGVkRW5lcmd5V2giOjc1MDAwLCJyZWN5Y2xlZENvbnRlbnRDb2JhbHRQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudExpdGhpdW1QY3QiOm51bGwsInJlY3ljbGVkQ29udGVudE5pY2tlbFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50UmVwb3J0aW5nWWVhciI6MjAyNiwicmVuZXdhYmxlQ29udGVudFBjdCI6MTYsInJvdW5kVHJpcEVmZmljaWVuY3lBdEhhbGZDeWNsZUxpZmVQY3QiOjg4LCJzYWZldHlNZWFzdXJlcyI6IkhpZ2gtdm9sdGFnZSAoPjYwIFYgREMpLiBIVi1xdWFsaWZpZWQgcGVyc29ubmVsIG9ubHkuIFNlcnZpY2UgZGlzY29ubmVjdCBwbHVnIHJlbW92ZWQgYW5kIGxvY2tlZCBvdXQuIEluc3VsYXRlZCBQUEUgY2xhc3MgMDAuIiwic3BhcmVQYXJ0c0NvbnRhY3RzIjoiaHYtc2VydmljZUB2b2x0ZXguZXhhbXBsZTsgKzQ5IDg0MSA1NTUwIDQwMCAoY2VydGlmaWVkIEhWIHdvcmtzaG9wcyBvbmx5KSIsInRlc3RSZXBvcnRSZXN1bHRzIjoiSUVDIDYyNjYwLTI6MjAxOCBwYXNzOyBJU08gNjQ2OS0xOjIwMTkgcGFzczsgVU4gMzguMyBwYXNzOyByZXBvcnQgVkJYLVRSLTIwMjYtMDUwNyIsInVzYWJsZUV4dGluZ3Vpc2hpbmdBZ2VudCI6IkxhcmdlIHZvbHVtZXMgb2Ygd2F0ZXIgZm9yIGNvb2xpbmc7IGRvIG5vdCB1c2UgQ08yIG9uIGNlbGwgZmlyZSIsIndhc3RlQmF0dGVyeUluZm9ybWF0aW9uIjoiRVYgYmF0dGVyeSAtIGhhbmRsZWQgYnkgdGhlIHZlaGljbGUgbWFudWZhY3R1cmVyIHRha2UtYmFjayBzY2hlbWUuIERvIG5vdCBvcGVuIG9yIGRpc2NoYXJnZS4gQ29udGFjdCBodi1zZXJ2aWNlQHZvbHRleC5leGFtcGxlLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDk4MS4ifSwicHJvZHVjdE5hbWUiOiJWb2x0ZXggRVYgVHJhY3Rpb24gQmF0dGVyeSA3NSBrV2giLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMjc5NDkzNzMxWiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDA0NS8yMS83MGMxYjRhM2E2MTZkMTYxZTk5NyIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjo1LjV9LCJyZXRlbnRpb25Mb2NrZWQiOnRydWUsInJldGVudGlvblVudGlsIjoiMjAzNi0xMS0wM1QyMDowOToxNC4yNzk0OTM3MzFaIiwic2NoZW1hVmVyc2lvbiI6IjIuOC4wIiwic3RhdHVzIjoiYWN0aXZlIiwidXBkYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC4yNzk0OTM3MzFaIiwidmVyc2lvbiI6MX0.Mkst-6kiKYgem9CjByaQDhn3gKyjWne-6Ha1TuVt13C0bJwYKCf7JECl4RlMx8-jSAqJzUXwHQQ0OwNXzgaEBQ", "public+restricted+individual": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJWQlgtRVYtMjAyNi0wNDIxIiwiY28yZVBlclVuaXQiOm51bGwsImNvbW1vZGl0eUNvZGUiOiI4NTA3NjAwMCIsImNvbXBsaWFuY2VSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMjU1MjE0NDE2WiIsImNvMmVTY29yZSI6bnVsbCwiY29tcGxpYW5jZVN0YXR1cyI6IlBBU1NUSFJPVUdIX05PX1ZBTElEQVRJT04iLCJyZWN5Y2xlZENvbnRlbnRQY3QiOm51bGwsInJlcGFpcmFiaWxpdHlJbmRleCI6bnVsbCwid2FybmluZ3MiOlt7ImNvZGUiOiJiYXR0ZXJ5LnJlY3ljbGVkX2NvbnRlbnQubm90X3lldF9iaW5kaW5nIiwiZmllbGQiOiIvcGxhY2VkT25NYXJrZXREYXRlIiwibWVzc2FnZSI6Ik5vIEFydC4gOCBtaW5pbXVtIHJlY3ljbGVkIHNoYXJlIGJpbmRzIGEgYmF0dGVyeSBwbGFjZWQgb24gdGhlIEVVIG1hcmtldCBvbiAyMDI2LTA3LTAxLiBSdWxlc2V0ICdiYXR0ZXJ5LXJlY3ljbGVkLWNvbnRlbnQtYXJ0OC0yJyBhcHBsaWVzIGZyb20gMjAzMS0wOC0xOC4ifV19LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjI1Mjk0MjA0M1oiLCJmYWNpbGl0eSI6eyJhZGRyZXNzIjoiV2Vya3N0cmFzc2UgMiwgMTAxMTUgQmVybGluIiwiY291bnRyeSI6IkRFIiwibmFtZSI6IkRlbW8gUGxhbnQiLCJzY2hlbWUiOiJnbG4iLCJ2YWx1ZSI6IjQwMTIzNDUwMDAwMDkifSwiaWQiOiIwMWExYTMwNy1iNzBjLTcwYzEtYjRhMy1hNjE2ZDE2MWU5OTciLCJsaW50UmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjI1NTIxNjE2N1oiLCJwYWNrVmVyc2lvbiI6IjEuMS4wIn0sIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiV2Vya3N0cmFzc2UgNSwgODUwNTUgSW5nb2xzdGFkdCwgREUiLCJjb3VudHJ5IjpudWxsLCJkaWRXZWJVcmwiOiJodHRwczovL3ZvbHRleC5leGFtcGxlLy53ZWxsLWtub3duL2RpZC5qc29uIiwibmFtZSI6IlZvbHRleCBCYXR0ZXJ5IEdtYkgifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJMaXRoaXVtIE5pY2tlbCBNYW5nYW5lc2UgQ29iYWx0IE94aWRlIChOTUMgODExKSIsInJlY3ljbGVkUGN0IjoxMywid2VpZ2h0S2ciOjIxMH0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJHcmFwaGl0ZS9zaWxpY29uIGFub2RlIiwicmVjeWNsZWRQY3QiOjQsIndlaWdodEtnIjo5NX0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJBbHVtaW5pdW0gcGFjayBob3VzaW5nIiwicmVjeWNsZWRQY3QiOjcxLCJ3ZWlnaHRLZyI6ODV9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA3LTAxIiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYW5vZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwibmFtZSI6IkdyYXBoaXRlIiwid2VpZ2h0UGN0Ijo5MH0seyJjYXNOdW1iZXIiOiIxMDA5Ny0yOC02IiwibmFtZSI6IlNpbGljb24gb3hpZGUiLCJ3ZWlnaHRQY3QiOjZ9LHsiY2FzTnVtYmVyIjoiOTAwMy01NS04IiwibmFtZSI6IlNCUi9DTUMgYmluZGVyIiwid2VpZ2h0UGN0Ijo0fV0sImJhdHRlcnlDaGVtaXN0cnkiOiJOTUMiLCJiYXR0ZXJ5TW9kZWxJZCI6IlZCWC1FVjc1LU5NQzgxMS1QNFM5NiIsImJhdHRlcnlQYXNzcG9ydE51bWJlciI6IlVSTjpPREw6QkFUVDowOTU5MDAwMDAwMDA0NTpWQlgtRVYtMjAyNi0wNDIxIiwiYmF0dGVyeVN0YXR1cyI6Im9yaWdpbmFsIiwiYmF0dGVyeVR5cGUiOiJldiIsImJhdHRlcnlXZWlnaHRLZyI6NDY4LCJjYXBhY2l0eVRocmVzaG9sZEZvckV4aGF1c3Rpb25QY3QiOjcwLCJjYXRob2RlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6IjM0NjQxNy05Ny04IiwibmFtZSI6IkxpTmkwLjhNbjAuMUNvMC4xTzIiLCJ3ZWlnaHRQY3QiOjk2LjV9LHsiY2FzTnVtYmVyIjoiMjQ5MzctNzktOSIsIm5hbWUiOiJQVkRGIGJpbmRlciIsIndlaWdodFBjdCI6Mn0seyJjYXNOdW1iZXIiOiIxMzMzLTg2LTQiLCJuYW1lIjoiQ29uZHVjdGl2ZSBjYXJib24gYmxhY2siLCJ3ZWlnaHRQY3QiOjEuNX1dLCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjk2LCJjb21wb25lbnRQYXJ0TnVtYmVycyI6WyJWQlgtQ0VMTC00NjgwLU5NQzgxMSIsIlZCWC1NT0RVTEUtMjRTIiwiVkJYLUJNUy1NQVNURVItVjQiLCJWQlgtUEFDSy1IVi03NSJdLCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQ0MC00OC00IiwiY291bnRyeU9mT3JpZ2luIjoiQ0QiLCJuYW1lIjoiQ29iYWx0Iiwid2VpZ2h0R3JhbXMiOjUyMDB9LHsiY2FzTnVtYmVyIjoiNzQzOS05My0yIiwiY291bnRyeU9mT3JpZ2luIjoiQ0wiLCJuYW1lIjoiTGl0aGl1bSIsIndlaWdodEdyYW1zIjo4MTAwfSx7ImNhc051bWJlciI6Ijc0NDAtMDItMCIsImNvdW50cnlPZk9yaWdpbiI6IklEIiwibmFtZSI6Ik5pY2tlbCIsIndlaWdodEdyYW1zIjozMjAwMH0seyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJjb3VudHJ5T2ZPcmlnaW4iOiJNWiIsIm5hbWUiOiJOYXR1cmFsIGdyYXBoaXRlIiwid2VpZ2h0R3JhbXMiOjc0MDAwfV0sImN5Y2xlTGlmZVRlc3RDUmF0ZSI6MSwiZGlzYXNzZW1ibHlJbnN0cnVjdGlvbnNVcmwiOiJodHRwczovL3ZvbHRleC5leGFtcGxlL3NlcnZpY2UvZXY3NS9kaXNhc3NlbWJseSIsImR5bmFtaWNQZXJmb3JtYW5jZSI6eyJjYXBhY2l0eUZhZGVQY3QiOjAuOSwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6MTc4NSwiZXhwZWN0ZWRMaWZldGltZVllYXJzIjo5LjUsImludGVybmFsUmVzaXN0YW5jZUluY3JlYXNlUGN0IjoxLCJpbnRlcm5hbFJlc2lzdGFuY2VNb2htIjo0Mi40LCJwb3dlckZhZGVQY3QiOjAuNiwicG93ZXJXIjoxNDgwMDAsInJhdGVkQ2FwYWNpdHlBaCI6MjA5LCJyb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5My42fSwiZWxlY3Ryb2x5dGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiOTYtNDktMSIsIm5hbWUiOiJFdGh5bGVuZSBjYXJib25hdGUiLCJ3ZWlnaHRQY3QiOjMwfSx7ImNhc051bWJlciI6IjYyMy01My0wIiwibmFtZSI6IkV0aHlsIG1ldGh5bCBjYXJib25hdGUiLCJ3ZWlnaHRQY3QiOjU1fSx7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIiwid2VpZ2h0UGN0IjoxM30seyJjYXNOdW1iZXIiOiI4NzItMzYtNiIsIm5hbWUiOiJWaW55bGVuZSBjYXJib25hdGUgYWRkaXRpdmUiLCJ3ZWlnaHRQY3QiOjJ9XSwiZXVEZWNsYXJhdGlvbk9mQ29uZm9ybWl0eSI6IkVVLURvQyBWQlgtMjAyNi1FVi0wMTQyIGRhdGVkIDIwMjYtMDUtMTgsIHBlciBSZWd1bGF0aW9uIChFVSkgMjAyMy8xNTQyIEFydC4gMTgiLCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjoxODAwLCJleHBlY3RlZExpZmV0aW1lUmVmZXJlbmNlVGVzdCI6IlVORUNFIEdUUiAyMiAvIElFQyA2MjY2MC0xOjIwMTgsIDI1IGRlZ0MiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiI3Nzg2LTgxLTQiLCJjb25jZW50cmF0aW9uUGN0IjoyLjQsIm5hbWUiOiJOaWNrZWwgc3VsZmF0ZSJ9LHsiY2FzTnVtYmVyIjoiMTAxMjQtNDMtMyIsImNvbmNlbnRyYXRpb25QY3QiOjAuOSwibmFtZSI6IkNvYmFsdCBzdWxmYXRlIn0seyJjYXNOdW1iZXIiOiIyMTMyNC00MC0zIiwiY29uY2VudHJhdGlvblBjdCI6MC45LCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIn1dLCJpbml0aWFsUm91bmRUcmlwRWZmaWNpZW5jeVBjdCI6OTQsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjoxLjEsImludGVybmFsUGFja1Jlc2lzdGFuY2VNb2htIjo0MiwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTA1LTExVDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkluZ29sc3RhZHQsIERFIChwbGFudCBJTkctMSkiLCJtYXJraW5nSW5mb3JtYXRpb24iOiJTZXBhcmF0ZS1jb2xsZWN0aW9uIHN5bWJvbCBwZXIgQXJ0LiAxMyg0KTsgY2FwYWNpdHkgNzUga1doOyBIViB3YXJuaW5nIGxhYmVsIiwibWF4aW11bVZvbHRhZ2VWIjo0MDMuMiwibWluaW1hbFZvbHRhZ2VWIjoyNzAsIm5vbWluYWxDYXBhY2l0eUFoIjoyMTEsIm5vbWluYWxWb2x0YWdlViI6MzU1LCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NTUsIm1pbkMiOi0zMH0sIm5vdEluVXNlVGVtcGVyYXR1cmVSZWZlcmVuY2VUZXN0IjoiSUVDIDYyNjYwLTE6MjAxOCBzdG9yYWdlIHByb2ZpbGUiLCJvcmlnaW5hbFBvd2VyQ2FwYWJpbGl0eVciOjE1MDAwMCwicG93ZXJMaW1pdE1heFciOjIyMDAwMCwicG93ZXJMaW1pdE1pblciOjUwMCwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RJZGVudGlmaWVyIjp7Imd0aW4iOiIwOTU5MDAwMDAwMDA0NSIsInNjaGVtZSI6ImdzMSJ9LCJyYXRlZENhcGFjaXR5S3doIjo3NSwicmF0ZWRFbmVyZ3lXaCI6NzUwMDAsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRSZXBvcnRpbmdZZWFyIjoyMDI2LCJyZW5ld2FibGVDb250ZW50UGN0IjoxNiwicm91bmRUcmlwRWZmaWNpZW5jeUF0SGFsZkN5Y2xlTGlmZVBjdCI6ODgsInNhZmV0eU1lYXN1cmVzIjoiSGlnaC12b2x0YWdlICg-NjAgViBEQykuIEhWLXF1YWxpZmllZCBwZXJzb25uZWwgb25seS4gU2VydmljZSBkaXNjb25uZWN0IHBsdWcgcmVtb3ZlZCBhbmQgbG9ja2VkIG91dC4gSW5zdWxhdGVkIFBQRSBjbGFzcyAwMC4iLCJzcGFyZVBhcnRzQ29udGFjdHMiOiJodi1zZXJ2aWNlQHZvbHRleC5leGFtcGxlOyArNDkgODQxIDU1NTAgNDAwIChjZXJ0aWZpZWQgSFYgd29ya3Nob3BzIG9ubHkpIiwic3RhdGVPZkhlYWx0aCI6eyJwYXJhbWV0ZXJTZXQiOiJlbGVjdHJpY1ZlaGljbGUiLCJzb2NlUGN0Ijo5OS4xfSwic3RhdGVPZkhlYWx0aFBjdCI6bnVsbCwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiTGFyZ2Ugdm9sdW1lcyBvZiB3YXRlciBmb3IgY29vbGluZzsgZG8gbm90IHVzZSBDTzIgb24gY2VsbCBmaXJlIiwidXNhZ2VIaXN0b3J5Ijp7ImNoYXJnZURpc2NoYXJnZUN5Y2xlcyI6OCwibmVnYXRpdmVFdmVudHMiOltdLCJzdGF0ZU9mQ2hhcmdlIjpbeyJyZWNvcmRlZEF0IjoiMjAyNi0wNy0xNVQwOTowMDowMFoiLCJzdGF0ZU9mQ2hhcmdlUGN0Ijo4Mn0seyJyZWNvcmRlZEF0IjoiMjAyNi0wNy0yMlQwOTowMDowMFoiLCJzdGF0ZU9mQ2hhcmdlUGN0Ijo2NH1dfSwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJFViBiYXR0ZXJ5IC0gaGFuZGxlZCBieSB0aGUgdmVoaWNsZSBtYW51ZmFjdHVyZXIgdGFrZS1iYWNrIHNjaGVtZS4gRG8gbm90IG9wZW4gb3IgZGlzY2hhcmdlLiBDb250YWN0IGh2LXNlcnZpY2VAdm9sdGV4LmV4YW1wbGUuIFByb2R1Y2VyIHJlZ2lzdHJhdGlvbiBERS1CQVRULTAwOTgxLiJ9LCJwcm9kdWN0TmFtZSI6IlZvbHRleCBFViBUcmFjdGlvbiBCYXR0ZXJ5IDc1IGtXaCIsInB1Ymxpc2hlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC4yNzk0OTM3MzFaIiwicXJDb2RlVXJsIjoiaHR0cDovLzEyNy4wLjAuMToxLzAxLzA5NTkwMDAwMDAwMDQ1LzIxLzcwYzFiNGEzYTYxNmQxNjFlOTk3IiwicmVwYWlyYWJpbGl0eVNjb3JlIjp7Im92ZXJhbGwiOjUuNX0sInJldGVudGlvblVudGlsIjoiMjAzNi0xMS0wM1QyMDowOToxNC4yNzk0OTM3MzFaIiwic2NoZW1hVmVyc2lvbiI6IjIuOC4wIiwic3RhdHVzIjoiYWN0aXZlIiwidXBkYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC4yNzk0OTM3MzFaIiwidmVyc2lvbiI6MX0.x4UuEbmWX3nAWvM5t0nFpA5qUZyKIFJI6j9mTNoq2A5HT3eiWYD6qP6hygH_W6X0kSI1WpucB7FdSsiWn7__Bw"}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}'); +INSERT INTO odal.passport VALUES ('01a1a307-b792-7764-a50c-b10fe65bf932', 'battery', 'active', true, '2.8.0', NULL, NULL, 1, NULL, NULL, '2026-11-03 20:09:14.389556+00', '2036-11-03 20:09:14.420275+00', NULL, '2026-11-03 20:09:14.389943+00', '2026-11-03 20:09:14.460817+00', '2026-11-03 20:09:14.420275+00', '{"id": "01a1a307-b792-7764-a50c-b10fe65bf932", "status": "active", "batchId": "VBX-LMT-2026-0188", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.386370527Z", "materials": [{"name": "Lithium Iron Phosphate", "weightKg": 1.4, "recycledPct": 7.0, "countryOfOrigin": "DE"}, {"name": "Graphite", "weightKg": 0.6, "recycledPct": 3.0, "countryOfOrigin": "DE"}, {"name": "ABS/PC housing", "weightKg": 0.5, "recycledPct": 30.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000052/21/7764a50cb10fe65bf932", "updatedAt": "2026-11-03T20:09:14.420275202Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.389558295Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Voltex e-Bike Battery 36V 14Ah", "publishedAt": "2026-11-03T20:09:14.420275202Z", "jwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJWQlgtTE1ULTIwMjYtMDE4OCIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjM4OTU1NTc0M1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNi0xNS4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMycgYXBwbGllcyBmcm9tIDIwMzYtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC4zODYzNzA1MjdaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjc5Mi03NzY0LWE1MGMtYjEwZmU2NWJmOTMyIiwiandzU2lnbmF0dXJlIjpudWxsLCJsaW50UmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjM4OTU1ODI5NVoiLCJwYWNrVmVyc2lvbiI6IjEuMS4wIn0sIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiV2Vya3N0cmFzc2UgNSwgODUwNTUgSW5nb2xzdGFkdCwgREUiLCJjb3VudHJ5IjpudWxsLCJkaWRXZWJVcmwiOiJodHRwczovL3ZvbHRleC5leGFtcGxlLy53ZWxsLWtub3duL2RpZC5qc29uIiwibmFtZSI6IlZvbHRleCBCYXR0ZXJ5IEdtYkgifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJMaXRoaXVtIElyb24gUGhvc3BoYXRlIiwicmVjeWNsZWRQY3QiOjcsIndlaWdodEtnIjoxLjR9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiR3JhcGhpdGUiLCJyZWN5Y2xlZFBjdCI6Mywid2VpZ2h0S2ciOjAuNn0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJBQlMvUEMgaG91c2luZyIsInJlY3ljbGVkUGN0IjozMCwid2VpZ2h0S2ciOjAuNX1dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInBsYWNlZE9uTWFya2V0RGF0ZSI6IjIwMjYtMDYtMTUiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJhbm9kZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJuYW1lIjoiR3JhcGhpdGUiLCJ3ZWlnaHRQY3QiOjk2fSx7ImNhc051bWJlciI6IjkwMDMtNTUtOCIsIm5hbWUiOiJTQlIvQ01DIGJpbmRlciIsIndlaWdodFBjdCI6NH1dLCJiYXR0ZXJ5Q2hlbWlzdHJ5IjoiTEZQIiwiYmF0dGVyeU1vZGVsSWQiOiJWQlgtTE1UMzYtMTQtMTBTIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDUyOlZCWC1MTVQtMjAyNi0wMTg4IiwiYmF0dGVyeVN0YXR1cyI6Im9yaWdpbmFsIiwiYmF0dGVyeVR5cGUiOiJsbXQiLCJiYXR0ZXJ5V2VpZ2h0S2ciOjIuNiwiY2F0aG9kZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiIxNTM2NS0xNC03IiwibmFtZSI6IkxpRmVQTzQiLCJ3ZWlnaHRQY3QiOjk1fSx7ImNhc051bWJlciI6IjI0OTM3LTc5LTkiLCJuYW1lIjoiUFZERiBiaW5kZXIiLCJ3ZWlnaHRQY3QiOjN9LHsiY2FzTnVtYmVyIjoiMTMzMy04Ni00IiwibmFtZSI6IkNvbmR1Y3RpdmUgY2FyYm9uIGJsYWNrIiwid2VpZ2h0UGN0IjoyfV0sImNvbW1lcmNpYWxXYXJyYW50eVBlcmlvZE1vbnRocyI6MjQsImNvbXBvbmVudFBhcnROdW1iZXJzIjpbIlZCWC1DRUxMLTIxNzAwLUxGUCIsIlZCWC1CTVMtMTBTLVYyIiwiVkJYLUxNVC1FTkNMLTM2Il0sImNyaXRpY2FsUmF3TWF0ZXJpYWxzIjpbeyJjYXNOdW1iZXIiOiI3NDM5LTkzLTIiLCJjb3VudHJ5T2ZPcmlnaW4iOiJBVSIsIm5hbWUiOiJMaXRoaXVtIiwid2VpZ2h0R3JhbXMiOjE2NX0seyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDTiIsIm5hbWUiOiJOYXR1cmFsIGdyYXBoaXRlIiwid2VpZ2h0R3JhbXMiOjYwMH1dLCJjeWNsZUxpZmVUZXN0Q1JhdGUiOjEsImRpc2Fzc2VtYmx5SW5zdHJ1Y3Rpb25zVXJsIjoiaHR0cHM6Ly92b2x0ZXguZXhhbXBsZS9zZXJ2aWNlL2xtdDM2LTE0L2Rpc2Fzc2VtYmx5IiwiZHluYW1pY1BlcmZvcm1hbmNlIjp7ImNhcGFjaXR5RmFkZVBjdCI6MC43LCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjo5OTUsImludGVybmFsUmVzaXN0YW5jZU1vaG0iOjk1LjQsInJhdGVkQ2FwYWNpdHlBaCI6MTMuOSwicm91bmRUcmlwRWZmaWNpZW5jeVBjdCI6OTQuN30sImVsZWN0cm9seXRlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6Ijk2LTQ5LTEiLCJuYW1lIjoiRXRoeWxlbmUgY2FyYm9uYXRlIiwid2VpZ2h0UGN0IjozOH0seyJjYXNOdW1iZXIiOiI2MTYtMzgtNiIsIm5hbWUiOiJEaW1ldGh5bCBjYXJib25hdGUiLCJ3ZWlnaHRQY3QiOjQ5fSx7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIiwid2VpZ2h0UGN0IjoxM31dLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIFZCWC0yMDI2LUxNVC0wMDg4IGRhdGVkIDIwMjYtMDUtMTIsIHBlciBSZWd1bGF0aW9uIChFVSkgMjAyMy8xNTQyIEFydC4gMTgiLCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjoxMDAwLCJleHBlY3RlZExpZmV0aW1lUmVmZXJlbmNlVGVzdCI6IkVOIDUwNjA0LTE6MjAxNiwgMjUgZGVnQywgMTAwJSBEb0QiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiIyMTMyNC00MC0zIiwiY29uY2VudHJhdGlvblBjdCI6MC42LCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIn1dLCJpbml0aWFsUm91bmRUcmlwRWZmaWNpZW5jeVBjdCI6OTUsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjo4LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6OTUsIm1hbnVmYWN0dXJpbmdEYXRlIjoiMjAyNi0wNS0wNVQwMDowMDowMFoiLCJtYW51ZmFjdHVyaW5nUGxhY2UiOiJJbmdvbHN0YWR0LCBERSAocGxhbnQgSU5HLTIpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDE0IEFoIC8gNTA0IFdoOyAzNiBWIExNVCIsIm1heGltdW1Wb2x0YWdlViI6NDIsIm1pbmltYWxWb2x0YWdlViI6MzAsIm5vbWluYWxDYXBhY2l0eUFoIjoxNCwibm9taW5hbFZvbHRhZ2VWIjozNiwibm90SW5Vc2VUZW1wZXJhdHVyZVJhbmdlIjp7Im1heEMiOjQ1LCJtaW5DIjotMTB9LCJub3RJblVzZVRlbXBlcmF0dXJlUmVmZXJlbmNlVGVzdCI6IkVOIDUwNjA0LTE6MjAxNiBzdG9yYWdlIGNsYXVzZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NTAwLCJwb3dlckxpbWl0TWF4VyI6NzUwLCJwb3dlckxpbWl0TWluVyI6MTAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwNTIiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6MC41MDQsInJhdGVkRW5lcmd5V2giOjUwNCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjEyLCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo5MCwic2FmZXR5TWVhc3VyZXMiOiJEbyBub3Qgb3BlbiB0aGUgcGFjay4gUmVwbGFjZSB2aWEgYXV0aG9yaXNlZCBkZWFsZXIuIEtlZXAgYXdheSBmcm9tIGhlYXQgYWJvdmUgNjAgZGVnQy4iLCJzcGFyZVBhcnRzQ29udGFjdHMiOiJzcGFyZXNAdm9sdGV4LmV4YW1wbGU7ICs0OSA4NDEgNTU1MCA0MTAiLCJzdGF0ZU9mSGVhbHRoIjp7InBhcmFtZXRlclNldCI6InN0YXRpb25hcnlPckxtdCIsInJlbWFpbmluZ0NhcGFjaXR5UGN0Ijo5OS4zLCJyZW1haW5pbmdQb3dlckNhcGFiaWxpdHlQY3QiOjk4LjgsInNlbGZEaXNjaGFyZ2VSYXRlUGN0UGVyTW9udGgiOjN9LCJzdGF0ZU9mSGVhbHRoUGN0IjpudWxsLCJ0ZXN0UmVwb3J0UmVzdWx0cyI6IkVOIDUwNjA0LTE6MjAxNiBwYXNzOyBVTiAzOC4zIHBhc3M7IHJlcG9ydCBWQlgtVFItMjAyNi0wMzExIiwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIgb3IgQUJDIGRyeSBwb3dkZXI7IHN1Ym1lcmdlIGluIHdhdGVyIGlmIHNhZmUgdG8gZG8gc28iLCJ1c2FnZUhpc3RvcnkiOnsiY2hhcmdlRGlzY2hhcmdlQ3ljbGVzIjo2fSwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJSZXR1cm4gdGhlIExNVCBiYXR0ZXJ5IHRvIGFueSBiaWN5Y2xlIHJldGFpbGVyIG9yIG11bmljaXBhbCBjb2xsZWN0aW9uIHBvaW50LiBEbyBub3QgcGxhY2UgaW4gaG91c2Vob2xkIHdhc3RlLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDk4MS4ifSwicHJvZHVjdE5hbWUiOiJWb2x0ZXggZS1CaWtlIEJhdHRlcnkgMzZWIDE0QWgiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNDIwMjc1MjAyWiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDA1Mi8yMS83NzY0YTUwY2IxMGZlNjViZjkzMiIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjo2fSwicmV0ZW50aW9uTG9ja2VkIjp0cnVlLCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuNDIwMjc1MjAyWiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNDIwMjc1MjAyWiIsInZlcnNpb24iOjF9.7Gb05PPET9E6Le1xshL4REUbMbrxVjYZZb_BhBUxVHOSCfwACrojwS3Qw55igxO8TzuetARKPDu1Y4tK5xlCAg", "manufacturer": {"name": "Voltex Battery GmbH", "address": "Werkstrasse 5, 85055 Ingolstadt, DE", "country": null, "didWebUrl": "https://voltex.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:14.420275202Z", "retentionLocked": true, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-06-15. Ruleset ''battery-recycled-content-art8-3'' applies from 2036-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:14.389555743Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "lmt", "productGroup": "battery", "usageHistory": {"chargeDischargeCycles": 6}, "anodeMaterial": [{"name": "Graphite", "casNumber": "7782-42-5", "weightPct": 96.0}, {"name": "SBR/CMC binder", "casNumber": "9003-55-8", "weightPct": 4.0}], "batteryStatus": "original", "ratedEnergyWh": 504.0, "stateOfHealth": {"parameterSet": "stationaryOrLmt", "remainingCapacityPct": 99.3, "remainingPowerCapabilityPct": 98.8, "selfDischargeRatePctPerMonth": 3.0}, "batteryModelId": "VBX-LMT36-14-10S", "powerLimitMaxW": 750.0, "powerLimitMinW": 10.0, "safetyMeasures": "Do not open the pack. Replace via authorised dealer. Keep away from heat above 60 degC.", "batteryWeightKg": 2.6, "cathodeMaterial": [{"name": "LiFePO4", "casNumber": "15365-14-7", "weightPct": 95.0}, {"name": "PVDF binder", "casNumber": "24937-79-9", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 2.0}], "maximumVoltageV": 42.0, "minimalVoltageV": 30.0, "nominalVoltageV": 36.0, "batteryChemistry": "LFP", "ratedCapacityKwh": 0.504, "stateOfHealthPct": null, "manufacturingDate": "2026-05-05T00:00:00Z", "nominalCapacityAh": 14.0, "productIdentifier": {"gtin": "09590000000052", "scheme": "gs1"}, "testReportResults": "EN 50604-1:2016 pass; UN 38.3 pass; report VBX-TR-2026-0311", "cycleLifeTestCRate": 1.0, "dynamicPerformance": {"capacityFadePct": 0.7, "ratedCapacityAh": 13.9, "expectedLifetimeCycles": 995, "internalResistanceMohm": 95.4, "roundTripEfficiencyPct": 94.7}, "manufacturingPlace": "Ingolstadt, DE (plant ING-2)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 14 Ah / 504 Wh; 36 V LMT", "sparePartsContacts": "spares@voltex.example; +49 841 5550 410", "electrolyteMaterial": [{"name": "Ethylene carbonate", "casNumber": "96-49-1", "weightPct": 38.0}, {"name": "Dimethyl carbonate", "casNumber": "616-38-6", "weightPct": 49.0}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "weightPct": 13.0}], "hazardousSubstances": [{"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.6}], "renewableContentPct": 12.0, "componentPartNumbers": ["VBX-CELL-21700-LFP", "VBX-BMS-10S-V2", "VBX-LMT-ENCL-36"], "criticalRawMaterials": [{"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 165.0, "countryOfOrigin": "AU"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 600.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000052:VBX-LMT-2026-0188", "expectedLifetimeCycles": 1000, "wasteBatteryInformation": "Return the LMT battery to any bicycle retailer or municipal collection point. Do not place in household waste. Producer registration DE-BATT-00981.", "notInUseTemperatureRange": {"maxC": 45.0, "minC": -10.0}, "originalPowerCapabilityW": 500.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Water or ABC dry powder; submerge in water if safe to do so", "euDeclarationOfConformity": "EU-DoC VBX-2026-LMT-0088 dated 2026-05-12, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://voltex.example/service/lmt36-14/disassembly", "internalCellResistanceMohm": 8.0, "internalPackResistanceMohm": 95.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "EN 50604-1:2016, 25 degC, 100% DoD", "initialRoundTripEfficiencyPct": 95.0, "commercialWarrantyPeriodMonths": 24, "notInUseTemperatureReferenceTest": "EN 50604-1:2016 storage clause", "roundTripEfficiencyAtHalfCycleLifePct": 90.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-06-15", "publicJwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJWQlgtTE1ULTIwMjYtMDE4OCIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjM4OTU1NTc0M1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNi0xNS4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMycgYXBwbGllcyBmcm9tIDIwMzYtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC4zODYzNzA1MjdaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjc5Mi03NzY0LWE1MGMtYjEwZmU2NWJmOTMyIiwibWFudWZhY3R1cmVyIjp7ImFkZHJlc3MiOiJXZXJrc3RyYXNzZSA1LCA4NTA1NSBJbmdvbHN0YWR0LCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vdm9sdGV4LmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiVm9sdGV4IEJhdHRlcnkgR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkxpdGhpdW0gSXJvbiBQaG9zcGhhdGUiLCJyZWN5Y2xlZFBjdCI6Nywid2VpZ2h0S2ciOjEuNH0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJHcmFwaGl0ZSIsInJlY3ljbGVkUGN0IjozLCJ3ZWlnaHRLZyI6MC42fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkFCUy9QQyBob3VzaW5nIiwicmVjeWNsZWRQY3QiOjMwLCJ3ZWlnaHRLZyI6MC41fV0sIm9wZXJhdG9ySWRlbnRpZmllciI6IkRFMTIzNDU2Nzg5IiwicGxhY2VkT25NYXJrZXREYXRlIjoiMjAyNi0wNi0xNSIsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImJhdHRlcnlDaGVtaXN0cnkiOiJMRlAiLCJiYXR0ZXJ5TW9kZWxJZCI6IlZCWC1MTVQzNi0xNC0xMFMiLCJiYXR0ZXJ5UGFzc3BvcnROdW1iZXIiOiJVUk46T0RMOkJBVFQ6MDk1OTAwMDAwMDAwNTI6VkJYLUxNVC0yMDI2LTAxODgiLCJiYXR0ZXJ5VHlwZSI6ImxtdCIsImJhdHRlcnlXZWlnaHRLZyI6Mi42LCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjI0LCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQzOS05My0yIiwiY291bnRyeU9mT3JpZ2luIjoiQVUiLCJuYW1lIjoiTGl0aGl1bSIsIndlaWdodEdyYW1zIjoxNjV9LHsiY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwiY291bnRyeU9mT3JpZ2luIjoiQ04iLCJuYW1lIjoiTmF0dXJhbCBncmFwaGl0ZSIsIndlaWdodEdyYW1zIjo2MDB9XSwiY3ljbGVMaWZlVGVzdENSYXRlIjoxLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIFZCWC0yMDI2LUxNVC0wMDg4IGRhdGVkIDIwMjYtMDUtMTIsIHBlciBSZWd1bGF0aW9uIChFVSkgMjAyMy8xNTQyIEFydC4gMTgiLCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjoxMDAwLCJleHBlY3RlZExpZmV0aW1lUmVmZXJlbmNlVGVzdCI6IkVOIDUwNjA0LTE6MjAxNiwgMjUgZGVnQywgMTAwJSBEb0QiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiIyMTMyNC00MC0zIiwiY29uY2VudHJhdGlvblBjdCI6MC42LCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIn1dLCJpbml0aWFsUm91bmRUcmlwRWZmaWNpZW5jeVBjdCI6OTUsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjo4LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6OTUsIm1hbnVmYWN0dXJpbmdEYXRlIjoiMjAyNi0wNS0wNVQwMDowMDowMFoiLCJtYW51ZmFjdHVyaW5nUGxhY2UiOiJJbmdvbHN0YWR0LCBERSAocGxhbnQgSU5HLTIpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDE0IEFoIC8gNTA0IFdoOyAzNiBWIExNVCIsIm1heGltdW1Wb2x0YWdlViI6NDIsIm1pbmltYWxWb2x0YWdlViI6MzAsIm5vbWluYWxDYXBhY2l0eUFoIjoxNCwibm9taW5hbFZvbHRhZ2VWIjozNiwibm90SW5Vc2VUZW1wZXJhdHVyZVJhbmdlIjp7Im1heEMiOjQ1LCJtaW5DIjotMTB9LCJub3RJblVzZVRlbXBlcmF0dXJlUmVmZXJlbmNlVGVzdCI6IkVOIDUwNjA0LTE6MjAxNiBzdG9yYWdlIGNsYXVzZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NTAwLCJwb3dlckxpbWl0TWF4VyI6NzUwLCJwb3dlckxpbWl0TWluVyI6MTAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwNTIiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6MC41MDQsInJhdGVkRW5lcmd5V2giOjUwNCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjEyLCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo5MCwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIgb3IgQUJDIGRyeSBwb3dkZXI7IHN1Ym1lcmdlIGluIHdhdGVyIGlmIHNhZmUgdG8gZG8gc28iLCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IlJldHVybiB0aGUgTE1UIGJhdHRlcnkgdG8gYW55IGJpY3ljbGUgcmV0YWlsZXIgb3IgbXVuaWNpcGFsIGNvbGxlY3Rpb24gcG9pbnQuIERvIG5vdCBwbGFjZSBpbiBob3VzZWhvbGQgd2FzdGUuIFByb2R1Y2VyIHJlZ2lzdHJhdGlvbiBERS1CQVRULTAwOTgxLiJ9LCJwcm9kdWN0TmFtZSI6IlZvbHRleCBlLUJpa2UgQmF0dGVyeSAzNlYgMTRBaCIsInB1Ymxpc2hlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40MjAyNzUyMDJaIiwicXJDb2RlVXJsIjoiaHR0cDovLzEyNy4wLjAuMToxLzAxLzA5NTkwMDAwMDAwMDUyLzIxLzc3NjRhNTBjYjEwZmU2NWJmOTMyIiwicmVwYWlyYWJpbGl0eVNjb3JlIjp7Im92ZXJhbGwiOjZ9LCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuNDIwMjc1MjAyWiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNDIwMjc1MjAyWiIsInZlcnNpb24iOjF9.mttt33dSVCirL2JM213ygXJq_v8_efhV2-zOQqE_-4rKWYeWqomq_icn6aYTPEUjBCzKg_ldjFPCwA_0XGbPDQ", "repairabilityScore": {"overall": 6.0}, "disclosureSignatures": {"public+restricted+conformity": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJWQlgtTE1ULTIwMjYtMDE4OCIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjM4OTU1NTc0M1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNi0xNS4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMycgYXBwbGllcyBmcm9tIDIwMzYtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC4zODYzNzA1MjdaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjc5Mi03NzY0LWE1MGMtYjEwZmU2NWJmOTMyIiwibGludFJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC4zODk1NTgyOTVaIiwicGFja1ZlcnNpb24iOiIxLjEuMCJ9LCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDUsIDg1MDU1IEluZ29sc3RhZHQsIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly92b2x0ZXguZXhhbXBsZS8ud2VsbC1rbm93bi9kaWQuanNvbiIsIm5hbWUiOiJWb2x0ZXggQmF0dGVyeSBHbWJIIn0sIm1hdGVyaWFscyI6W3siY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiTGl0aGl1bSBJcm9uIFBob3NwaGF0ZSIsInJlY3ljbGVkUGN0Ijo3LCJ3ZWlnaHRLZyI6MS40fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkdyYXBoaXRlIiwicmVjeWNsZWRQY3QiOjMsIndlaWdodEtnIjowLjZ9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiQUJTL1BDIGhvdXNpbmciLCJyZWN5Y2xlZFBjdCI6MzAsIndlaWdodEtnIjowLjV9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA2LTE1IiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYW5vZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwibmFtZSI6IkdyYXBoaXRlIiwid2VpZ2h0UGN0Ijo5Nn0seyJjYXNOdW1iZXIiOiI5MDAzLTU1LTgiLCJuYW1lIjoiU0JSL0NNQyBiaW5kZXIiLCJ3ZWlnaHRQY3QiOjR9XSwiYmF0dGVyeUNoZW1pc3RyeSI6IkxGUCIsImJhdHRlcnlNb2RlbElkIjoiVkJYLUxNVDM2LTE0LTEwUyIsImJhdHRlcnlQYXNzcG9ydE51bWJlciI6IlVSTjpPREw6QkFUVDowOTU5MDAwMDAwMDA1MjpWQlgtTE1ULTIwMjYtMDE4OCIsImJhdHRlcnlUeXBlIjoibG10IiwiYmF0dGVyeVdlaWdodEtnIjoyLjYsImNhdGhvZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiMTUzNjUtMTQtNyIsIm5hbWUiOiJMaUZlUE80Iiwid2VpZ2h0UGN0Ijo5NX0seyJjYXNOdW1iZXIiOiIyNDkzNy03OS05IiwibmFtZSI6IlBWREYgYmluZGVyIiwid2VpZ2h0UGN0IjozfSx7ImNhc051bWJlciI6IjEzMzMtODYtNCIsIm5hbWUiOiJDb25kdWN0aXZlIGNhcmJvbiBibGFjayIsIndlaWdodFBjdCI6Mn1dLCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjI0LCJjb21wb25lbnRQYXJ0TnVtYmVycyI6WyJWQlgtQ0VMTC0yMTcwMC1MRlAiLCJWQlgtQk1TLTEwUy1WMiIsIlZCWC1MTVQtRU5DTC0zNiJdLCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQzOS05My0yIiwiY291bnRyeU9mT3JpZ2luIjoiQVUiLCJuYW1lIjoiTGl0aGl1bSIsIndlaWdodEdyYW1zIjoxNjV9LHsiY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwiY291bnRyeU9mT3JpZ2luIjoiQ04iLCJuYW1lIjoiTmF0dXJhbCBncmFwaGl0ZSIsIndlaWdodEdyYW1zIjo2MDB9XSwiY3ljbGVMaWZlVGVzdENSYXRlIjoxLCJkaXNhc3NlbWJseUluc3RydWN0aW9uc1VybCI6Imh0dHBzOi8vdm9sdGV4LmV4YW1wbGUvc2VydmljZS9sbXQzNi0xNC9kaXNhc3NlbWJseSIsImVsZWN0cm9seXRlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6Ijk2LTQ5LTEiLCJuYW1lIjoiRXRoeWxlbmUgY2FyYm9uYXRlIiwid2VpZ2h0UGN0IjozOH0seyJjYXNOdW1iZXIiOiI2MTYtMzgtNiIsIm5hbWUiOiJEaW1ldGh5bCBjYXJib25hdGUiLCJ3ZWlnaHRQY3QiOjQ5fSx7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIiwid2VpZ2h0UGN0IjoxM31dLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIFZCWC0yMDI2LUxNVC0wMDg4IGRhdGVkIDIwMjYtMDUtMTIsIHBlciBSZWd1bGF0aW9uIChFVSkgMjAyMy8xNTQyIEFydC4gMTgiLCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjoxMDAwLCJleHBlY3RlZExpZmV0aW1lUmVmZXJlbmNlVGVzdCI6IkVOIDUwNjA0LTE6MjAxNiwgMjUgZGVnQywgMTAwJSBEb0QiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiIyMTMyNC00MC0zIiwiY29uY2VudHJhdGlvblBjdCI6MC42LCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIn1dLCJpbml0aWFsUm91bmRUcmlwRWZmaWNpZW5jeVBjdCI6OTUsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjo4LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6OTUsIm1hbnVmYWN0dXJpbmdEYXRlIjoiMjAyNi0wNS0wNVQwMDowMDowMFoiLCJtYW51ZmFjdHVyaW5nUGxhY2UiOiJJbmdvbHN0YWR0LCBERSAocGxhbnQgSU5HLTIpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDE0IEFoIC8gNTA0IFdoOyAzNiBWIExNVCIsIm1heGltdW1Wb2x0YWdlViI6NDIsIm1pbmltYWxWb2x0YWdlViI6MzAsIm5vbWluYWxDYXBhY2l0eUFoIjoxNCwibm9taW5hbFZvbHRhZ2VWIjozNiwibm90SW5Vc2VUZW1wZXJhdHVyZVJhbmdlIjp7Im1heEMiOjQ1LCJtaW5DIjotMTB9LCJub3RJblVzZVRlbXBlcmF0dXJlUmVmZXJlbmNlVGVzdCI6IkVOIDUwNjA0LTE6MjAxNiBzdG9yYWdlIGNsYXVzZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NTAwLCJwb3dlckxpbWl0TWF4VyI6NzUwLCJwb3dlckxpbWl0TWluVyI6MTAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwNTIiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6MC41MDQsInJhdGVkRW5lcmd5V2giOjUwNCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjEyLCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo5MCwic2FmZXR5TWVhc3VyZXMiOiJEbyBub3Qgb3BlbiB0aGUgcGFjay4gUmVwbGFjZSB2aWEgYXV0aG9yaXNlZCBkZWFsZXIuIEtlZXAgYXdheSBmcm9tIGhlYXQgYWJvdmUgNjAgZGVnQy4iLCJzcGFyZVBhcnRzQ29udGFjdHMiOiJzcGFyZXNAdm9sdGV4LmV4YW1wbGU7ICs0OSA4NDEgNTU1MCA0MTAiLCJ0ZXN0UmVwb3J0UmVzdWx0cyI6IkVOIDUwNjA0LTE6MjAxNiBwYXNzOyBVTiAzOC4zIHBhc3M7IHJlcG9ydCBWQlgtVFItMjAyNi0wMzExIiwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIgb3IgQUJDIGRyeSBwb3dkZXI7IHN1Ym1lcmdlIGluIHdhdGVyIGlmIHNhZmUgdG8gZG8gc28iLCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IlJldHVybiB0aGUgTE1UIGJhdHRlcnkgdG8gYW55IGJpY3ljbGUgcmV0YWlsZXIgb3IgbXVuaWNpcGFsIGNvbGxlY3Rpb24gcG9pbnQuIERvIG5vdCBwbGFjZSBpbiBob3VzZWhvbGQgd2FzdGUuIFByb2R1Y2VyIHJlZ2lzdHJhdGlvbiBERS1CQVRULTAwOTgxLiJ9LCJwcm9kdWN0TmFtZSI6IlZvbHRleCBlLUJpa2UgQmF0dGVyeSAzNlYgMTRBaCIsInB1Ymxpc2hlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40MjAyNzUyMDJaIiwicXJDb2RlVXJsIjoiaHR0cDovLzEyNy4wLjAuMToxLzAxLzA5NTkwMDAwMDAwMDUyLzIxLzc3NjRhNTBjYjEwZmU2NWJmOTMyIiwicmVwYWlyYWJpbGl0eVNjb3JlIjp7Im92ZXJhbGwiOjZ9LCJyZXRlbnRpb25Mb2NrZWQiOnRydWUsInJldGVudGlvblVudGlsIjoiMjAzNi0xMS0wM1QyMDowOToxNC40MjAyNzUyMDJaIiwic2NoZW1hVmVyc2lvbiI6IjIuOC4wIiwic3RhdHVzIjoiYWN0aXZlIiwidXBkYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40MjAyNzUyMDJaIiwidmVyc2lvbiI6MX0.k38oyyFR9OuGVf2OJvFvPeADhKSqaMYox6VujK6jcpDHUJVI7sCdVDDnKgzdwQMV-y_jLJLeGzD95OLDFWzlBQ", "public+restricted+individual": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJWQlgtTE1ULTIwMjYtMDE4OCIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjM4OTU1NTc0M1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNi0xNS4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMycgYXBwbGllcyBmcm9tIDIwMzYtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC4zODYzNzA1MjdaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjc5Mi03NzY0LWE1MGMtYjEwZmU2NWJmOTMyIiwibGludFJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC4zODk1NTgyOTVaIiwicGFja1ZlcnNpb24iOiIxLjEuMCJ9LCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDUsIDg1MDU1IEluZ29sc3RhZHQsIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly92b2x0ZXguZXhhbXBsZS8ud2VsbC1rbm93bi9kaWQuanNvbiIsIm5hbWUiOiJWb2x0ZXggQmF0dGVyeSBHbWJIIn0sIm1hdGVyaWFscyI6W3siY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiTGl0aGl1bSBJcm9uIFBob3NwaGF0ZSIsInJlY3ljbGVkUGN0Ijo3LCJ3ZWlnaHRLZyI6MS40fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkdyYXBoaXRlIiwicmVjeWNsZWRQY3QiOjMsIndlaWdodEtnIjowLjZ9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiQUJTL1BDIGhvdXNpbmciLCJyZWN5Y2xlZFBjdCI6MzAsIndlaWdodEtnIjowLjV9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA2LTE1IiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYW5vZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwibmFtZSI6IkdyYXBoaXRlIiwid2VpZ2h0UGN0Ijo5Nn0seyJjYXNOdW1iZXIiOiI5MDAzLTU1LTgiLCJuYW1lIjoiU0JSL0NNQyBiaW5kZXIiLCJ3ZWlnaHRQY3QiOjR9XSwiYmF0dGVyeUNoZW1pc3RyeSI6IkxGUCIsImJhdHRlcnlNb2RlbElkIjoiVkJYLUxNVDM2LTE0LTEwUyIsImJhdHRlcnlQYXNzcG9ydE51bWJlciI6IlVSTjpPREw6QkFUVDowOTU5MDAwMDAwMDA1MjpWQlgtTE1ULTIwMjYtMDE4OCIsImJhdHRlcnlTdGF0dXMiOiJvcmlnaW5hbCIsImJhdHRlcnlUeXBlIjoibG10IiwiYmF0dGVyeVdlaWdodEtnIjoyLjYsImNhdGhvZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiMTUzNjUtMTQtNyIsIm5hbWUiOiJMaUZlUE80Iiwid2VpZ2h0UGN0Ijo5NX0seyJjYXNOdW1iZXIiOiIyNDkzNy03OS05IiwibmFtZSI6IlBWREYgYmluZGVyIiwid2VpZ2h0UGN0IjozfSx7ImNhc051bWJlciI6IjEzMzMtODYtNCIsIm5hbWUiOiJDb25kdWN0aXZlIGNhcmJvbiBibGFjayIsIndlaWdodFBjdCI6Mn1dLCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjI0LCJjb21wb25lbnRQYXJ0TnVtYmVycyI6WyJWQlgtQ0VMTC0yMTcwMC1MRlAiLCJWQlgtQk1TLTEwUy1WMiIsIlZCWC1MTVQtRU5DTC0zNiJdLCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQzOS05My0yIiwiY291bnRyeU9mT3JpZ2luIjoiQVUiLCJuYW1lIjoiTGl0aGl1bSIsIndlaWdodEdyYW1zIjoxNjV9LHsiY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwiY291bnRyeU9mT3JpZ2luIjoiQ04iLCJuYW1lIjoiTmF0dXJhbCBncmFwaGl0ZSIsIndlaWdodEdyYW1zIjo2MDB9XSwiY3ljbGVMaWZlVGVzdENSYXRlIjoxLCJkaXNhc3NlbWJseUluc3RydWN0aW9uc1VybCI6Imh0dHBzOi8vdm9sdGV4LmV4YW1wbGUvc2VydmljZS9sbXQzNi0xNC9kaXNhc3NlbWJseSIsImR5bmFtaWNQZXJmb3JtYW5jZSI6eyJjYXBhY2l0eUZhZGVQY3QiOjAuNywiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6OTk1LCJpbnRlcm5hbFJlc2lzdGFuY2VNb2htIjo5NS40LCJyYXRlZENhcGFjaXR5QWgiOjEzLjksInJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk0Ljd9LCJlbGVjdHJvbHl0ZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI5Ni00OS0xIiwibmFtZSI6IkV0aHlsZW5lIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6Mzh9LHsiY2FzTnVtYmVyIjoiNjE2LTM4LTYiLCJuYW1lIjoiRGltZXRoeWwgY2FyYm9uYXRlIiwid2VpZ2h0UGN0Ijo0OX0seyJjYXNOdW1iZXIiOiIyMTMyNC00MC0zIiwibmFtZSI6IkxpdGhpdW0gaGV4YWZsdW9yb3Bob3NwaGF0ZSIsIndlaWdodFBjdCI6MTN9XSwiZXVEZWNsYXJhdGlvbk9mQ29uZm9ybWl0eSI6IkVVLURvQyBWQlgtMjAyNi1MTVQtMDA4OCBkYXRlZCAyMDI2LTA1LTEyLCBwZXIgUmVndWxhdGlvbiAoRVUpIDIwMjMvMTU0MiBBcnQuIDE4IiwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6MTAwMCwiZXhwZWN0ZWRMaWZldGltZVJlZmVyZW5jZVRlc3QiOiJFTiA1MDYwNC0xOjIwMTYsIDI1IGRlZ0MsIDEwMCUgRG9EIiwiaGF6YXJkb3VzU3Vic3RhbmNlcyI6W3siY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsImNvbmNlbnRyYXRpb25QY3QiOjAuNiwibmFtZSI6IkxpdGhpdW0gaGV4YWZsdW9yb3Bob3NwaGF0ZSJ9XSwiaW5pdGlhbFJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk1LCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6OCwiaW50ZXJuYWxQYWNrUmVzaXN0YW5jZU1vaG0iOjk1LCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDUtMDVUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiSW5nb2xzdGFkdCwgREUgKHBsYW50IElORy0yKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIHBlciBBcnQuIDEzKDQpOyBjYXBhY2l0eSAxNCBBaCAvIDUwNCBXaDsgMzYgViBMTVQiLCJtYXhpbXVtVm9sdGFnZVYiOjQyLCJtaW5pbWFsVm9sdGFnZVYiOjMwLCJub21pbmFsQ2FwYWNpdHlBaCI6MTQsIm5vbWluYWxWb2x0YWdlViI6MzYsIm5vdEluVXNlVGVtcGVyYXR1cmVSYW5nZSI6eyJtYXhDIjo0NSwibWluQyI6LTEwfSwibm90SW5Vc2VUZW1wZXJhdHVyZVJlZmVyZW5jZVRlc3QiOiJFTiA1MDYwNC0xOjIwMTYgc3RvcmFnZSBjbGF1c2UiLCJvcmlnaW5hbFBvd2VyQ2FwYWJpbGl0eVciOjUwMCwicG93ZXJMaW1pdE1heFciOjc1MCwicG93ZXJMaW1pdE1pblciOjEwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDUyIiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOjAuNTA0LCJyYXRlZEVuZXJneVdoIjo1MDQsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRSZXBvcnRpbmdZZWFyIjoyMDI2LCJyZW5ld2FibGVDb250ZW50UGN0IjoxMiwicm91bmRUcmlwRWZmaWNpZW5jeUF0SGFsZkN5Y2xlTGlmZVBjdCI6OTAsInNhZmV0eU1lYXN1cmVzIjoiRG8gbm90IG9wZW4gdGhlIHBhY2suIFJlcGxhY2UgdmlhIGF1dGhvcmlzZWQgZGVhbGVyLiBLZWVwIGF3YXkgZnJvbSBoZWF0IGFib3ZlIDYwIGRlZ0MuIiwic3BhcmVQYXJ0c0NvbnRhY3RzIjoic3BhcmVzQHZvbHRleC5leGFtcGxlOyArNDkgODQxIDU1NTAgNDEwIiwic3RhdGVPZkhlYWx0aCI6eyJwYXJhbWV0ZXJTZXQiOiJzdGF0aW9uYXJ5T3JMbXQiLCJyZW1haW5pbmdDYXBhY2l0eVBjdCI6OTkuMywicmVtYWluaW5nUG93ZXJDYXBhYmlsaXR5UGN0Ijo5OC44LCJzZWxmRGlzY2hhcmdlUmF0ZVBjdFBlck1vbnRoIjozfSwic3RhdGVPZkhlYWx0aFBjdCI6bnVsbCwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIgb3IgQUJDIGRyeSBwb3dkZXI7IHN1Ym1lcmdlIGluIHdhdGVyIGlmIHNhZmUgdG8gZG8gc28iLCJ1c2FnZUhpc3RvcnkiOnsiY2hhcmdlRGlzY2hhcmdlQ3ljbGVzIjo2fSwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJSZXR1cm4gdGhlIExNVCBiYXR0ZXJ5IHRvIGFueSBiaWN5Y2xlIHJldGFpbGVyIG9yIG11bmljaXBhbCBjb2xsZWN0aW9uIHBvaW50LiBEbyBub3QgcGxhY2UgaW4gaG91c2Vob2xkIHdhc3RlLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDk4MS4ifSwicHJvZHVjdE5hbWUiOiJWb2x0ZXggZS1CaWtlIEJhdHRlcnkgMzZWIDE0QWgiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNDIwMjc1MjAyWiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDA1Mi8yMS83NzY0YTUwY2IxMGZlNjViZjkzMiIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjo2fSwicmV0ZW50aW9uVW50aWwiOiIyMDM2LTExLTAzVDIwOjA5OjE0LjQyMDI3NTIwMloiLCJzY2hlbWFWZXJzaW9uIjoiMi44LjAiLCJzdGF0dXMiOiJhY3RpdmUiLCJ1cGRhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjQyMDI3NTIwMloiLCJ2ZXJzaW9uIjoxfQ.R07ZdKem7jDPzYR3dWFY5aJkMncMtKZF-1NrUmcUldAMxm-8y_5l3urvZHjQsMfJqQqx_X2S6H7b5pxZRA21Bw"}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}'); +INSERT INTO odal.passport VALUES ('01a1a307-b7f5-72ed-abdb-036d686a2932', 'battery', 'suspended', true, '2.8.0', NULL, NULL, 1, NULL, NULL, '2026-11-03 20:09:14.48781+00', '2036-11-03 20:09:14.508496+00', NULL, '2026-11-03 20:09:14.488159+00', '2026-11-03 20:09:14.550274+00', '2026-11-03 20:09:14.508496+00', '{"id": "01a1a307-b7f5-72ed-abdb-036d686a2932", "status": "suspended", "batchId": "ODL-PORT-2026-0342", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.485874190Z", "materials": [{"name": "Nickel", "weightKg": 0.045, "recycledPct": 20.0, "countryOfOrigin": "DE"}, {"name": "Steel can", "weightKg": 0.03, "recycledPct": 60.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000069/21/72edabdb036d686a2932", "updatedAt": "2026-11-03T20:09:14.552532843Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.487812197Z", "packVersion": "1.1.0"}, "co2ePerUnit": {"valueKg": 0.42}, "productName": "Odal Portable Power Pack AA NiMH (4-pack)", "publishedAt": "2026-11-03T20:09:14.508496352Z", "jwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtUE9SVC0yMDI2LTAzNDIiLCJjbzJlUGVyVW5pdCI6eyJ2YWx1ZUtnIjowLjQyfSwiY29tbW9kaXR5Q29kZSI6Ijg1MDczMDAwIiwiY29tcGxpYW5jZVJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODc4MTAwOTVaIiwiY28yZVNjb3JlIjowLjQyLCJjb21wbGlhbmNlU3RhdHVzIjoiUEFTU1RIUk9VR0hfTk9fVkFMSURBVElPTiIsInJlY3ljbGVkQ29udGVudFBjdCI6bnVsbCwicmVwYWlyYWJpbGl0eUluZGV4IjpudWxsfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODU4NzQxOTBaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjdmNS03MmVkLWFiZGItMDM2ZDY4NmEyOTMyIiwiandzU2lnbmF0dXJlIjpudWxsLCJsaW50UmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjQ4NzgxMjE5N1oiLCJwYWNrVmVyc2lvbiI6IjEuMS4wIn0sIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiSW5kdXN0cmllc3RyYXNzZSA4LCA0NzA1OSBEdWlzYnVyZywgREUiLCJjb3VudHJ5IjpudWxsLCJkaWRXZWJVcmwiOiJodHRwczovL3JoZWlucG93ZXIuZXhhbXBsZS8ud2VsbC1rbm93bi9kaWQuanNvbiIsIm5hbWUiOiJSaGVpbnBvd2VyIEJhdHRlcmllbiBHbWJIIn0sIm1hdGVyaWFscyI6W3siY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiTmlja2VsIiwicmVjeWNsZWRQY3QiOjIwLCJ3ZWlnaHRLZyI6MC4wNDV9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiU3RlZWwgY2FuIiwicmVjeWNsZWRQY3QiOjYwLCJ3ZWlnaHRLZyI6MC4wM31dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInBsYWNlZE9uTWFya2V0RGF0ZSI6IjIwMjYtMDQtMDEiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJiYXR0ZXJ5Q2hlbWlzdHJ5IjoiTmlNSCIsImJhdHRlcnlNb2RlbElkIjoiUlAtQUEtTklNSC0yMDAwLTQiLCJiYXR0ZXJ5U3RhdHVzIjoib3JpZ2luYWwiLCJiYXR0ZXJ5VHlwZSI6InBvcnRhYmxlIiwiYmF0dGVyeVdlaWdodEtnIjowLjEyLCJjbzJlUGVyVW5pdEtnIjowLjQyLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiIxMzEwLTU4LTMiLCJjb25jZW50cmF0aW9uUGN0IjozLCJuYW1lIjoiUG90YXNzaXVtIGh5ZHJveGlkZSJ9XSwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTAyLTIwVDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkR1aXNidXJnLCBERSAocGxhbnQgRFUtMSkiLCJtYXJraW5nSW5mb3JtYXRpb24iOiJTZXBhcmF0ZS1jb2xsZWN0aW9uIHN5bWJvbCBwZXIgQXJ0LiAxMyg0KTsgY2FwYWNpdHkgMjAwMCBtQWg7IHJlY2hhcmdlYWJsZSIsIm5vbWluYWxDYXBhY2l0eUFoIjoyLCJub21pbmFsVm9sdGFnZVYiOjEuMiwib3BlcmF0aW5nVGVtcE1heEMiOjQ1LCJvcGVyYXRpbmdUZW1wTWluQyI6LTEwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDY5Iiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOm51bGwsInJhdGVkRW5lcmd5V2giOjIuNCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjowLCJyZWN5Y2xlZENvbnRlbnRMZWFkUGN0IjowLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjowLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOjE4LCJzdGF0ZU9mSGVhbHRoUGN0IjpudWxsLCJ1c2FibGVFeHRpbmd1aXNoaW5nQWdlbnQiOiJXYXRlciwgQ08yLCBvciBkcnkgcG93ZGVyIiwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJSZXR1cm4gc3BlbnQgY2VsbHMgdG8gYW55IHJldGFpbGVyIGNvbGxlY3Rpb24gYm94LiBEbyBub3QgaW5jaW5lcmF0ZS4gUHJvZHVjZXIgcmVnaXN0cmF0aW9uIERFLUJBVFQtMDA0NTcuIn0sInByb2R1Y3ROYW1lIjoiT2RhbCBQb3J0YWJsZSBQb3dlciBQYWNrIEFBIE5pTUggKDQtcGFjaykiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNTA4NDk2MzUyWiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDA2OS8yMS83MmVkYWJkYjAzNmQ2ODZhMjkzMiIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjoxfSwicmV0ZW50aW9uTG9ja2VkIjp0cnVlLCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuNTA4NDk2MzUyWiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNTA4NDk2MzUyWiIsInZlcnNpb24iOjF9.j3mIBOcpst6hdk-oskF3Fe891VAWjN_Th78F8zv0jxl6oOIRH4o6YCkVtnw3-hY8Db4ICsweUisPJx-qfqVTDw", "manufacturer": {"name": "Rheinpower Batterien GmbH", "address": "Industriestrasse 8, 47059 Duisburg, DE", "country": null, "didWebUrl": "https://rheinpower.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85073000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:14.508496352Z", "retentionLocked": true, "complianceResult": {"co2eScore": 0.42, "assessedAt": "2026-11-03T20:09:14.487810095Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "portable", "productGroup": "battery", "batteryStatus": "original", "co2ePerUnitKg": 0.42, "ratedEnergyWh": 2.4, "batteryModelId": "RP-AA-NIMH-2000-4", "batteryWeightKg": 0.12, "nominalVoltageV": 1.2, "batteryChemistry": "NiMH", "ratedCapacityKwh": null, "stateOfHealthPct": null, "manufacturingDate": "2026-02-20T00:00:00Z", "nominalCapacityAh": 2.0, "operatingTempMaxC": 45.0, "operatingTempMinC": -10.0, "productIdentifier": {"gtin": "09590000000069", "scheme": "gs1"}, "manufacturingPlace": "Duisburg, DE (plant DU-1)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 2000 mAh; rechargeable", "hazardousSubstances": [{"name": "Potassium hydroxide", "casNumber": "1310-58-3", "concentrationPct": 3.0}], "recycledContentLeadPct": 0.0, "wasteBatteryInformation": "Return spent cells to any retailer collection box. Do not incinerate. Producer registration DE-BATT-00457.", "recycledContentCobaltPct": 0.0, "recycledContentNickelPct": 18.0, "usableExtinguishingAgent": "Water, CO2, or dry powder", "recycledContentLithiumPct": 0.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-04-01", "publicJwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtUE9SVC0yMDI2LTAzNDIiLCJjbzJlUGVyVW5pdCI6eyJ2YWx1ZUtnIjowLjQyfSwiY29tbW9kaXR5Q29kZSI6Ijg1MDczMDAwIiwiY29tcGxpYW5jZVJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODc4MTAwOTVaIiwiY28yZVNjb3JlIjowLjQyLCJjb21wbGlhbmNlU3RhdHVzIjoiUEFTU1RIUk9VR0hfTk9fVkFMSURBVElPTiIsInJlY3ljbGVkQ29udGVudFBjdCI6bnVsbCwicmVwYWlyYWJpbGl0eUluZGV4IjpudWxsfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODU4NzQxOTBaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjdmNS03MmVkLWFiZGItMDM2ZDY4NmEyOTMyIiwibWFudWZhY3R1cmVyIjp7ImFkZHJlc3MiOiJJbmR1c3RyaWVzdHJhc3NlIDgsIDQ3MDU5IER1aXNidXJnLCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vcmhlaW5wb3dlci5leGFtcGxlLy53ZWxsLWtub3duL2RpZC5qc29uIiwibmFtZSI6IlJoZWlucG93ZXIgQmF0dGVyaWVuIEdtYkgifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJOaWNrZWwiLCJyZWN5Y2xlZFBjdCI6MjAsIndlaWdodEtnIjowLjA0NX0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJTdGVlbCBjYW4iLCJyZWN5Y2xlZFBjdCI6NjAsIndlaWdodEtnIjowLjAzfV0sIm9wZXJhdG9ySWRlbnRpZmllciI6IkRFMTIzNDU2Nzg5IiwicGxhY2VkT25NYXJrZXREYXRlIjoiMjAyNi0wNC0wMSIsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImJhdHRlcnlDaGVtaXN0cnkiOiJOaU1IIiwiYmF0dGVyeU1vZGVsSWQiOiJSUC1BQS1OSU1ILTIwMDAtNCIsImJhdHRlcnlUeXBlIjoicG9ydGFibGUiLCJiYXR0ZXJ5V2VpZ2h0S2ciOjAuMTIsImNvMmVQZXJVbml0S2ciOjAuNDIsImhhemFyZG91c1N1YnN0YW5jZXMiOlt7ImNhc051bWJlciI6IjEzMTAtNTgtMyIsImNvbmNlbnRyYXRpb25QY3QiOjMsIm5hbWUiOiJQb3Rhc3NpdW0gaHlkcm94aWRlIn1dLCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDItMjBUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiRHVpc2J1cmcsIERFIChwbGFudCBEVS0xKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIHBlciBBcnQuIDEzKDQpOyBjYXBhY2l0eSAyMDAwIG1BaDsgcmVjaGFyZ2VhYmxlIiwibm9taW5hbENhcGFjaXR5QWgiOjIsIm5vbWluYWxWb2x0YWdlViI6MS4yLCJvcGVyYXRpbmdUZW1wTWF4QyI6NDUsIm9wZXJhdGluZ1RlbXBNaW5DIjotMTAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwNjkiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6bnVsbCwicmF0ZWRFbmVyZ3lXaCI6Mi40LCJyZWN5Y2xlZENvbnRlbnRDb2JhbHRQY3QiOjAsInJlY3ljbGVkQ29udGVudExlYWRQY3QiOjAsInJlY3ljbGVkQ29udGVudExpdGhpdW1QY3QiOjAsInJlY3ljbGVkQ29udGVudE5pY2tlbFBjdCI6MTgsInVzYWJsZUV4dGluZ3Vpc2hpbmdBZ2VudCI6IldhdGVyLCBDTzIsIG9yIGRyeSBwb3dkZXIiLCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IlJldHVybiBzcGVudCBjZWxscyB0byBhbnkgcmV0YWlsZXIgY29sbGVjdGlvbiBib3guIERvIG5vdCBpbmNpbmVyYXRlLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDQ1Ny4ifSwicHJvZHVjdE5hbWUiOiJPZGFsIFBvcnRhYmxlIFBvd2VyIFBhY2sgQUEgTmlNSCAoNC1wYWNrKSIsInB1Ymxpc2hlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC41MDg0OTYzNTJaIiwicXJDb2RlVXJsIjoiaHR0cDovLzEyNy4wLjAuMToxLzAxLzA5NTkwMDAwMDAwMDY5LzIxLzcyZWRhYmRiMDM2ZDY4NmEyOTMyIiwicmVwYWlyYWJpbGl0eVNjb3JlIjp7Im92ZXJhbGwiOjF9LCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuNTA4NDk2MzUyWiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNTA4NDk2MzUyWiIsInZlcnNpb24iOjF9.nsHCI8VFxvrnPxNxs-KuFtnh06-6R2ITh3vodOGyNjFqJAwhNfoiNANUs_yIMeuaOaxL1rwR5tqzL6F3pc6CBw", "repairabilityScore": {"overall": 1.0}, "disclosureSignatures": {"public+restricted+conformity": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtUE9SVC0yMDI2LTAzNDIiLCJjbzJlUGVyVW5pdCI6eyJ2YWx1ZUtnIjowLjQyfSwiY29tbW9kaXR5Q29kZSI6Ijg1MDczMDAwIiwiY29tcGxpYW5jZVJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODc4MTAwOTVaIiwiY28yZVNjb3JlIjowLjQyLCJjb21wbGlhbmNlU3RhdHVzIjoiUEFTU1RIUk9VR0hfTk9fVkFMSURBVElPTiIsInJlY3ljbGVkQ29udGVudFBjdCI6bnVsbCwicmVwYWlyYWJpbGl0eUluZGV4IjpudWxsfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODU4NzQxOTBaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjdmNS03MmVkLWFiZGItMDM2ZDY4NmEyOTMyIiwibGludFJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODc4MTIxOTdaIiwicGFja1ZlcnNpb24iOiIxLjEuMCJ9LCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IkluZHVzdHJpZXN0cmFzc2UgOCwgNDcwNTkgRHVpc2J1cmcsIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly9yaGVpbnBvd2VyLmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiUmhlaW5wb3dlciBCYXR0ZXJpZW4gR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6Ik5pY2tlbCIsInJlY3ljbGVkUGN0IjoyMCwid2VpZ2h0S2ciOjAuMDQ1fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IlN0ZWVsIGNhbiIsInJlY3ljbGVkUGN0Ijo2MCwid2VpZ2h0S2ciOjAuMDN9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA0LTAxIiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYmF0dGVyeUNoZW1pc3RyeSI6Ik5pTUgiLCJiYXR0ZXJ5TW9kZWxJZCI6IlJQLUFBLU5JTUgtMjAwMC00IiwiYmF0dGVyeVR5cGUiOiJwb3J0YWJsZSIsImJhdHRlcnlXZWlnaHRLZyI6MC4xMiwiY28yZVBlclVuaXRLZyI6MC40MiwiaGF6YXJkb3VzU3Vic3RhbmNlcyI6W3siY2FzTnVtYmVyIjoiMTMxMC01OC0zIiwiY29uY2VudHJhdGlvblBjdCI6MywibmFtZSI6IlBvdGFzc2l1bSBoeWRyb3hpZGUifV0sIm1hbnVmYWN0dXJpbmdEYXRlIjoiMjAyNi0wMi0yMFQwMDowMDowMFoiLCJtYW51ZmFjdHVyaW5nUGxhY2UiOiJEdWlzYnVyZywgREUgKHBsYW50IERVLTEpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDIwMDAgbUFoOyByZWNoYXJnZWFibGUiLCJub21pbmFsQ2FwYWNpdHlBaCI6Miwibm9taW5hbFZvbHRhZ2VWIjoxLjIsIm9wZXJhdGluZ1RlbXBNYXhDIjo0NSwib3BlcmF0aW5nVGVtcE1pbkMiOi0xMCwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RJZGVudGlmaWVyIjp7Imd0aW4iOiIwOTU5MDAwMDAwMDA2OSIsInNjaGVtZSI6ImdzMSJ9LCJyYXRlZENhcGFjaXR5S3doIjpudWxsLCJyYXRlZEVuZXJneVdoIjoyLjQsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6MCwicmVjeWNsZWRDb250ZW50TGVhZFBjdCI6MCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6MCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjoxOCwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIsIENPMiwgb3IgZHJ5IHBvd2RlciIsIndhc3RlQmF0dGVyeUluZm9ybWF0aW9uIjoiUmV0dXJuIHNwZW50IGNlbGxzIHRvIGFueSByZXRhaWxlciBjb2xsZWN0aW9uIGJveC4gRG8gbm90IGluY2luZXJhdGUuIFByb2R1Y2VyIHJlZ2lzdHJhdGlvbiBERS1CQVRULTAwNDU3LiJ9LCJwcm9kdWN0TmFtZSI6Ik9kYWwgUG9ydGFibGUgUG93ZXIgUGFjayBBQSBOaU1IICg0LXBhY2spIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjUwODQ5NjM1MloiLCJxckNvZGVVcmwiOiJodHRwOi8vMTI3LjAuMC4xOjEvMDEvMDk1OTAwMDAwMDAwNjkvMjEvNzJlZGFiZGIwMzZkNjg2YTI5MzIiLCJyZXBhaXJhYmlsaXR5U2NvcmUiOnsib3ZlcmFsbCI6MX0sInJldGVudGlvbkxvY2tlZCI6dHJ1ZSwicmV0ZW50aW9uVW50aWwiOiIyMDM2LTExLTAzVDIwOjA5OjE0LjUwODQ5NjM1MloiLCJzY2hlbWFWZXJzaW9uIjoiMi44LjAiLCJzdGF0dXMiOiJhY3RpdmUiLCJ1cGRhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjUwODQ5NjM1MloiLCJ2ZXJzaW9uIjoxfQ.SYz2C-w7lDIwXW5Z-7YBbigmsJm5XikhQ2DGrwc_1AQKlA3zhl1OSbi8FD7dLmXHJLE_dtWj4aoy5i5osk_yAQ", "public+restricted+individual": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtUE9SVC0yMDI2LTAzNDIiLCJjbzJlUGVyVW5pdCI6eyJ2YWx1ZUtnIjowLjQyfSwiY29tbW9kaXR5Q29kZSI6Ijg1MDczMDAwIiwiY29tcGxpYW5jZVJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODc4MTAwOTVaIiwiY28yZVNjb3JlIjowLjQyLCJjb21wbGlhbmNlU3RhdHVzIjoiUEFTU1RIUk9VR0hfTk9fVkFMSURBVElPTiIsInJlY3ljbGVkQ29udGVudFBjdCI6bnVsbCwicmVwYWlyYWJpbGl0eUluZGV4IjpudWxsfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODU4NzQxOTBaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjdmNS03MmVkLWFiZGItMDM2ZDY4NmEyOTMyIiwibGludFJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODc4MTIxOTdaIiwicGFja1ZlcnNpb24iOiIxLjEuMCJ9LCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IkluZHVzdHJpZXN0cmFzc2UgOCwgNDcwNTkgRHVpc2J1cmcsIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly9yaGVpbnBvd2VyLmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiUmhlaW5wb3dlciBCYXR0ZXJpZW4gR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6Ik5pY2tlbCIsInJlY3ljbGVkUGN0IjoyMCwid2VpZ2h0S2ciOjAuMDQ1fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IlN0ZWVsIGNhbiIsInJlY3ljbGVkUGN0Ijo2MCwid2VpZ2h0S2ciOjAuMDN9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA0LTAxIiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYmF0dGVyeUNoZW1pc3RyeSI6Ik5pTUgiLCJiYXR0ZXJ5TW9kZWxJZCI6IlJQLUFBLU5JTUgtMjAwMC00IiwiYmF0dGVyeVN0YXR1cyI6Im9yaWdpbmFsIiwiYmF0dGVyeVR5cGUiOiJwb3J0YWJsZSIsImJhdHRlcnlXZWlnaHRLZyI6MC4xMiwiY28yZVBlclVuaXRLZyI6MC40MiwiaGF6YXJkb3VzU3Vic3RhbmNlcyI6W3siY2FzTnVtYmVyIjoiMTMxMC01OC0zIiwiY29uY2VudHJhdGlvblBjdCI6MywibmFtZSI6IlBvdGFzc2l1bSBoeWRyb3hpZGUifV0sIm1hbnVmYWN0dXJpbmdEYXRlIjoiMjAyNi0wMi0yMFQwMDowMDowMFoiLCJtYW51ZmFjdHVyaW5nUGxhY2UiOiJEdWlzYnVyZywgREUgKHBsYW50IERVLTEpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDIwMDAgbUFoOyByZWNoYXJnZWFibGUiLCJub21pbmFsQ2FwYWNpdHlBaCI6Miwibm9taW5hbFZvbHRhZ2VWIjoxLjIsIm9wZXJhdGluZ1RlbXBNYXhDIjo0NSwib3BlcmF0aW5nVGVtcE1pbkMiOi0xMCwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RJZGVudGlmaWVyIjp7Imd0aW4iOiIwOTU5MDAwMDAwMDA2OSIsInNjaGVtZSI6ImdzMSJ9LCJyYXRlZENhcGFjaXR5S3doIjpudWxsLCJyYXRlZEVuZXJneVdoIjoyLjQsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6MCwicmVjeWNsZWRDb250ZW50TGVhZFBjdCI6MCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6MCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjoxOCwic3RhdGVPZkhlYWx0aFBjdCI6bnVsbCwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIsIENPMiwgb3IgZHJ5IHBvd2RlciIsIndhc3RlQmF0dGVyeUluZm9ybWF0aW9uIjoiUmV0dXJuIHNwZW50IGNlbGxzIHRvIGFueSByZXRhaWxlciBjb2xsZWN0aW9uIGJveC4gRG8gbm90IGluY2luZXJhdGUuIFByb2R1Y2VyIHJlZ2lzdHJhdGlvbiBERS1CQVRULTAwNDU3LiJ9LCJwcm9kdWN0TmFtZSI6Ik9kYWwgUG9ydGFibGUgUG93ZXIgUGFjayBBQSBOaU1IICg0LXBhY2spIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjUwODQ5NjM1MloiLCJxckNvZGVVcmwiOiJodHRwOi8vMTI3LjAuMC4xOjEvMDEvMDk1OTAwMDAwMDAwNjkvMjEvNzJlZGFiZGIwMzZkNjg2YTI5MzIiLCJyZXBhaXJhYmlsaXR5U2NvcmUiOnsib3ZlcmFsbCI6MX0sInJldGVudGlvblVudGlsIjoiMjAzNi0xMS0wM1QyMDowOToxNC41MDg0OTYzNTJaIiwic2NoZW1hVmVyc2lvbiI6IjIuOC4wIiwic3RhdHVzIjoiYWN0aXZlIiwidXBkYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC41MDg0OTYzNTJaIiwidmVyc2lvbiI6MX0.DaSoJ8XHNHjSftulWk5S2kA-oFan_UCu5hN9HcpbFpf9MwMPRoNnCkIEXyHlMBMbfS8AhyojyJX-qoRYS495CQ"}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}'); +INSERT INTO odal.passport VALUES ('01a1a307-b84c-7245-8576-ea67194809ea', 'battery', 'active', true, '2.8.0', NULL, NULL, 2, '01a1a307-b5bd-71ac-9c86-4696540c3b21', NULL, '2026-11-03 20:09:14.573475+00', '2036-11-03 20:09:14.585049+00', NULL, '2026-11-03 20:09:14.573804+00', '2026-11-03 20:09:14.626531+00', '2026-11-03 20:09:14.585049+00', '{"id": "01a1a307-b84c-7245-8576-ea67194809ea", "status": "active", "batchId": "ODL-IND-LFP-2026-Q2-014", "version": 2, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.572051734Z", "materials": [{"name": "Lithium Iron Phosphate", "weightKg": 22.0, "recycledPct": 8.0, "countryOfOrigin": "DE"}, {"name": "Graphite", "weightKg": 9.5, "recycledPct": 6.0, "countryOfOrigin": "DE"}, {"name": "Steel enclosure", "weightKg": 12.0, "recycledPct": 82.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000021/21/71ac9c864696540c3b21", "updatedAt": "2026-11-03T20:09:14.585049272Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.573477185Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "LFP stationary pack, corrected", "publishedAt": "2026-11-03T20:09:14.585049272Z", "jwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELUxGUC0yMDI2LVEyLTAxNCIsImNhcnJpZXJTZXJpYWwiOiI3MWFjOWM4NjQ2OTY1NDBjM2IyMSIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjU3MzQ3NDc0N1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNS0yMC4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC41NzIwNTE3MzRaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjg0Yy03MjQ1LTg1NzYtZWE2NzE5NDgwOWVhIiwiandzU2lnbmF0dXJlIjpudWxsLCJsaW50UmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjU3MzQ3NzE4NVoiLCJwYWNrVmVyc2lvbiI6IjEuMS4wIn0sIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiUHJlbnpsYXVlciBCZXJnIDEyLCAxMDQwNSBCZXJsaW4sIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly9iYXR0ZXJpZXMuZXhhbXBsZS8ud2VsbC1rbm93bi9kaWQuanNvbiIsIm5hbWUiOiJFeGFtcGxlIEJhdHRlcmllcyBHbWJIIn0sIm1hdGVyaWFscyI6W3siY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiTGl0aGl1bSBJcm9uIFBob3NwaGF0ZSIsInJlY3ljbGVkUGN0Ijo4LCJ3ZWlnaHRLZyI6MjJ9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiR3JhcGhpdGUiLCJyZWN5Y2xlZFBjdCI6Niwid2VpZ2h0S2ciOjkuNX0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJTdGVlbCBlbmNsb3N1cmUiLCJyZWN5Y2xlZFBjdCI6ODIsIndlaWdodEtnIjoxMn1dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInBsYWNlZE9uTWFya2V0RGF0ZSI6IjIwMjYtMDUtMjAiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJhbm9kZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJuYW1lIjoiR3JhcGhpdGUiLCJ3ZWlnaHRQY3QiOjk2fSx7ImNhc051bWJlciI6IjkwMDMtNTUtOCIsIm5hbWUiOiJTQlIvQ01DIGJpbmRlciIsIndlaWdodFBjdCI6M30seyJjYXNOdW1iZXIiOiIxMzMzLTg2LTQiLCJuYW1lIjoiQ29uZHVjdGl2ZSBjYXJib24gYmxhY2siLCJ3ZWlnaHRQY3QiOjF9XSwiYmF0dGVyeUNoZW1pc3RyeSI6IkxGUCIsImJhdHRlcnlNb2RlbElkIjoiR0MtRVNTLUxGUDUxMi0xMDAiLCJiYXR0ZXJ5UGFzc3BvcnROdW1iZXIiOiJVUk46T0RMOkJBVFQ6MDk1OTAwMDAwMDAwMjE6T0RMLUlORC1MRlAtMjAyNi1RMi0wMTQiLCJiYXR0ZXJ5U3RhdHVzIjoib3JpZ2luYWwiLCJiYXR0ZXJ5VHlwZSI6ImluZHVzdHJpYWwiLCJiYXR0ZXJ5V2VpZ2h0S2ciOjQ2LjUsImNhdGhvZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiMTUzNjUtMTQtNyIsIm5hbWUiOiJMaUZlUE80Iiwid2VpZ2h0UGN0Ijo5NX0seyJjYXNOdW1iZXIiOiIyNDkzNy03OS05IiwibmFtZSI6IlBWREYgYmluZGVyIiwid2VpZ2h0UGN0IjozfSx7ImNhc051bWJlciI6IjEzMzMtODYtNCIsIm5hbWUiOiJDb25kdWN0aXZlIGNhcmJvbiBibGFjayIsIndlaWdodFBjdCI6Mn1dLCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjE0NCwiY29tcG9uZW50UGFydE51bWJlcnMiOlsiR0MtQ0VMTC0zMjcwMC1MRlAiLCJHQy1CTVMtMTZTLVYyIiwiR0MtRVNTLUVOQ0wtNTEyIl0sImNyaXRpY2FsUmF3TWF0ZXJpYWxzIjpbeyJjYXNOdW1iZXIiOiI3NDM5LTkzLTIiLCJjb3VudHJ5T2ZPcmlnaW4iOiJBVSIsIm5hbWUiOiJMaXRoaXVtIiwid2VpZ2h0R3JhbXMiOjI2MDB9LHsiY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwiY291bnRyeU9mT3JpZ2luIjoiQ04iLCJuYW1lIjoiTmF0dXJhbCBncmFwaGl0ZSIsIndlaWdodEdyYW1zIjo5NTAwfV0sImN5Y2xlTGlmZVRlc3RDUmF0ZSI6MC4zMywiZGlzYXNzZW1ibHlJbnN0cnVjdGlvbnNVcmwiOiJodHRwczovL2JhdHRlcmllcy5leGFtcGxlL3NlcnZpY2UvZXNzLWxmcDUxMi9kaXNhc3NlbWJseSIsImR5bmFtaWNQZXJmb3JtYW5jZSI6eyJjYXBhY2l0eUZhZGVQY3QiOjAuNCwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6NTk4MCwiaW50ZXJuYWxSZXNpc3RhbmNlTW9obSI6OS4xLCJyYXRlZENhcGFjaXR5QWgiOjk5LjYsInJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk2LjJ9LCJlbGVjdHJvbHl0ZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI5Ni00OS0xIiwibmFtZSI6IkV0aHlsZW5lIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6Mzh9LHsiY2FzTnVtYmVyIjoiNjIzLTUzLTAiLCJuYW1lIjoiRXRoeWwgbWV0aHlsIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6NDl9LHsiY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUiLCJ3ZWlnaHRQY3QiOjEzfV0sImV1RGVjbGFyYXRpb25PZkNvbmZvcm1pdHkiOiJFVS1Eb0MgR0MtMjAyNi1JTkQtMDA2NyBkYXRlZCAyMDI2LTAzLTEwLCBwZXIgUmVndWxhdGlvbiAoRVUpIDIwMjMvMTU0MiBBcnQuIDE4IiwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6NjAwMCwiZXhwZWN0ZWRMaWZldGltZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjE0MjctMToyMDEzLCAyNSBkZWdDLCA5MCUgRG9EIiwiaGF6YXJkb3VzU3Vic3RhbmNlcyI6W3siY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsImNvbmNlbnRyYXRpb25QY3QiOjAuNywibmFtZSI6IkxpdGhpdW0gaGV4YWZsdW9yb3Bob3NwaGF0ZSJ9XSwiaW5pdGlhbFJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk2LjUsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjowLjYsImludGVybmFsUGFja1Jlc2lzdGFuY2VNb2htIjo5LCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDMtMDJUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiQmVybGluLCBERSAocGxhbnQgQkxOLTIpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDEwMCBBaCAvIDUuMTIga1doOyBjaGVtaXN0cnkgTEZQIiwibWF4aW11bVZvbHRhZ2VWIjo1OC40LCJtaW5pbWFsVm9sdGFnZVYiOjQwLCJub21pbmFsQ2FwYWNpdHlBaCI6MTAwLCJub21pbmFsVm9sdGFnZVYiOjUxLjIsIm5vdEluVXNlVGVtcGVyYXR1cmVSYW5nZSI6eyJtYXhDIjo1MCwibWluQyI6LTEwfSwibm90SW5Vc2VUZW1wZXJhdHVyZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjI2NjAtMToyMDE4IHN0b3JhZ2UgcHJvZmlsZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NTAwMCwicG93ZXJMaW1pdE1heFciOjc1MDAsInBvd2VyTGltaXRNaW5XIjoxNTAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwMjEiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6NS4xMiwicmF0ZWRFbmVyZ3lXaCI6NTEyMCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjIyLCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo5Miwic2FmZXR5TWVhc3VyZXMiOiJJc29sYXRlIERDIGJyZWFrZXIgYmVmb3JlIHNlcnZpY2luZy4gSW5zdWxhdGVkIHRvb2xzIG9ubHkuIFZlbnRpbGF0ZSBlbmNsb3N1cmUgYmVmb3JlIG9wZW5pbmcuIiwic3BhcmVQYXJ0c0NvbnRhY3RzIjoic3BhcmVzQGJhdHRlcmllcy5leGFtcGxlOyArNDkgMzAgNTU1MCAxMjAwIiwic3RhdGVPZkhlYWx0aCI6eyJvaG1pY1Jlc2lzdGFuY2VNb2htIjo5LjEsInBhcmFtZXRlclNldCI6InN0YXRpb25hcnlPckxtdCIsInJlbWFpbmluZ0NhcGFjaXR5UGN0Ijo5OS42LCJyZW1haW5pbmdQb3dlckNhcGFiaWxpdHlQY3QiOjk5LjQsInJlbWFpbmluZ1JvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk5LjEsInNlbGZEaXNjaGFyZ2VSYXRlUGN0UGVyTW9udGgiOjEuNH0sInN0YXRlT2ZIZWFsdGhQY3QiOm51bGwsInRlc3RSZXBvcnRSZXN1bHRzIjoiSUVDIDYyNjE5OjIwMjIgcGFzczsgSUVDIDYzMDU2OjIwMjAgcGFzczsgVU4gMzguMyBwYXNzOyByZXBvcnQgR0MtVFItMjAyNi0wMzAxIiwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIgb3IgQUJDIGRyeSBwb3dkZXI7IGxhcmdlIHZvbHVtZXMgb2Ygd2F0ZXIgZm9yIHRoZXJtYWwgcnVuYXdheSIsInVzYWdlSGlzdG9yeSI6eyJjaGFyZ2VEaXNjaGFyZ2VDeWNsZXMiOjUsIm5lZ2F0aXZlRXZlbnRzIjpbXX0sIndhc3RlQmF0dGVyeUluZm9ybWF0aW9uIjoiUmV0dXJuIHRvIGFuIGF1dGhvcmlzZWQgY29sbGVjdGlvbiBwb2ludC4gRG8gbm90IGluY2luZXJhdGUgb3Igc2hyZWQuIFByb2R1Y2VyIHJlZ2lzdHJhdGlvbiBERS1CQVRULTAwMTIzLiJ9LCJwcm9kdWN0TmFtZSI6IkxGUCBzdGF0aW9uYXJ5IHBhY2ssIGNvcnJlY3RlZCIsInB1Ymxpc2hlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC41ODUwNDkyNzJaIiwicXJDb2RlVXJsIjoiaHR0cDovLzEyNy4wLjAuMToxLzAxLzA5NTkwMDAwMDAwMDIxLzIxLzcxYWM5Yzg2NDY5NjU0MGMzYjIxIiwicmVwYWlyYWJpbGl0eVNjb3JlIjp7Im92ZXJhbGwiOjguMX0sInJldGVudGlvbkxvY2tlZCI6dHJ1ZSwicmV0ZW50aW9uVW50aWwiOiIyMDM2LTExLTAzVDIwOjA5OjE0LjU4NTA0OTI3MloiLCJzY2hlbWFWZXJzaW9uIjoiMi44LjAiLCJzdGF0dXMiOiJhY3RpdmUiLCJzdXBlcnNlZGVzSWQiOiIwMWExYTMwNy1iNWJkLTcxYWMtOWM4Ni00Njk2NTQwYzNiMjEiLCJ1cGRhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjU4NTA0OTI3MloiLCJ2ZXJzaW9uIjoyfQ.MPlNiIgt_OdDPjIwg44_t9ksmhr8o5F_WfkKHppMgwbkYPKPMw-dhtLBtnPmMJfekc0QWJnzdrMqv0Tg6sNCCQ", "manufacturer": {"name": "Example Batteries GmbH", "address": "Prenzlauer Berg 12, 10405 Berlin, DE", "country": null, "didWebUrl": "https://batteries.example/.well-known/did.json"}, "productGroup": "battery", "supersedesId": "01a1a307-b5bd-71ac-9c86-4696540c3b21", "carrierSerial": "71ac9c864696540c3b21", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:14.585049272Z", "retentionLocked": true, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-05-20. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:14.573474747Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "industrial", "productGroup": "battery", "usageHistory": {"negativeEvents": [], "chargeDischargeCycles": 5}, "anodeMaterial": [{"name": "Graphite", "casNumber": "7782-42-5", "weightPct": 96.0}, {"name": "SBR/CMC binder", "casNumber": "9003-55-8", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 1.0}], "batteryStatus": "original", "ratedEnergyWh": 5120.0, "stateOfHealth": {"parameterSet": "stationaryOrLmt", "ohmicResistanceMohm": 9.1, "remainingCapacityPct": 99.6, "remainingPowerCapabilityPct": 99.4, "selfDischargeRatePctPerMonth": 1.4, "remainingRoundTripEfficiencyPct": 99.1}, "batteryModelId": "GC-ESS-LFP512-100", "powerLimitMaxW": 7500.0, "powerLimitMinW": 150.0, "safetyMeasures": "Isolate DC breaker before servicing. Insulated tools only. Ventilate enclosure before opening.", "batteryWeightKg": 46.5, "cathodeMaterial": [{"name": "LiFePO4", "casNumber": "15365-14-7", "weightPct": 95.0}, {"name": "PVDF binder", "casNumber": "24937-79-9", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 2.0}], "maximumVoltageV": 58.4, "minimalVoltageV": 40.0, "nominalVoltageV": 51.2, "batteryChemistry": "LFP", "ratedCapacityKwh": 5.12, "stateOfHealthPct": null, "manufacturingDate": "2026-03-02T00:00:00Z", "nominalCapacityAh": 100.0, "productIdentifier": {"gtin": "09590000000021", "scheme": "gs1"}, "testReportResults": "IEC 62619:2022 pass; IEC 63056:2020 pass; UN 38.3 pass; report GC-TR-2026-0301", "cycleLifeTestCRate": 0.33, "dynamicPerformance": {"capacityFadePct": 0.4, "ratedCapacityAh": 99.6, "expectedLifetimeCycles": 5980, "internalResistanceMohm": 9.1, "roundTripEfficiencyPct": 96.2}, "manufacturingPlace": "Berlin, DE (plant BLN-2)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 100 Ah / 5.12 kWh; chemistry LFP", "sparePartsContacts": "spares@batteries.example; +49 30 5550 1200", "electrolyteMaterial": [{"name": "Ethylene carbonate", "casNumber": "96-49-1", "weightPct": 38.0}, {"name": "Ethyl methyl carbonate", "casNumber": "623-53-0", "weightPct": 49.0}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "weightPct": 13.0}], "hazardousSubstances": [{"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.7}], "renewableContentPct": 22.0, "componentPartNumbers": ["GC-CELL-32700-LFP", "GC-BMS-16S-V2", "GC-ESS-ENCL-512"], "criticalRawMaterials": [{"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 2600.0, "countryOfOrigin": "AU"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 9500.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000021:ODL-IND-LFP-2026-Q2-014", "expectedLifetimeCycles": 6000, "wasteBatteryInformation": "Return to an authorised collection point. Do not incinerate or shred. Producer registration DE-BATT-00123.", "notInUseTemperatureRange": {"maxC": 50.0, "minC": -10.0}, "originalPowerCapabilityW": 5000.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Water or ABC dry powder; large volumes of water for thermal runaway", "euDeclarationOfConformity": "EU-DoC GC-2026-IND-0067 dated 2026-03-10, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://batteries.example/service/ess-lfp512/disassembly", "internalCellResistanceMohm": 0.6, "internalPackResistanceMohm": 9.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "IEC 61427-1:2013, 25 degC, 90% DoD", "initialRoundTripEfficiencyPct": 96.5, "commercialWarrantyPeriodMonths": 144, "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage profile", "roundTripEfficiencyAtHalfCycleLifePct": 92.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-05-20", "publicJwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELUxGUC0yMDI2LVEyLTAxNCIsImNhcnJpZXJTZXJpYWwiOiI3MWFjOWM4NjQ2OTY1NDBjM2IyMSIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjU3MzQ3NDc0N1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNS0yMC4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC41NzIwNTE3MzRaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjg0Yy03MjQ1LTg1NzYtZWE2NzE5NDgwOWVhIiwibWFudWZhY3R1cmVyIjp7ImFkZHJlc3MiOiJQcmVuemxhdWVyIEJlcmcgMTIsIDEwNDA1IEJlcmxpbiwgREUiLCJjb3VudHJ5IjpudWxsLCJkaWRXZWJVcmwiOiJodHRwczovL2JhdHRlcmllcy5leGFtcGxlLy53ZWxsLWtub3duL2RpZC5qc29uIiwibmFtZSI6IkV4YW1wbGUgQmF0dGVyaWVzIEdtYkgifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJMaXRoaXVtIElyb24gUGhvc3BoYXRlIiwicmVjeWNsZWRQY3QiOjgsIndlaWdodEtnIjoyMn0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJHcmFwaGl0ZSIsInJlY3ljbGVkUGN0Ijo2LCJ3ZWlnaHRLZyI6OS41fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IlN0ZWVsIGVuY2xvc3VyZSIsInJlY3ljbGVkUGN0Ijo4Miwid2VpZ2h0S2ciOjEyfV0sIm9wZXJhdG9ySWRlbnRpZmllciI6IkRFMTIzNDU2Nzg5IiwicGxhY2VkT25NYXJrZXREYXRlIjoiMjAyNi0wNS0yMCIsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImJhdHRlcnlDaGVtaXN0cnkiOiJMRlAiLCJiYXR0ZXJ5TW9kZWxJZCI6IkdDLUVTUy1MRlA1MTItMTAwIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDIxOk9ETC1JTkQtTEZQLTIwMjYtUTItMDE0IiwiYmF0dGVyeVR5cGUiOiJpbmR1c3RyaWFsIiwiYmF0dGVyeVdlaWdodEtnIjo0Ni41LCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjE0NCwiY3JpdGljYWxSYXdNYXRlcmlhbHMiOlt7ImNhc051bWJlciI6Ijc0MzktOTMtMiIsImNvdW50cnlPZk9yaWdpbiI6IkFVIiwibmFtZSI6IkxpdGhpdW0iLCJ3ZWlnaHRHcmFtcyI6MjYwMH0seyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDTiIsIm5hbWUiOiJOYXR1cmFsIGdyYXBoaXRlIiwid2VpZ2h0R3JhbXMiOjk1MDB9XSwiY3ljbGVMaWZlVGVzdENSYXRlIjowLjMzLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIEdDLTIwMjYtSU5ELTAwNjcgZGF0ZWQgMjAyNi0wMy0xMCwgcGVyIFJlZ3VsYXRpb24gKEVVKSAyMDIzLzE1NDIgQXJ0LiAxOCIsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjYwMDAsImV4cGVjdGVkTGlmZXRpbWVSZWZlcmVuY2VUZXN0IjoiSUVDIDYxNDI3LTE6MjAxMywgMjUgZGVnQywgOTAlIERvRCIsImhhemFyZG91c1N1YnN0YW5jZXMiOlt7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJjb25jZW50cmF0aW9uUGN0IjowLjcsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUifV0sImluaXRpYWxSb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5Ni41LCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6MC42LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6OSwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTAzLTAyVDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkJlcmxpbiwgREUgKHBsYW50IEJMTi0yKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIHBlciBBcnQuIDEzKDQpOyBjYXBhY2l0eSAxMDAgQWggLyA1LjEyIGtXaDsgY2hlbWlzdHJ5IExGUCIsIm1heGltdW1Wb2x0YWdlViI6NTguNCwibWluaW1hbFZvbHRhZ2VWIjo0MCwibm9taW5hbENhcGFjaXR5QWgiOjEwMCwibm9taW5hbFZvbHRhZ2VWIjo1MS4yLCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NTAsIm1pbkMiOi0xMH0sIm5vdEluVXNlVGVtcGVyYXR1cmVSZWZlcmVuY2VUZXN0IjoiSUVDIDYyNjYwLTE6MjAxOCBzdG9yYWdlIHByb2ZpbGUiLCJvcmlnaW5hbFBvd2VyQ2FwYWJpbGl0eVciOjUwMDAsInBvd2VyTGltaXRNYXhXIjo3NTAwLCJwb3dlckxpbWl0TWluVyI6MTUwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDIxIiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOjUuMTIsInJhdGVkRW5lcmd5V2giOjUxMjAsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRSZXBvcnRpbmdZZWFyIjoyMDI2LCJyZW5ld2FibGVDb250ZW50UGN0IjoyMiwicm91bmRUcmlwRWZmaWNpZW5jeUF0SGFsZkN5Y2xlTGlmZVBjdCI6OTIsInVzYWJsZUV4dGluZ3Vpc2hpbmdBZ2VudCI6IldhdGVyIG9yIEFCQyBkcnkgcG93ZGVyOyBsYXJnZSB2b2x1bWVzIG9mIHdhdGVyIGZvciB0aGVybWFsIHJ1bmF3YXkiLCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IlJldHVybiB0byBhbiBhdXRob3Jpc2VkIGNvbGxlY3Rpb24gcG9pbnQuIERvIG5vdCBpbmNpbmVyYXRlIG9yIHNocmVkLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDEyMy4ifSwicHJvZHVjdE5hbWUiOiJMRlAgc3RhdGlvbmFyeSBwYWNrLCBjb3JyZWN0ZWQiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNTg1MDQ5MjcyWiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDAyMS8yMS83MWFjOWM4NjQ2OTY1NDBjM2IyMSIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjo4LjF9LCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuNTg1MDQ5MjcyWiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInN1cGVyc2VkZXNJZCI6IjAxYTFhMzA3LWI1YmQtNzFhYy05Yzg2LTQ2OTY1NDBjM2IyMSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNTg1MDQ5MjcyWiIsInZlcnNpb24iOjJ9.eUpdL2Rti-tkqYKFpz3AmYCthWqDEKBVFoZq44jXJAhIv14G-G1KVxZrjWcizjVLyMtTj04p-IN7IdttyodNCA", "repairabilityScore": {"overall": 8.1}, "disclosureSignatures": {"public+restricted+conformity": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELUxGUC0yMDI2LVEyLTAxNCIsImNhcnJpZXJTZXJpYWwiOiI3MWFjOWM4NjQ2OTY1NDBjM2IyMSIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjU3MzQ3NDc0N1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNS0yMC4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC41NzIwNTE3MzRaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjg0Yy03MjQ1LTg1NzYtZWE2NzE5NDgwOWVhIiwibGludFJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC41NzM0NzcxODVaIiwicGFja1ZlcnNpb24iOiIxLjEuMCJ9LCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IlByZW56bGF1ZXIgQmVyZyAxMiwgMTA0MDUgQmVybGluLCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vYmF0dGVyaWVzLmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiRXhhbXBsZSBCYXR0ZXJpZXMgR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkxpdGhpdW0gSXJvbiBQaG9zcGhhdGUiLCJyZWN5Y2xlZFBjdCI6OCwid2VpZ2h0S2ciOjIyfSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkdyYXBoaXRlIiwicmVjeWNsZWRQY3QiOjYsIndlaWdodEtnIjo5LjV9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiU3RlZWwgZW5jbG9zdXJlIiwicmVjeWNsZWRQY3QiOjgyLCJ3ZWlnaHRLZyI6MTJ9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA1LTIwIiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYW5vZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwibmFtZSI6IkdyYXBoaXRlIiwid2VpZ2h0UGN0Ijo5Nn0seyJjYXNOdW1iZXIiOiI5MDAzLTU1LTgiLCJuYW1lIjoiU0JSL0NNQyBiaW5kZXIiLCJ3ZWlnaHRQY3QiOjN9LHsiY2FzTnVtYmVyIjoiMTMzMy04Ni00IiwibmFtZSI6IkNvbmR1Y3RpdmUgY2FyYm9uIGJsYWNrIiwid2VpZ2h0UGN0IjoxfV0sImJhdHRlcnlDaGVtaXN0cnkiOiJMRlAiLCJiYXR0ZXJ5TW9kZWxJZCI6IkdDLUVTUy1MRlA1MTItMTAwIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDIxOk9ETC1JTkQtTEZQLTIwMjYtUTItMDE0IiwiYmF0dGVyeVR5cGUiOiJpbmR1c3RyaWFsIiwiYmF0dGVyeVdlaWdodEtnIjo0Ni41LCJjYXRob2RlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6IjE1MzY1LTE0LTciLCJuYW1lIjoiTGlGZVBPNCIsIndlaWdodFBjdCI6OTV9LHsiY2FzTnVtYmVyIjoiMjQ5MzctNzktOSIsIm5hbWUiOiJQVkRGIGJpbmRlciIsIndlaWdodFBjdCI6M30seyJjYXNOdW1iZXIiOiIxMzMzLTg2LTQiLCJuYW1lIjoiQ29uZHVjdGl2ZSBjYXJib24gYmxhY2siLCJ3ZWlnaHRQY3QiOjJ9XSwiY29tbWVyY2lhbFdhcnJhbnR5UGVyaW9kTW9udGhzIjoxNDQsImNvbXBvbmVudFBhcnROdW1iZXJzIjpbIkdDLUNFTEwtMzI3MDAtTEZQIiwiR0MtQk1TLTE2Uy1WMiIsIkdDLUVTUy1FTkNMLTUxMiJdLCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQzOS05My0yIiwiY291bnRyeU9mT3JpZ2luIjoiQVUiLCJuYW1lIjoiTGl0aGl1bSIsIndlaWdodEdyYW1zIjoyNjAwfSx7ImNhc051bWJlciI6Ijc3ODItNDItNSIsImNvdW50cnlPZk9yaWdpbiI6IkNOIiwibmFtZSI6Ik5hdHVyYWwgZ3JhcGhpdGUiLCJ3ZWlnaHRHcmFtcyI6OTUwMH1dLCJjeWNsZUxpZmVUZXN0Q1JhdGUiOjAuMzMsImRpc2Fzc2VtYmx5SW5zdHJ1Y3Rpb25zVXJsIjoiaHR0cHM6Ly9iYXR0ZXJpZXMuZXhhbXBsZS9zZXJ2aWNlL2Vzcy1sZnA1MTIvZGlzYXNzZW1ibHkiLCJlbGVjdHJvbHl0ZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI5Ni00OS0xIiwibmFtZSI6IkV0aHlsZW5lIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6Mzh9LHsiY2FzTnVtYmVyIjoiNjIzLTUzLTAiLCJuYW1lIjoiRXRoeWwgbWV0aHlsIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6NDl9LHsiY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUiLCJ3ZWlnaHRQY3QiOjEzfV0sImV1RGVjbGFyYXRpb25PZkNvbmZvcm1pdHkiOiJFVS1Eb0MgR0MtMjAyNi1JTkQtMDA2NyBkYXRlZCAyMDI2LTAzLTEwLCBwZXIgUmVndWxhdGlvbiAoRVUpIDIwMjMvMTU0MiBBcnQuIDE4IiwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6NjAwMCwiZXhwZWN0ZWRMaWZldGltZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjE0MjctMToyMDEzLCAyNSBkZWdDLCA5MCUgRG9EIiwiaGF6YXJkb3VzU3Vic3RhbmNlcyI6W3siY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsImNvbmNlbnRyYXRpb25QY3QiOjAuNywibmFtZSI6IkxpdGhpdW0gaGV4YWZsdW9yb3Bob3NwaGF0ZSJ9XSwiaW5pdGlhbFJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk2LjUsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjowLjYsImludGVybmFsUGFja1Jlc2lzdGFuY2VNb2htIjo5LCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDMtMDJUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiQmVybGluLCBERSAocGxhbnQgQkxOLTIpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDEwMCBBaCAvIDUuMTIga1doOyBjaGVtaXN0cnkgTEZQIiwibWF4aW11bVZvbHRhZ2VWIjo1OC40LCJtaW5pbWFsVm9sdGFnZVYiOjQwLCJub21pbmFsQ2FwYWNpdHlBaCI6MTAwLCJub21pbmFsVm9sdGFnZVYiOjUxLjIsIm5vdEluVXNlVGVtcGVyYXR1cmVSYW5nZSI6eyJtYXhDIjo1MCwibWluQyI6LTEwfSwibm90SW5Vc2VUZW1wZXJhdHVyZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjI2NjAtMToyMDE4IHN0b3JhZ2UgcHJvZmlsZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NTAwMCwicG93ZXJMaW1pdE1heFciOjc1MDAsInBvd2VyTGltaXRNaW5XIjoxNTAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwMjEiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6NS4xMiwicmF0ZWRFbmVyZ3lXaCI6NTEyMCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjIyLCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo5Miwic2FmZXR5TWVhc3VyZXMiOiJJc29sYXRlIERDIGJyZWFrZXIgYmVmb3JlIHNlcnZpY2luZy4gSW5zdWxhdGVkIHRvb2xzIG9ubHkuIFZlbnRpbGF0ZSBlbmNsb3N1cmUgYmVmb3JlIG9wZW5pbmcuIiwic3BhcmVQYXJ0c0NvbnRhY3RzIjoic3BhcmVzQGJhdHRlcmllcy5leGFtcGxlOyArNDkgMzAgNTU1MCAxMjAwIiwidGVzdFJlcG9ydFJlc3VsdHMiOiJJRUMgNjI2MTk6MjAyMiBwYXNzOyBJRUMgNjMwNTY6MjAyMCBwYXNzOyBVTiAzOC4zIHBhc3M7IHJlcG9ydCBHQy1UUi0yMDI2LTAzMDEiLCJ1c2FibGVFeHRpbmd1aXNoaW5nQWdlbnQiOiJXYXRlciBvciBBQkMgZHJ5IHBvd2RlcjsgbGFyZ2Ugdm9sdW1lcyBvZiB3YXRlciBmb3IgdGhlcm1hbCBydW5hd2F5Iiwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJSZXR1cm4gdG8gYW4gYXV0aG9yaXNlZCBjb2xsZWN0aW9uIHBvaW50LiBEbyBub3QgaW5jaW5lcmF0ZSBvciBzaHJlZC4gUHJvZHVjZXIgcmVnaXN0cmF0aW9uIERFLUJBVFQtMDAxMjMuIn0sInByb2R1Y3ROYW1lIjoiTEZQIHN0YXRpb25hcnkgcGFjaywgY29ycmVjdGVkIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjU4NTA0OTI3MloiLCJxckNvZGVVcmwiOiJodHRwOi8vMTI3LjAuMC4xOjEvMDEvMDk1OTAwMDAwMDAwMjEvMjEvNzFhYzljODY0Njk2NTQwYzNiMjEiLCJyZXBhaXJhYmlsaXR5U2NvcmUiOnsib3ZlcmFsbCI6OC4xfSwicmV0ZW50aW9uTG9ja2VkIjp0cnVlLCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuNTg1MDQ5MjcyWiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInN1cGVyc2VkZXNJZCI6IjAxYTFhMzA3LWI1YmQtNzFhYy05Yzg2LTQ2OTY1NDBjM2IyMSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNTg1MDQ5MjcyWiIsInZlcnNpb24iOjJ9.k8exwCDFPVM0t3Jnj3roNo5cHFMeekYr1QScAtYvBNVIaQmq0yLBDy-HJ3cLLos-7SQHC6ruuO1tIOrKZmWzBA", "public+restricted+individual": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELUxGUC0yMDI2LVEyLTAxNCIsImNhcnJpZXJTZXJpYWwiOiI3MWFjOWM4NjQ2OTY1NDBjM2IyMSIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjU3MzQ3NDc0N1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNS0yMC4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC41NzIwNTE3MzRaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjg0Yy03MjQ1LTg1NzYtZWE2NzE5NDgwOWVhIiwibGludFJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC41NzM0NzcxODVaIiwicGFja1ZlcnNpb24iOiIxLjEuMCJ9LCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IlByZW56bGF1ZXIgQmVyZyAxMiwgMTA0MDUgQmVybGluLCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vYmF0dGVyaWVzLmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiRXhhbXBsZSBCYXR0ZXJpZXMgR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkxpdGhpdW0gSXJvbiBQaG9zcGhhdGUiLCJyZWN5Y2xlZFBjdCI6OCwid2VpZ2h0S2ciOjIyfSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkdyYXBoaXRlIiwicmVjeWNsZWRQY3QiOjYsIndlaWdodEtnIjo5LjV9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiU3RlZWwgZW5jbG9zdXJlIiwicmVjeWNsZWRQY3QiOjgyLCJ3ZWlnaHRLZyI6MTJ9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA1LTIwIiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYW5vZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwibmFtZSI6IkdyYXBoaXRlIiwid2VpZ2h0UGN0Ijo5Nn0seyJjYXNOdW1iZXIiOiI5MDAzLTU1LTgiLCJuYW1lIjoiU0JSL0NNQyBiaW5kZXIiLCJ3ZWlnaHRQY3QiOjN9LHsiY2FzTnVtYmVyIjoiMTMzMy04Ni00IiwibmFtZSI6IkNvbmR1Y3RpdmUgY2FyYm9uIGJsYWNrIiwid2VpZ2h0UGN0IjoxfV0sImJhdHRlcnlDaGVtaXN0cnkiOiJMRlAiLCJiYXR0ZXJ5TW9kZWxJZCI6IkdDLUVTUy1MRlA1MTItMTAwIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDIxOk9ETC1JTkQtTEZQLTIwMjYtUTItMDE0IiwiYmF0dGVyeVN0YXR1cyI6Im9yaWdpbmFsIiwiYmF0dGVyeVR5cGUiOiJpbmR1c3RyaWFsIiwiYmF0dGVyeVdlaWdodEtnIjo0Ni41LCJjYXRob2RlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6IjE1MzY1LTE0LTciLCJuYW1lIjoiTGlGZVBPNCIsIndlaWdodFBjdCI6OTV9LHsiY2FzTnVtYmVyIjoiMjQ5MzctNzktOSIsIm5hbWUiOiJQVkRGIGJpbmRlciIsIndlaWdodFBjdCI6M30seyJjYXNOdW1iZXIiOiIxMzMzLTg2LTQiLCJuYW1lIjoiQ29uZHVjdGl2ZSBjYXJib24gYmxhY2siLCJ3ZWlnaHRQY3QiOjJ9XSwiY29tbWVyY2lhbFdhcnJhbnR5UGVyaW9kTW9udGhzIjoxNDQsImNvbXBvbmVudFBhcnROdW1iZXJzIjpbIkdDLUNFTEwtMzI3MDAtTEZQIiwiR0MtQk1TLTE2Uy1WMiIsIkdDLUVTUy1FTkNMLTUxMiJdLCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQzOS05My0yIiwiY291bnRyeU9mT3JpZ2luIjoiQVUiLCJuYW1lIjoiTGl0aGl1bSIsIndlaWdodEdyYW1zIjoyNjAwfSx7ImNhc051bWJlciI6Ijc3ODItNDItNSIsImNvdW50cnlPZk9yaWdpbiI6IkNOIiwibmFtZSI6Ik5hdHVyYWwgZ3JhcGhpdGUiLCJ3ZWlnaHRHcmFtcyI6OTUwMH1dLCJjeWNsZUxpZmVUZXN0Q1JhdGUiOjAuMzMsImRpc2Fzc2VtYmx5SW5zdHJ1Y3Rpb25zVXJsIjoiaHR0cHM6Ly9iYXR0ZXJpZXMuZXhhbXBsZS9zZXJ2aWNlL2Vzcy1sZnA1MTIvZGlzYXNzZW1ibHkiLCJkeW5hbWljUGVyZm9ybWFuY2UiOnsiY2FwYWNpdHlGYWRlUGN0IjowLjQsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjU5ODAsImludGVybmFsUmVzaXN0YW5jZU1vaG0iOjkuMSwicmF0ZWRDYXBhY2l0eUFoIjo5OS42LCJyb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5Ni4yfSwiZWxlY3Ryb2x5dGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiOTYtNDktMSIsIm5hbWUiOiJFdGh5bGVuZSBjYXJib25hdGUiLCJ3ZWlnaHRQY3QiOjM4fSx7ImNhc051bWJlciI6IjYyMy01My0wIiwibmFtZSI6IkV0aHlsIG1ldGh5bCBjYXJib25hdGUiLCJ3ZWlnaHRQY3QiOjQ5fSx7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIiwid2VpZ2h0UGN0IjoxM31dLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIEdDLTIwMjYtSU5ELTAwNjcgZGF0ZWQgMjAyNi0wMy0xMCwgcGVyIFJlZ3VsYXRpb24gKEVVKSAyMDIzLzE1NDIgQXJ0LiAxOCIsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjYwMDAsImV4cGVjdGVkTGlmZXRpbWVSZWZlcmVuY2VUZXN0IjoiSUVDIDYxNDI3LTE6MjAxMywgMjUgZGVnQywgOTAlIERvRCIsImhhemFyZG91c1N1YnN0YW5jZXMiOlt7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJjb25jZW50cmF0aW9uUGN0IjowLjcsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUifV0sImluaXRpYWxSb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5Ni41LCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6MC42LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6OSwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTAzLTAyVDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkJlcmxpbiwgREUgKHBsYW50IEJMTi0yKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIHBlciBBcnQuIDEzKDQpOyBjYXBhY2l0eSAxMDAgQWggLyA1LjEyIGtXaDsgY2hlbWlzdHJ5IExGUCIsIm1heGltdW1Wb2x0YWdlViI6NTguNCwibWluaW1hbFZvbHRhZ2VWIjo0MCwibm9taW5hbENhcGFjaXR5QWgiOjEwMCwibm9taW5hbFZvbHRhZ2VWIjo1MS4yLCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NTAsIm1pbkMiOi0xMH0sIm5vdEluVXNlVGVtcGVyYXR1cmVSZWZlcmVuY2VUZXN0IjoiSUVDIDYyNjYwLTE6MjAxOCBzdG9yYWdlIHByb2ZpbGUiLCJvcmlnaW5hbFBvd2VyQ2FwYWJpbGl0eVciOjUwMDAsInBvd2VyTGltaXRNYXhXIjo3NTAwLCJwb3dlckxpbWl0TWluVyI6MTUwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDIxIiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOjUuMTIsInJhdGVkRW5lcmd5V2giOjUxMjAsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRSZXBvcnRpbmdZZWFyIjoyMDI2LCJyZW5ld2FibGVDb250ZW50UGN0IjoyMiwicm91bmRUcmlwRWZmaWNpZW5jeUF0SGFsZkN5Y2xlTGlmZVBjdCI6OTIsInNhZmV0eU1lYXN1cmVzIjoiSXNvbGF0ZSBEQyBicmVha2VyIGJlZm9yZSBzZXJ2aWNpbmcuIEluc3VsYXRlZCB0b29scyBvbmx5LiBWZW50aWxhdGUgZW5jbG9zdXJlIGJlZm9yZSBvcGVuaW5nLiIsInNwYXJlUGFydHNDb250YWN0cyI6InNwYXJlc0BiYXR0ZXJpZXMuZXhhbXBsZTsgKzQ5IDMwIDU1NTAgMTIwMCIsInN0YXRlT2ZIZWFsdGgiOnsib2htaWNSZXNpc3RhbmNlTW9obSI6OS4xLCJwYXJhbWV0ZXJTZXQiOiJzdGF0aW9uYXJ5T3JMbXQiLCJyZW1haW5pbmdDYXBhY2l0eVBjdCI6OTkuNiwicmVtYWluaW5nUG93ZXJDYXBhYmlsaXR5UGN0Ijo5OS40LCJyZW1haW5pbmdSb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5OS4xLCJzZWxmRGlzY2hhcmdlUmF0ZVBjdFBlck1vbnRoIjoxLjR9LCJzdGF0ZU9mSGVhbHRoUGN0IjpudWxsLCJ1c2FibGVFeHRpbmd1aXNoaW5nQWdlbnQiOiJXYXRlciBvciBBQkMgZHJ5IHBvd2RlcjsgbGFyZ2Ugdm9sdW1lcyBvZiB3YXRlciBmb3IgdGhlcm1hbCBydW5hd2F5IiwidXNhZ2VIaXN0b3J5Ijp7ImNoYXJnZURpc2NoYXJnZUN5Y2xlcyI6NSwibmVnYXRpdmVFdmVudHMiOltdfSwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJSZXR1cm4gdG8gYW4gYXV0aG9yaXNlZCBjb2xsZWN0aW9uIHBvaW50LiBEbyBub3QgaW5jaW5lcmF0ZSBvciBzaHJlZC4gUHJvZHVjZXIgcmVnaXN0cmF0aW9uIERFLUJBVFQtMDAxMjMuIn0sInByb2R1Y3ROYW1lIjoiTEZQIHN0YXRpb25hcnkgcGFjaywgY29ycmVjdGVkIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjU4NTA0OTI3MloiLCJxckNvZGVVcmwiOiJodHRwOi8vMTI3LjAuMC4xOjEvMDEvMDk1OTAwMDAwMDAwMjEvMjEvNzFhYzljODY0Njk2NTQwYzNiMjEiLCJyZXBhaXJhYmlsaXR5U2NvcmUiOnsib3ZlcmFsbCI6OC4xfSwicmV0ZW50aW9uVW50aWwiOiIyMDM2LTExLTAzVDIwOjA5OjE0LjU4NTA0OTI3MloiLCJzY2hlbWFWZXJzaW9uIjoiMi44LjAiLCJzdGF0dXMiOiJhY3RpdmUiLCJzdXBlcnNlZGVzSWQiOiIwMWExYTMwNy1iNWJkLTcxYWMtOWM4Ni00Njk2NTQwYzNiMjEiLCJ1cGRhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjU4NTA0OTI3MloiLCJ2ZXJzaW9uIjoyfQ.19u5fOWE9fVDJooh4XUII3Zh-wgvWeYRcET7DYeBWMKBcEIJ_8yIo6U97qaX3PGKGAhc3HwkrUu5gvtntBPwCQ"}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}'); +INSERT INTO odal.passport VALUES ('01a1a307-b5bd-71ac-9c86-4696540c3b21', 'battery', 'superseded', true, '2.8.0', NULL, NULL, 1, NULL, NULL, '2026-11-03 20:09:13.921704+00', '2036-11-03 20:09:13.987955+00', NULL, '2026-11-03 20:09:13.92244+00', '2026-11-03 20:09:14.640666+00', '2026-11-03 20:09:13.987955+00', '{"id": "01a1a307-b5bd-71ac-9c86-4696540c3b21", "status": "superseded", "batchId": "ODL-IND-LFP-2026-Q2-014", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:13.917135531Z", "materials": [{"name": "Lithium Iron Phosphate", "weightKg": 22.0, "recycledPct": 8.0, "countryOfOrigin": "DE"}, {"name": "Graphite", "weightKg": 9.5, "recycledPct": 6.0, "countryOfOrigin": "DE"}, {"name": "Steel enclosure", "weightKg": 12.0, "recycledPct": 82.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000021/21/71ac9c864696540c3b21", "updatedAt": "2026-11-03T20:09:14.645432378Z", "lintResult": {"assessedAt": "2026-11-03T20:09:13.921707611Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Odal Stationary Storage Battery LFP-51.2", "publishedAt": "2026-11-03T20:09:13.987954878Z", "jwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELUxGUC0yMDI2LVEyLTAxNCIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjkyMTcwNDA0OVoiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNS0yMC4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45MTcxMzU1MzFaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjViZC03MWFjLTljODYtNDY5NjU0MGMzYjIxIiwiandzU2lnbmF0dXJlIjpudWxsLCJsaW50UmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjkyMTcwNzYxMVoiLCJwYWNrVmVyc2lvbiI6IjEuMS4wIn0sIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiUHJlbnpsYXVlciBCZXJnIDEyLCAxMDQwNSBCZXJsaW4sIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly9iYXR0ZXJpZXMuZXhhbXBsZS8ud2VsbC1rbm93bi9kaWQuanNvbiIsIm5hbWUiOiJFeGFtcGxlIEJhdHRlcmllcyBHbWJIIn0sIm1hdGVyaWFscyI6W3siY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiTGl0aGl1bSBJcm9uIFBob3NwaGF0ZSIsInJlY3ljbGVkUGN0Ijo4LCJ3ZWlnaHRLZyI6MjJ9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiR3JhcGhpdGUiLCJyZWN5Y2xlZFBjdCI6Niwid2VpZ2h0S2ciOjkuNX0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJTdGVlbCBlbmNsb3N1cmUiLCJyZWN5Y2xlZFBjdCI6ODIsIndlaWdodEtnIjoxMn1dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInBsYWNlZE9uTWFya2V0RGF0ZSI6IjIwMjYtMDUtMjAiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJhbm9kZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJuYW1lIjoiR3JhcGhpdGUiLCJ3ZWlnaHRQY3QiOjk2fSx7ImNhc051bWJlciI6IjkwMDMtNTUtOCIsIm5hbWUiOiJTQlIvQ01DIGJpbmRlciIsIndlaWdodFBjdCI6M30seyJjYXNOdW1iZXIiOiIxMzMzLTg2LTQiLCJuYW1lIjoiQ29uZHVjdGl2ZSBjYXJib24gYmxhY2siLCJ3ZWlnaHRQY3QiOjF9XSwiYmF0dGVyeUNoZW1pc3RyeSI6IkxGUCIsImJhdHRlcnlNb2RlbElkIjoiR0MtRVNTLUxGUDUxMi0xMDAiLCJiYXR0ZXJ5UGFzc3BvcnROdW1iZXIiOiJVUk46T0RMOkJBVFQ6MDk1OTAwMDAwMDAwMjE6T0RMLUlORC1MRlAtMjAyNi1RMi0wMTQiLCJiYXR0ZXJ5U3RhdHVzIjoib3JpZ2luYWwiLCJiYXR0ZXJ5VHlwZSI6ImluZHVzdHJpYWwiLCJiYXR0ZXJ5V2VpZ2h0S2ciOjQ2LjUsImNhdGhvZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiMTUzNjUtMTQtNyIsIm5hbWUiOiJMaUZlUE80Iiwid2VpZ2h0UGN0Ijo5NX0seyJjYXNOdW1iZXIiOiIyNDkzNy03OS05IiwibmFtZSI6IlBWREYgYmluZGVyIiwid2VpZ2h0UGN0IjozfSx7ImNhc051bWJlciI6IjEzMzMtODYtNCIsIm5hbWUiOiJDb25kdWN0aXZlIGNhcmJvbiBibGFjayIsIndlaWdodFBjdCI6Mn1dLCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjE0NCwiY29tcG9uZW50UGFydE51bWJlcnMiOlsiR0MtQ0VMTC0zMjcwMC1MRlAiLCJHQy1CTVMtMTZTLVYyIiwiR0MtRVNTLUVOQ0wtNTEyIl0sImNyaXRpY2FsUmF3TWF0ZXJpYWxzIjpbeyJjYXNOdW1iZXIiOiI3NDM5LTkzLTIiLCJjb3VudHJ5T2ZPcmlnaW4iOiJBVSIsIm5hbWUiOiJMaXRoaXVtIiwid2VpZ2h0R3JhbXMiOjI2MDB9LHsiY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwiY291bnRyeU9mT3JpZ2luIjoiQ04iLCJuYW1lIjoiTmF0dXJhbCBncmFwaGl0ZSIsIndlaWdodEdyYW1zIjo5NTAwfV0sImN5Y2xlTGlmZVRlc3RDUmF0ZSI6MC4zMywiZGlzYXNzZW1ibHlJbnN0cnVjdGlvbnNVcmwiOiJodHRwczovL2JhdHRlcmllcy5leGFtcGxlL3NlcnZpY2UvZXNzLWxmcDUxMi9kaXNhc3NlbWJseSIsImR5bmFtaWNQZXJmb3JtYW5jZSI6eyJjYXBhY2l0eUZhZGVQY3QiOjAuNCwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6NTk4MCwiaW50ZXJuYWxSZXNpc3RhbmNlTW9obSI6OS4xLCJyYXRlZENhcGFjaXR5QWgiOjk5LjYsInJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk2LjJ9LCJlbGVjdHJvbHl0ZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI5Ni00OS0xIiwibmFtZSI6IkV0aHlsZW5lIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6Mzh9LHsiY2FzTnVtYmVyIjoiNjIzLTUzLTAiLCJuYW1lIjoiRXRoeWwgbWV0aHlsIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6NDl9LHsiY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUiLCJ3ZWlnaHRQY3QiOjEzfV0sImV1RGVjbGFyYXRpb25PZkNvbmZvcm1pdHkiOiJFVS1Eb0MgR0MtMjAyNi1JTkQtMDA2NyBkYXRlZCAyMDI2LTAzLTEwLCBwZXIgUmVndWxhdGlvbiAoRVUpIDIwMjMvMTU0MiBBcnQuIDE4IiwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6NjAwMCwiZXhwZWN0ZWRMaWZldGltZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjE0MjctMToyMDEzLCAyNSBkZWdDLCA5MCUgRG9EIiwiaGF6YXJkb3VzU3Vic3RhbmNlcyI6W3siY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsImNvbmNlbnRyYXRpb25QY3QiOjAuNywibmFtZSI6IkxpdGhpdW0gaGV4YWZsdW9yb3Bob3NwaGF0ZSJ9XSwiaW5pdGlhbFJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk2LjUsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjowLjYsImludGVybmFsUGFja1Jlc2lzdGFuY2VNb2htIjo5LCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDMtMDJUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiQmVybGluLCBERSAocGxhbnQgQkxOLTIpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDEwMCBBaCAvIDUuMTIga1doOyBjaGVtaXN0cnkgTEZQIiwibWF4aW11bVZvbHRhZ2VWIjo1OC40LCJtaW5pbWFsVm9sdGFnZVYiOjQwLCJub21pbmFsQ2FwYWNpdHlBaCI6MTAwLCJub21pbmFsVm9sdGFnZVYiOjUxLjIsIm5vdEluVXNlVGVtcGVyYXR1cmVSYW5nZSI6eyJtYXhDIjo1MCwibWluQyI6LTEwfSwibm90SW5Vc2VUZW1wZXJhdHVyZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjI2NjAtMToyMDE4IHN0b3JhZ2UgcHJvZmlsZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NTAwMCwicG93ZXJMaW1pdE1heFciOjc1MDAsInBvd2VyTGltaXRNaW5XIjoxNTAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwMjEiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6NS4xMiwicmF0ZWRFbmVyZ3lXaCI6NTEyMCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjIyLCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo5Miwic2FmZXR5TWVhc3VyZXMiOiJJc29sYXRlIERDIGJyZWFrZXIgYmVmb3JlIHNlcnZpY2luZy4gSW5zdWxhdGVkIHRvb2xzIG9ubHkuIFZlbnRpbGF0ZSBlbmNsb3N1cmUgYmVmb3JlIG9wZW5pbmcuIiwic3BhcmVQYXJ0c0NvbnRhY3RzIjoic3BhcmVzQGJhdHRlcmllcy5leGFtcGxlOyArNDkgMzAgNTU1MCAxMjAwIiwic3RhdGVPZkhlYWx0aCI6eyJvaG1pY1Jlc2lzdGFuY2VNb2htIjo5LjEsInBhcmFtZXRlclNldCI6InN0YXRpb25hcnlPckxtdCIsInJlbWFpbmluZ0NhcGFjaXR5UGN0Ijo5OS42LCJyZW1haW5pbmdQb3dlckNhcGFiaWxpdHlQY3QiOjk5LjQsInJlbWFpbmluZ1JvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk5LjEsInNlbGZEaXNjaGFyZ2VSYXRlUGN0UGVyTW9udGgiOjEuNH0sInN0YXRlT2ZIZWFsdGhQY3QiOm51bGwsInRlc3RSZXBvcnRSZXN1bHRzIjoiSUVDIDYyNjE5OjIwMjIgcGFzczsgSUVDIDYzMDU2OjIwMjAgcGFzczsgVU4gMzguMyBwYXNzOyByZXBvcnQgR0MtVFItMjAyNi0wMzAxIiwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIgb3IgQUJDIGRyeSBwb3dkZXI7IGxhcmdlIHZvbHVtZXMgb2Ygd2F0ZXIgZm9yIHRoZXJtYWwgcnVuYXdheSIsInVzYWdlSGlzdG9yeSI6eyJjaGFyZ2VEaXNjaGFyZ2VDeWNsZXMiOjUsIm5lZ2F0aXZlRXZlbnRzIjpbXX0sIndhc3RlQmF0dGVyeUluZm9ybWF0aW9uIjoiUmV0dXJuIHRvIGFuIGF1dGhvcmlzZWQgY29sbGVjdGlvbiBwb2ludC4gRG8gbm90IGluY2luZXJhdGUgb3Igc2hyZWQuIFByb2R1Y2VyIHJlZ2lzdHJhdGlvbiBERS1CQVRULTAwMTIzLiJ9LCJwcm9kdWN0TmFtZSI6Ik9kYWwgU3RhdGlvbmFyeSBTdG9yYWdlIEJhdHRlcnkgTEZQLTUxLjIiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTMuOTg3OTU0ODc4WiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDAyMS8yMS83MWFjOWM4NjQ2OTY1NDBjM2IyMSIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjo4LjF9LCJyZXRlbnRpb25Mb2NrZWQiOnRydWUsInJldGVudGlvblVudGlsIjoiMjAzNi0xMS0wM1QyMDowOToxMy45ODc5NTQ4NzhaIiwic2NoZW1hVmVyc2lvbiI6IjIuOC4wIiwic3RhdHVzIjoiYWN0aXZlIiwidXBkYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45ODc5NTQ4NzhaIiwidmVyc2lvbiI6MX0.inyiRNW7RsdQojuWFAIjW07Rr6HezmP9kWPsCPYLA50HXEpLHhUYEXWReJH9D2lYoALCO20rUyU-mgcStk9GDQ", "manufacturer": {"name": "Example Batteries GmbH", "address": "Prenzlauer Berg 12, 10405 Berlin, DE", "country": null, "didWebUrl": "https://batteries.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:13.987954878Z", "retentionLocked": true, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-05-20. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:13.921704049Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "industrial", "productGroup": "battery", "usageHistory": {"negativeEvents": [], "chargeDischargeCycles": 5}, "anodeMaterial": [{"name": "Graphite", "casNumber": "7782-42-5", "weightPct": 96.0}, {"name": "SBR/CMC binder", "casNumber": "9003-55-8", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 1.0}], "batteryStatus": "original", "ratedEnergyWh": 5120.0, "stateOfHealth": {"parameterSet": "stationaryOrLmt", "ohmicResistanceMohm": 9.1, "remainingCapacityPct": 99.6, "remainingPowerCapabilityPct": 99.4, "selfDischargeRatePctPerMonth": 1.4, "remainingRoundTripEfficiencyPct": 99.1}, "batteryModelId": "GC-ESS-LFP512-100", "powerLimitMaxW": 7500.0, "powerLimitMinW": 150.0, "safetyMeasures": "Isolate DC breaker before servicing. Insulated tools only. Ventilate enclosure before opening.", "batteryWeightKg": 46.5, "cathodeMaterial": [{"name": "LiFePO4", "casNumber": "15365-14-7", "weightPct": 95.0}, {"name": "PVDF binder", "casNumber": "24937-79-9", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 2.0}], "maximumVoltageV": 58.4, "minimalVoltageV": 40.0, "nominalVoltageV": 51.2, "batteryChemistry": "LFP", "ratedCapacityKwh": 5.12, "stateOfHealthPct": null, "manufacturingDate": "2026-03-02T00:00:00Z", "nominalCapacityAh": 100.0, "productIdentifier": {"gtin": "09590000000021", "scheme": "gs1"}, "testReportResults": "IEC 62619:2022 pass; IEC 63056:2020 pass; UN 38.3 pass; report GC-TR-2026-0301", "cycleLifeTestCRate": 0.33, "dynamicPerformance": {"capacityFadePct": 0.4, "ratedCapacityAh": 99.6, "expectedLifetimeCycles": 5980, "internalResistanceMohm": 9.1, "roundTripEfficiencyPct": 96.2}, "manufacturingPlace": "Berlin, DE (plant BLN-2)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 100 Ah / 5.12 kWh; chemistry LFP", "sparePartsContacts": "spares@batteries.example; +49 30 5550 1200", "electrolyteMaterial": [{"name": "Ethylene carbonate", "casNumber": "96-49-1", "weightPct": 38.0}, {"name": "Ethyl methyl carbonate", "casNumber": "623-53-0", "weightPct": 49.0}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "weightPct": 13.0}], "hazardousSubstances": [{"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.7}], "renewableContentPct": 22.0, "componentPartNumbers": ["GC-CELL-32700-LFP", "GC-BMS-16S-V2", "GC-ESS-ENCL-512"], "criticalRawMaterials": [{"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 2600.0, "countryOfOrigin": "AU"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 9500.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000021:ODL-IND-LFP-2026-Q2-014", "expectedLifetimeCycles": 6000, "wasteBatteryInformation": "Return to an authorised collection point. Do not incinerate or shred. Producer registration DE-BATT-00123.", "notInUseTemperatureRange": {"maxC": 50.0, "minC": -10.0}, "originalPowerCapabilityW": 5000.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Water or ABC dry powder; large volumes of water for thermal runaway", "euDeclarationOfConformity": "EU-DoC GC-2026-IND-0067 dated 2026-03-10, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://batteries.example/service/ess-lfp512/disassembly", "internalCellResistanceMohm": 0.6, "internalPackResistanceMohm": 9.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "IEC 61427-1:2013, 25 degC, 90% DoD", "initialRoundTripEfficiencyPct": 96.5, "commercialWarrantyPeriodMonths": 144, "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage profile", "roundTripEfficiencyAtHalfCycleLifePct": 92.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-05-20", "publicJwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELUxGUC0yMDI2LVEyLTAxNCIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjkyMTcwNDA0OVoiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNS0yMC4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45MTcxMzU1MzFaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjViZC03MWFjLTljODYtNDY5NjU0MGMzYjIxIiwibWFudWZhY3R1cmVyIjp7ImFkZHJlc3MiOiJQcmVuemxhdWVyIEJlcmcgMTIsIDEwNDA1IEJlcmxpbiwgREUiLCJjb3VudHJ5IjpudWxsLCJkaWRXZWJVcmwiOiJodHRwczovL2JhdHRlcmllcy5leGFtcGxlLy53ZWxsLWtub3duL2RpZC5qc29uIiwibmFtZSI6IkV4YW1wbGUgQmF0dGVyaWVzIEdtYkgifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJMaXRoaXVtIElyb24gUGhvc3BoYXRlIiwicmVjeWNsZWRQY3QiOjgsIndlaWdodEtnIjoyMn0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJHcmFwaGl0ZSIsInJlY3ljbGVkUGN0Ijo2LCJ3ZWlnaHRLZyI6OS41fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IlN0ZWVsIGVuY2xvc3VyZSIsInJlY3ljbGVkUGN0Ijo4Miwid2VpZ2h0S2ciOjEyfV0sIm9wZXJhdG9ySWRlbnRpZmllciI6IkRFMTIzNDU2Nzg5IiwicGxhY2VkT25NYXJrZXREYXRlIjoiMjAyNi0wNS0yMCIsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImJhdHRlcnlDaGVtaXN0cnkiOiJMRlAiLCJiYXR0ZXJ5TW9kZWxJZCI6IkdDLUVTUy1MRlA1MTItMTAwIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDIxOk9ETC1JTkQtTEZQLTIwMjYtUTItMDE0IiwiYmF0dGVyeVR5cGUiOiJpbmR1c3RyaWFsIiwiYmF0dGVyeVdlaWdodEtnIjo0Ni41LCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjE0NCwiY3JpdGljYWxSYXdNYXRlcmlhbHMiOlt7ImNhc051bWJlciI6Ijc0MzktOTMtMiIsImNvdW50cnlPZk9yaWdpbiI6IkFVIiwibmFtZSI6IkxpdGhpdW0iLCJ3ZWlnaHRHcmFtcyI6MjYwMH0seyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDTiIsIm5hbWUiOiJOYXR1cmFsIGdyYXBoaXRlIiwid2VpZ2h0R3JhbXMiOjk1MDB9XSwiY3ljbGVMaWZlVGVzdENSYXRlIjowLjMzLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIEdDLTIwMjYtSU5ELTAwNjcgZGF0ZWQgMjAyNi0wMy0xMCwgcGVyIFJlZ3VsYXRpb24gKEVVKSAyMDIzLzE1NDIgQXJ0LiAxOCIsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjYwMDAsImV4cGVjdGVkTGlmZXRpbWVSZWZlcmVuY2VUZXN0IjoiSUVDIDYxNDI3LTE6MjAxMywgMjUgZGVnQywgOTAlIERvRCIsImhhemFyZG91c1N1YnN0YW5jZXMiOlt7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJjb25jZW50cmF0aW9uUGN0IjowLjcsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUifV0sImluaXRpYWxSb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5Ni41LCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6MC42LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6OSwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTAzLTAyVDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkJlcmxpbiwgREUgKHBsYW50IEJMTi0yKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIHBlciBBcnQuIDEzKDQpOyBjYXBhY2l0eSAxMDAgQWggLyA1LjEyIGtXaDsgY2hlbWlzdHJ5IExGUCIsIm1heGltdW1Wb2x0YWdlViI6NTguNCwibWluaW1hbFZvbHRhZ2VWIjo0MCwibm9taW5hbENhcGFjaXR5QWgiOjEwMCwibm9taW5hbFZvbHRhZ2VWIjo1MS4yLCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NTAsIm1pbkMiOi0xMH0sIm5vdEluVXNlVGVtcGVyYXR1cmVSZWZlcmVuY2VUZXN0IjoiSUVDIDYyNjYwLTE6MjAxOCBzdG9yYWdlIHByb2ZpbGUiLCJvcmlnaW5hbFBvd2VyQ2FwYWJpbGl0eVciOjUwMDAsInBvd2VyTGltaXRNYXhXIjo3NTAwLCJwb3dlckxpbWl0TWluVyI6MTUwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDIxIiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOjUuMTIsInJhdGVkRW5lcmd5V2giOjUxMjAsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRSZXBvcnRpbmdZZWFyIjoyMDI2LCJyZW5ld2FibGVDb250ZW50UGN0IjoyMiwicm91bmRUcmlwRWZmaWNpZW5jeUF0SGFsZkN5Y2xlTGlmZVBjdCI6OTIsInVzYWJsZUV4dGluZ3Vpc2hpbmdBZ2VudCI6IldhdGVyIG9yIEFCQyBkcnkgcG93ZGVyOyBsYXJnZSB2b2x1bWVzIG9mIHdhdGVyIGZvciB0aGVybWFsIHJ1bmF3YXkiLCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IlJldHVybiB0byBhbiBhdXRob3Jpc2VkIGNvbGxlY3Rpb24gcG9pbnQuIERvIG5vdCBpbmNpbmVyYXRlIG9yIHNocmVkLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDEyMy4ifSwicHJvZHVjdE5hbWUiOiJPZGFsIFN0YXRpb25hcnkgU3RvcmFnZSBCYXR0ZXJ5IExGUC01MS4yIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjk4Nzk1NDg3OFoiLCJxckNvZGVVcmwiOiJodHRwOi8vMTI3LjAuMC4xOjEvMDEvMDk1OTAwMDAwMDAwMjEvMjEvNzFhYzljODY0Njk2NTQwYzNiMjEiLCJyZXBhaXJhYmlsaXR5U2NvcmUiOnsib3ZlcmFsbCI6OC4xfSwicmV0ZW50aW9uVW50aWwiOiIyMDM2LTExLTAzVDIwOjA5OjEzLjk4Nzk1NDg3OFoiLCJzY2hlbWFWZXJzaW9uIjoiMi44LjAiLCJzdGF0dXMiOiJhY3RpdmUiLCJ1cGRhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjk4Nzk1NDg3OFoiLCJ2ZXJzaW9uIjoxfQ.vrsJlWhcGD5eljm-5Y5TOAIDhXXEC-lekH67LPNC643xKG8QJXc7Gb4oatkaEcXq1qbBjeyG-UL41TfYjjEJBQ", "repairabilityScore": {"overall": 8.1}, "disclosureSignatures": {"public+restricted+conformity": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELUxGUC0yMDI2LVEyLTAxNCIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjkyMTcwNDA0OVoiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNS0yMC4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45MTcxMzU1MzFaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjViZC03MWFjLTljODYtNDY5NjU0MGMzYjIxIiwibGludFJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45MjE3MDc2MTFaIiwicGFja1ZlcnNpb24iOiIxLjEuMCJ9LCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IlByZW56bGF1ZXIgQmVyZyAxMiwgMTA0MDUgQmVybGluLCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vYmF0dGVyaWVzLmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiRXhhbXBsZSBCYXR0ZXJpZXMgR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkxpdGhpdW0gSXJvbiBQaG9zcGhhdGUiLCJyZWN5Y2xlZFBjdCI6OCwid2VpZ2h0S2ciOjIyfSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkdyYXBoaXRlIiwicmVjeWNsZWRQY3QiOjYsIndlaWdodEtnIjo5LjV9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiU3RlZWwgZW5jbG9zdXJlIiwicmVjeWNsZWRQY3QiOjgyLCJ3ZWlnaHRLZyI6MTJ9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA1LTIwIiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYW5vZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwibmFtZSI6IkdyYXBoaXRlIiwid2VpZ2h0UGN0Ijo5Nn0seyJjYXNOdW1iZXIiOiI5MDAzLTU1LTgiLCJuYW1lIjoiU0JSL0NNQyBiaW5kZXIiLCJ3ZWlnaHRQY3QiOjN9LHsiY2FzTnVtYmVyIjoiMTMzMy04Ni00IiwibmFtZSI6IkNvbmR1Y3RpdmUgY2FyYm9uIGJsYWNrIiwid2VpZ2h0UGN0IjoxfV0sImJhdHRlcnlDaGVtaXN0cnkiOiJMRlAiLCJiYXR0ZXJ5TW9kZWxJZCI6IkdDLUVTUy1MRlA1MTItMTAwIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDIxOk9ETC1JTkQtTEZQLTIwMjYtUTItMDE0IiwiYmF0dGVyeVR5cGUiOiJpbmR1c3RyaWFsIiwiYmF0dGVyeVdlaWdodEtnIjo0Ni41LCJjYXRob2RlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6IjE1MzY1LTE0LTciLCJuYW1lIjoiTGlGZVBPNCIsIndlaWdodFBjdCI6OTV9LHsiY2FzTnVtYmVyIjoiMjQ5MzctNzktOSIsIm5hbWUiOiJQVkRGIGJpbmRlciIsIndlaWdodFBjdCI6M30seyJjYXNOdW1iZXIiOiIxMzMzLTg2LTQiLCJuYW1lIjoiQ29uZHVjdGl2ZSBjYXJib24gYmxhY2siLCJ3ZWlnaHRQY3QiOjJ9XSwiY29tbWVyY2lhbFdhcnJhbnR5UGVyaW9kTW9udGhzIjoxNDQsImNvbXBvbmVudFBhcnROdW1iZXJzIjpbIkdDLUNFTEwtMzI3MDAtTEZQIiwiR0MtQk1TLTE2Uy1WMiIsIkdDLUVTUy1FTkNMLTUxMiJdLCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQzOS05My0yIiwiY291bnRyeU9mT3JpZ2luIjoiQVUiLCJuYW1lIjoiTGl0aGl1bSIsIndlaWdodEdyYW1zIjoyNjAwfSx7ImNhc051bWJlciI6Ijc3ODItNDItNSIsImNvdW50cnlPZk9yaWdpbiI6IkNOIiwibmFtZSI6Ik5hdHVyYWwgZ3JhcGhpdGUiLCJ3ZWlnaHRHcmFtcyI6OTUwMH1dLCJjeWNsZUxpZmVUZXN0Q1JhdGUiOjAuMzMsImRpc2Fzc2VtYmx5SW5zdHJ1Y3Rpb25zVXJsIjoiaHR0cHM6Ly9iYXR0ZXJpZXMuZXhhbXBsZS9zZXJ2aWNlL2Vzcy1sZnA1MTIvZGlzYXNzZW1ibHkiLCJlbGVjdHJvbHl0ZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI5Ni00OS0xIiwibmFtZSI6IkV0aHlsZW5lIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6Mzh9LHsiY2FzTnVtYmVyIjoiNjIzLTUzLTAiLCJuYW1lIjoiRXRoeWwgbWV0aHlsIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6NDl9LHsiY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUiLCJ3ZWlnaHRQY3QiOjEzfV0sImV1RGVjbGFyYXRpb25PZkNvbmZvcm1pdHkiOiJFVS1Eb0MgR0MtMjAyNi1JTkQtMDA2NyBkYXRlZCAyMDI2LTAzLTEwLCBwZXIgUmVndWxhdGlvbiAoRVUpIDIwMjMvMTU0MiBBcnQuIDE4IiwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6NjAwMCwiZXhwZWN0ZWRMaWZldGltZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjE0MjctMToyMDEzLCAyNSBkZWdDLCA5MCUgRG9EIiwiaGF6YXJkb3VzU3Vic3RhbmNlcyI6W3siY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsImNvbmNlbnRyYXRpb25QY3QiOjAuNywibmFtZSI6IkxpdGhpdW0gaGV4YWZsdW9yb3Bob3NwaGF0ZSJ9XSwiaW5pdGlhbFJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk2LjUsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjowLjYsImludGVybmFsUGFja1Jlc2lzdGFuY2VNb2htIjo5LCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDMtMDJUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiQmVybGluLCBERSAocGxhbnQgQkxOLTIpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDEwMCBBaCAvIDUuMTIga1doOyBjaGVtaXN0cnkgTEZQIiwibWF4aW11bVZvbHRhZ2VWIjo1OC40LCJtaW5pbWFsVm9sdGFnZVYiOjQwLCJub21pbmFsQ2FwYWNpdHlBaCI6MTAwLCJub21pbmFsVm9sdGFnZVYiOjUxLjIsIm5vdEluVXNlVGVtcGVyYXR1cmVSYW5nZSI6eyJtYXhDIjo1MCwibWluQyI6LTEwfSwibm90SW5Vc2VUZW1wZXJhdHVyZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjI2NjAtMToyMDE4IHN0b3JhZ2UgcHJvZmlsZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NTAwMCwicG93ZXJMaW1pdE1heFciOjc1MDAsInBvd2VyTGltaXRNaW5XIjoxNTAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwMjEiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6NS4xMiwicmF0ZWRFbmVyZ3lXaCI6NTEyMCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjIyLCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo5Miwic2FmZXR5TWVhc3VyZXMiOiJJc29sYXRlIERDIGJyZWFrZXIgYmVmb3JlIHNlcnZpY2luZy4gSW5zdWxhdGVkIHRvb2xzIG9ubHkuIFZlbnRpbGF0ZSBlbmNsb3N1cmUgYmVmb3JlIG9wZW5pbmcuIiwic3BhcmVQYXJ0c0NvbnRhY3RzIjoic3BhcmVzQGJhdHRlcmllcy5leGFtcGxlOyArNDkgMzAgNTU1MCAxMjAwIiwidGVzdFJlcG9ydFJlc3VsdHMiOiJJRUMgNjI2MTk6MjAyMiBwYXNzOyBJRUMgNjMwNTY6MjAyMCBwYXNzOyBVTiAzOC4zIHBhc3M7IHJlcG9ydCBHQy1UUi0yMDI2LTAzMDEiLCJ1c2FibGVFeHRpbmd1aXNoaW5nQWdlbnQiOiJXYXRlciBvciBBQkMgZHJ5IHBvd2RlcjsgbGFyZ2Ugdm9sdW1lcyBvZiB3YXRlciBmb3IgdGhlcm1hbCBydW5hd2F5Iiwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJSZXR1cm4gdG8gYW4gYXV0aG9yaXNlZCBjb2xsZWN0aW9uIHBvaW50LiBEbyBub3QgaW5jaW5lcmF0ZSBvciBzaHJlZC4gUHJvZHVjZXIgcmVnaXN0cmF0aW9uIERFLUJBVFQtMDAxMjMuIn0sInByb2R1Y3ROYW1lIjoiT2RhbCBTdGF0aW9uYXJ5IFN0b3JhZ2UgQmF0dGVyeSBMRlAtNTEuMiIsInB1Ymxpc2hlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45ODc5NTQ4NzhaIiwicXJDb2RlVXJsIjoiaHR0cDovLzEyNy4wLjAuMToxLzAxLzA5NTkwMDAwMDAwMDIxLzIxLzcxYWM5Yzg2NDY5NjU0MGMzYjIxIiwicmVwYWlyYWJpbGl0eVNjb3JlIjp7Im92ZXJhbGwiOjguMX0sInJldGVudGlvbkxvY2tlZCI6dHJ1ZSwicmV0ZW50aW9uVW50aWwiOiIyMDM2LTExLTAzVDIwOjA5OjEzLjk4Nzk1NDg3OFoiLCJzY2hlbWFWZXJzaW9uIjoiMi44LjAiLCJzdGF0dXMiOiJhY3RpdmUiLCJ1cGRhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjk4Nzk1NDg3OFoiLCJ2ZXJzaW9uIjoxfQ.HgGb20suOuqSvmzrBgNeQATCWlQ8L71Q1xSevVcUZNqI9Szt1I_tBNDRMWR-W4idyB1LbN9p81N3z6lkekNIAg", "public+restricted+individual": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELUxGUC0yMDI2LVEyLTAxNCIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjkyMTcwNDA0OVoiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNS0yMC4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45MTcxMzU1MzFaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjViZC03MWFjLTljODYtNDY5NjU0MGMzYjIxIiwibGludFJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45MjE3MDc2MTFaIiwicGFja1ZlcnNpb24iOiIxLjEuMCJ9LCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IlByZW56bGF1ZXIgQmVyZyAxMiwgMTA0MDUgQmVybGluLCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vYmF0dGVyaWVzLmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiRXhhbXBsZSBCYXR0ZXJpZXMgR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkxpdGhpdW0gSXJvbiBQaG9zcGhhdGUiLCJyZWN5Y2xlZFBjdCI6OCwid2VpZ2h0S2ciOjIyfSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkdyYXBoaXRlIiwicmVjeWNsZWRQY3QiOjYsIndlaWdodEtnIjo5LjV9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiU3RlZWwgZW5jbG9zdXJlIiwicmVjeWNsZWRQY3QiOjgyLCJ3ZWlnaHRLZyI6MTJ9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA1LTIwIiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYW5vZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwibmFtZSI6IkdyYXBoaXRlIiwid2VpZ2h0UGN0Ijo5Nn0seyJjYXNOdW1iZXIiOiI5MDAzLTU1LTgiLCJuYW1lIjoiU0JSL0NNQyBiaW5kZXIiLCJ3ZWlnaHRQY3QiOjN9LHsiY2FzTnVtYmVyIjoiMTMzMy04Ni00IiwibmFtZSI6IkNvbmR1Y3RpdmUgY2FyYm9uIGJsYWNrIiwid2VpZ2h0UGN0IjoxfV0sImJhdHRlcnlDaGVtaXN0cnkiOiJMRlAiLCJiYXR0ZXJ5TW9kZWxJZCI6IkdDLUVTUy1MRlA1MTItMTAwIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDIxOk9ETC1JTkQtTEZQLTIwMjYtUTItMDE0IiwiYmF0dGVyeVN0YXR1cyI6Im9yaWdpbmFsIiwiYmF0dGVyeVR5cGUiOiJpbmR1c3RyaWFsIiwiYmF0dGVyeVdlaWdodEtnIjo0Ni41LCJjYXRob2RlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6IjE1MzY1LTE0LTciLCJuYW1lIjoiTGlGZVBPNCIsIndlaWdodFBjdCI6OTV9LHsiY2FzTnVtYmVyIjoiMjQ5MzctNzktOSIsIm5hbWUiOiJQVkRGIGJpbmRlciIsIndlaWdodFBjdCI6M30seyJjYXNOdW1iZXIiOiIxMzMzLTg2LTQiLCJuYW1lIjoiQ29uZHVjdGl2ZSBjYXJib24gYmxhY2siLCJ3ZWlnaHRQY3QiOjJ9XSwiY29tbWVyY2lhbFdhcnJhbnR5UGVyaW9kTW9udGhzIjoxNDQsImNvbXBvbmVudFBhcnROdW1iZXJzIjpbIkdDLUNFTEwtMzI3MDAtTEZQIiwiR0MtQk1TLTE2Uy1WMiIsIkdDLUVTUy1FTkNMLTUxMiJdLCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQzOS05My0yIiwiY291bnRyeU9mT3JpZ2luIjoiQVUiLCJuYW1lIjoiTGl0aGl1bSIsIndlaWdodEdyYW1zIjoyNjAwfSx7ImNhc051bWJlciI6Ijc3ODItNDItNSIsImNvdW50cnlPZk9yaWdpbiI6IkNOIiwibmFtZSI6Ik5hdHVyYWwgZ3JhcGhpdGUiLCJ3ZWlnaHRHcmFtcyI6OTUwMH1dLCJjeWNsZUxpZmVUZXN0Q1JhdGUiOjAuMzMsImRpc2Fzc2VtYmx5SW5zdHJ1Y3Rpb25zVXJsIjoiaHR0cHM6Ly9iYXR0ZXJpZXMuZXhhbXBsZS9zZXJ2aWNlL2Vzcy1sZnA1MTIvZGlzYXNzZW1ibHkiLCJkeW5hbWljUGVyZm9ybWFuY2UiOnsiY2FwYWNpdHlGYWRlUGN0IjowLjQsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjU5ODAsImludGVybmFsUmVzaXN0YW5jZU1vaG0iOjkuMSwicmF0ZWRDYXBhY2l0eUFoIjo5OS42LCJyb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5Ni4yfSwiZWxlY3Ryb2x5dGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiOTYtNDktMSIsIm5hbWUiOiJFdGh5bGVuZSBjYXJib25hdGUiLCJ3ZWlnaHRQY3QiOjM4fSx7ImNhc051bWJlciI6IjYyMy01My0wIiwibmFtZSI6IkV0aHlsIG1ldGh5bCBjYXJib25hdGUiLCJ3ZWlnaHRQY3QiOjQ5fSx7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIiwid2VpZ2h0UGN0IjoxM31dLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIEdDLTIwMjYtSU5ELTAwNjcgZGF0ZWQgMjAyNi0wMy0xMCwgcGVyIFJlZ3VsYXRpb24gKEVVKSAyMDIzLzE1NDIgQXJ0LiAxOCIsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjYwMDAsImV4cGVjdGVkTGlmZXRpbWVSZWZlcmVuY2VUZXN0IjoiSUVDIDYxNDI3LTE6MjAxMywgMjUgZGVnQywgOTAlIERvRCIsImhhemFyZG91c1N1YnN0YW5jZXMiOlt7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJjb25jZW50cmF0aW9uUGN0IjowLjcsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUifV0sImluaXRpYWxSb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5Ni41LCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6MC42LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6OSwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTAzLTAyVDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkJlcmxpbiwgREUgKHBsYW50IEJMTi0yKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIHBlciBBcnQuIDEzKDQpOyBjYXBhY2l0eSAxMDAgQWggLyA1LjEyIGtXaDsgY2hlbWlzdHJ5IExGUCIsIm1heGltdW1Wb2x0YWdlViI6NTguNCwibWluaW1hbFZvbHRhZ2VWIjo0MCwibm9taW5hbENhcGFjaXR5QWgiOjEwMCwibm9taW5hbFZvbHRhZ2VWIjo1MS4yLCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NTAsIm1pbkMiOi0xMH0sIm5vdEluVXNlVGVtcGVyYXR1cmVSZWZlcmVuY2VUZXN0IjoiSUVDIDYyNjYwLTE6MjAxOCBzdG9yYWdlIHByb2ZpbGUiLCJvcmlnaW5hbFBvd2VyQ2FwYWJpbGl0eVciOjUwMDAsInBvd2VyTGltaXRNYXhXIjo3NTAwLCJwb3dlckxpbWl0TWluVyI6MTUwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDIxIiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOjUuMTIsInJhdGVkRW5lcmd5V2giOjUxMjAsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRSZXBvcnRpbmdZZWFyIjoyMDI2LCJyZW5ld2FibGVDb250ZW50UGN0IjoyMiwicm91bmRUcmlwRWZmaWNpZW5jeUF0SGFsZkN5Y2xlTGlmZVBjdCI6OTIsInNhZmV0eU1lYXN1cmVzIjoiSXNvbGF0ZSBEQyBicmVha2VyIGJlZm9yZSBzZXJ2aWNpbmcuIEluc3VsYXRlZCB0b29scyBvbmx5LiBWZW50aWxhdGUgZW5jbG9zdXJlIGJlZm9yZSBvcGVuaW5nLiIsInNwYXJlUGFydHNDb250YWN0cyI6InNwYXJlc0BiYXR0ZXJpZXMuZXhhbXBsZTsgKzQ5IDMwIDU1NTAgMTIwMCIsInN0YXRlT2ZIZWFsdGgiOnsib2htaWNSZXNpc3RhbmNlTW9obSI6OS4xLCJwYXJhbWV0ZXJTZXQiOiJzdGF0aW9uYXJ5T3JMbXQiLCJyZW1haW5pbmdDYXBhY2l0eVBjdCI6OTkuNiwicmVtYWluaW5nUG93ZXJDYXBhYmlsaXR5UGN0Ijo5OS40LCJyZW1haW5pbmdSb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5OS4xLCJzZWxmRGlzY2hhcmdlUmF0ZVBjdFBlck1vbnRoIjoxLjR9LCJzdGF0ZU9mSGVhbHRoUGN0IjpudWxsLCJ1c2FibGVFeHRpbmd1aXNoaW5nQWdlbnQiOiJXYXRlciBvciBBQkMgZHJ5IHBvd2RlcjsgbGFyZ2Ugdm9sdW1lcyBvZiB3YXRlciBmb3IgdGhlcm1hbCBydW5hd2F5IiwidXNhZ2VIaXN0b3J5Ijp7ImNoYXJnZURpc2NoYXJnZUN5Y2xlcyI6NSwibmVnYXRpdmVFdmVudHMiOltdfSwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJSZXR1cm4gdG8gYW4gYXV0aG9yaXNlZCBjb2xsZWN0aW9uIHBvaW50LiBEbyBub3QgaW5jaW5lcmF0ZSBvciBzaHJlZC4gUHJvZHVjZXIgcmVnaXN0cmF0aW9uIERFLUJBVFQtMDAxMjMuIn0sInByb2R1Y3ROYW1lIjoiT2RhbCBTdGF0aW9uYXJ5IFN0b3JhZ2UgQmF0dGVyeSBMRlAtNTEuMiIsInB1Ymxpc2hlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45ODc5NTQ4NzhaIiwicXJDb2RlVXJsIjoiaHR0cDovLzEyNy4wLjAuMToxLzAxLzA5NTkwMDAwMDAwMDIxLzIxLzcxYWM5Yzg2NDY5NjU0MGMzYjIxIiwicmVwYWlyYWJpbGl0eVNjb3JlIjp7Im92ZXJhbGwiOjguMX0sInJldGVudGlvblVudGlsIjoiMjAzNi0xMS0wM1QyMDowOToxMy45ODc5NTQ4NzhaIiwic2NoZW1hVmVyc2lvbiI6IjIuOC4wIiwic3RhdHVzIjoiYWN0aXZlIiwidXBkYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45ODc5NTQ4NzhaIiwidmVyc2lvbiI6MX0.M2ufqIdiPLFPrlOl46H2W8cUouLEhxDikDVOo6DkwK9p_JvEThvi56ALneqoTFUYluhvrqk6eIm4FMIA5H2GDg"}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}'); +INSERT INTO odal.passport VALUES ('01a1a307-b97c-73a6-99ea-eb052b807483', 'textile', 'draft', false, '1.4.0', NULL, NULL, 1, NULL, NULL, '2026-11-03 20:09:14.879912+00', NULL, NULL, '2026-11-03 20:09:14.8802+00', '2026-11-03 20:09:14.8802+00', NULL, '{"id": "01a1a307-b97c-73a6-99ea-eb052b807483", "status": "draft", "batchId": "BATCH-AW26-001", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.876826273Z", "materials": [], "qrCodeUrl": null, "updatedAt": "2026-11-03T20:09:14.876826791Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.879913315Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Glamor Kids Hoodie", "publishedAt": null, "jwsSignature": null, "manufacturer": {"name": "Glamor Clothing", "address": "DE", "country": "DE", "didWebUrl": null}, "productGroup": "textile", "schemaVersion": "1.4.0", "retentionLocked": false, "complianceResult": {"co2eScore": null, "assessedAt": "2026-11-03T20:09:14.879911706Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"productGroup": "textile", "countryOfOrigin": "DE", "careInstructions": "Machine wash 40°C. Tumble dry low.", "fibreComposition": [{"pct": 80.0, "fibre": "cotton"}, {"pct": 20.0, "fibre": "polyester"}], "productIdentifier": {"gtin": "38012345679202", "scheme": "gs1"}, "chemicalComplianceStandard": "OEKO-TEX Standard 100"}, "operatorIdentifier": "DE123456789", "repairabilityScore": null, "applicableInstruments": [{"recorded": "catalog", "instrument": "espr"}]}'); +INSERT INTO odal.passport VALUES ('01a1a307-b97c-73a6-99ea-eb041612e7d1', 'textile', 'draft', false, '1.4.0', NULL, NULL, 1, NULL, NULL, '2026-11-03 20:09:14.880264+00', NULL, NULL, '2026-11-03 20:09:14.880946+00', '2026-11-03 20:09:14.880946+00', NULL, '{"id": "01a1a307-b97c-73a6-99ea-eb041612e7d1", "status": "draft", "batchId": "BATCH-SS26-001", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.876816045Z", "materials": [], "qrCodeUrl": null, "updatedAt": "2026-11-03T20:09:14.876816537Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.880265599Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Glamor Classic T-Shirt", "publishedAt": null, "jwsSignature": null, "manufacturer": {"name": "Glamor Clothing", "address": "DE", "country": "DE", "didWebUrl": null}, "productGroup": "textile", "schemaVersion": "1.4.0", "retentionLocked": false, "complianceResult": {"co2eScore": null, "assessedAt": "2026-11-03T20:09:14.880263826Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"productGroup": "textile", "countryOfOrigin": "DE", "careInstructions": "Machine wash 40°C. Iron medium heat.", "fibreComposition": [{"pct": 100.0, "fibre": "cotton"}], "productIdentifier": {"gtin": "38012345678908", "scheme": "gs1"}, "chemicalComplianceStandard": "OEKO-TEX Standard 100"}, "operatorIdentifier": "DE123456789", "repairabilityScore": null, "applicableInstruments": [{"recorded": "catalog", "instrument": "espr"}]}'); +INSERT INTO odal.passport VALUES ('01a1a307-b97c-73a6-99ea-eb06f0520a8f', 'textile', 'draft', false, '1.4.0', NULL, NULL, 1, NULL, NULL, '2026-11-03 20:09:14.95106+00', NULL, NULL, '2026-11-03 20:09:14.951544+00', '2026-11-03 20:09:14.951544+00', NULL, '{"id": "01a1a307-b97c-73a6-99ea-eb06f0520a8f", "status": "draft", "batchId": "BATCH-SS26-002", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.876840775Z", "materials": [], "qrCodeUrl": null, "updatedAt": "2026-11-03T20:09:14.876841289Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.951063071Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Glamor Summer Dress", "publishedAt": null, "jwsSignature": null, "manufacturer": {"name": "Glamor Clothing", "address": "DE", "country": "DE", "didWebUrl": null}, "productGroup": "textile", "schemaVersion": "1.4.0", "retentionLocked": false, "complianceResult": {"co2eScore": null, "assessedAt": "2026-11-03T20:09:14.951059735Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"productGroup": "textile", "countryOfOrigin": "DE", "careInstructions": "Hand wash 30°C. Do not bleach.", "fibreComposition": [{"pct": 70.0, "fibre": "viscose"}, {"pct": 30.0, "fibre": "linen"}], "productIdentifier": {"gtin": "38012345679066", "scheme": "gs1"}, "chemicalComplianceStandard": "REACH Compliant"}, "operatorIdentifier": "DE123456789", "repairabilityScore": null, "applicableInstruments": [{"recorded": "catalog", "instrument": "espr"}]}'); +INSERT INTO odal.passport VALUES ('01a1a307-b9c9-73a1-94d4-44a02ace7223', 'textile', 'draft', false, '1.4.0', NULL, NULL, 1, NULL, NULL, '2026-11-03 20:09:14.955631+00', NULL, NULL, '2026-11-03 20:09:14.955902+00', '2026-11-03 20:09:14.955902+00', NULL, '{"id": "01a1a307-b9c9-73a1-94d4-44a02ace7223", "status": "draft", "batchId": "BATCH-SS26-004", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.953487557Z", "materials": [], "qrCodeUrl": null, "updatedAt": "2026-11-03T20:09:14.953488031Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.955633179Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Glamor Linen Blouse", "publishedAt": null, "jwsSignature": null, "manufacturer": {"name": "Glamor Clothing", "address": "DE", "country": "DE", "didWebUrl": null}, "productGroup": "textile", "schemaVersion": "1.4.0", "retentionLocked": false, "complianceResult": {"co2eScore": null, "assessedAt": "2026-11-03T20:09:14.955630689Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"productGroup": "textile", "countryOfOrigin": "DE", "careInstructions": "Hand wash cold. Line dry. Iron low heat.", "fibreComposition": [{"pct": 100.0, "fibre": "linen"}], "productIdentifier": {"gtin": "38012345679370", "scheme": "gs1"}, "chemicalComplianceStandard": "EU REACH Annex XVII"}, "operatorIdentifier": "DE123456789", "repairabilityScore": null, "applicableInstruments": [{"recorded": "catalog", "instrument": "espr"}]}'); +INSERT INTO odal.passport VALUES ('01a1a307-b97c-73a6-99ea-eb004fceb13a', 'textile', 'retired', false, '1.4.0', NULL, NULL, 1, NULL, NULL, '2026-11-03 20:09:14.879318+00', NULL, NULL, '2026-11-03 20:09:14.879604+00', '2026-11-03 20:09:15.184636+00', NULL, '{"id": "01a1a307-b97c-73a6-99ea-eb004fceb13a", "status": "retired", "batchId": "BATCH-SS26-003", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.876201919Z", "materials": [], "qrCodeUrl": null, "updatedAt": "2026-11-03T20:09:15.188542444Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.879321282Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Glamor Sport Jacket", "publishedAt": null, "jwsSignature": null, "manufacturer": {"name": "Glamor Clothing", "address": "DE", "country": "DE", "didWebUrl": null}, "productGroup": "textile", "schemaVersion": "1.4.0", "retentionLocked": false, "complianceResult": {"co2eScore": null, "assessedAt": "2026-11-03T20:09:14.879318011Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"productGroup": "textile", "countryOfOrigin": "DE", "careInstructions": "Machine wash 30°C. Do not tumble dry.", "fibreComposition": [{"pct": 85.0, "fibre": "polyester"}, {"pct": 15.0, "fibre": "elastane"}], "productIdentifier": {"gtin": "38012345679134", "scheme": "gs1"}, "chemicalComplianceStandard": "OEKO-TEX Standard 100"}, "operatorIdentifier": "DE123456789", "repairabilityScore": null, "applicableInstruments": [{"recorded": "catalog", "instrument": "espr"}]}'); + + +-- +-- Data for Name: passport_audit; Type: TABLE DATA; Schema: odal; Owner: - +-- + +INSERT INTO odal.passport_audit VALUES ('01a1a307-b513-70b6-9bed-a64e8dd64106', '01a1a307-b510-75c3-b5cc-1ccbddeb9787', 'api-key', NULL, 'created', NULL, 'draft', '01a1a307-b4ee-7641-a047-2e2957bc74d5', NULL, NULL, '2026-11-03 20:09:13.747961+00', '', '3dc1f296bf61e1db6f1fe3d6065e028909edb405fb9f009b4a00da25ee502108'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b561-7445-bde3-3d2fbdd7fc22', '01a1a307-b510-75c3-b5cc-1ccbddeb9787', 'api-key', NULL, 'published', NULL, 'active', '01a1a307-b527-7150-af9e-cc9192664e02', NULL, '{"fullViewPayload": {"id": "01a1a307-b510-75c3-b5cc-1ccbddeb9787", "status": "active", "batchId": "ODL-IND-NMC-2026-Q2-001", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:13.744378034Z", "materials": [{"name": "Lithium Nickel Manganese Cobalt Oxide", "weightKg": 14.2, "recycledPct": 11.0, "countryOfOrigin": "DE"}, {"name": "Graphite", "weightKg": 6.4, "recycledPct": 5.0, "countryOfOrigin": "DE"}, {"name": "Aluminium enclosure", "weightKg": 8.1, "recycledPct": 78.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000014/21/75c3b5cc1ccbddeb9787", "updatedAt": "2026-11-03T20:09:13.789298757Z", "lintResult": {"assessedAt": "2026-11-03T20:09:13.747933990Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Odal Industrial Battery NMC-48", "publishedAt": "2026-11-03T20:09:13.789298757Z", "jwsSignature": null, "manufacturer": {"name": "Example Batteries GmbH", "address": "Prenzlauer Berg 12, 10405 Berlin, DE", "country": null, "didWebUrl": "https://batteries.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:13.789298757Z", "retentionLocked": true, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-06-01. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:13.747931997Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "industrial", "productGroup": "battery", "usageHistory": {"negativeEvents": [], "chargeDischargeCycles": 12}, "anodeMaterial": [{"name": "Graphite", "casNumber": "7782-42-5", "weightPct": 95.0}, {"name": "SBR/CMC binder", "casNumber": "9003-55-8", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 2.0}], "batteryStatus": "original", "ratedEnergyWh": 4320.0, "stateOfHealth": {"parameterSet": "stationaryOrLmt", "ohmicResistanceMohm": 14.2, "remainingCapacityPct": 99.3, "remainingPowerCapabilityPct": 99.0, "selfDischargeRatePctPerMonth": 2.1, "remainingRoundTripEfficiencyPct": 98.5}, "batteryModelId": "GC-IND-NMC48-320", "powerLimitMaxW": 6000.0, "powerLimitMinW": 200.0, "safetyMeasures": "De-energise and wait 15 min before opening. Class 0 gloves. No conductive tools.", "batteryWeightKg": 32.0, "cathodeMaterial": [{"name": "LiNi0.6Mn0.2Co0.2O2", "casNumber": "346417-97-8", "weightPct": 96.0}, {"name": "PVDF binder", "casNumber": "24937-79-9", "weightPct": 2.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 2.0}], "maximumVoltageV": 54.6, "minimalVoltageV": 42.0, "nominalVoltageV": 48.0, "batteryChemistry": "NMC", "ratedCapacityKwh": 4.32, "stateOfHealthPct": null, "manufacturingDate": "2026-04-15T00:00:00Z", "nominalCapacityAh": 90.0, "productIdentifier": {"gtin": "09590000000014", "scheme": "gs1"}, "testReportResults": "IEC 62619:2022 pass; UN 38.3 pass; report GC-TR-2026-0412", "cycleLifeTestCRate": 0.5, "dynamicPerformance": {"capacityFadePct": 0.7, "ratedCapacityAh": 89.4, "expectedLifetimeCycles": 2790, "internalResistanceMohm": 14.2, "roundTripEfficiencyPct": 94.6}, "manufacturingPlace": "Berlin, DE (plant BLN-1)", "markingInformation": "Separate-collection symbol (crossed-out wheeled bin) per Art. 13(4); capacity 90 Ah / 4.32 kWh", "sparePartsContacts": "spares@batteries.example; +49 30 5550 1200", "electrolyteMaterial": [{"name": "Ethylene carbonate", "casNumber": "96-49-1", "weightPct": 40.0}, {"name": "Dimethyl carbonate", "casNumber": "616-38-6", "weightPct": 47.0}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "weightPct": 13.0}], "hazardousSubstances": [{"name": "Nickel sulfate", "casNumber": "7786-81-4", "concentrationPct": 1.2}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.8}], "renewableContentPct": 18.0, "componentPartNumbers": ["GC-CELL-2170-NMC", "GC-BMS-48-V3", "GC-PACK-ENCL-320"], "criticalRawMaterials": [{"name": "Cobalt", "casNumber": "7440-48-4", "weightGrams": 1450.0, "countryOfOrigin": "CD"}, {"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 2100.0, "countryOfOrigin": "CL"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 6400.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000014:ODL-IND-NMC-2026-Q2-001", "expectedLifetimeCycles": 2800, "wasteBatteryInformation": "Return to an authorised collection point or installer. Do not shred. Contains lithium; fire risk if damaged. Producer registration DE-BATT-00123.", "notInUseTemperatureRange": {"maxC": 45.0, "minC": -20.0}, "originalPowerCapabilityW": 4600.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Water mist or ABC dry powder; do not use CO2", "euDeclarationOfConformity": "EU-DoC GC-2026-IND-0091 dated 2026-04-20, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://batteries.example/service/ind-nmc48/disassembly", "internalCellResistanceMohm": 0.9, "internalPackResistanceMohm": 14.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "IEC 61427-1:2013, 25 degC, 80% DoD", "initialRoundTripEfficiencyPct": 95.0, "commercialWarrantyPeriodMonths": 120, "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage profile", "roundTripEfficiencyAtHalfCycleLifePct": 89.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-06-01", "repairabilityScore": {"overall": 7.5}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}, "publicViewPayload": {"id": "01a1a307-b510-75c3-b5cc-1ccbddeb9787", "status": "active", "batchId": "ODL-IND-NMC-2026-Q2-001", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:13.744378034Z", "materials": [{"name": "Lithium Nickel Manganese Cobalt Oxide", "weightKg": 14.2, "recycledPct": 11.0, "countryOfOrigin": "DE"}, {"name": "Graphite", "weightKg": 6.4, "recycledPct": 5.0, "countryOfOrigin": "DE"}, {"name": "Aluminium enclosure", "weightKg": 8.1, "recycledPct": 78.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000014/21/75c3b5cc1ccbddeb9787", "updatedAt": "2026-11-03T20:09:13.789298757Z", "co2ePerUnit": null, "productName": "Odal Industrial Battery NMC-48", "publishedAt": "2026-11-03T20:09:13.789298757Z", "manufacturer": {"name": "Example Batteries GmbH", "address": "Prenzlauer Berg 12, 10405 Berlin, DE", "country": null, "didWebUrl": "https://batteries.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:13.789298757Z", "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-06-01. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:13.747931997Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "industrial", "productGroup": "battery", "ratedEnergyWh": 4320.0, "batteryModelId": "GC-IND-NMC48-320", "powerLimitMaxW": 6000.0, "powerLimitMinW": 200.0, "batteryWeightKg": 32.0, "maximumVoltageV": 54.6, "minimalVoltageV": 42.0, "nominalVoltageV": 48.0, "batteryChemistry": "NMC", "ratedCapacityKwh": 4.32, "manufacturingDate": "2026-04-15T00:00:00Z", "nominalCapacityAh": 90.0, "productIdentifier": {"gtin": "09590000000014", "scheme": "gs1"}, "cycleLifeTestCRate": 0.5, "manufacturingPlace": "Berlin, DE (plant BLN-1)", "markingInformation": "Separate-collection symbol (crossed-out wheeled bin) per Art. 13(4); capacity 90 Ah / 4.32 kWh", "hazardousSubstances": [{"name": "Nickel sulfate", "casNumber": "7786-81-4", "concentrationPct": 1.2}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.8}], "renewableContentPct": 18.0, "criticalRawMaterials": [{"name": "Cobalt", "casNumber": "7440-48-4", "weightGrams": 1450.0, "countryOfOrigin": "CD"}, {"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 2100.0, "countryOfOrigin": "CL"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 6400.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000014:ODL-IND-NMC-2026-Q2-001", "expectedLifetimeCycles": 2800, "wasteBatteryInformation": "Return to an authorised collection point or installer. Do not shred. Contains lithium; fire risk if damaged. Producer registration DE-BATT-00123.", "notInUseTemperatureRange": {"maxC": 45.0, "minC": -20.0}, "originalPowerCapabilityW": 4600.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Water mist or ABC dry powder; do not use CO2", "euDeclarationOfConformity": "EU-DoC GC-2026-IND-0091 dated 2026-04-20, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "internalCellResistanceMohm": 0.9, "internalPackResistanceMohm": 14.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "IEC 61427-1:2013, 25 degC, 80% DoD", "initialRoundTripEfficiencyPct": 95.0, "commercialWarrantyPeriodMonths": 120, "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage profile", "roundTripEfficiencyAtHalfCycleLifePct": 89.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-06-01", "repairabilityScore": {"overall": 7.5}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}}', '2026-11-03 20:09:13.825846+00', '3dc1f296bf61e1db6f1fe3d6065e028909edb405fb9f009b4a00da25ee502108', '40db43647ae8428d8c99f4d51b2714178f3d01242c9f47e78b6da6638aa03e13'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b5c1-76b6-b8a6-f311f0715468', '01a1a307-b5bd-71ac-9c86-4696540c3b21', 'api-key', NULL, 'created', NULL, 'draft', '01a1a307-b5b7-7726-abec-fa2f61081d33', NULL, NULL, '2026-11-03 20:09:13.921752+00', '', '56c76009ae2a95dfae4dd3cb01855ccd944208b5b738b8df60356d3bceb80cf7'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b633-7057-b277-68e01ea07a71', '01a1a307-b5bd-71ac-9c86-4696540c3b21', 'api-key', NULL, 'published', NULL, 'active', '01a1a307-b5f1-770d-af73-aa36607d7af2', NULL, '{"fullViewPayload": {"id": "01a1a307-b5bd-71ac-9c86-4696540c3b21", "status": "active", "batchId": "ODL-IND-LFP-2026-Q2-014", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:13.917135531Z", "materials": [{"name": "Lithium Iron Phosphate", "weightKg": 22.0, "recycledPct": 8.0, "countryOfOrigin": "DE"}, {"name": "Graphite", "weightKg": 9.5, "recycledPct": 6.0, "countryOfOrigin": "DE"}, {"name": "Steel enclosure", "weightKg": 12.0, "recycledPct": 82.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000021/21/71ac9c864696540c3b21", "updatedAt": "2026-11-03T20:09:13.987954878Z", "lintResult": {"assessedAt": "2026-11-03T20:09:13.921707611Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Odal Stationary Storage Battery LFP-51.2", "publishedAt": "2026-11-03T20:09:13.987954878Z", "jwsSignature": null, "manufacturer": {"name": "Example Batteries GmbH", "address": "Prenzlauer Berg 12, 10405 Berlin, DE", "country": null, "didWebUrl": "https://batteries.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:13.987954878Z", "retentionLocked": true, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-05-20. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:13.921704049Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "industrial", "productGroup": "battery", "usageHistory": {"negativeEvents": [], "chargeDischargeCycles": 5}, "anodeMaterial": [{"name": "Graphite", "casNumber": "7782-42-5", "weightPct": 96.0}, {"name": "SBR/CMC binder", "casNumber": "9003-55-8", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 1.0}], "batteryStatus": "original", "ratedEnergyWh": 5120.0, "stateOfHealth": {"parameterSet": "stationaryOrLmt", "ohmicResistanceMohm": 9.1, "remainingCapacityPct": 99.6, "remainingPowerCapabilityPct": 99.4, "selfDischargeRatePctPerMonth": 1.4, "remainingRoundTripEfficiencyPct": 99.1}, "batteryModelId": "GC-ESS-LFP512-100", "powerLimitMaxW": 7500.0, "powerLimitMinW": 150.0, "safetyMeasures": "Isolate DC breaker before servicing. Insulated tools only. Ventilate enclosure before opening.", "batteryWeightKg": 46.5, "cathodeMaterial": [{"name": "LiFePO4", "casNumber": "15365-14-7", "weightPct": 95.0}, {"name": "PVDF binder", "casNumber": "24937-79-9", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 2.0}], "maximumVoltageV": 58.4, "minimalVoltageV": 40.0, "nominalVoltageV": 51.2, "batteryChemistry": "LFP", "ratedCapacityKwh": 5.12, "stateOfHealthPct": null, "manufacturingDate": "2026-03-02T00:00:00Z", "nominalCapacityAh": 100.0, "productIdentifier": {"gtin": "09590000000021", "scheme": "gs1"}, "testReportResults": "IEC 62619:2022 pass; IEC 63056:2020 pass; UN 38.3 pass; report GC-TR-2026-0301", "cycleLifeTestCRate": 0.33, "dynamicPerformance": {"capacityFadePct": 0.4, "ratedCapacityAh": 99.6, "expectedLifetimeCycles": 5980, "internalResistanceMohm": 9.1, "roundTripEfficiencyPct": 96.2}, "manufacturingPlace": "Berlin, DE (plant BLN-2)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 100 Ah / 5.12 kWh; chemistry LFP", "sparePartsContacts": "spares@batteries.example; +49 30 5550 1200", "electrolyteMaterial": [{"name": "Ethylene carbonate", "casNumber": "96-49-1", "weightPct": 38.0}, {"name": "Ethyl methyl carbonate", "casNumber": "623-53-0", "weightPct": 49.0}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "weightPct": 13.0}], "hazardousSubstances": [{"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.7}], "renewableContentPct": 22.0, "componentPartNumbers": ["GC-CELL-32700-LFP", "GC-BMS-16S-V2", "GC-ESS-ENCL-512"], "criticalRawMaterials": [{"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 2600.0, "countryOfOrigin": "AU"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 9500.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000021:ODL-IND-LFP-2026-Q2-014", "expectedLifetimeCycles": 6000, "wasteBatteryInformation": "Return to an authorised collection point. Do not incinerate or shred. Producer registration DE-BATT-00123.", "notInUseTemperatureRange": {"maxC": 50.0, "minC": -10.0}, "originalPowerCapabilityW": 5000.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Water or ABC dry powder; large volumes of water for thermal runaway", "euDeclarationOfConformity": "EU-DoC GC-2026-IND-0067 dated 2026-03-10, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://batteries.example/service/ess-lfp512/disassembly", "internalCellResistanceMohm": 0.6, "internalPackResistanceMohm": 9.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "IEC 61427-1:2013, 25 degC, 90% DoD", "initialRoundTripEfficiencyPct": 96.5, "commercialWarrantyPeriodMonths": 144, "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage profile", "roundTripEfficiencyAtHalfCycleLifePct": 92.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-05-20", "repairabilityScore": {"overall": 8.1}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}, "publicViewPayload": {"id": "01a1a307-b5bd-71ac-9c86-4696540c3b21", "status": "active", "batchId": "ODL-IND-LFP-2026-Q2-014", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:13.917135531Z", "materials": [{"name": "Lithium Iron Phosphate", "weightKg": 22.0, "recycledPct": 8.0, "countryOfOrigin": "DE"}, {"name": "Graphite", "weightKg": 9.5, "recycledPct": 6.0, "countryOfOrigin": "DE"}, {"name": "Steel enclosure", "weightKg": 12.0, "recycledPct": 82.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000021/21/71ac9c864696540c3b21", "updatedAt": "2026-11-03T20:09:13.987954878Z", "co2ePerUnit": null, "productName": "Odal Stationary Storage Battery LFP-51.2", "publishedAt": "2026-11-03T20:09:13.987954878Z", "manufacturer": {"name": "Example Batteries GmbH", "address": "Prenzlauer Berg 12, 10405 Berlin, DE", "country": null, "didWebUrl": "https://batteries.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:13.987954878Z", "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-05-20. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:13.921704049Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "industrial", "productGroup": "battery", "ratedEnergyWh": 5120.0, "batteryModelId": "GC-ESS-LFP512-100", "powerLimitMaxW": 7500.0, "powerLimitMinW": 150.0, "batteryWeightKg": 46.5, "maximumVoltageV": 58.4, "minimalVoltageV": 40.0, "nominalVoltageV": 51.2, "batteryChemistry": "LFP", "ratedCapacityKwh": 5.12, "manufacturingDate": "2026-03-02T00:00:00Z", "nominalCapacityAh": 100.0, "productIdentifier": {"gtin": "09590000000021", "scheme": "gs1"}, "cycleLifeTestCRate": 0.33, "manufacturingPlace": "Berlin, DE (plant BLN-2)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 100 Ah / 5.12 kWh; chemistry LFP", "hazardousSubstances": [{"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.7}], "renewableContentPct": 22.0, "criticalRawMaterials": [{"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 2600.0, "countryOfOrigin": "AU"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 9500.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000021:ODL-IND-LFP-2026-Q2-014", "expectedLifetimeCycles": 6000, "wasteBatteryInformation": "Return to an authorised collection point. Do not incinerate or shred. Producer registration DE-BATT-00123.", "notInUseTemperatureRange": {"maxC": 50.0, "minC": -10.0}, "originalPowerCapabilityW": 5000.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Water or ABC dry powder; large volumes of water for thermal runaway", "euDeclarationOfConformity": "EU-DoC GC-2026-IND-0067 dated 2026-03-10, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "internalCellResistanceMohm": 0.6, "internalPackResistanceMohm": 9.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "IEC 61427-1:2013, 25 degC, 90% DoD", "initialRoundTripEfficiencyPct": 96.5, "commercialWarrantyPeriodMonths": 144, "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage profile", "roundTripEfficiencyAtHalfCycleLifePct": 92.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-05-20", "repairabilityScore": {"overall": 8.1}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}}', '2026-11-03 20:09:14.035317+00', '56c76009ae2a95dfae4dd3cb01855ccd944208b5b738b8df60356d3bceb80cf7', '1e39b1b817e0ea234ebe7bcae2c3ef5f4dcdac3070971a15e8a17551b51757db'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b680-72af-9622-76525d0938d0', '01a1a307-b67c-706a-a55e-689712fcd122', 'api-key', NULL, 'created', NULL, 'draft', '01a1a307-b676-71a4-8092-319a3e0d2947', NULL, NULL, '2026-11-03 20:09:14.112583+00', '', 'a070aee107a68da46cc4294e1495b1d8ab6de0138f77306383d1ce32ce3230d1'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b6ec-705d-a285-a14dab43eec4', '01a1a307-b67c-706a-a55e-689712fcd122', 'api-key', NULL, 'published', NULL, 'active', '01a1a307-b6a9-7620-a2e9-4875e32b9a4f', NULL, '{"fullViewPayload": {"id": "01a1a307-b67c-706a-a55e-689712fcd122", "status": "active", "batchId": "ODL-IND-PB-2026-Q1-007", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.108085427Z", "materials": [{"name": "Lead", "weightKg": 38.0, "recycledPct": 82.0, "countryOfOrigin": "DE"}, {"name": "Sulfuric acid electrolyte", "weightKg": 9.0, "recycledPct": 0.0, "countryOfOrigin": "DE"}, {"name": "Polypropylene case", "weightKg": 3.2, "recycledPct": 45.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000038/21/706aa55e689712fcd122", "updatedAt": "2026-11-03T20:09:14.173554734Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.112531026Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Odal Industrial Standby Battery 12V-200Ah (VRLA)", "publishedAt": "2026-11-03T20:09:14.173554734Z", "jwsSignature": null, "manufacturer": {"name": "Rheinpower Batterien GmbH", "address": "Industriestrasse 8, 47059 Duisburg, DE", "country": null, "didWebUrl": "https://rheinpower.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85072000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:14.173554734Z", "retentionLocked": true, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-02-10. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:14.112526945Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "industrial", "hazardSymbol": "lead", "productGroup": "battery", "anodeMaterial": [{"name": "Sponge lead (negative active material)", "casNumber": "7439-92-1", "weightPct": 100.0}], "batteryStatus": "original", "ratedEnergyWh": 2400.0, "batteryModelId": "RP-VRLA-12-200", "powerLimitMaxW": 2400.0, "powerLimitMinW": 50.0, "safetyMeasures": "Acid hazard. Face shield and acid-resistant gloves. Do not tip. Vent hydrogen before servicing.", "batteryWeightKg": 52.0, "cathodeMaterial": [{"name": "Lead dioxide (positive active material)", "casNumber": "1309-60-0", "weightPct": 100.0}], "maximumVoltageV": 14.4, "minimalVoltageV": 10.5, "nominalVoltageV": 12.0, "batteryChemistry": "lead-acid", "ratedCapacityKwh": 2.4, "stateOfHealthPct": null, "manufacturingDate": "2026-01-08T00:00:00Z", "nominalCapacityAh": 200.0, "productIdentifier": {"gtin": "09590000000038", "scheme": "gs1"}, "testReportResults": "IEC 60896-21/22:2004 pass; UN 2800 (non-spillable) pass; report RP-TR-2026-0021", "cycleLifeTestCRate": 0.1, "manufacturingPlace": "Duisburg, DE (plant DU-3)", "markingInformation": "Separate-collection symbol per Art. 13(4); Pb symbol per Art. 13(5); capacity 200 Ah / 12 V", "sparePartsContacts": "service@rheinpower.example; +49 203 5550 800", "electrolyteMaterial": [{"name": "Sulfuric acid (aqueous, ~37%)", "casNumber": "7664-93-9", "weightPct": 37.0}, {"name": "Water", "casNumber": "7732-18-5", "weightPct": 63.0}], "hazardousSubstances": [{"name": "Lead", "casNumber": "7439-92-1", "concentrationPct": 68.0}, {"name": "Sulfuric acid", "casNumber": "7664-93-9", "concentrationPct": 17.0}], "renewableContentPct": 2.0, "componentPartNumbers": ["RP-PLATE-POS-200", "RP-PLATE-NEG-200", "RP-CASE-12-200", "RP-VALVE-VRLA"], "criticalRawMaterials": [{"name": "Antimony", "casNumber": "7440-36-0", "weightGrams": 380.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000038:ODL-IND-PB-2026-Q1-007", "expectedLifetimeCycles": 1200, "wasteBatteryInformation": "Lead-acid battery - return to retailer or authorised collector for recycling. Never dispose in household waste. Producer registration DE-BATT-00457.", "notInUseTemperatureRange": {"maxC": 40.0, "minC": -15.0}, "originalPowerCapabilityW": 1800.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "CO2, ABC dry powder, or water; neutralise acid spills with soda ash", "euDeclarationOfConformity": "EU-DoC RP-2026-IND-0014 dated 2026-01-20, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://rheinpower.example/service/vrla-12-200/disassembly", "internalCellResistanceMohm": 3.5, "internalPackResistanceMohm": 4.2, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "IEC 60896-21/22, 25 degC, 50% DoD", "initialRoundTripEfficiencyPct": 82.0, "commercialWarrantyPeriodMonths": 60, "notInUseTemperatureReferenceTest": "IEC 60896-21:2004 storage clause", "roundTripEfficiencyAtHalfCycleLifePct": 74.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-02-10", "repairabilityScore": {"overall": 3.0}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}, "publicViewPayload": {"id": "01a1a307-b67c-706a-a55e-689712fcd122", "status": "active", "batchId": "ODL-IND-PB-2026-Q1-007", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.108085427Z", "materials": [{"name": "Lead", "weightKg": 38.0, "recycledPct": 82.0, "countryOfOrigin": "DE"}, {"name": "Sulfuric acid electrolyte", "weightKg": 9.0, "recycledPct": 0.0, "countryOfOrigin": "DE"}, {"name": "Polypropylene case", "weightKg": 3.2, "recycledPct": 45.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000038/21/706aa55e689712fcd122", "updatedAt": "2026-11-03T20:09:14.173554734Z", "co2ePerUnit": null, "productName": "Odal Industrial Standby Battery 12V-200Ah (VRLA)", "publishedAt": "2026-11-03T20:09:14.173554734Z", "manufacturer": {"name": "Rheinpower Batterien GmbH", "address": "Industriestrasse 8, 47059 Duisburg, DE", "country": null, "didWebUrl": "https://rheinpower.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85072000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:14.173554734Z", "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-02-10. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:14.112526945Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "industrial", "hazardSymbol": "lead", "productGroup": "battery", "ratedEnergyWh": 2400.0, "batteryModelId": "RP-VRLA-12-200", "powerLimitMaxW": 2400.0, "powerLimitMinW": 50.0, "batteryWeightKg": 52.0, "maximumVoltageV": 14.4, "minimalVoltageV": 10.5, "nominalVoltageV": 12.0, "batteryChemistry": "lead-acid", "ratedCapacityKwh": 2.4, "manufacturingDate": "2026-01-08T00:00:00Z", "nominalCapacityAh": 200.0, "productIdentifier": {"gtin": "09590000000038", "scheme": "gs1"}, "cycleLifeTestCRate": 0.1, "manufacturingPlace": "Duisburg, DE (plant DU-3)", "markingInformation": "Separate-collection symbol per Art. 13(4); Pb symbol per Art. 13(5); capacity 200 Ah / 12 V", "hazardousSubstances": [{"name": "Lead", "casNumber": "7439-92-1", "concentrationPct": 68.0}, {"name": "Sulfuric acid", "casNumber": "7664-93-9", "concentrationPct": 17.0}], "renewableContentPct": 2.0, "criticalRawMaterials": [{"name": "Antimony", "casNumber": "7440-36-0", "weightGrams": 380.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000038:ODL-IND-PB-2026-Q1-007", "expectedLifetimeCycles": 1200, "wasteBatteryInformation": "Lead-acid battery - return to retailer or authorised collector for recycling. Never dispose in household waste. Producer registration DE-BATT-00457.", "notInUseTemperatureRange": {"maxC": 40.0, "minC": -15.0}, "originalPowerCapabilityW": 1800.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "CO2, ABC dry powder, or water; neutralise acid spills with soda ash", "euDeclarationOfConformity": "EU-DoC RP-2026-IND-0014 dated 2026-01-20, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "internalCellResistanceMohm": 3.5, "internalPackResistanceMohm": 4.2, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "IEC 60896-21/22, 25 degC, 50% DoD", "initialRoundTripEfficiencyPct": 82.0, "commercialWarrantyPeriodMonths": 60, "notInUseTemperatureReferenceTest": "IEC 60896-21:2004 storage clause", "roundTripEfficiencyAtHalfCycleLifePct": 74.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-02-10", "repairabilityScore": {"overall": 3.0}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}}', '2026-11-03 20:09:14.220906+00', 'a070aee107a68da46cc4294e1495b1d8ab6de0138f77306383d1ce32ce3230d1', '1b0c78f867e4fe340bac59cc6f95c15655d2b3185f29b7e43b5077c6308bff67'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b70f-7652-a913-d49e0ba03961', '01a1a307-b70c-70c1-b4a3-a616d161e997', 'api-key', NULL, 'created', NULL, 'draft', '01a1a307-b709-72a0-9341-0ae144936a2b', NULL, NULL, '2026-11-03 20:09:14.25524+00', '', 'a5d2e8b8473641a9b0dec2ae16e29d8877ff32bbd21d4ca9d6bcb940f1a91094'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b74b-777d-8295-99a98b31f0cb', '01a1a307-b70c-70c1-b4a3-a616d161e997', 'api-key', NULL, 'published', NULL, 'active', '01a1a307-b720-74a2-93e4-67709107a151', NULL, '{"fullViewPayload": {"id": "01a1a307-b70c-70c1-b4a3-a616d161e997", "status": "active", "batchId": "VBX-EV-2026-0421", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.252942043Z", "materials": [{"name": "Lithium Nickel Manganese Cobalt Oxide (NMC 811)", "weightKg": 210.0, "recycledPct": 13.0, "countryOfOrigin": "DE"}, {"name": "Graphite/silicon anode", "weightKg": 95.0, "recycledPct": 4.0, "countryOfOrigin": "DE"}, {"name": "Aluminium pack housing", "weightKg": 85.0, "recycledPct": 71.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000045/21/70c1b4a3a616d161e997", "updatedAt": "2026-11-03T20:09:14.279493731Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.255216167Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Voltex EV Traction Battery 75 kWh", "publishedAt": "2026-11-03T20:09:14.279493731Z", "jwsSignature": null, "manufacturer": {"name": "Voltex Battery GmbH", "address": "Werkstrasse 5, 85055 Ingolstadt, DE", "country": null, "didWebUrl": "https://voltex.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:14.279493731Z", "retentionLocked": true, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-07-01. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:14.255214416Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "ev", "productGroup": "battery", "usageHistory": {"stateOfCharge": [{"recordedAt": "2026-07-15T09:00:00Z", "stateOfChargePct": 82.0}, {"recordedAt": "2026-07-22T09:00:00Z", "stateOfChargePct": 64.0}], "negativeEvents": [], "chargeDischargeCycles": 8}, "anodeMaterial": [{"name": "Graphite", "casNumber": "7782-42-5", "weightPct": 90.0}, {"name": "Silicon oxide", "casNumber": "10097-28-6", "weightPct": 6.0}, {"name": "SBR/CMC binder", "casNumber": "9003-55-8", "weightPct": 4.0}], "batteryStatus": "original", "ratedEnergyWh": 75000.0, "stateOfHealth": {"socePct": 99.1, "parameterSet": "electricVehicle"}, "batteryModelId": "VBX-EV75-NMC811-P4S96", "powerLimitMaxW": 220000.0, "powerLimitMinW": 500.0, "safetyMeasures": "High-voltage (>60 V DC). HV-qualified personnel only. Service disconnect plug removed and locked out. Insulated PPE class 00.", "batteryWeightKg": 468.0, "cathodeMaterial": [{"name": "LiNi0.8Mn0.1Co0.1O2", "casNumber": "346417-97-8", "weightPct": 96.5}, {"name": "PVDF binder", "casNumber": "24937-79-9", "weightPct": 2.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 1.5}], "maximumVoltageV": 403.2, "minimalVoltageV": 270.0, "nominalVoltageV": 355.0, "batteryChemistry": "NMC", "ratedCapacityKwh": 75.0, "stateOfHealthPct": null, "manufacturingDate": "2026-05-11T00:00:00Z", "nominalCapacityAh": 211.0, "productIdentifier": {"gtin": "09590000000045", "scheme": "gs1"}, "testReportResults": "IEC 62660-2:2018 pass; ISO 6469-1:2019 pass; UN 38.3 pass; report VBX-TR-2026-0507", "cycleLifeTestCRate": 1.0, "dynamicPerformance": {"powerW": 148000.0, "powerFadePct": 0.6, "capacityFadePct": 0.9, "ratedCapacityAh": 209.0, "expectedLifetimeYears": 9.5, "expectedLifetimeCycles": 1785, "internalResistanceMohm": 42.4, "roundTripEfficiencyPct": 93.6, "internalResistanceIncreasePct": 1.0}, "manufacturingPlace": "Ingolstadt, DE (plant ING-1)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 75 kWh; HV warning label", "sparePartsContacts": "hv-service@voltex.example; +49 841 5550 400 (certified HV workshops only)", "electrolyteMaterial": [{"name": "Ethylene carbonate", "casNumber": "96-49-1", "weightPct": 30.0}, {"name": "Ethyl methyl carbonate", "casNumber": "623-53-0", "weightPct": 55.0}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "weightPct": 13.0}, {"name": "Vinylene carbonate additive", "casNumber": "872-36-6", "weightPct": 2.0}], "hazardousSubstances": [{"name": "Nickel sulfate", "casNumber": "7786-81-4", "concentrationPct": 2.4}, {"name": "Cobalt sulfate", "casNumber": "10124-43-3", "concentrationPct": 0.9}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.9}], "renewableContentPct": 16.0, "componentPartNumbers": ["VBX-CELL-4680-NMC811", "VBX-MODULE-24S", "VBX-BMS-MASTER-V4", "VBX-PACK-HV-75"], "criticalRawMaterials": [{"name": "Cobalt", "casNumber": "7440-48-4", "weightGrams": 5200.0, "countryOfOrigin": "CD"}, {"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 8100.0, "countryOfOrigin": "CL"}, {"name": "Nickel", "casNumber": "7440-02-0", "weightGrams": 32000.0, "countryOfOrigin": "ID"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 74000.0, "countryOfOrigin": "MZ"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000045:VBX-EV-2026-0421", "expectedLifetimeCycles": 1800, "wasteBatteryInformation": "EV battery - handled by the vehicle manufacturer take-back scheme. Do not open or discharge. Contact hv-service@voltex.example. Producer registration DE-BATT-00981.", "notInUseTemperatureRange": {"maxC": 55.0, "minC": -30.0}, "originalPowerCapabilityW": 150000.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Large volumes of water for cooling; do not use CO2 on cell fire", "euDeclarationOfConformity": "EU-DoC VBX-2026-EV-0142 dated 2026-05-18, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://voltex.example/service/ev75/disassembly", "internalCellResistanceMohm": 1.1, "internalPackResistanceMohm": 42.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "UNECE GTR 22 / IEC 62660-1:2018, 25 degC", "initialRoundTripEfficiencyPct": 94.0, "commercialWarrantyPeriodMonths": 96, "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage profile", "capacityThresholdForExhaustionPct": 70.0, "roundTripEfficiencyAtHalfCycleLifePct": 88.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-07-01", "repairabilityScore": {"overall": 5.5}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}, "publicViewPayload": {"id": "01a1a307-b70c-70c1-b4a3-a616d161e997", "status": "active", "batchId": "VBX-EV-2026-0421", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.252942043Z", "materials": [{"name": "Lithium Nickel Manganese Cobalt Oxide (NMC 811)", "weightKg": 210.0, "recycledPct": 13.0, "countryOfOrigin": "DE"}, {"name": "Graphite/silicon anode", "weightKg": 95.0, "recycledPct": 4.0, "countryOfOrigin": "DE"}, {"name": "Aluminium pack housing", "weightKg": 85.0, "recycledPct": 71.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000045/21/70c1b4a3a616d161e997", "updatedAt": "2026-11-03T20:09:14.279493731Z", "co2ePerUnit": null, "productName": "Voltex EV Traction Battery 75 kWh", "publishedAt": "2026-11-03T20:09:14.279493731Z", "manufacturer": {"name": "Voltex Battery GmbH", "address": "Werkstrasse 5, 85055 Ingolstadt, DE", "country": null, "didWebUrl": "https://voltex.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:14.279493731Z", "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-07-01. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:14.255214416Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "ev", "productGroup": "battery", "ratedEnergyWh": 75000.0, "batteryModelId": "VBX-EV75-NMC811-P4S96", "powerLimitMaxW": 220000.0, "powerLimitMinW": 500.0, "batteryWeightKg": 468.0, "maximumVoltageV": 403.2, "minimalVoltageV": 270.0, "nominalVoltageV": 355.0, "batteryChemistry": "NMC", "ratedCapacityKwh": 75.0, "manufacturingDate": "2026-05-11T00:00:00Z", "nominalCapacityAh": 211.0, "productIdentifier": {"gtin": "09590000000045", "scheme": "gs1"}, "cycleLifeTestCRate": 1.0, "manufacturingPlace": "Ingolstadt, DE (plant ING-1)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 75 kWh; HV warning label", "hazardousSubstances": [{"name": "Nickel sulfate", "casNumber": "7786-81-4", "concentrationPct": 2.4}, {"name": "Cobalt sulfate", "casNumber": "10124-43-3", "concentrationPct": 0.9}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.9}], "renewableContentPct": 16.0, "criticalRawMaterials": [{"name": "Cobalt", "casNumber": "7440-48-4", "weightGrams": 5200.0, "countryOfOrigin": "CD"}, {"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 8100.0, "countryOfOrigin": "CL"}, {"name": "Nickel", "casNumber": "7440-02-0", "weightGrams": 32000.0, "countryOfOrigin": "ID"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 74000.0, "countryOfOrigin": "MZ"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000045:VBX-EV-2026-0421", "expectedLifetimeCycles": 1800, "wasteBatteryInformation": "EV battery - handled by the vehicle manufacturer take-back scheme. Do not open or discharge. Contact hv-service@voltex.example. Producer registration DE-BATT-00981.", "notInUseTemperatureRange": {"maxC": 55.0, "minC": -30.0}, "originalPowerCapabilityW": 150000.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Large volumes of water for cooling; do not use CO2 on cell fire", "euDeclarationOfConformity": "EU-DoC VBX-2026-EV-0142 dated 2026-05-18, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "internalCellResistanceMohm": 1.1, "internalPackResistanceMohm": 42.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "UNECE GTR 22 / IEC 62660-1:2018, 25 degC", "initialRoundTripEfficiencyPct": 94.0, "commercialWarrantyPeriodMonths": 96, "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage profile", "capacityThresholdForExhaustionPct": 70.0, "roundTripEfficiencyAtHalfCycleLifePct": 88.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-07-01", "repairabilityScore": {"overall": 5.5}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}}', '2026-11-03 20:09:14.315891+00', 'a5d2e8b8473641a9b0dec2ae16e29d8877ff32bbd21d4ca9d6bcb940f1a91094', '2ca7a5da2a763fe27e2ca4cd3120322cf5b441c74bd09e94a9745e78898192e0'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b795-7716-ac41-8c3ef8affa1b', '01a1a307-b792-7764-a50c-b10fe65bf932', 'api-key', NULL, 'created', NULL, 'draft', '01a1a307-b78e-7176-a83f-e643a5ef1a64', NULL, NULL, '2026-11-03 20:09:14.389592+00', '', '42e10a3ee41aa30cb1ccb35bdcd23d26a54884dde8b51ae201407e2b1e9de067'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b7dc-7605-8f6b-6ab717f72d08', '01a1a307-b792-7764-a50c-b10fe65bf932', 'api-key', NULL, 'published', NULL, 'active', '01a1a307-b7aa-771d-bac0-fd22b805e42c', NULL, '{"fullViewPayload": {"id": "01a1a307-b792-7764-a50c-b10fe65bf932", "status": "active", "batchId": "VBX-LMT-2026-0188", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.386370527Z", "materials": [{"name": "Lithium Iron Phosphate", "weightKg": 1.4, "recycledPct": 7.0, "countryOfOrigin": "DE"}, {"name": "Graphite", "weightKg": 0.6, "recycledPct": 3.0, "countryOfOrigin": "DE"}, {"name": "ABS/PC housing", "weightKg": 0.5, "recycledPct": 30.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000052/21/7764a50cb10fe65bf932", "updatedAt": "2026-11-03T20:09:14.420275202Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.389558295Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Voltex e-Bike Battery 36V 14Ah", "publishedAt": "2026-11-03T20:09:14.420275202Z", "jwsSignature": null, "manufacturer": {"name": "Voltex Battery GmbH", "address": "Werkstrasse 5, 85055 Ingolstadt, DE", "country": null, "didWebUrl": "https://voltex.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:14.420275202Z", "retentionLocked": true, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-06-15. Ruleset ''battery-recycled-content-art8-3'' applies from 2036-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:14.389555743Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "lmt", "productGroup": "battery", "usageHistory": {"chargeDischargeCycles": 6}, "anodeMaterial": [{"name": "Graphite", "casNumber": "7782-42-5", "weightPct": 96.0}, {"name": "SBR/CMC binder", "casNumber": "9003-55-8", "weightPct": 4.0}], "batteryStatus": "original", "ratedEnergyWh": 504.0, "stateOfHealth": {"parameterSet": "stationaryOrLmt", "remainingCapacityPct": 99.3, "remainingPowerCapabilityPct": 98.8, "selfDischargeRatePctPerMonth": 3.0}, "batteryModelId": "VBX-LMT36-14-10S", "powerLimitMaxW": 750.0, "powerLimitMinW": 10.0, "safetyMeasures": "Do not open the pack. Replace via authorised dealer. Keep away from heat above 60 degC.", "batteryWeightKg": 2.6, "cathodeMaterial": [{"name": "LiFePO4", "casNumber": "15365-14-7", "weightPct": 95.0}, {"name": "PVDF binder", "casNumber": "24937-79-9", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 2.0}], "maximumVoltageV": 42.0, "minimalVoltageV": 30.0, "nominalVoltageV": 36.0, "batteryChemistry": "LFP", "ratedCapacityKwh": 0.504, "stateOfHealthPct": null, "manufacturingDate": "2026-05-05T00:00:00Z", "nominalCapacityAh": 14.0, "productIdentifier": {"gtin": "09590000000052", "scheme": "gs1"}, "testReportResults": "EN 50604-1:2016 pass; UN 38.3 pass; report VBX-TR-2026-0311", "cycleLifeTestCRate": 1.0, "dynamicPerformance": {"capacityFadePct": 0.7, "ratedCapacityAh": 13.9, "expectedLifetimeCycles": 995, "internalResistanceMohm": 95.4, "roundTripEfficiencyPct": 94.7}, "manufacturingPlace": "Ingolstadt, DE (plant ING-2)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 14 Ah / 504 Wh; 36 V LMT", "sparePartsContacts": "spares@voltex.example; +49 841 5550 410", "electrolyteMaterial": [{"name": "Ethylene carbonate", "casNumber": "96-49-1", "weightPct": 38.0}, {"name": "Dimethyl carbonate", "casNumber": "616-38-6", "weightPct": 49.0}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "weightPct": 13.0}], "hazardousSubstances": [{"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.6}], "renewableContentPct": 12.0, "componentPartNumbers": ["VBX-CELL-21700-LFP", "VBX-BMS-10S-V2", "VBX-LMT-ENCL-36"], "criticalRawMaterials": [{"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 165.0, "countryOfOrigin": "AU"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 600.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000052:VBX-LMT-2026-0188", "expectedLifetimeCycles": 1000, "wasteBatteryInformation": "Return the LMT battery to any bicycle retailer or municipal collection point. Do not place in household waste. Producer registration DE-BATT-00981.", "notInUseTemperatureRange": {"maxC": 45.0, "minC": -10.0}, "originalPowerCapabilityW": 500.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Water or ABC dry powder; submerge in water if safe to do so", "euDeclarationOfConformity": "EU-DoC VBX-2026-LMT-0088 dated 2026-05-12, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://voltex.example/service/lmt36-14/disassembly", "internalCellResistanceMohm": 8.0, "internalPackResistanceMohm": 95.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "EN 50604-1:2016, 25 degC, 100% DoD", "initialRoundTripEfficiencyPct": 95.0, "commercialWarrantyPeriodMonths": 24, "notInUseTemperatureReferenceTest": "EN 50604-1:2016 storage clause", "roundTripEfficiencyAtHalfCycleLifePct": 90.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-06-15", "repairabilityScore": {"overall": 6.0}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}, "publicViewPayload": {"id": "01a1a307-b792-7764-a50c-b10fe65bf932", "status": "active", "batchId": "VBX-LMT-2026-0188", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.386370527Z", "materials": [{"name": "Lithium Iron Phosphate", "weightKg": 1.4, "recycledPct": 7.0, "countryOfOrigin": "DE"}, {"name": "Graphite", "weightKg": 0.6, "recycledPct": 3.0, "countryOfOrigin": "DE"}, {"name": "ABS/PC housing", "weightKg": 0.5, "recycledPct": 30.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000052/21/7764a50cb10fe65bf932", "updatedAt": "2026-11-03T20:09:14.420275202Z", "co2ePerUnit": null, "productName": "Voltex e-Bike Battery 36V 14Ah", "publishedAt": "2026-11-03T20:09:14.420275202Z", "manufacturer": {"name": "Voltex Battery GmbH", "address": "Werkstrasse 5, 85055 Ingolstadt, DE", "country": null, "didWebUrl": "https://voltex.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:14.420275202Z", "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-06-15. Ruleset ''battery-recycled-content-art8-3'' applies from 2036-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:14.389555743Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "lmt", "productGroup": "battery", "ratedEnergyWh": 504.0, "batteryModelId": "VBX-LMT36-14-10S", "powerLimitMaxW": 750.0, "powerLimitMinW": 10.0, "batteryWeightKg": 2.6, "maximumVoltageV": 42.0, "minimalVoltageV": 30.0, "nominalVoltageV": 36.0, "batteryChemistry": "LFP", "ratedCapacityKwh": 0.504, "manufacturingDate": "2026-05-05T00:00:00Z", "nominalCapacityAh": 14.0, "productIdentifier": {"gtin": "09590000000052", "scheme": "gs1"}, "cycleLifeTestCRate": 1.0, "manufacturingPlace": "Ingolstadt, DE (plant ING-2)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 14 Ah / 504 Wh; 36 V LMT", "hazardousSubstances": [{"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.6}], "renewableContentPct": 12.0, "criticalRawMaterials": [{"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 165.0, "countryOfOrigin": "AU"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 600.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000052:VBX-LMT-2026-0188", "expectedLifetimeCycles": 1000, "wasteBatteryInformation": "Return the LMT battery to any bicycle retailer or municipal collection point. Do not place in household waste. Producer registration DE-BATT-00981.", "notInUseTemperatureRange": {"maxC": 45.0, "minC": -10.0}, "originalPowerCapabilityW": 500.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Water or ABC dry powder; submerge in water if safe to do so", "euDeclarationOfConformity": "EU-DoC VBX-2026-LMT-0088 dated 2026-05-12, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "internalCellResistanceMohm": 8.0, "internalPackResistanceMohm": 95.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "EN 50604-1:2016, 25 degC, 100% DoD", "initialRoundTripEfficiencyPct": 95.0, "commercialWarrantyPeriodMonths": 24, "notInUseTemperatureReferenceTest": "EN 50604-1:2016 storage clause", "roundTripEfficiencyAtHalfCycleLifePct": 90.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-06-15", "repairabilityScore": {"overall": 6.0}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}}', '2026-11-03 20:09:14.460178+00', '42e10a3ee41aa30cb1ccb35bdcd23d26a54884dde8b51ae201407e2b1e9de067', 'eae920fc57287d17b7007f6b7da50183a1db3faad04b0b288e2992577da7ed71'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b7f7-745b-9d6f-b53915260007', '01a1a307-b7f5-72ed-abdb-036d686a2932', 'api-key', NULL, 'created', NULL, 'draft', '01a1a307-b7f3-7072-a4e9-3cc84fae4165', NULL, NULL, '2026-11-03 20:09:14.487835+00', '', 'db3352d6758966a9722769b7a221543d0d52995535a68a09a64ef7162a291260'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b81e-74ed-8ac2-ee1825906b49', '01a1a307-b7f5-72ed-abdb-036d686a2932', 'api-key', NULL, 'published', NULL, 'active', '01a1a307-b806-73e5-80e5-0a28d254e75a', NULL, '{"fullViewPayload": {"id": "01a1a307-b7f5-72ed-abdb-036d686a2932", "status": "active", "batchId": "ODL-PORT-2026-0342", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.485874190Z", "materials": [{"name": "Nickel", "weightKg": 0.045, "recycledPct": 20.0, "countryOfOrigin": "DE"}, {"name": "Steel can", "weightKg": 0.03, "recycledPct": 60.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000069/21/72edabdb036d686a2932", "updatedAt": "2026-11-03T20:09:14.508496352Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.487812197Z", "packVersion": "1.1.0"}, "co2ePerUnit": {"valueKg": 0.42}, "productName": "Odal Portable Power Pack AA NiMH (4-pack)", "publishedAt": "2026-11-03T20:09:14.508496352Z", "jwsSignature": null, "manufacturer": {"name": "Rheinpower Batterien GmbH", "address": "Industriestrasse 8, 47059 Duisburg, DE", "country": null, "didWebUrl": "https://rheinpower.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85073000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:14.508496352Z", "retentionLocked": true, "complianceResult": {"co2eScore": 0.42, "assessedAt": "2026-11-03T20:09:14.487810095Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "portable", "productGroup": "battery", "batteryStatus": "original", "co2ePerUnitKg": 0.42, "ratedEnergyWh": 2.4, "batteryModelId": "RP-AA-NIMH-2000-4", "batteryWeightKg": 0.12, "nominalVoltageV": 1.2, "batteryChemistry": "NiMH", "ratedCapacityKwh": null, "stateOfHealthPct": null, "manufacturingDate": "2026-02-20T00:00:00Z", "nominalCapacityAh": 2.0, "operatingTempMaxC": 45.0, "operatingTempMinC": -10.0, "productIdentifier": {"gtin": "09590000000069", "scheme": "gs1"}, "manufacturingPlace": "Duisburg, DE (plant DU-1)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 2000 mAh; rechargeable", "hazardousSubstances": [{"name": "Potassium hydroxide", "casNumber": "1310-58-3", "concentrationPct": 3.0}], "recycledContentLeadPct": 0.0, "wasteBatteryInformation": "Return spent cells to any retailer collection box. Do not incinerate. Producer registration DE-BATT-00457.", "recycledContentCobaltPct": 0.0, "recycledContentNickelPct": 18.0, "usableExtinguishingAgent": "Water, CO2, or dry powder", "recycledContentLithiumPct": 0.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-04-01", "repairabilityScore": {"overall": 1.0}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}, "publicViewPayload": {"id": "01a1a307-b7f5-72ed-abdb-036d686a2932", "status": "active", "batchId": "ODL-PORT-2026-0342", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.485874190Z", "materials": [{"name": "Nickel", "weightKg": 0.045, "recycledPct": 20.0, "countryOfOrigin": "DE"}, {"name": "Steel can", "weightKg": 0.03, "recycledPct": 60.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000069/21/72edabdb036d686a2932", "updatedAt": "2026-11-03T20:09:14.508496352Z", "co2ePerUnit": {"valueKg": 0.42}, "productName": "Odal Portable Power Pack AA NiMH (4-pack)", "publishedAt": "2026-11-03T20:09:14.508496352Z", "manufacturer": {"name": "Rheinpower Batterien GmbH", "address": "Industriestrasse 8, 47059 Duisburg, DE", "country": null, "didWebUrl": "https://rheinpower.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85073000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:14.508496352Z", "complianceResult": {"co2eScore": 0.42, "assessedAt": "2026-11-03T20:09:14.487810095Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "portable", "productGroup": "battery", "co2ePerUnitKg": 0.42, "ratedEnergyWh": 2.4, "batteryModelId": "RP-AA-NIMH-2000-4", "batteryWeightKg": 0.12, "nominalVoltageV": 1.2, "batteryChemistry": "NiMH", "ratedCapacityKwh": null, "manufacturingDate": "2026-02-20T00:00:00Z", "nominalCapacityAh": 2.0, "operatingTempMaxC": 45.0, "operatingTempMinC": -10.0, "productIdentifier": {"gtin": "09590000000069", "scheme": "gs1"}, "manufacturingPlace": "Duisburg, DE (plant DU-1)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 2000 mAh; rechargeable", "hazardousSubstances": [{"name": "Potassium hydroxide", "casNumber": "1310-58-3", "concentrationPct": 3.0}], "recycledContentLeadPct": 0.0, "wasteBatteryInformation": "Return spent cells to any retailer collection box. Do not incinerate. Producer registration DE-BATT-00457.", "recycledContentCobaltPct": 0.0, "recycledContentNickelPct": 18.0, "usableExtinguishingAgent": "Water, CO2, or dry powder", "recycledContentLithiumPct": 0.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-04-01", "repairabilityScore": {"overall": 1.0}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}}', '2026-11-03 20:09:14.526875+00', 'db3352d6758966a9722769b7a221543d0d52995535a68a09a64ef7162a291260', 'e5fdf5b5e732852b6b0d7d66c58ca7bf37cd71948c30080c6bb05cb604825f4d'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b835-70ee-8fb5-fef5874ad79d', '01a1a307-b7f5-72ed-abdb-036d686a2932', 'api-key', NULL, 'suspended', 'active', 'suspended', '01a1a307-b833-738b-828c-ffea6a909ecb', NULL, '{"reason": "upgrade fixture"}', '2026-11-03 20:09:14.54998+00', 'e5fdf5b5e732852b6b0d7d66c58ca7bf37cd71948c30080c6bb05cb604825f4d', 'f4ca2a9dd017d7d640cac47a6cb1b008dacf07935e0991f856533edfad054dc3'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b881-776a-b3b1-bbb8794db535', '01a1a307-b84c-7245-8576-ea67194809ea', 'api-key', NULL, 'published', NULL, 'active', '01a1a307-b848-73cd-8751-5730dbd0b98a', NULL, '{"fullViewPayload": {"id": "01a1a307-b84c-7245-8576-ea67194809ea", "status": "active", "batchId": "ODL-IND-LFP-2026-Q2-014", "version": 2, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.572051734Z", "materials": [{"name": "Lithium Iron Phosphate", "weightKg": 22.0, "recycledPct": 8.0, "countryOfOrigin": "DE"}, {"name": "Graphite", "weightKg": 9.5, "recycledPct": 6.0, "countryOfOrigin": "DE"}, {"name": "Steel enclosure", "weightKg": 12.0, "recycledPct": 82.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000021/21/71ac9c864696540c3b21", "updatedAt": "2026-11-03T20:09:14.585049272Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.573477185Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "LFP stationary pack, corrected", "publishedAt": "2026-11-03T20:09:14.585049272Z", "jwsSignature": null, "manufacturer": {"name": "Example Batteries GmbH", "address": "Prenzlauer Berg 12, 10405 Berlin, DE", "country": null, "didWebUrl": "https://batteries.example/.well-known/did.json"}, "productGroup": "battery", "supersedesId": "01a1a307-b5bd-71ac-9c86-4696540c3b21", "carrierSerial": "71ac9c864696540c3b21", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:14.585049272Z", "retentionLocked": true, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-05-20. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:14.573474747Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "industrial", "productGroup": "battery", "usageHistory": {"negativeEvents": [], "chargeDischargeCycles": 5}, "anodeMaterial": [{"name": "Graphite", "casNumber": "7782-42-5", "weightPct": 96.0}, {"name": "SBR/CMC binder", "casNumber": "9003-55-8", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 1.0}], "batteryStatus": "original", "ratedEnergyWh": 5120.0, "stateOfHealth": {"parameterSet": "stationaryOrLmt", "ohmicResistanceMohm": 9.1, "remainingCapacityPct": 99.6, "remainingPowerCapabilityPct": 99.4, "selfDischargeRatePctPerMonth": 1.4, "remainingRoundTripEfficiencyPct": 99.1}, "batteryModelId": "GC-ESS-LFP512-100", "powerLimitMaxW": 7500.0, "powerLimitMinW": 150.0, "safetyMeasures": "Isolate DC breaker before servicing. Insulated tools only. Ventilate enclosure before opening.", "batteryWeightKg": 46.5, "cathodeMaterial": [{"name": "LiFePO4", "casNumber": "15365-14-7", "weightPct": 95.0}, {"name": "PVDF binder", "casNumber": "24937-79-9", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 2.0}], "maximumVoltageV": 58.4, "minimalVoltageV": 40.0, "nominalVoltageV": 51.2, "batteryChemistry": "LFP", "ratedCapacityKwh": 5.12, "stateOfHealthPct": null, "manufacturingDate": "2026-03-02T00:00:00Z", "nominalCapacityAh": 100.0, "productIdentifier": {"gtin": "09590000000021", "scheme": "gs1"}, "testReportResults": "IEC 62619:2022 pass; IEC 63056:2020 pass; UN 38.3 pass; report GC-TR-2026-0301", "cycleLifeTestCRate": 0.33, "dynamicPerformance": {"capacityFadePct": 0.4, "ratedCapacityAh": 99.6, "expectedLifetimeCycles": 5980, "internalResistanceMohm": 9.1, "roundTripEfficiencyPct": 96.2}, "manufacturingPlace": "Berlin, DE (plant BLN-2)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 100 Ah / 5.12 kWh; chemistry LFP", "sparePartsContacts": "spares@batteries.example; +49 30 5550 1200", "electrolyteMaterial": [{"name": "Ethylene carbonate", "casNumber": "96-49-1", "weightPct": 38.0}, {"name": "Ethyl methyl carbonate", "casNumber": "623-53-0", "weightPct": 49.0}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "weightPct": 13.0}], "hazardousSubstances": [{"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.7}], "renewableContentPct": 22.0, "componentPartNumbers": ["GC-CELL-32700-LFP", "GC-BMS-16S-V2", "GC-ESS-ENCL-512"], "criticalRawMaterials": [{"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 2600.0, "countryOfOrigin": "AU"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 9500.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000021:ODL-IND-LFP-2026-Q2-014", "expectedLifetimeCycles": 6000, "wasteBatteryInformation": "Return to an authorised collection point. Do not incinerate or shred. Producer registration DE-BATT-00123.", "notInUseTemperatureRange": {"maxC": 50.0, "minC": -10.0}, "originalPowerCapabilityW": 5000.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Water or ABC dry powder; large volumes of water for thermal runaway", "euDeclarationOfConformity": "EU-DoC GC-2026-IND-0067 dated 2026-03-10, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://batteries.example/service/ess-lfp512/disassembly", "internalCellResistanceMohm": 0.6, "internalPackResistanceMohm": 9.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "IEC 61427-1:2013, 25 degC, 90% DoD", "initialRoundTripEfficiencyPct": 96.5, "commercialWarrantyPeriodMonths": 144, "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage profile", "roundTripEfficiencyAtHalfCycleLifePct": 92.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-05-20", "repairabilityScore": {"overall": 8.1}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}, "publicViewPayload": {"id": "01a1a307-b84c-7245-8576-ea67194809ea", "status": "active", "batchId": "ODL-IND-LFP-2026-Q2-014", "version": 2, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.572051734Z", "materials": [{"name": "Lithium Iron Phosphate", "weightKg": 22.0, "recycledPct": 8.0, "countryOfOrigin": "DE"}, {"name": "Graphite", "weightKg": 9.5, "recycledPct": 6.0, "countryOfOrigin": "DE"}, {"name": "Steel enclosure", "weightKg": 12.0, "recycledPct": 82.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000021/21/71ac9c864696540c3b21", "updatedAt": "2026-11-03T20:09:14.585049272Z", "co2ePerUnit": null, "productName": "LFP stationary pack, corrected", "publishedAt": "2026-11-03T20:09:14.585049272Z", "manufacturer": {"name": "Example Batteries GmbH", "address": "Prenzlauer Berg 12, 10405 Berlin, DE", "country": null, "didWebUrl": "https://batteries.example/.well-known/did.json"}, "productGroup": "battery", "supersedesId": "01a1a307-b5bd-71ac-9c86-4696540c3b21", "carrierSerial": "71ac9c864696540c3b21", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:14.585049272Z", "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-05-20. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:14.573474747Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "industrial", "productGroup": "battery", "ratedEnergyWh": 5120.0, "batteryModelId": "GC-ESS-LFP512-100", "powerLimitMaxW": 7500.0, "powerLimitMinW": 150.0, "batteryWeightKg": 46.5, "maximumVoltageV": 58.4, "minimalVoltageV": 40.0, "nominalVoltageV": 51.2, "batteryChemistry": "LFP", "ratedCapacityKwh": 5.12, "manufacturingDate": "2026-03-02T00:00:00Z", "nominalCapacityAh": 100.0, "productIdentifier": {"gtin": "09590000000021", "scheme": "gs1"}, "cycleLifeTestCRate": 0.33, "manufacturingPlace": "Berlin, DE (plant BLN-2)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 100 Ah / 5.12 kWh; chemistry LFP", "hazardousSubstances": [{"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.7}], "renewableContentPct": 22.0, "criticalRawMaterials": [{"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 2600.0, "countryOfOrigin": "AU"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 9500.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000021:ODL-IND-LFP-2026-Q2-014", "expectedLifetimeCycles": 6000, "wasteBatteryInformation": "Return to an authorised collection point. Do not incinerate or shred. Producer registration DE-BATT-00123.", "notInUseTemperatureRange": {"maxC": 50.0, "minC": -10.0}, "originalPowerCapabilityW": 5000.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Water or ABC dry powder; large volumes of water for thermal runaway", "euDeclarationOfConformity": "EU-DoC GC-2026-IND-0067 dated 2026-03-10, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "internalCellResistanceMohm": 0.6, "internalPackResistanceMohm": 9.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "IEC 61427-1:2013, 25 degC, 90% DoD", "initialRoundTripEfficiencyPct": 96.5, "commercialWarrantyPeriodMonths": 144, "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage profile", "roundTripEfficiencyAtHalfCycleLifePct": 92.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-05-20", "repairabilityScore": {"overall": 8.1}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}}', '2026-11-03 20:09:14.625817+00', '', '3318887c4b5421ba31f9955fc8e6fe8db0f4974393fcf5df3fbe75a0df3ccb70'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b890-742c-886e-c24978c52e95', '01a1a307-b5bd-71ac-9c86-4696540c3b21', 'api-key', NULL, 'superseded', 'active', 'superseded', '01a1a307-b848-73cd-8751-5730dbd0b98a', NULL, '{"reason": "upgrade fixture", "successorId": "01a1a307-b84c-7245-8576-ea67194809ea"}', '2026-11-03 20:09:14.640221+00', '1e39b1b817e0ea234ebe7bcae2c3ef5f4dcdac3070971a15e8a17551b51757db', 'dc0e878388e999394e27a025d9e4f97b2dbceb96fa00c10f47a9cfa52eb19be8'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b97f-7542-9c50-826b23d70e94', '01a1a307-b97c-73a6-99ea-eb004fceb13a', 'api-key', NULL, 'created', NULL, 'draft', '01a1a307-b94b-7257-afeb-20a80e0b9061', NULL, NULL, '2026-11-03 20:09:14.879347+00', '', 'e9fae599452b2013abae2a17acb363c28b7c29e9648c0a85fd519daf0e32c9ec'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b980-7041-98c0-45631fb541cb', '01a1a307-b97c-73a6-99ea-eb041612e7d1', 'api-key', NULL, 'created', NULL, 'draft', '01a1a307-b949-752f-90f7-4dc46b7f6d55', NULL, NULL, '2026-11-03 20:09:14.880278+00', '', 'b53e133ab11eea78707ad52cc846f56ea1aa081aaf488627581e075f5f5e46a8'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b97f-7542-9c50-826d17cdc9a5', '01a1a307-b97c-73a6-99ea-eb052b807483', 'api-key', NULL, 'created', NULL, 'draft', '01a1a307-b94c-70ae-b1d3-a7a0915f06a4', NULL, NULL, '2026-11-03 20:09:14.879929+00', '', '1ed0c446bd6e5da1ce1be12b2083c3f60e31876db2f211b564a9c0e9888bdb14'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b9c7-73cd-9558-57d271528aa2', '01a1a307-b97c-73a6-99ea-eb06f0520a8f', 'api-key', NULL, 'created', NULL, 'draft', '01a1a307-b94a-7392-a3e7-35e62d95dee3', NULL, NULL, '2026-11-03 20:09:14.951091+00', '', '9e2d1cdfb1b9d36963d390fb710a19dd184ae45313fd9dee7773280cbea6c992'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-b9cb-727a-9721-c322dfbe57e5', '01a1a307-b9c9-73a1-94d4-44a02ace7223', 'api-key', NULL, 'created', NULL, 'draft', '01a1a307-b94c-70ae-b1d3-a79fcb8c4239', NULL, NULL, '2026-11-03 20:09:14.955655+00', '', '4ba39313f4d4cfe79ebd8c686e7859688ecc3939bef5e138b5cce5cafb34ee65'); +INSERT INTO odal.passport_audit VALUES ('01a1a307-baaf-7194-aa5b-a4c7cd29d349', '01a1a307-b97c-73a6-99ea-eb004fceb13a', 'api-key', NULL, 'retired', 'draft', 'retired', '01a1a307-baa8-773c-bde3-083f2f517297', NULL, NULL, '2026-11-03 20:09:15.183748+00', 'e9fae599452b2013abae2a17acb363c28b7c29e9648c0a85fd519daf0e32c9ec', '980e83e420ee06c99546f66dcc35e9892a681b83b786803eb74310227ff0b1ec'); + + +-- +-- Data for Name: passport_transfer; Type: TABLE DATA; Schema: odal; Owner: - +-- + + + +-- +-- Data for Name: passport_version; Type: TABLE DATA; Schema: odal; Owner: - +-- + +INSERT INTO odal.passport_version VALUES ('01a1a307-b563-70ae-8891-ad9563c48a66', '01a1a307-b510-75c3-b5cc-1ccbddeb9787', '{"id": "01a1a307-b510-75c3-b5cc-1ccbddeb9787", "status": "draft", "batchId": "ODL-IND-NMC-2026-Q2-001", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:13.744378034Z", "materials": [{"name": "Lithium Nickel Manganese Cobalt Oxide", "weightKg": 14.2, "recycledPct": 11.0, "countryOfOrigin": "DE"}, {"name": "Graphite", "weightKg": 6.4, "recycledPct": 5.0, "countryOfOrigin": "DE"}, {"name": "Aluminium enclosure", "weightKg": 8.1, "recycledPct": 78.0, "countryOfOrigin": "DE"}], "qrCodeUrl": null, "updatedAt": "2026-11-03T20:09:13.744378553Z", "lintResult": {"assessedAt": "2026-11-03T20:09:13.747933990Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Odal Industrial Battery NMC-48", "publishedAt": null, "jwsSignature": null, "manufacturer": {"name": "Example Batteries GmbH", "address": "Prenzlauer Berg 12, 10405 Berlin, DE", "country": null, "didWebUrl": "https://batteries.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionLocked": false, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-06-01. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:13.747931997Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "industrial", "productGroup": "battery", "usageHistory": {"negativeEvents": [], "chargeDischargeCycles": 12}, "anodeMaterial": [{"name": "Graphite", "casNumber": "7782-42-5", "weightPct": 95.0}, {"name": "SBR/CMC binder", "casNumber": "9003-55-8", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 2.0}], "batteryStatus": "original", "ratedEnergyWh": 4320.0, "stateOfHealth": {"parameterSet": "stationaryOrLmt", "ohmicResistanceMohm": 14.2, "remainingCapacityPct": 99.3, "remainingPowerCapabilityPct": 99.0, "selfDischargeRatePctPerMonth": 2.1, "remainingRoundTripEfficiencyPct": 98.5}, "batteryModelId": "GC-IND-NMC48-320", "powerLimitMaxW": 6000.0, "powerLimitMinW": 200.0, "safetyMeasures": "De-energise and wait 15 min before opening. Class 0 gloves. No conductive tools.", "batteryWeightKg": 32.0, "cathodeMaterial": [{"name": "LiNi0.6Mn0.2Co0.2O2", "casNumber": "346417-97-8", "weightPct": 96.0}, {"name": "PVDF binder", "casNumber": "24937-79-9", "weightPct": 2.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 2.0}], "maximumVoltageV": 54.6, "minimalVoltageV": 42.0, "nominalVoltageV": 48.0, "batteryChemistry": "NMC", "ratedCapacityKwh": 4.32, "stateOfHealthPct": null, "manufacturingDate": "2026-04-15T00:00:00Z", "nominalCapacityAh": 90.0, "productIdentifier": {"gtin": "09590000000014", "scheme": "gs1"}, "testReportResults": "IEC 62619:2022 pass; UN 38.3 pass; report GC-TR-2026-0412", "cycleLifeTestCRate": 0.5, "dynamicPerformance": {"capacityFadePct": 0.7, "ratedCapacityAh": 89.4, "expectedLifetimeCycles": 2790, "internalResistanceMohm": 14.2, "roundTripEfficiencyPct": 94.6}, "manufacturingPlace": "Berlin, DE (plant BLN-1)", "markingInformation": "Separate-collection symbol (crossed-out wheeled bin) per Art. 13(4); capacity 90 Ah / 4.32 kWh", "sparePartsContacts": "spares@batteries.example; +49 30 5550 1200", "electrolyteMaterial": [{"name": "Ethylene carbonate", "casNumber": "96-49-1", "weightPct": 40.0}, {"name": "Dimethyl carbonate", "casNumber": "616-38-6", "weightPct": 47.0}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "weightPct": 13.0}], "hazardousSubstances": [{"name": "Nickel sulfate", "casNumber": "7786-81-4", "concentrationPct": 1.2}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.8}], "renewableContentPct": 18.0, "componentPartNumbers": ["GC-CELL-2170-NMC", "GC-BMS-48-V3", "GC-PACK-ENCL-320"], "criticalRawMaterials": [{"name": "Cobalt", "casNumber": "7440-48-4", "weightGrams": 1450.0, "countryOfOrigin": "CD"}, {"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 2100.0, "countryOfOrigin": "CL"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 6400.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000014:ODL-IND-NMC-2026-Q2-001", "expectedLifetimeCycles": 2800, "wasteBatteryInformation": "Return to an authorised collection point or installer. Do not shred. Contains lithium; fire risk if damaged. Producer registration DE-BATT-00123.", "notInUseTemperatureRange": {"maxC": 45.0, "minC": -20.0}, "originalPowerCapabilityW": 4600.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Water mist or ABC dry powder; do not use CO2", "euDeclarationOfConformity": "EU-DoC GC-2026-IND-0091 dated 2026-04-20, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://batteries.example/service/ind-nmc48/disassembly", "internalCellResistanceMohm": 0.9, "internalPackResistanceMohm": 14.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "IEC 61427-1:2013, 25 degC, 80% DoD", "initialRoundTripEfficiencyPct": 95.0, "commercialWarrantyPeriodMonths": 120, "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage profile", "roundTripEfficiencyAtHalfCycleLifePct": 89.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-06-01", "repairabilityScore": {"overall": 7.5}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}', '2026-11-03 20:09:13.827842+00', '2026-11-03 20:09:13.82635+00'); +INSERT INTO odal.passport_version VALUES ('01a1a307-b635-75e1-84ee-541a815ff5ba', '01a1a307-b5bd-71ac-9c86-4696540c3b21', '{"id": "01a1a307-b5bd-71ac-9c86-4696540c3b21", "status": "draft", "batchId": "ODL-IND-LFP-2026-Q2-014", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:13.917135531Z", "materials": [{"name": "Lithium Iron Phosphate", "weightKg": 22.0, "recycledPct": 8.0, "countryOfOrigin": "DE"}, {"name": "Graphite", "weightKg": 9.5, "recycledPct": 6.0, "countryOfOrigin": "DE"}, {"name": "Steel enclosure", "weightKg": 12.0, "recycledPct": 82.0, "countryOfOrigin": "DE"}], "qrCodeUrl": null, "updatedAt": "2026-11-03T20:09:13.917136654Z", "lintResult": {"assessedAt": "2026-11-03T20:09:13.921707611Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Odal Stationary Storage Battery LFP-51.2", "publishedAt": null, "jwsSignature": null, "manufacturer": {"name": "Example Batteries GmbH", "address": "Prenzlauer Berg 12, 10405 Berlin, DE", "country": null, "didWebUrl": "https://batteries.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionLocked": false, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-05-20. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:13.921704049Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "industrial", "productGroup": "battery", "usageHistory": {"negativeEvents": [], "chargeDischargeCycles": 5}, "anodeMaterial": [{"name": "Graphite", "casNumber": "7782-42-5", "weightPct": 96.0}, {"name": "SBR/CMC binder", "casNumber": "9003-55-8", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 1.0}], "batteryStatus": "original", "ratedEnergyWh": 5120.0, "stateOfHealth": {"parameterSet": "stationaryOrLmt", "ohmicResistanceMohm": 9.1, "remainingCapacityPct": 99.6, "remainingPowerCapabilityPct": 99.4, "selfDischargeRatePctPerMonth": 1.4, "remainingRoundTripEfficiencyPct": 99.1}, "batteryModelId": "GC-ESS-LFP512-100", "powerLimitMaxW": 7500.0, "powerLimitMinW": 150.0, "safetyMeasures": "Isolate DC breaker before servicing. Insulated tools only. Ventilate enclosure before opening.", "batteryWeightKg": 46.5, "cathodeMaterial": [{"name": "LiFePO4", "casNumber": "15365-14-7", "weightPct": 95.0}, {"name": "PVDF binder", "casNumber": "24937-79-9", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 2.0}], "maximumVoltageV": 58.4, "minimalVoltageV": 40.0, "nominalVoltageV": 51.2, "batteryChemistry": "LFP", "ratedCapacityKwh": 5.12, "stateOfHealthPct": null, "manufacturingDate": "2026-03-02T00:00:00Z", "nominalCapacityAh": 100.0, "productIdentifier": {"gtin": "09590000000021", "scheme": "gs1"}, "testReportResults": "IEC 62619:2022 pass; IEC 63056:2020 pass; UN 38.3 pass; report GC-TR-2026-0301", "cycleLifeTestCRate": 0.33, "dynamicPerformance": {"capacityFadePct": 0.4, "ratedCapacityAh": 99.6, "expectedLifetimeCycles": 5980, "internalResistanceMohm": 9.1, "roundTripEfficiencyPct": 96.2}, "manufacturingPlace": "Berlin, DE (plant BLN-2)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 100 Ah / 5.12 kWh; chemistry LFP", "sparePartsContacts": "spares@batteries.example; +49 30 5550 1200", "electrolyteMaterial": [{"name": "Ethylene carbonate", "casNumber": "96-49-1", "weightPct": 38.0}, {"name": "Ethyl methyl carbonate", "casNumber": "623-53-0", "weightPct": 49.0}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "weightPct": 13.0}], "hazardousSubstances": [{"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.7}], "renewableContentPct": 22.0, "componentPartNumbers": ["GC-CELL-32700-LFP", "GC-BMS-16S-V2", "GC-ESS-ENCL-512"], "criticalRawMaterials": [{"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 2600.0, "countryOfOrigin": "AU"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 9500.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000021:ODL-IND-LFP-2026-Q2-014", "expectedLifetimeCycles": 6000, "wasteBatteryInformation": "Return to an authorised collection point. Do not incinerate or shred. Producer registration DE-BATT-00123.", "notInUseTemperatureRange": {"maxC": 50.0, "minC": -10.0}, "originalPowerCapabilityW": 5000.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Water or ABC dry powder; large volumes of water for thermal runaway", "euDeclarationOfConformity": "EU-DoC GC-2026-IND-0067 dated 2026-03-10, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://batteries.example/service/ess-lfp512/disassembly", "internalCellResistanceMohm": 0.6, "internalPackResistanceMohm": 9.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "IEC 61427-1:2013, 25 degC, 90% DoD", "initialRoundTripEfficiencyPct": 96.5, "commercialWarrantyPeriodMonths": 144, "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage profile", "roundTripEfficiencyAtHalfCycleLifePct": 92.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-05-20", "repairabilityScore": {"overall": 8.1}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}', '2026-11-03 20:09:14.037198+00', '2026-11-03 20:09:14.03597+00'); +INSERT INTO odal.passport_version VALUES ('01a1a307-b6ee-77c8-a33c-9f03a5246e47', '01a1a307-b67c-706a-a55e-689712fcd122', '{"id": "01a1a307-b67c-706a-a55e-689712fcd122", "status": "draft", "batchId": "ODL-IND-PB-2026-Q1-007", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.108085427Z", "materials": [{"name": "Lead", "weightKg": 38.0, "recycledPct": 82.0, "countryOfOrigin": "DE"}, {"name": "Sulfuric acid electrolyte", "weightKg": 9.0, "recycledPct": 0.0, "countryOfOrigin": "DE"}, {"name": "Polypropylene case", "weightKg": 3.2, "recycledPct": 45.0, "countryOfOrigin": "DE"}], "qrCodeUrl": null, "updatedAt": "2026-11-03T20:09:14.108086420Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.112531026Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Odal Industrial Standby Battery 12V-200Ah (VRLA)", "publishedAt": null, "jwsSignature": null, "manufacturer": {"name": "Rheinpower Batterien GmbH", "address": "Industriestrasse 8, 47059 Duisburg, DE", "country": null, "didWebUrl": "https://rheinpower.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85072000", "schemaVersion": "2.8.0", "retentionLocked": false, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-02-10. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:14.112526945Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "industrial", "hazardSymbol": "lead", "productGroup": "battery", "anodeMaterial": [{"name": "Sponge lead (negative active material)", "casNumber": "7439-92-1", "weightPct": 100.0}], "batteryStatus": "original", "ratedEnergyWh": 2400.0, "batteryModelId": "RP-VRLA-12-200", "powerLimitMaxW": 2400.0, "powerLimitMinW": 50.0, "safetyMeasures": "Acid hazard. Face shield and acid-resistant gloves. Do not tip. Vent hydrogen before servicing.", "batteryWeightKg": 52.0, "cathodeMaterial": [{"name": "Lead dioxide (positive active material)", "casNumber": "1309-60-0", "weightPct": 100.0}], "maximumVoltageV": 14.4, "minimalVoltageV": 10.5, "nominalVoltageV": 12.0, "batteryChemistry": "lead-acid", "ratedCapacityKwh": 2.4, "stateOfHealthPct": null, "manufacturingDate": "2026-01-08T00:00:00Z", "nominalCapacityAh": 200.0, "productIdentifier": {"gtin": "09590000000038", "scheme": "gs1"}, "testReportResults": "IEC 60896-21/22:2004 pass; UN 2800 (non-spillable) pass; report RP-TR-2026-0021", "cycleLifeTestCRate": 0.1, "manufacturingPlace": "Duisburg, DE (plant DU-3)", "markingInformation": "Separate-collection symbol per Art. 13(4); Pb symbol per Art. 13(5); capacity 200 Ah / 12 V", "sparePartsContacts": "service@rheinpower.example; +49 203 5550 800", "electrolyteMaterial": [{"name": "Sulfuric acid (aqueous, ~37%)", "casNumber": "7664-93-9", "weightPct": 37.0}, {"name": "Water", "casNumber": "7732-18-5", "weightPct": 63.0}], "hazardousSubstances": [{"name": "Lead", "casNumber": "7439-92-1", "concentrationPct": 68.0}, {"name": "Sulfuric acid", "casNumber": "7664-93-9", "concentrationPct": 17.0}], "renewableContentPct": 2.0, "componentPartNumbers": ["RP-PLATE-POS-200", "RP-PLATE-NEG-200", "RP-CASE-12-200", "RP-VALVE-VRLA"], "criticalRawMaterials": [{"name": "Antimony", "casNumber": "7440-36-0", "weightGrams": 380.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000038:ODL-IND-PB-2026-Q1-007", "expectedLifetimeCycles": 1200, "wasteBatteryInformation": "Lead-acid battery - return to retailer or authorised collector for recycling. Never dispose in household waste. Producer registration DE-BATT-00457.", "notInUseTemperatureRange": {"maxC": 40.0, "minC": -15.0}, "originalPowerCapabilityW": 1800.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "CO2, ABC dry powder, or water; neutralise acid spills with soda ash", "euDeclarationOfConformity": "EU-DoC RP-2026-IND-0014 dated 2026-01-20, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://rheinpower.example/service/vrla-12-200/disassembly", "internalCellResistanceMohm": 3.5, "internalPackResistanceMohm": 4.2, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "IEC 60896-21/22, 25 degC, 50% DoD", "initialRoundTripEfficiencyPct": 82.0, "commercialWarrantyPeriodMonths": 60, "notInUseTemperatureReferenceTest": "IEC 60896-21:2004 storage clause", "roundTripEfficiencyAtHalfCycleLifePct": 74.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-02-10", "repairabilityScore": {"overall": 3.0}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}', '2026-11-03 20:09:14.222713+00', '2026-11-03 20:09:14.221426+00'); +INSERT INTO odal.passport_version VALUES ('01a1a307-b74d-7098-874e-399026b97a12', '01a1a307-b70c-70c1-b4a3-a616d161e997', '{"id": "01a1a307-b70c-70c1-b4a3-a616d161e997", "status": "draft", "batchId": "VBX-EV-2026-0421", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.252942043Z", "materials": [{"name": "Lithium Nickel Manganese Cobalt Oxide (NMC 811)", "weightKg": 210.0, "recycledPct": 13.0, "countryOfOrigin": "DE"}, {"name": "Graphite/silicon anode", "weightKg": 95.0, "recycledPct": 4.0, "countryOfOrigin": "DE"}, {"name": "Aluminium pack housing", "weightKg": 85.0, "recycledPct": 71.0, "countryOfOrigin": "DE"}], "qrCodeUrl": null, "updatedAt": "2026-11-03T20:09:14.252942520Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.255216167Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Voltex EV Traction Battery 75 kWh", "publishedAt": null, "jwsSignature": null, "manufacturer": {"name": "Voltex Battery GmbH", "address": "Werkstrasse 5, 85055 Ingolstadt, DE", "country": null, "didWebUrl": "https://voltex.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionLocked": false, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-07-01. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:14.255214416Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "ev", "productGroup": "battery", "usageHistory": {"stateOfCharge": [{"recordedAt": "2026-07-15T09:00:00Z", "stateOfChargePct": 82.0}, {"recordedAt": "2026-07-22T09:00:00Z", "stateOfChargePct": 64.0}], "negativeEvents": [], "chargeDischargeCycles": 8}, "anodeMaterial": [{"name": "Graphite", "casNumber": "7782-42-5", "weightPct": 90.0}, {"name": "Silicon oxide", "casNumber": "10097-28-6", "weightPct": 6.0}, {"name": "SBR/CMC binder", "casNumber": "9003-55-8", "weightPct": 4.0}], "batteryStatus": "original", "ratedEnergyWh": 75000.0, "stateOfHealth": {"socePct": 99.1, "parameterSet": "electricVehicle"}, "batteryModelId": "VBX-EV75-NMC811-P4S96", "powerLimitMaxW": 220000.0, "powerLimitMinW": 500.0, "safetyMeasures": "High-voltage (>60 V DC). HV-qualified personnel only. Service disconnect plug removed and locked out. Insulated PPE class 00.", "batteryWeightKg": 468.0, "cathodeMaterial": [{"name": "LiNi0.8Mn0.1Co0.1O2", "casNumber": "346417-97-8", "weightPct": 96.5}, {"name": "PVDF binder", "casNumber": "24937-79-9", "weightPct": 2.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 1.5}], "maximumVoltageV": 403.2, "minimalVoltageV": 270.0, "nominalVoltageV": 355.0, "batteryChemistry": "NMC", "ratedCapacityKwh": 75.0, "stateOfHealthPct": null, "manufacturingDate": "2026-05-11T00:00:00Z", "nominalCapacityAh": 211.0, "productIdentifier": {"gtin": "09590000000045", "scheme": "gs1"}, "testReportResults": "IEC 62660-2:2018 pass; ISO 6469-1:2019 pass; UN 38.3 pass; report VBX-TR-2026-0507", "cycleLifeTestCRate": 1.0, "dynamicPerformance": {"powerW": 148000.0, "powerFadePct": 0.6, "capacityFadePct": 0.9, "ratedCapacityAh": 209.0, "expectedLifetimeYears": 9.5, "expectedLifetimeCycles": 1785, "internalResistanceMohm": 42.4, "roundTripEfficiencyPct": 93.6, "internalResistanceIncreasePct": 1.0}, "manufacturingPlace": "Ingolstadt, DE (plant ING-1)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 75 kWh; HV warning label", "sparePartsContacts": "hv-service@voltex.example; +49 841 5550 400 (certified HV workshops only)", "electrolyteMaterial": [{"name": "Ethylene carbonate", "casNumber": "96-49-1", "weightPct": 30.0}, {"name": "Ethyl methyl carbonate", "casNumber": "623-53-0", "weightPct": 55.0}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "weightPct": 13.0}, {"name": "Vinylene carbonate additive", "casNumber": "872-36-6", "weightPct": 2.0}], "hazardousSubstances": [{"name": "Nickel sulfate", "casNumber": "7786-81-4", "concentrationPct": 2.4}, {"name": "Cobalt sulfate", "casNumber": "10124-43-3", "concentrationPct": 0.9}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.9}], "renewableContentPct": 16.0, "componentPartNumbers": ["VBX-CELL-4680-NMC811", "VBX-MODULE-24S", "VBX-BMS-MASTER-V4", "VBX-PACK-HV-75"], "criticalRawMaterials": [{"name": "Cobalt", "casNumber": "7440-48-4", "weightGrams": 5200.0, "countryOfOrigin": "CD"}, {"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 8100.0, "countryOfOrigin": "CL"}, {"name": "Nickel", "casNumber": "7440-02-0", "weightGrams": 32000.0, "countryOfOrigin": "ID"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 74000.0, "countryOfOrigin": "MZ"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000045:VBX-EV-2026-0421", "expectedLifetimeCycles": 1800, "wasteBatteryInformation": "EV battery - handled by the vehicle manufacturer take-back scheme. Do not open or discharge. Contact hv-service@voltex.example. Producer registration DE-BATT-00981.", "notInUseTemperatureRange": {"maxC": 55.0, "minC": -30.0}, "originalPowerCapabilityW": 150000.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Large volumes of water for cooling; do not use CO2 on cell fire", "euDeclarationOfConformity": "EU-DoC VBX-2026-EV-0142 dated 2026-05-18, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://voltex.example/service/ev75/disassembly", "internalCellResistanceMohm": 1.1, "internalPackResistanceMohm": 42.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "UNECE GTR 22 / IEC 62660-1:2018, 25 degC", "initialRoundTripEfficiencyPct": 94.0, "commercialWarrantyPeriodMonths": 96, "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage profile", "capacityThresholdForExhaustionPct": 70.0, "roundTripEfficiencyAtHalfCycleLifePct": 88.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-07-01", "repairabilityScore": {"overall": 5.5}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}', '2026-11-03 20:09:14.317736+00', '2026-11-03 20:09:14.316459+00'); +INSERT INTO odal.passport_version VALUES ('01a1a307-b7de-73af-b48e-63187a9acbff', '01a1a307-b792-7764-a50c-b10fe65bf932', '{"id": "01a1a307-b792-7764-a50c-b10fe65bf932", "status": "draft", "batchId": "VBX-LMT-2026-0188", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.386370527Z", "materials": [{"name": "Lithium Iron Phosphate", "weightKg": 1.4, "recycledPct": 7.0, "countryOfOrigin": "DE"}, {"name": "Graphite", "weightKg": 0.6, "recycledPct": 3.0, "countryOfOrigin": "DE"}, {"name": "ABS/PC housing", "weightKg": 0.5, "recycledPct": 30.0, "countryOfOrigin": "DE"}], "qrCodeUrl": null, "updatedAt": "2026-11-03T20:09:14.386371142Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.389558295Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Voltex e-Bike Battery 36V 14Ah", "publishedAt": null, "jwsSignature": null, "manufacturer": {"name": "Voltex Battery GmbH", "address": "Werkstrasse 5, 85055 Ingolstadt, DE", "country": null, "didWebUrl": "https://voltex.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionLocked": false, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-06-15. Ruleset ''battery-recycled-content-art8-3'' applies from 2036-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:14.389555743Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "lmt", "productGroup": "battery", "usageHistory": {"chargeDischargeCycles": 6}, "anodeMaterial": [{"name": "Graphite", "casNumber": "7782-42-5", "weightPct": 96.0}, {"name": "SBR/CMC binder", "casNumber": "9003-55-8", "weightPct": 4.0}], "batteryStatus": "original", "ratedEnergyWh": 504.0, "stateOfHealth": {"parameterSet": "stationaryOrLmt", "remainingCapacityPct": 99.3, "remainingPowerCapabilityPct": 98.8, "selfDischargeRatePctPerMonth": 3.0}, "batteryModelId": "VBX-LMT36-14-10S", "powerLimitMaxW": 750.0, "powerLimitMinW": 10.0, "safetyMeasures": "Do not open the pack. Replace via authorised dealer. Keep away from heat above 60 degC.", "batteryWeightKg": 2.6, "cathodeMaterial": [{"name": "LiFePO4", "casNumber": "15365-14-7", "weightPct": 95.0}, {"name": "PVDF binder", "casNumber": "24937-79-9", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 2.0}], "maximumVoltageV": 42.0, "minimalVoltageV": 30.0, "nominalVoltageV": 36.0, "batteryChemistry": "LFP", "ratedCapacityKwh": 0.504, "stateOfHealthPct": null, "manufacturingDate": "2026-05-05T00:00:00Z", "nominalCapacityAh": 14.0, "productIdentifier": {"gtin": "09590000000052", "scheme": "gs1"}, "testReportResults": "EN 50604-1:2016 pass; UN 38.3 pass; report VBX-TR-2026-0311", "cycleLifeTestCRate": 1.0, "dynamicPerformance": {"capacityFadePct": 0.7, "ratedCapacityAh": 13.9, "expectedLifetimeCycles": 995, "internalResistanceMohm": 95.4, "roundTripEfficiencyPct": 94.7}, "manufacturingPlace": "Ingolstadt, DE (plant ING-2)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 14 Ah / 504 Wh; 36 V LMT", "sparePartsContacts": "spares@voltex.example; +49 841 5550 410", "electrolyteMaterial": [{"name": "Ethylene carbonate", "casNumber": "96-49-1", "weightPct": 38.0}, {"name": "Dimethyl carbonate", "casNumber": "616-38-6", "weightPct": 49.0}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "weightPct": 13.0}], "hazardousSubstances": [{"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.6}], "renewableContentPct": 12.0, "componentPartNumbers": ["VBX-CELL-21700-LFP", "VBX-BMS-10S-V2", "VBX-LMT-ENCL-36"], "criticalRawMaterials": [{"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 165.0, "countryOfOrigin": "AU"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 600.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000052:VBX-LMT-2026-0188", "expectedLifetimeCycles": 1000, "wasteBatteryInformation": "Return the LMT battery to any bicycle retailer or municipal collection point. Do not place in household waste. Producer registration DE-BATT-00981.", "notInUseTemperatureRange": {"maxC": 45.0, "minC": -10.0}, "originalPowerCapabilityW": 500.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Water or ABC dry powder; submerge in water if safe to do so", "euDeclarationOfConformity": "EU-DoC VBX-2026-LMT-0088 dated 2026-05-12, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://voltex.example/service/lmt36-14/disassembly", "internalCellResistanceMohm": 8.0, "internalPackResistanceMohm": 95.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "EN 50604-1:2016, 25 degC, 100% DoD", "initialRoundTripEfficiencyPct": 95.0, "commercialWarrantyPeriodMonths": 24, "notInUseTemperatureReferenceTest": "EN 50604-1:2016 storage clause", "roundTripEfficiencyAtHalfCycleLifePct": 90.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-06-15", "repairabilityScore": {"overall": 6.0}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}', '2026-11-03 20:09:14.462099+00', '2026-11-03 20:09:14.460817+00'); +INSERT INTO odal.passport_version VALUES ('01a1a307-b820-729c-a3b8-62ce65393d6f', '01a1a307-b7f5-72ed-abdb-036d686a2932', '{"id": "01a1a307-b7f5-72ed-abdb-036d686a2932", "status": "draft", "batchId": "ODL-PORT-2026-0342", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.485874190Z", "materials": [{"name": "Nickel", "weightKg": 0.045, "recycledPct": 20.0, "countryOfOrigin": "DE"}, {"name": "Steel can", "weightKg": 0.03, "recycledPct": 60.0, "countryOfOrigin": "DE"}], "qrCodeUrl": null, "updatedAt": "2026-11-03T20:09:14.485874640Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.487812197Z", "packVersion": "1.1.0"}, "co2ePerUnit": {"valueKg": 0.42}, "productName": "Odal Portable Power Pack AA NiMH (4-pack)", "publishedAt": null, "jwsSignature": null, "manufacturer": {"name": "Rheinpower Batterien GmbH", "address": "Industriestrasse 8, 47059 Duisburg, DE", "country": null, "didWebUrl": "https://rheinpower.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85073000", "schemaVersion": "2.8.0", "retentionLocked": false, "complianceResult": {"co2eScore": 0.42, "assessedAt": "2026-11-03T20:09:14.487810095Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "portable", "productGroup": "battery", "batteryStatus": "original", "co2ePerUnitKg": 0.42, "ratedEnergyWh": 2.4, "batteryModelId": "RP-AA-NIMH-2000-4", "batteryWeightKg": 0.12, "nominalVoltageV": 1.2, "batteryChemistry": "NiMH", "ratedCapacityKwh": null, "stateOfHealthPct": null, "manufacturingDate": "2026-02-20T00:00:00Z", "nominalCapacityAh": 2.0, "operatingTempMaxC": 45.0, "operatingTempMinC": -10.0, "productIdentifier": {"gtin": "09590000000069", "scheme": "gs1"}, "manufacturingPlace": "Duisburg, DE (plant DU-1)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 2000 mAh; rechargeable", "hazardousSubstances": [{"name": "Potassium hydroxide", "casNumber": "1310-58-3", "concentrationPct": 3.0}], "recycledContentLeadPct": 0.0, "wasteBatteryInformation": "Return spent cells to any retailer collection box. Do not incinerate. Producer registration DE-BATT-00457.", "recycledContentCobaltPct": 0.0, "recycledContentNickelPct": 18.0, "usableExtinguishingAgent": "Water, CO2, or dry powder", "recycledContentLithiumPct": 0.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-04-01", "repairabilityScore": {"overall": 1.0}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}', '2026-11-03 20:09:14.528312+00', '2026-11-03 20:09:14.527408+00'); +INSERT INTO odal.passport_version VALUES ('01a1a307-b837-70ac-8a1b-b9cc9e3da89a', '01a1a307-b7f5-72ed-abdb-036d686a2932', '{"id": "01a1a307-b7f5-72ed-abdb-036d686a2932", "status": "active", "batchId": "ODL-PORT-2026-0342", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.485874190Z", "materials": [{"name": "Nickel", "weightKg": 0.045, "recycledPct": 20.0, "countryOfOrigin": "DE"}, {"name": "Steel can", "weightKg": 0.03, "recycledPct": 60.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000069/21/72edabdb036d686a2932", "updatedAt": "2026-11-03T20:09:14.508496352Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.487812197Z", "packVersion": "1.1.0"}, "co2ePerUnit": {"valueKg": 0.42}, "productName": "Odal Portable Power Pack AA NiMH (4-pack)", "publishedAt": "2026-11-03T20:09:14.508496352Z", "jwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtUE9SVC0yMDI2LTAzNDIiLCJjbzJlUGVyVW5pdCI6eyJ2YWx1ZUtnIjowLjQyfSwiY29tbW9kaXR5Q29kZSI6Ijg1MDczMDAwIiwiY29tcGxpYW5jZVJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODc4MTAwOTVaIiwiY28yZVNjb3JlIjowLjQyLCJjb21wbGlhbmNlU3RhdHVzIjoiUEFTU1RIUk9VR0hfTk9fVkFMSURBVElPTiIsInJlY3ljbGVkQ29udGVudFBjdCI6bnVsbCwicmVwYWlyYWJpbGl0eUluZGV4IjpudWxsfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODU4NzQxOTBaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjdmNS03MmVkLWFiZGItMDM2ZDY4NmEyOTMyIiwiandzU2lnbmF0dXJlIjpudWxsLCJsaW50UmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjQ4NzgxMjE5N1oiLCJwYWNrVmVyc2lvbiI6IjEuMS4wIn0sIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiSW5kdXN0cmllc3RyYXNzZSA4LCA0NzA1OSBEdWlzYnVyZywgREUiLCJjb3VudHJ5IjpudWxsLCJkaWRXZWJVcmwiOiJodHRwczovL3JoZWlucG93ZXIuZXhhbXBsZS8ud2VsbC1rbm93bi9kaWQuanNvbiIsIm5hbWUiOiJSaGVpbnBvd2VyIEJhdHRlcmllbiBHbWJIIn0sIm1hdGVyaWFscyI6W3siY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiTmlja2VsIiwicmVjeWNsZWRQY3QiOjIwLCJ3ZWlnaHRLZyI6MC4wNDV9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiU3RlZWwgY2FuIiwicmVjeWNsZWRQY3QiOjYwLCJ3ZWlnaHRLZyI6MC4wM31dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInBsYWNlZE9uTWFya2V0RGF0ZSI6IjIwMjYtMDQtMDEiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJiYXR0ZXJ5Q2hlbWlzdHJ5IjoiTmlNSCIsImJhdHRlcnlNb2RlbElkIjoiUlAtQUEtTklNSC0yMDAwLTQiLCJiYXR0ZXJ5U3RhdHVzIjoib3JpZ2luYWwiLCJiYXR0ZXJ5VHlwZSI6InBvcnRhYmxlIiwiYmF0dGVyeVdlaWdodEtnIjowLjEyLCJjbzJlUGVyVW5pdEtnIjowLjQyLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiIxMzEwLTU4LTMiLCJjb25jZW50cmF0aW9uUGN0IjozLCJuYW1lIjoiUG90YXNzaXVtIGh5ZHJveGlkZSJ9XSwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTAyLTIwVDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkR1aXNidXJnLCBERSAocGxhbnQgRFUtMSkiLCJtYXJraW5nSW5mb3JtYXRpb24iOiJTZXBhcmF0ZS1jb2xsZWN0aW9uIHN5bWJvbCBwZXIgQXJ0LiAxMyg0KTsgY2FwYWNpdHkgMjAwMCBtQWg7IHJlY2hhcmdlYWJsZSIsIm5vbWluYWxDYXBhY2l0eUFoIjoyLCJub21pbmFsVm9sdGFnZVYiOjEuMiwib3BlcmF0aW5nVGVtcE1heEMiOjQ1LCJvcGVyYXRpbmdUZW1wTWluQyI6LTEwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDY5Iiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOm51bGwsInJhdGVkRW5lcmd5V2giOjIuNCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjowLCJyZWN5Y2xlZENvbnRlbnRMZWFkUGN0IjowLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjowLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOjE4LCJzdGF0ZU9mSGVhbHRoUGN0IjpudWxsLCJ1c2FibGVFeHRpbmd1aXNoaW5nQWdlbnQiOiJXYXRlciwgQ08yLCBvciBkcnkgcG93ZGVyIiwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJSZXR1cm4gc3BlbnQgY2VsbHMgdG8gYW55IHJldGFpbGVyIGNvbGxlY3Rpb24gYm94LiBEbyBub3QgaW5jaW5lcmF0ZS4gUHJvZHVjZXIgcmVnaXN0cmF0aW9uIERFLUJBVFQtMDA0NTcuIn0sInByb2R1Y3ROYW1lIjoiT2RhbCBQb3J0YWJsZSBQb3dlciBQYWNrIEFBIE5pTUggKDQtcGFjaykiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNTA4NDk2MzUyWiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDA2OS8yMS83MmVkYWJkYjAzNmQ2ODZhMjkzMiIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjoxfSwicmV0ZW50aW9uTG9ja2VkIjp0cnVlLCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuNTA4NDk2MzUyWiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNTA4NDk2MzUyWiIsInZlcnNpb24iOjF9.j3mIBOcpst6hdk-oskF3Fe891VAWjN_Th78F8zv0jxl6oOIRH4o6YCkVtnw3-hY8Db4ICsweUisPJx-qfqVTDw", "manufacturer": {"name": "Rheinpower Batterien GmbH", "address": "Industriestrasse 8, 47059 Duisburg, DE", "country": null, "didWebUrl": "https://rheinpower.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85073000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:14.508496352Z", "retentionLocked": true, "complianceResult": {"co2eScore": 0.42, "assessedAt": "2026-11-03T20:09:14.487810095Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "portable", "productGroup": "battery", "batteryStatus": "original", "co2ePerUnitKg": 0.42, "ratedEnergyWh": 2.4, "batteryModelId": "RP-AA-NIMH-2000-4", "batteryWeightKg": 0.12, "nominalVoltageV": 1.2, "batteryChemistry": "NiMH", "ratedCapacityKwh": null, "stateOfHealthPct": null, "manufacturingDate": "2026-02-20T00:00:00Z", "nominalCapacityAh": 2.0, "operatingTempMaxC": 45.0, "operatingTempMinC": -10.0, "productIdentifier": {"gtin": "09590000000069", "scheme": "gs1"}, "manufacturingPlace": "Duisburg, DE (plant DU-1)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 2000 mAh; rechargeable", "hazardousSubstances": [{"name": "Potassium hydroxide", "casNumber": "1310-58-3", "concentrationPct": 3.0}], "recycledContentLeadPct": 0.0, "wasteBatteryInformation": "Return spent cells to any retailer collection box. Do not incinerate. Producer registration DE-BATT-00457.", "recycledContentCobaltPct": 0.0, "recycledContentNickelPct": 18.0, "usableExtinguishingAgent": "Water, CO2, or dry powder", "recycledContentLithiumPct": 0.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-04-01", "publicJwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtUE9SVC0yMDI2LTAzNDIiLCJjbzJlUGVyVW5pdCI6eyJ2YWx1ZUtnIjowLjQyfSwiY29tbW9kaXR5Q29kZSI6Ijg1MDczMDAwIiwiY29tcGxpYW5jZVJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODc4MTAwOTVaIiwiY28yZVNjb3JlIjowLjQyLCJjb21wbGlhbmNlU3RhdHVzIjoiUEFTU1RIUk9VR0hfTk9fVkFMSURBVElPTiIsInJlY3ljbGVkQ29udGVudFBjdCI6bnVsbCwicmVwYWlyYWJpbGl0eUluZGV4IjpudWxsfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODU4NzQxOTBaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjdmNS03MmVkLWFiZGItMDM2ZDY4NmEyOTMyIiwibWFudWZhY3R1cmVyIjp7ImFkZHJlc3MiOiJJbmR1c3RyaWVzdHJhc3NlIDgsIDQ3MDU5IER1aXNidXJnLCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vcmhlaW5wb3dlci5leGFtcGxlLy53ZWxsLWtub3duL2RpZC5qc29uIiwibmFtZSI6IlJoZWlucG93ZXIgQmF0dGVyaWVuIEdtYkgifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJOaWNrZWwiLCJyZWN5Y2xlZFBjdCI6MjAsIndlaWdodEtnIjowLjA0NX0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJTdGVlbCBjYW4iLCJyZWN5Y2xlZFBjdCI6NjAsIndlaWdodEtnIjowLjAzfV0sIm9wZXJhdG9ySWRlbnRpZmllciI6IkRFMTIzNDU2Nzg5IiwicGxhY2VkT25NYXJrZXREYXRlIjoiMjAyNi0wNC0wMSIsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImJhdHRlcnlDaGVtaXN0cnkiOiJOaU1IIiwiYmF0dGVyeU1vZGVsSWQiOiJSUC1BQS1OSU1ILTIwMDAtNCIsImJhdHRlcnlUeXBlIjoicG9ydGFibGUiLCJiYXR0ZXJ5V2VpZ2h0S2ciOjAuMTIsImNvMmVQZXJVbml0S2ciOjAuNDIsImhhemFyZG91c1N1YnN0YW5jZXMiOlt7ImNhc051bWJlciI6IjEzMTAtNTgtMyIsImNvbmNlbnRyYXRpb25QY3QiOjMsIm5hbWUiOiJQb3Rhc3NpdW0gaHlkcm94aWRlIn1dLCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDItMjBUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiRHVpc2J1cmcsIERFIChwbGFudCBEVS0xKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIHBlciBBcnQuIDEzKDQpOyBjYXBhY2l0eSAyMDAwIG1BaDsgcmVjaGFyZ2VhYmxlIiwibm9taW5hbENhcGFjaXR5QWgiOjIsIm5vbWluYWxWb2x0YWdlViI6MS4yLCJvcGVyYXRpbmdUZW1wTWF4QyI6NDUsIm9wZXJhdGluZ1RlbXBNaW5DIjotMTAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwNjkiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6bnVsbCwicmF0ZWRFbmVyZ3lXaCI6Mi40LCJyZWN5Y2xlZENvbnRlbnRDb2JhbHRQY3QiOjAsInJlY3ljbGVkQ29udGVudExlYWRQY3QiOjAsInJlY3ljbGVkQ29udGVudExpdGhpdW1QY3QiOjAsInJlY3ljbGVkQ29udGVudE5pY2tlbFBjdCI6MTgsInVzYWJsZUV4dGluZ3Vpc2hpbmdBZ2VudCI6IldhdGVyLCBDTzIsIG9yIGRyeSBwb3dkZXIiLCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IlJldHVybiBzcGVudCBjZWxscyB0byBhbnkgcmV0YWlsZXIgY29sbGVjdGlvbiBib3guIERvIG5vdCBpbmNpbmVyYXRlLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDQ1Ny4ifSwicHJvZHVjdE5hbWUiOiJPZGFsIFBvcnRhYmxlIFBvd2VyIFBhY2sgQUEgTmlNSCAoNC1wYWNrKSIsInB1Ymxpc2hlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC41MDg0OTYzNTJaIiwicXJDb2RlVXJsIjoiaHR0cDovLzEyNy4wLjAuMToxLzAxLzA5NTkwMDAwMDAwMDY5LzIxLzcyZWRhYmRiMDM2ZDY4NmEyOTMyIiwicmVwYWlyYWJpbGl0eVNjb3JlIjp7Im92ZXJhbGwiOjF9LCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuNTA4NDk2MzUyWiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNTA4NDk2MzUyWiIsInZlcnNpb24iOjF9.nsHCI8VFxvrnPxNxs-KuFtnh06-6R2ITh3vodOGyNjFqJAwhNfoiNANUs_yIMeuaOaxL1rwR5tqzL6F3pc6CBw", "repairabilityScore": {"overall": 1.0}, "disclosureSignatures": {"public+restricted+conformity": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtUE9SVC0yMDI2LTAzNDIiLCJjbzJlUGVyVW5pdCI6eyJ2YWx1ZUtnIjowLjQyfSwiY29tbW9kaXR5Q29kZSI6Ijg1MDczMDAwIiwiY29tcGxpYW5jZVJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODc4MTAwOTVaIiwiY28yZVNjb3JlIjowLjQyLCJjb21wbGlhbmNlU3RhdHVzIjoiUEFTU1RIUk9VR0hfTk9fVkFMSURBVElPTiIsInJlY3ljbGVkQ29udGVudFBjdCI6bnVsbCwicmVwYWlyYWJpbGl0eUluZGV4IjpudWxsfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODU4NzQxOTBaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjdmNS03MmVkLWFiZGItMDM2ZDY4NmEyOTMyIiwibGludFJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODc4MTIxOTdaIiwicGFja1ZlcnNpb24iOiIxLjEuMCJ9LCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IkluZHVzdHJpZXN0cmFzc2UgOCwgNDcwNTkgRHVpc2J1cmcsIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly9yaGVpbnBvd2VyLmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiUmhlaW5wb3dlciBCYXR0ZXJpZW4gR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6Ik5pY2tlbCIsInJlY3ljbGVkUGN0IjoyMCwid2VpZ2h0S2ciOjAuMDQ1fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IlN0ZWVsIGNhbiIsInJlY3ljbGVkUGN0Ijo2MCwid2VpZ2h0S2ciOjAuMDN9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA0LTAxIiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYmF0dGVyeUNoZW1pc3RyeSI6Ik5pTUgiLCJiYXR0ZXJ5TW9kZWxJZCI6IlJQLUFBLU5JTUgtMjAwMC00IiwiYmF0dGVyeVR5cGUiOiJwb3J0YWJsZSIsImJhdHRlcnlXZWlnaHRLZyI6MC4xMiwiY28yZVBlclVuaXRLZyI6MC40MiwiaGF6YXJkb3VzU3Vic3RhbmNlcyI6W3siY2FzTnVtYmVyIjoiMTMxMC01OC0zIiwiY29uY2VudHJhdGlvblBjdCI6MywibmFtZSI6IlBvdGFzc2l1bSBoeWRyb3hpZGUifV0sIm1hbnVmYWN0dXJpbmdEYXRlIjoiMjAyNi0wMi0yMFQwMDowMDowMFoiLCJtYW51ZmFjdHVyaW5nUGxhY2UiOiJEdWlzYnVyZywgREUgKHBsYW50IERVLTEpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDIwMDAgbUFoOyByZWNoYXJnZWFibGUiLCJub21pbmFsQ2FwYWNpdHlBaCI6Miwibm9taW5hbFZvbHRhZ2VWIjoxLjIsIm9wZXJhdGluZ1RlbXBNYXhDIjo0NSwib3BlcmF0aW5nVGVtcE1pbkMiOi0xMCwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RJZGVudGlmaWVyIjp7Imd0aW4iOiIwOTU5MDAwMDAwMDA2OSIsInNjaGVtZSI6ImdzMSJ9LCJyYXRlZENhcGFjaXR5S3doIjpudWxsLCJyYXRlZEVuZXJneVdoIjoyLjQsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6MCwicmVjeWNsZWRDb250ZW50TGVhZFBjdCI6MCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6MCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjoxOCwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIsIENPMiwgb3IgZHJ5IHBvd2RlciIsIndhc3RlQmF0dGVyeUluZm9ybWF0aW9uIjoiUmV0dXJuIHNwZW50IGNlbGxzIHRvIGFueSByZXRhaWxlciBjb2xsZWN0aW9uIGJveC4gRG8gbm90IGluY2luZXJhdGUuIFByb2R1Y2VyIHJlZ2lzdHJhdGlvbiBERS1CQVRULTAwNDU3LiJ9LCJwcm9kdWN0TmFtZSI6Ik9kYWwgUG9ydGFibGUgUG93ZXIgUGFjayBBQSBOaU1IICg0LXBhY2spIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjUwODQ5NjM1MloiLCJxckNvZGVVcmwiOiJodHRwOi8vMTI3LjAuMC4xOjEvMDEvMDk1OTAwMDAwMDAwNjkvMjEvNzJlZGFiZGIwMzZkNjg2YTI5MzIiLCJyZXBhaXJhYmlsaXR5U2NvcmUiOnsib3ZlcmFsbCI6MX0sInJldGVudGlvbkxvY2tlZCI6dHJ1ZSwicmV0ZW50aW9uVW50aWwiOiIyMDM2LTExLTAzVDIwOjA5OjE0LjUwODQ5NjM1MloiLCJzY2hlbWFWZXJzaW9uIjoiMi44LjAiLCJzdGF0dXMiOiJhY3RpdmUiLCJ1cGRhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjUwODQ5NjM1MloiLCJ2ZXJzaW9uIjoxfQ.SYz2C-w7lDIwXW5Z-7YBbigmsJm5XikhQ2DGrwc_1AQKlA3zhl1OSbi8FD7dLmXHJLE_dtWj4aoy5i5osk_yAQ", "public+restricted+individual": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtUE9SVC0yMDI2LTAzNDIiLCJjbzJlUGVyVW5pdCI6eyJ2YWx1ZUtnIjowLjQyfSwiY29tbW9kaXR5Q29kZSI6Ijg1MDczMDAwIiwiY29tcGxpYW5jZVJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODc4MTAwOTVaIiwiY28yZVNjb3JlIjowLjQyLCJjb21wbGlhbmNlU3RhdHVzIjoiUEFTU1RIUk9VR0hfTk9fVkFMSURBVElPTiIsInJlY3ljbGVkQ29udGVudFBjdCI6bnVsbCwicmVwYWlyYWJpbGl0eUluZGV4IjpudWxsfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODU4NzQxOTBaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjdmNS03MmVkLWFiZGItMDM2ZDY4NmEyOTMyIiwibGludFJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODc4MTIxOTdaIiwicGFja1ZlcnNpb24iOiIxLjEuMCJ9LCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IkluZHVzdHJpZXN0cmFzc2UgOCwgNDcwNTkgRHVpc2J1cmcsIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly9yaGVpbnBvd2VyLmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiUmhlaW5wb3dlciBCYXR0ZXJpZW4gR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6Ik5pY2tlbCIsInJlY3ljbGVkUGN0IjoyMCwid2VpZ2h0S2ciOjAuMDQ1fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IlN0ZWVsIGNhbiIsInJlY3ljbGVkUGN0Ijo2MCwid2VpZ2h0S2ciOjAuMDN9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA0LTAxIiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYmF0dGVyeUNoZW1pc3RyeSI6Ik5pTUgiLCJiYXR0ZXJ5TW9kZWxJZCI6IlJQLUFBLU5JTUgtMjAwMC00IiwiYmF0dGVyeVN0YXR1cyI6Im9yaWdpbmFsIiwiYmF0dGVyeVR5cGUiOiJwb3J0YWJsZSIsImJhdHRlcnlXZWlnaHRLZyI6MC4xMiwiY28yZVBlclVuaXRLZyI6MC40MiwiaGF6YXJkb3VzU3Vic3RhbmNlcyI6W3siY2FzTnVtYmVyIjoiMTMxMC01OC0zIiwiY29uY2VudHJhdGlvblBjdCI6MywibmFtZSI6IlBvdGFzc2l1bSBoeWRyb3hpZGUifV0sIm1hbnVmYWN0dXJpbmdEYXRlIjoiMjAyNi0wMi0yMFQwMDowMDowMFoiLCJtYW51ZmFjdHVyaW5nUGxhY2UiOiJEdWlzYnVyZywgREUgKHBsYW50IERVLTEpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDIwMDAgbUFoOyByZWNoYXJnZWFibGUiLCJub21pbmFsQ2FwYWNpdHlBaCI6Miwibm9taW5hbFZvbHRhZ2VWIjoxLjIsIm9wZXJhdGluZ1RlbXBNYXhDIjo0NSwib3BlcmF0aW5nVGVtcE1pbkMiOi0xMCwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RJZGVudGlmaWVyIjp7Imd0aW4iOiIwOTU5MDAwMDAwMDA2OSIsInNjaGVtZSI6ImdzMSJ9LCJyYXRlZENhcGFjaXR5S3doIjpudWxsLCJyYXRlZEVuZXJneVdoIjoyLjQsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6MCwicmVjeWNsZWRDb250ZW50TGVhZFBjdCI6MCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6MCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjoxOCwic3RhdGVPZkhlYWx0aFBjdCI6bnVsbCwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIsIENPMiwgb3IgZHJ5IHBvd2RlciIsIndhc3RlQmF0dGVyeUluZm9ybWF0aW9uIjoiUmV0dXJuIHNwZW50IGNlbGxzIHRvIGFueSByZXRhaWxlciBjb2xsZWN0aW9uIGJveC4gRG8gbm90IGluY2luZXJhdGUuIFByb2R1Y2VyIHJlZ2lzdHJhdGlvbiBERS1CQVRULTAwNDU3LiJ9LCJwcm9kdWN0TmFtZSI6Ik9kYWwgUG9ydGFibGUgUG93ZXIgUGFjayBBQSBOaU1IICg0LXBhY2spIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjUwODQ5NjM1MloiLCJxckNvZGVVcmwiOiJodHRwOi8vMTI3LjAuMC4xOjEvMDEvMDk1OTAwMDAwMDAwNjkvMjEvNzJlZGFiZGIwMzZkNjg2YTI5MzIiLCJyZXBhaXJhYmlsaXR5U2NvcmUiOnsib3ZlcmFsbCI6MX0sInJldGVudGlvblVudGlsIjoiMjAzNi0xMS0wM1QyMDowOToxNC41MDg0OTYzNTJaIiwic2NoZW1hVmVyc2lvbiI6IjIuOC4wIiwic3RhdHVzIjoiYWN0aXZlIiwidXBkYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC41MDg0OTYzNTJaIiwidmVyc2lvbiI6MX0.DaSoJ8XHNHjSftulWk5S2kA-oFan_UCu5hN9HcpbFpf9MwMPRoNnCkIEXyHlMBMbfS8AhyojyJX-qoRYS495CQ"}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}', '2026-11-03 20:09:14.551202+00', '2026-11-03 20:09:14.550274+00'); +INSERT INTO odal.passport_version VALUES ('01a1a307-b883-72d1-af66-bc9ea8efb8bd', '01a1a307-b84c-7245-8576-ea67194809ea', '{"id": "01a1a307-b84c-7245-8576-ea67194809ea", "status": "draft", "batchId": "ODL-IND-LFP-2026-Q2-014", "version": 2, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.572051734Z", "materials": [{"name": "Lithium Iron Phosphate", "weightKg": 22.0, "recycledPct": 8.0, "countryOfOrigin": "DE"}, {"name": "Graphite", "weightKg": 9.5, "recycledPct": 6.0, "countryOfOrigin": "DE"}, {"name": "Steel enclosure", "weightKg": 12.0, "recycledPct": 82.0, "countryOfOrigin": "DE"}], "qrCodeUrl": null, "updatedAt": "2026-11-03T20:09:14.572052716Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.573477185Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "LFP stationary pack, corrected", "publishedAt": null, "jwsSignature": null, "manufacturer": {"name": "Example Batteries GmbH", "address": "Prenzlauer Berg 12, 10405 Berlin, DE", "country": null, "didWebUrl": "https://batteries.example/.well-known/did.json"}, "productGroup": "battery", "supersedesId": "01a1a307-b5bd-71ac-9c86-4696540c3b21", "carrierSerial": "71ac9c864696540c3b21", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionLocked": false, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-05-20. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:14.573474747Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "industrial", "productGroup": "battery", "usageHistory": {"negativeEvents": [], "chargeDischargeCycles": 5}, "anodeMaterial": [{"name": "Graphite", "casNumber": "7782-42-5", "weightPct": 96.0}, {"name": "SBR/CMC binder", "casNumber": "9003-55-8", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 1.0}], "batteryStatus": "original", "ratedEnergyWh": 5120.0, "stateOfHealth": {"parameterSet": "stationaryOrLmt", "ohmicResistanceMohm": 9.1, "remainingCapacityPct": 99.6, "remainingPowerCapabilityPct": 99.4, "selfDischargeRatePctPerMonth": 1.4, "remainingRoundTripEfficiencyPct": 99.1}, "batteryModelId": "GC-ESS-LFP512-100", "powerLimitMaxW": 7500.0, "powerLimitMinW": 150.0, "safetyMeasures": "Isolate DC breaker before servicing. Insulated tools only. Ventilate enclosure before opening.", "batteryWeightKg": 46.5, "cathodeMaterial": [{"name": "LiFePO4", "casNumber": "15365-14-7", "weightPct": 95.0}, {"name": "PVDF binder", "casNumber": "24937-79-9", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 2.0}], "maximumVoltageV": 58.4, "minimalVoltageV": 40.0, "nominalVoltageV": 51.2, "batteryChemistry": "LFP", "ratedCapacityKwh": 5.12, "stateOfHealthPct": null, "manufacturingDate": "2026-03-02T00:00:00Z", "nominalCapacityAh": 100.0, "productIdentifier": {"gtin": "09590000000021", "scheme": "gs1"}, "testReportResults": "IEC 62619:2022 pass; IEC 63056:2020 pass; UN 38.3 pass; report GC-TR-2026-0301", "cycleLifeTestCRate": 0.33, "dynamicPerformance": {"capacityFadePct": 0.4, "ratedCapacityAh": 99.6, "expectedLifetimeCycles": 5980, "internalResistanceMohm": 9.1, "roundTripEfficiencyPct": 96.2}, "manufacturingPlace": "Berlin, DE (plant BLN-2)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 100 Ah / 5.12 kWh; chemistry LFP", "sparePartsContacts": "spares@batteries.example; +49 30 5550 1200", "electrolyteMaterial": [{"name": "Ethylene carbonate", "casNumber": "96-49-1", "weightPct": 38.0}, {"name": "Ethyl methyl carbonate", "casNumber": "623-53-0", "weightPct": 49.0}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "weightPct": 13.0}], "hazardousSubstances": [{"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.7}], "renewableContentPct": 22.0, "componentPartNumbers": ["GC-CELL-32700-LFP", "GC-BMS-16S-V2", "GC-ESS-ENCL-512"], "criticalRawMaterials": [{"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 2600.0, "countryOfOrigin": "AU"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 9500.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000021:ODL-IND-LFP-2026-Q2-014", "expectedLifetimeCycles": 6000, "wasteBatteryInformation": "Return to an authorised collection point. Do not incinerate or shred. Producer registration DE-BATT-00123.", "notInUseTemperatureRange": {"maxC": 50.0, "minC": -10.0}, "originalPowerCapabilityW": 5000.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Water or ABC dry powder; large volumes of water for thermal runaway", "euDeclarationOfConformity": "EU-DoC GC-2026-IND-0067 dated 2026-03-10, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://batteries.example/service/ess-lfp512/disassembly", "internalCellResistanceMohm": 0.6, "internalPackResistanceMohm": 9.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "IEC 61427-1:2013, 25 degC, 90% DoD", "initialRoundTripEfficiencyPct": 96.5, "commercialWarrantyPeriodMonths": 144, "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage profile", "roundTripEfficiencyAtHalfCycleLifePct": 92.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-05-20", "repairabilityScore": {"overall": 8.1}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}', '2026-11-03 20:09:14.627748+00', '2026-11-03 20:09:14.626531+00'); +INSERT INTO odal.passport_version VALUES ('01a1a307-b892-736f-8f24-95130fb37ab4', '01a1a307-b5bd-71ac-9c86-4696540c3b21', '{"id": "01a1a307-b5bd-71ac-9c86-4696540c3b21", "status": "active", "batchId": "ODL-IND-LFP-2026-Q2-014", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:13.917135531Z", "materials": [{"name": "Lithium Iron Phosphate", "weightKg": 22.0, "recycledPct": 8.0, "countryOfOrigin": "DE"}, {"name": "Graphite", "weightKg": 9.5, "recycledPct": 6.0, "countryOfOrigin": "DE"}, {"name": "Steel enclosure", "weightKg": 12.0, "recycledPct": 82.0, "countryOfOrigin": "DE"}], "qrCodeUrl": "http://127.0.0.1:1/01/09590000000021/21/71ac9c864696540c3b21", "updatedAt": "2026-11-03T20:09:13.987954878Z", "lintResult": {"assessedAt": "2026-11-03T20:09:13.921707611Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Odal Stationary Storage Battery LFP-51.2", "publishedAt": "2026-11-03T20:09:13.987954878Z", "jwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELUxGUC0yMDI2LVEyLTAxNCIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjkyMTcwNDA0OVoiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNS0yMC4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45MTcxMzU1MzFaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjViZC03MWFjLTljODYtNDY5NjU0MGMzYjIxIiwiandzU2lnbmF0dXJlIjpudWxsLCJsaW50UmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjkyMTcwNzYxMVoiLCJwYWNrVmVyc2lvbiI6IjEuMS4wIn0sIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiUHJlbnpsYXVlciBCZXJnIDEyLCAxMDQwNSBCZXJsaW4sIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly9iYXR0ZXJpZXMuZXhhbXBsZS8ud2VsbC1rbm93bi9kaWQuanNvbiIsIm5hbWUiOiJFeGFtcGxlIEJhdHRlcmllcyBHbWJIIn0sIm1hdGVyaWFscyI6W3siY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiTGl0aGl1bSBJcm9uIFBob3NwaGF0ZSIsInJlY3ljbGVkUGN0Ijo4LCJ3ZWlnaHRLZyI6MjJ9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiR3JhcGhpdGUiLCJyZWN5Y2xlZFBjdCI6Niwid2VpZ2h0S2ciOjkuNX0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJTdGVlbCBlbmNsb3N1cmUiLCJyZWN5Y2xlZFBjdCI6ODIsIndlaWdodEtnIjoxMn1dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInBsYWNlZE9uTWFya2V0RGF0ZSI6IjIwMjYtMDUtMjAiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJhbm9kZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJuYW1lIjoiR3JhcGhpdGUiLCJ3ZWlnaHRQY3QiOjk2fSx7ImNhc051bWJlciI6IjkwMDMtNTUtOCIsIm5hbWUiOiJTQlIvQ01DIGJpbmRlciIsIndlaWdodFBjdCI6M30seyJjYXNOdW1iZXIiOiIxMzMzLTg2LTQiLCJuYW1lIjoiQ29uZHVjdGl2ZSBjYXJib24gYmxhY2siLCJ3ZWlnaHRQY3QiOjF9XSwiYmF0dGVyeUNoZW1pc3RyeSI6IkxGUCIsImJhdHRlcnlNb2RlbElkIjoiR0MtRVNTLUxGUDUxMi0xMDAiLCJiYXR0ZXJ5UGFzc3BvcnROdW1iZXIiOiJVUk46T0RMOkJBVFQ6MDk1OTAwMDAwMDAwMjE6T0RMLUlORC1MRlAtMjAyNi1RMi0wMTQiLCJiYXR0ZXJ5U3RhdHVzIjoib3JpZ2luYWwiLCJiYXR0ZXJ5VHlwZSI6ImluZHVzdHJpYWwiLCJiYXR0ZXJ5V2VpZ2h0S2ciOjQ2LjUsImNhdGhvZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiMTUzNjUtMTQtNyIsIm5hbWUiOiJMaUZlUE80Iiwid2VpZ2h0UGN0Ijo5NX0seyJjYXNOdW1iZXIiOiIyNDkzNy03OS05IiwibmFtZSI6IlBWREYgYmluZGVyIiwid2VpZ2h0UGN0IjozfSx7ImNhc051bWJlciI6IjEzMzMtODYtNCIsIm5hbWUiOiJDb25kdWN0aXZlIGNhcmJvbiBibGFjayIsIndlaWdodFBjdCI6Mn1dLCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjE0NCwiY29tcG9uZW50UGFydE51bWJlcnMiOlsiR0MtQ0VMTC0zMjcwMC1MRlAiLCJHQy1CTVMtMTZTLVYyIiwiR0MtRVNTLUVOQ0wtNTEyIl0sImNyaXRpY2FsUmF3TWF0ZXJpYWxzIjpbeyJjYXNOdW1iZXIiOiI3NDM5LTkzLTIiLCJjb3VudHJ5T2ZPcmlnaW4iOiJBVSIsIm5hbWUiOiJMaXRoaXVtIiwid2VpZ2h0R3JhbXMiOjI2MDB9LHsiY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwiY291bnRyeU9mT3JpZ2luIjoiQ04iLCJuYW1lIjoiTmF0dXJhbCBncmFwaGl0ZSIsIndlaWdodEdyYW1zIjo5NTAwfV0sImN5Y2xlTGlmZVRlc3RDUmF0ZSI6MC4zMywiZGlzYXNzZW1ibHlJbnN0cnVjdGlvbnNVcmwiOiJodHRwczovL2JhdHRlcmllcy5leGFtcGxlL3NlcnZpY2UvZXNzLWxmcDUxMi9kaXNhc3NlbWJseSIsImR5bmFtaWNQZXJmb3JtYW5jZSI6eyJjYXBhY2l0eUZhZGVQY3QiOjAuNCwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6NTk4MCwiaW50ZXJuYWxSZXNpc3RhbmNlTW9obSI6OS4xLCJyYXRlZENhcGFjaXR5QWgiOjk5LjYsInJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk2LjJ9LCJlbGVjdHJvbHl0ZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI5Ni00OS0xIiwibmFtZSI6IkV0aHlsZW5lIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6Mzh9LHsiY2FzTnVtYmVyIjoiNjIzLTUzLTAiLCJuYW1lIjoiRXRoeWwgbWV0aHlsIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6NDl9LHsiY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUiLCJ3ZWlnaHRQY3QiOjEzfV0sImV1RGVjbGFyYXRpb25PZkNvbmZvcm1pdHkiOiJFVS1Eb0MgR0MtMjAyNi1JTkQtMDA2NyBkYXRlZCAyMDI2LTAzLTEwLCBwZXIgUmVndWxhdGlvbiAoRVUpIDIwMjMvMTU0MiBBcnQuIDE4IiwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6NjAwMCwiZXhwZWN0ZWRMaWZldGltZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjE0MjctMToyMDEzLCAyNSBkZWdDLCA5MCUgRG9EIiwiaGF6YXJkb3VzU3Vic3RhbmNlcyI6W3siY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsImNvbmNlbnRyYXRpb25QY3QiOjAuNywibmFtZSI6IkxpdGhpdW0gaGV4YWZsdW9yb3Bob3NwaGF0ZSJ9XSwiaW5pdGlhbFJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk2LjUsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjowLjYsImludGVybmFsUGFja1Jlc2lzdGFuY2VNb2htIjo5LCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDMtMDJUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiQmVybGluLCBERSAocGxhbnQgQkxOLTIpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDEwMCBBaCAvIDUuMTIga1doOyBjaGVtaXN0cnkgTEZQIiwibWF4aW11bVZvbHRhZ2VWIjo1OC40LCJtaW5pbWFsVm9sdGFnZVYiOjQwLCJub21pbmFsQ2FwYWNpdHlBaCI6MTAwLCJub21pbmFsVm9sdGFnZVYiOjUxLjIsIm5vdEluVXNlVGVtcGVyYXR1cmVSYW5nZSI6eyJtYXhDIjo1MCwibWluQyI6LTEwfSwibm90SW5Vc2VUZW1wZXJhdHVyZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjI2NjAtMToyMDE4IHN0b3JhZ2UgcHJvZmlsZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NTAwMCwicG93ZXJMaW1pdE1heFciOjc1MDAsInBvd2VyTGltaXRNaW5XIjoxNTAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwMjEiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6NS4xMiwicmF0ZWRFbmVyZ3lXaCI6NTEyMCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjIyLCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo5Miwic2FmZXR5TWVhc3VyZXMiOiJJc29sYXRlIERDIGJyZWFrZXIgYmVmb3JlIHNlcnZpY2luZy4gSW5zdWxhdGVkIHRvb2xzIG9ubHkuIFZlbnRpbGF0ZSBlbmNsb3N1cmUgYmVmb3JlIG9wZW5pbmcuIiwic3BhcmVQYXJ0c0NvbnRhY3RzIjoic3BhcmVzQGJhdHRlcmllcy5leGFtcGxlOyArNDkgMzAgNTU1MCAxMjAwIiwic3RhdGVPZkhlYWx0aCI6eyJvaG1pY1Jlc2lzdGFuY2VNb2htIjo5LjEsInBhcmFtZXRlclNldCI6InN0YXRpb25hcnlPckxtdCIsInJlbWFpbmluZ0NhcGFjaXR5UGN0Ijo5OS42LCJyZW1haW5pbmdQb3dlckNhcGFiaWxpdHlQY3QiOjk5LjQsInJlbWFpbmluZ1JvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk5LjEsInNlbGZEaXNjaGFyZ2VSYXRlUGN0UGVyTW9udGgiOjEuNH0sInN0YXRlT2ZIZWFsdGhQY3QiOm51bGwsInRlc3RSZXBvcnRSZXN1bHRzIjoiSUVDIDYyNjE5OjIwMjIgcGFzczsgSUVDIDYzMDU2OjIwMjAgcGFzczsgVU4gMzguMyBwYXNzOyByZXBvcnQgR0MtVFItMjAyNi0wMzAxIiwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIgb3IgQUJDIGRyeSBwb3dkZXI7IGxhcmdlIHZvbHVtZXMgb2Ygd2F0ZXIgZm9yIHRoZXJtYWwgcnVuYXdheSIsInVzYWdlSGlzdG9yeSI6eyJjaGFyZ2VEaXNjaGFyZ2VDeWNsZXMiOjUsIm5lZ2F0aXZlRXZlbnRzIjpbXX0sIndhc3RlQmF0dGVyeUluZm9ybWF0aW9uIjoiUmV0dXJuIHRvIGFuIGF1dGhvcmlzZWQgY29sbGVjdGlvbiBwb2ludC4gRG8gbm90IGluY2luZXJhdGUgb3Igc2hyZWQuIFByb2R1Y2VyIHJlZ2lzdHJhdGlvbiBERS1CQVRULTAwMTIzLiJ9LCJwcm9kdWN0TmFtZSI6Ik9kYWwgU3RhdGlvbmFyeSBTdG9yYWdlIEJhdHRlcnkgTEZQLTUxLjIiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTMuOTg3OTU0ODc4WiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDAyMS8yMS83MWFjOWM4NjQ2OTY1NDBjM2IyMSIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjo4LjF9LCJyZXRlbnRpb25Mb2NrZWQiOnRydWUsInJldGVudGlvblVudGlsIjoiMjAzNi0xMS0wM1QyMDowOToxMy45ODc5NTQ4NzhaIiwic2NoZW1hVmVyc2lvbiI6IjIuOC4wIiwic3RhdHVzIjoiYWN0aXZlIiwidXBkYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45ODc5NTQ4NzhaIiwidmVyc2lvbiI6MX0.inyiRNW7RsdQojuWFAIjW07Rr6HezmP9kWPsCPYLA50HXEpLHhUYEXWReJH9D2lYoALCO20rUyU-mgcStk9GDQ", "manufacturer": {"name": "Example Batteries GmbH", "address": "Prenzlauer Berg 12, 10405 Berlin, DE", "country": null, "didWebUrl": "https://batteries.example/.well-known/did.json"}, "productGroup": "battery", "commodityCode": "85076000", "schemaVersion": "2.8.0", "retentionUntil": "2036-11-03T20:09:13.987954878Z", "retentionLocked": true, "complianceResult": {"warnings": [{"code": "battery.recycled_content.not_yet_binding", "field": "/placedOnMarketDate", "message": "No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-05-20. Ruleset ''battery-recycled-content-art8-2'' applies from 2031-08-18."}], "co2eScore": null, "assessedAt": "2026-11-03T20:09:13.921704049Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"batteryType": "industrial", "productGroup": "battery", "usageHistory": {"negativeEvents": [], "chargeDischargeCycles": 5}, "anodeMaterial": [{"name": "Graphite", "casNumber": "7782-42-5", "weightPct": 96.0}, {"name": "SBR/CMC binder", "casNumber": "9003-55-8", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 1.0}], "batteryStatus": "original", "ratedEnergyWh": 5120.0, "stateOfHealth": {"parameterSet": "stationaryOrLmt", "ohmicResistanceMohm": 9.1, "remainingCapacityPct": 99.6, "remainingPowerCapabilityPct": 99.4, "selfDischargeRatePctPerMonth": 1.4, "remainingRoundTripEfficiencyPct": 99.1}, "batteryModelId": "GC-ESS-LFP512-100", "powerLimitMaxW": 7500.0, "powerLimitMinW": 150.0, "safetyMeasures": "Isolate DC breaker before servicing. Insulated tools only. Ventilate enclosure before opening.", "batteryWeightKg": 46.5, "cathodeMaterial": [{"name": "LiFePO4", "casNumber": "15365-14-7", "weightPct": 95.0}, {"name": "PVDF binder", "casNumber": "24937-79-9", "weightPct": 3.0}, {"name": "Conductive carbon black", "casNumber": "1333-86-4", "weightPct": 2.0}], "maximumVoltageV": 58.4, "minimalVoltageV": 40.0, "nominalVoltageV": 51.2, "batteryChemistry": "LFP", "ratedCapacityKwh": 5.12, "stateOfHealthPct": null, "manufacturingDate": "2026-03-02T00:00:00Z", "nominalCapacityAh": 100.0, "productIdentifier": {"gtin": "09590000000021", "scheme": "gs1"}, "testReportResults": "IEC 62619:2022 pass; IEC 63056:2020 pass; UN 38.3 pass; report GC-TR-2026-0301", "cycleLifeTestCRate": 0.33, "dynamicPerformance": {"capacityFadePct": 0.4, "ratedCapacityAh": 99.6, "expectedLifetimeCycles": 5980, "internalResistanceMohm": 9.1, "roundTripEfficiencyPct": 96.2}, "manufacturingPlace": "Berlin, DE (plant BLN-2)", "markingInformation": "Separate-collection symbol per Art. 13(4); capacity 100 Ah / 5.12 kWh; chemistry LFP", "sparePartsContacts": "spares@batteries.example; +49 30 5550 1200", "electrolyteMaterial": [{"name": "Ethylene carbonate", "casNumber": "96-49-1", "weightPct": 38.0}, {"name": "Ethyl methyl carbonate", "casNumber": "623-53-0", "weightPct": 49.0}, {"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "weightPct": 13.0}], "hazardousSubstances": [{"name": "Lithium hexafluorophosphate", "casNumber": "21324-40-3", "concentrationPct": 0.7}], "renewableContentPct": 22.0, "componentPartNumbers": ["GC-CELL-32700-LFP", "GC-BMS-16S-V2", "GC-ESS-ENCL-512"], "criticalRawMaterials": [{"name": "Lithium", "casNumber": "7439-93-2", "weightGrams": 2600.0, "countryOfOrigin": "AU"}, {"name": "Natural graphite", "casNumber": "7782-42-5", "weightGrams": 9500.0, "countryOfOrigin": "CN"}], "batteryPassportNumber": "URN:ODL:BATT:09590000000021:ODL-IND-LFP-2026-Q2-014", "expectedLifetimeCycles": 6000, "wasteBatteryInformation": "Return to an authorised collection point. Do not incinerate or shred. Producer registration DE-BATT-00123.", "notInUseTemperatureRange": {"maxC": 50.0, "minC": -10.0}, "originalPowerCapabilityW": 5000.0, "recycledContentCobaltPct": null, "recycledContentNickelPct": null, "usableExtinguishingAgent": "Water or ABC dry powder; large volumes of water for thermal runaway", "euDeclarationOfConformity": "EU-DoC GC-2026-IND-0067 dated 2026-03-10, per Regulation (EU) 2023/1542 Art. 18", "recycledContentLithiumPct": null, "disassemblyInstructionsUrl": "https://batteries.example/service/ess-lfp512/disassembly", "internalCellResistanceMohm": 0.6, "internalPackResistanceMohm": 9.0, "recycledContentReportingYear": 2026, "expectedLifetimeReferenceTest": "IEC 61427-1:2013, 25 degC, 90% DoD", "initialRoundTripEfficiencyPct": 96.5, "commercialWarrantyPeriodMonths": 144, "notInUseTemperatureReferenceTest": "IEC 62660-1:2018 storage profile", "roundTripEfficiencyAtHalfCycleLifePct": 92.0}, "operatorIdentifier": "DE123456789", "placedOnMarketDate": "2026-05-20", "publicJwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELUxGUC0yMDI2LVEyLTAxNCIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjkyMTcwNDA0OVoiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNS0yMC4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45MTcxMzU1MzFaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjViZC03MWFjLTljODYtNDY5NjU0MGMzYjIxIiwibWFudWZhY3R1cmVyIjp7ImFkZHJlc3MiOiJQcmVuemxhdWVyIEJlcmcgMTIsIDEwNDA1IEJlcmxpbiwgREUiLCJjb3VudHJ5IjpudWxsLCJkaWRXZWJVcmwiOiJodHRwczovL2JhdHRlcmllcy5leGFtcGxlLy53ZWxsLWtub3duL2RpZC5qc29uIiwibmFtZSI6IkV4YW1wbGUgQmF0dGVyaWVzIEdtYkgifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJMaXRoaXVtIElyb24gUGhvc3BoYXRlIiwicmVjeWNsZWRQY3QiOjgsIndlaWdodEtnIjoyMn0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJHcmFwaGl0ZSIsInJlY3ljbGVkUGN0Ijo2LCJ3ZWlnaHRLZyI6OS41fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IlN0ZWVsIGVuY2xvc3VyZSIsInJlY3ljbGVkUGN0Ijo4Miwid2VpZ2h0S2ciOjEyfV0sIm9wZXJhdG9ySWRlbnRpZmllciI6IkRFMTIzNDU2Nzg5IiwicGxhY2VkT25NYXJrZXREYXRlIjoiMjAyNi0wNS0yMCIsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImJhdHRlcnlDaGVtaXN0cnkiOiJMRlAiLCJiYXR0ZXJ5TW9kZWxJZCI6IkdDLUVTUy1MRlA1MTItMTAwIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDIxOk9ETC1JTkQtTEZQLTIwMjYtUTItMDE0IiwiYmF0dGVyeVR5cGUiOiJpbmR1c3RyaWFsIiwiYmF0dGVyeVdlaWdodEtnIjo0Ni41LCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjE0NCwiY3JpdGljYWxSYXdNYXRlcmlhbHMiOlt7ImNhc051bWJlciI6Ijc0MzktOTMtMiIsImNvdW50cnlPZk9yaWdpbiI6IkFVIiwibmFtZSI6IkxpdGhpdW0iLCJ3ZWlnaHRHcmFtcyI6MjYwMH0seyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDTiIsIm5hbWUiOiJOYXR1cmFsIGdyYXBoaXRlIiwid2VpZ2h0R3JhbXMiOjk1MDB9XSwiY3ljbGVMaWZlVGVzdENSYXRlIjowLjMzLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIEdDLTIwMjYtSU5ELTAwNjcgZGF0ZWQgMjAyNi0wMy0xMCwgcGVyIFJlZ3VsYXRpb24gKEVVKSAyMDIzLzE1NDIgQXJ0LiAxOCIsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjYwMDAsImV4cGVjdGVkTGlmZXRpbWVSZWZlcmVuY2VUZXN0IjoiSUVDIDYxNDI3LTE6MjAxMywgMjUgZGVnQywgOTAlIERvRCIsImhhemFyZG91c1N1YnN0YW5jZXMiOlt7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJjb25jZW50cmF0aW9uUGN0IjowLjcsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUifV0sImluaXRpYWxSb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5Ni41LCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6MC42LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6OSwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTAzLTAyVDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkJlcmxpbiwgREUgKHBsYW50IEJMTi0yKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIHBlciBBcnQuIDEzKDQpOyBjYXBhY2l0eSAxMDAgQWggLyA1LjEyIGtXaDsgY2hlbWlzdHJ5IExGUCIsIm1heGltdW1Wb2x0YWdlViI6NTguNCwibWluaW1hbFZvbHRhZ2VWIjo0MCwibm9taW5hbENhcGFjaXR5QWgiOjEwMCwibm9taW5hbFZvbHRhZ2VWIjo1MS4yLCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NTAsIm1pbkMiOi0xMH0sIm5vdEluVXNlVGVtcGVyYXR1cmVSZWZlcmVuY2VUZXN0IjoiSUVDIDYyNjYwLTE6MjAxOCBzdG9yYWdlIHByb2ZpbGUiLCJvcmlnaW5hbFBvd2VyQ2FwYWJpbGl0eVciOjUwMDAsInBvd2VyTGltaXRNYXhXIjo3NTAwLCJwb3dlckxpbWl0TWluVyI6MTUwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDIxIiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOjUuMTIsInJhdGVkRW5lcmd5V2giOjUxMjAsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRSZXBvcnRpbmdZZWFyIjoyMDI2LCJyZW5ld2FibGVDb250ZW50UGN0IjoyMiwicm91bmRUcmlwRWZmaWNpZW5jeUF0SGFsZkN5Y2xlTGlmZVBjdCI6OTIsInVzYWJsZUV4dGluZ3Vpc2hpbmdBZ2VudCI6IldhdGVyIG9yIEFCQyBkcnkgcG93ZGVyOyBsYXJnZSB2b2x1bWVzIG9mIHdhdGVyIGZvciB0aGVybWFsIHJ1bmF3YXkiLCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IlJldHVybiB0byBhbiBhdXRob3Jpc2VkIGNvbGxlY3Rpb24gcG9pbnQuIERvIG5vdCBpbmNpbmVyYXRlIG9yIHNocmVkLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDEyMy4ifSwicHJvZHVjdE5hbWUiOiJPZGFsIFN0YXRpb25hcnkgU3RvcmFnZSBCYXR0ZXJ5IExGUC01MS4yIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjk4Nzk1NDg3OFoiLCJxckNvZGVVcmwiOiJodHRwOi8vMTI3LjAuMC4xOjEvMDEvMDk1OTAwMDAwMDAwMjEvMjEvNzFhYzljODY0Njk2NTQwYzNiMjEiLCJyZXBhaXJhYmlsaXR5U2NvcmUiOnsib3ZlcmFsbCI6OC4xfSwicmV0ZW50aW9uVW50aWwiOiIyMDM2LTExLTAzVDIwOjA5OjEzLjk4Nzk1NDg3OFoiLCJzY2hlbWFWZXJzaW9uIjoiMi44LjAiLCJzdGF0dXMiOiJhY3RpdmUiLCJ1cGRhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjk4Nzk1NDg3OFoiLCJ2ZXJzaW9uIjoxfQ.vrsJlWhcGD5eljm-5Y5TOAIDhXXEC-lekH67LPNC643xKG8QJXc7Gb4oatkaEcXq1qbBjeyG-UL41TfYjjEJBQ", "repairabilityScore": {"overall": 8.1}, "disclosureSignatures": {"public+restricted+conformity": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELUxGUC0yMDI2LVEyLTAxNCIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjkyMTcwNDA0OVoiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNS0yMC4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45MTcxMzU1MzFaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjViZC03MWFjLTljODYtNDY5NjU0MGMzYjIxIiwibGludFJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45MjE3MDc2MTFaIiwicGFja1ZlcnNpb24iOiIxLjEuMCJ9LCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IlByZW56bGF1ZXIgQmVyZyAxMiwgMTA0MDUgQmVybGluLCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vYmF0dGVyaWVzLmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiRXhhbXBsZSBCYXR0ZXJpZXMgR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkxpdGhpdW0gSXJvbiBQaG9zcGhhdGUiLCJyZWN5Y2xlZFBjdCI6OCwid2VpZ2h0S2ciOjIyfSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkdyYXBoaXRlIiwicmVjeWNsZWRQY3QiOjYsIndlaWdodEtnIjo5LjV9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiU3RlZWwgZW5jbG9zdXJlIiwicmVjeWNsZWRQY3QiOjgyLCJ3ZWlnaHRLZyI6MTJ9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA1LTIwIiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYW5vZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwibmFtZSI6IkdyYXBoaXRlIiwid2VpZ2h0UGN0Ijo5Nn0seyJjYXNOdW1iZXIiOiI5MDAzLTU1LTgiLCJuYW1lIjoiU0JSL0NNQyBiaW5kZXIiLCJ3ZWlnaHRQY3QiOjN9LHsiY2FzTnVtYmVyIjoiMTMzMy04Ni00IiwibmFtZSI6IkNvbmR1Y3RpdmUgY2FyYm9uIGJsYWNrIiwid2VpZ2h0UGN0IjoxfV0sImJhdHRlcnlDaGVtaXN0cnkiOiJMRlAiLCJiYXR0ZXJ5TW9kZWxJZCI6IkdDLUVTUy1MRlA1MTItMTAwIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDIxOk9ETC1JTkQtTEZQLTIwMjYtUTItMDE0IiwiYmF0dGVyeVR5cGUiOiJpbmR1c3RyaWFsIiwiYmF0dGVyeVdlaWdodEtnIjo0Ni41LCJjYXRob2RlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6IjE1MzY1LTE0LTciLCJuYW1lIjoiTGlGZVBPNCIsIndlaWdodFBjdCI6OTV9LHsiY2FzTnVtYmVyIjoiMjQ5MzctNzktOSIsIm5hbWUiOiJQVkRGIGJpbmRlciIsIndlaWdodFBjdCI6M30seyJjYXNOdW1iZXIiOiIxMzMzLTg2LTQiLCJuYW1lIjoiQ29uZHVjdGl2ZSBjYXJib24gYmxhY2siLCJ3ZWlnaHRQY3QiOjJ9XSwiY29tbWVyY2lhbFdhcnJhbnR5UGVyaW9kTW9udGhzIjoxNDQsImNvbXBvbmVudFBhcnROdW1iZXJzIjpbIkdDLUNFTEwtMzI3MDAtTEZQIiwiR0MtQk1TLTE2Uy1WMiIsIkdDLUVTUy1FTkNMLTUxMiJdLCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQzOS05My0yIiwiY291bnRyeU9mT3JpZ2luIjoiQVUiLCJuYW1lIjoiTGl0aGl1bSIsIndlaWdodEdyYW1zIjoyNjAwfSx7ImNhc051bWJlciI6Ijc3ODItNDItNSIsImNvdW50cnlPZk9yaWdpbiI6IkNOIiwibmFtZSI6Ik5hdHVyYWwgZ3JhcGhpdGUiLCJ3ZWlnaHRHcmFtcyI6OTUwMH1dLCJjeWNsZUxpZmVUZXN0Q1JhdGUiOjAuMzMsImRpc2Fzc2VtYmx5SW5zdHJ1Y3Rpb25zVXJsIjoiaHR0cHM6Ly9iYXR0ZXJpZXMuZXhhbXBsZS9zZXJ2aWNlL2Vzcy1sZnA1MTIvZGlzYXNzZW1ibHkiLCJlbGVjdHJvbHl0ZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI5Ni00OS0xIiwibmFtZSI6IkV0aHlsZW5lIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6Mzh9LHsiY2FzTnVtYmVyIjoiNjIzLTUzLTAiLCJuYW1lIjoiRXRoeWwgbWV0aHlsIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6NDl9LHsiY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUiLCJ3ZWlnaHRQY3QiOjEzfV0sImV1RGVjbGFyYXRpb25PZkNvbmZvcm1pdHkiOiJFVS1Eb0MgR0MtMjAyNi1JTkQtMDA2NyBkYXRlZCAyMDI2LTAzLTEwLCBwZXIgUmVndWxhdGlvbiAoRVUpIDIwMjMvMTU0MiBBcnQuIDE4IiwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6NjAwMCwiZXhwZWN0ZWRMaWZldGltZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjE0MjctMToyMDEzLCAyNSBkZWdDLCA5MCUgRG9EIiwiaGF6YXJkb3VzU3Vic3RhbmNlcyI6W3siY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsImNvbmNlbnRyYXRpb25QY3QiOjAuNywibmFtZSI6IkxpdGhpdW0gaGV4YWZsdW9yb3Bob3NwaGF0ZSJ9XSwiaW5pdGlhbFJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk2LjUsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjowLjYsImludGVybmFsUGFja1Jlc2lzdGFuY2VNb2htIjo5LCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDMtMDJUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiQmVybGluLCBERSAocGxhbnQgQkxOLTIpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDEwMCBBaCAvIDUuMTIga1doOyBjaGVtaXN0cnkgTEZQIiwibWF4aW11bVZvbHRhZ2VWIjo1OC40LCJtaW5pbWFsVm9sdGFnZVYiOjQwLCJub21pbmFsQ2FwYWNpdHlBaCI6MTAwLCJub21pbmFsVm9sdGFnZVYiOjUxLjIsIm5vdEluVXNlVGVtcGVyYXR1cmVSYW5nZSI6eyJtYXhDIjo1MCwibWluQyI6LTEwfSwibm90SW5Vc2VUZW1wZXJhdHVyZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjI2NjAtMToyMDE4IHN0b3JhZ2UgcHJvZmlsZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NTAwMCwicG93ZXJMaW1pdE1heFciOjc1MDAsInBvd2VyTGltaXRNaW5XIjoxNTAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwMjEiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6NS4xMiwicmF0ZWRFbmVyZ3lXaCI6NTEyMCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjIyLCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo5Miwic2FmZXR5TWVhc3VyZXMiOiJJc29sYXRlIERDIGJyZWFrZXIgYmVmb3JlIHNlcnZpY2luZy4gSW5zdWxhdGVkIHRvb2xzIG9ubHkuIFZlbnRpbGF0ZSBlbmNsb3N1cmUgYmVmb3JlIG9wZW5pbmcuIiwic3BhcmVQYXJ0c0NvbnRhY3RzIjoic3BhcmVzQGJhdHRlcmllcy5leGFtcGxlOyArNDkgMzAgNTU1MCAxMjAwIiwidGVzdFJlcG9ydFJlc3VsdHMiOiJJRUMgNjI2MTk6MjAyMiBwYXNzOyBJRUMgNjMwNTY6MjAyMCBwYXNzOyBVTiAzOC4zIHBhc3M7IHJlcG9ydCBHQy1UUi0yMDI2LTAzMDEiLCJ1c2FibGVFeHRpbmd1aXNoaW5nQWdlbnQiOiJXYXRlciBvciBBQkMgZHJ5IHBvd2RlcjsgbGFyZ2Ugdm9sdW1lcyBvZiB3YXRlciBmb3IgdGhlcm1hbCBydW5hd2F5Iiwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJSZXR1cm4gdG8gYW4gYXV0aG9yaXNlZCBjb2xsZWN0aW9uIHBvaW50LiBEbyBub3QgaW5jaW5lcmF0ZSBvciBzaHJlZC4gUHJvZHVjZXIgcmVnaXN0cmF0aW9uIERFLUJBVFQtMDAxMjMuIn0sInByb2R1Y3ROYW1lIjoiT2RhbCBTdGF0aW9uYXJ5IFN0b3JhZ2UgQmF0dGVyeSBMRlAtNTEuMiIsInB1Ymxpc2hlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45ODc5NTQ4NzhaIiwicXJDb2RlVXJsIjoiaHR0cDovLzEyNy4wLjAuMToxLzAxLzA5NTkwMDAwMDAwMDIxLzIxLzcxYWM5Yzg2NDY5NjU0MGMzYjIxIiwicmVwYWlyYWJpbGl0eVNjb3JlIjp7Im92ZXJhbGwiOjguMX0sInJldGVudGlvbkxvY2tlZCI6dHJ1ZSwicmV0ZW50aW9uVW50aWwiOiIyMDM2LTExLTAzVDIwOjA5OjEzLjk4Nzk1NDg3OFoiLCJzY2hlbWFWZXJzaW9uIjoiMi44LjAiLCJzdGF0dXMiOiJhY3RpdmUiLCJ1cGRhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjk4Nzk1NDg3OFoiLCJ2ZXJzaW9uIjoxfQ.HgGb20suOuqSvmzrBgNeQATCWlQ8L71Q1xSevVcUZNqI9Szt1I_tBNDRMWR-W4idyB1LbN9p81N3z6lkekNIAg", "public+restricted+individual": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELUxGUC0yMDI2LVEyLTAxNCIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjkyMTcwNDA0OVoiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNS0yMC4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45MTcxMzU1MzFaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjViZC03MWFjLTljODYtNDY5NjU0MGMzYjIxIiwibGludFJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45MjE3MDc2MTFaIiwicGFja1ZlcnNpb24iOiIxLjEuMCJ9LCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IlByZW56bGF1ZXIgQmVyZyAxMiwgMTA0MDUgQmVybGluLCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vYmF0dGVyaWVzLmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiRXhhbXBsZSBCYXR0ZXJpZXMgR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkxpdGhpdW0gSXJvbiBQaG9zcGhhdGUiLCJyZWN5Y2xlZFBjdCI6OCwid2VpZ2h0S2ciOjIyfSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkdyYXBoaXRlIiwicmVjeWNsZWRQY3QiOjYsIndlaWdodEtnIjo5LjV9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiU3RlZWwgZW5jbG9zdXJlIiwicmVjeWNsZWRQY3QiOjgyLCJ3ZWlnaHRLZyI6MTJ9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA1LTIwIiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYW5vZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwibmFtZSI6IkdyYXBoaXRlIiwid2VpZ2h0UGN0Ijo5Nn0seyJjYXNOdW1iZXIiOiI5MDAzLTU1LTgiLCJuYW1lIjoiU0JSL0NNQyBiaW5kZXIiLCJ3ZWlnaHRQY3QiOjN9LHsiY2FzTnVtYmVyIjoiMTMzMy04Ni00IiwibmFtZSI6IkNvbmR1Y3RpdmUgY2FyYm9uIGJsYWNrIiwid2VpZ2h0UGN0IjoxfV0sImJhdHRlcnlDaGVtaXN0cnkiOiJMRlAiLCJiYXR0ZXJ5TW9kZWxJZCI6IkdDLUVTUy1MRlA1MTItMTAwIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDIxOk9ETC1JTkQtTEZQLTIwMjYtUTItMDE0IiwiYmF0dGVyeVN0YXR1cyI6Im9yaWdpbmFsIiwiYmF0dGVyeVR5cGUiOiJpbmR1c3RyaWFsIiwiYmF0dGVyeVdlaWdodEtnIjo0Ni41LCJjYXRob2RlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6IjE1MzY1LTE0LTciLCJuYW1lIjoiTGlGZVBPNCIsIndlaWdodFBjdCI6OTV9LHsiY2FzTnVtYmVyIjoiMjQ5MzctNzktOSIsIm5hbWUiOiJQVkRGIGJpbmRlciIsIndlaWdodFBjdCI6M30seyJjYXNOdW1iZXIiOiIxMzMzLTg2LTQiLCJuYW1lIjoiQ29uZHVjdGl2ZSBjYXJib24gYmxhY2siLCJ3ZWlnaHRQY3QiOjJ9XSwiY29tbWVyY2lhbFdhcnJhbnR5UGVyaW9kTW9udGhzIjoxNDQsImNvbXBvbmVudFBhcnROdW1iZXJzIjpbIkdDLUNFTEwtMzI3MDAtTEZQIiwiR0MtQk1TLTE2Uy1WMiIsIkdDLUVTUy1FTkNMLTUxMiJdLCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQzOS05My0yIiwiY291bnRyeU9mT3JpZ2luIjoiQVUiLCJuYW1lIjoiTGl0aGl1bSIsIndlaWdodEdyYW1zIjoyNjAwfSx7ImNhc051bWJlciI6Ijc3ODItNDItNSIsImNvdW50cnlPZk9yaWdpbiI6IkNOIiwibmFtZSI6Ik5hdHVyYWwgZ3JhcGhpdGUiLCJ3ZWlnaHRHcmFtcyI6OTUwMH1dLCJjeWNsZUxpZmVUZXN0Q1JhdGUiOjAuMzMsImRpc2Fzc2VtYmx5SW5zdHJ1Y3Rpb25zVXJsIjoiaHR0cHM6Ly9iYXR0ZXJpZXMuZXhhbXBsZS9zZXJ2aWNlL2Vzcy1sZnA1MTIvZGlzYXNzZW1ibHkiLCJkeW5hbWljUGVyZm9ybWFuY2UiOnsiY2FwYWNpdHlGYWRlUGN0IjowLjQsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjU5ODAsImludGVybmFsUmVzaXN0YW5jZU1vaG0iOjkuMSwicmF0ZWRDYXBhY2l0eUFoIjo5OS42LCJyb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5Ni4yfSwiZWxlY3Ryb2x5dGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiOTYtNDktMSIsIm5hbWUiOiJFdGh5bGVuZSBjYXJib25hdGUiLCJ3ZWlnaHRQY3QiOjM4fSx7ImNhc051bWJlciI6IjYyMy01My0wIiwibmFtZSI6IkV0aHlsIG1ldGh5bCBjYXJib25hdGUiLCJ3ZWlnaHRQY3QiOjQ5fSx7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIiwid2VpZ2h0UGN0IjoxM31dLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIEdDLTIwMjYtSU5ELTAwNjcgZGF0ZWQgMjAyNi0wMy0xMCwgcGVyIFJlZ3VsYXRpb24gKEVVKSAyMDIzLzE1NDIgQXJ0LiAxOCIsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjYwMDAsImV4cGVjdGVkTGlmZXRpbWVSZWZlcmVuY2VUZXN0IjoiSUVDIDYxNDI3LTE6MjAxMywgMjUgZGVnQywgOTAlIERvRCIsImhhemFyZG91c1N1YnN0YW5jZXMiOlt7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJjb25jZW50cmF0aW9uUGN0IjowLjcsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUifV0sImluaXRpYWxSb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5Ni41LCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6MC42LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6OSwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTAzLTAyVDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkJlcmxpbiwgREUgKHBsYW50IEJMTi0yKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIHBlciBBcnQuIDEzKDQpOyBjYXBhY2l0eSAxMDAgQWggLyA1LjEyIGtXaDsgY2hlbWlzdHJ5IExGUCIsIm1heGltdW1Wb2x0YWdlViI6NTguNCwibWluaW1hbFZvbHRhZ2VWIjo0MCwibm9taW5hbENhcGFjaXR5QWgiOjEwMCwibm9taW5hbFZvbHRhZ2VWIjo1MS4yLCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NTAsIm1pbkMiOi0xMH0sIm5vdEluVXNlVGVtcGVyYXR1cmVSZWZlcmVuY2VUZXN0IjoiSUVDIDYyNjYwLTE6MjAxOCBzdG9yYWdlIHByb2ZpbGUiLCJvcmlnaW5hbFBvd2VyQ2FwYWJpbGl0eVciOjUwMDAsInBvd2VyTGltaXRNYXhXIjo3NTAwLCJwb3dlckxpbWl0TWluVyI6MTUwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDIxIiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOjUuMTIsInJhdGVkRW5lcmd5V2giOjUxMjAsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRSZXBvcnRpbmdZZWFyIjoyMDI2LCJyZW5ld2FibGVDb250ZW50UGN0IjoyMiwicm91bmRUcmlwRWZmaWNpZW5jeUF0SGFsZkN5Y2xlTGlmZVBjdCI6OTIsInNhZmV0eU1lYXN1cmVzIjoiSXNvbGF0ZSBEQyBicmVha2VyIGJlZm9yZSBzZXJ2aWNpbmcuIEluc3VsYXRlZCB0b29scyBvbmx5LiBWZW50aWxhdGUgZW5jbG9zdXJlIGJlZm9yZSBvcGVuaW5nLiIsInNwYXJlUGFydHNDb250YWN0cyI6InNwYXJlc0BiYXR0ZXJpZXMuZXhhbXBsZTsgKzQ5IDMwIDU1NTAgMTIwMCIsInN0YXRlT2ZIZWFsdGgiOnsib2htaWNSZXNpc3RhbmNlTW9obSI6OS4xLCJwYXJhbWV0ZXJTZXQiOiJzdGF0aW9uYXJ5T3JMbXQiLCJyZW1haW5pbmdDYXBhY2l0eVBjdCI6OTkuNiwicmVtYWluaW5nUG93ZXJDYXBhYmlsaXR5UGN0Ijo5OS40LCJyZW1haW5pbmdSb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5OS4xLCJzZWxmRGlzY2hhcmdlUmF0ZVBjdFBlck1vbnRoIjoxLjR9LCJzdGF0ZU9mSGVhbHRoUGN0IjpudWxsLCJ1c2FibGVFeHRpbmd1aXNoaW5nQWdlbnQiOiJXYXRlciBvciBBQkMgZHJ5IHBvd2RlcjsgbGFyZ2Ugdm9sdW1lcyBvZiB3YXRlciBmb3IgdGhlcm1hbCBydW5hd2F5IiwidXNhZ2VIaXN0b3J5Ijp7ImNoYXJnZURpc2NoYXJnZUN5Y2xlcyI6NSwibmVnYXRpdmVFdmVudHMiOltdfSwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJSZXR1cm4gdG8gYW4gYXV0aG9yaXNlZCBjb2xsZWN0aW9uIHBvaW50LiBEbyBub3QgaW5jaW5lcmF0ZSBvciBzaHJlZC4gUHJvZHVjZXIgcmVnaXN0cmF0aW9uIERFLUJBVFQtMDAxMjMuIn0sInByb2R1Y3ROYW1lIjoiT2RhbCBTdGF0aW9uYXJ5IFN0b3JhZ2UgQmF0dGVyeSBMRlAtNTEuMiIsInB1Ymxpc2hlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45ODc5NTQ4NzhaIiwicXJDb2RlVXJsIjoiaHR0cDovLzEyNy4wLjAuMToxLzAxLzA5NTkwMDAwMDAwMDIxLzIxLzcxYWM5Yzg2NDY5NjU0MGMzYjIxIiwicmVwYWlyYWJpbGl0eVNjb3JlIjp7Im92ZXJhbGwiOjguMX0sInJldGVudGlvblVudGlsIjoiMjAzNi0xMS0wM1QyMDowOToxMy45ODc5NTQ4NzhaIiwic2NoZW1hVmVyc2lvbiI6IjIuOC4wIiwic3RhdHVzIjoiYWN0aXZlIiwidXBkYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45ODc5NTQ4NzhaIiwidmVyc2lvbiI6MX0.M2ufqIdiPLFPrlOl46H2W8cUouLEhxDikDVOo6DkwK9p_JvEThvi56ALneqoTFUYluhvrqk6eIm4FMIA5H2GDg"}, "applicableInstruments": [{"recorded": "catalog", "instrument": "battery-reg-2023-1542"}]}', '2026-11-03 20:09:14.64249+00', '2026-11-03 20:09:14.640666+00'); +INSERT INTO odal.passport_version VALUES ('01a1a307-bab2-7249-81a3-9363c2b1cea7', '01a1a307-b97c-73a6-99ea-eb004fceb13a', '{"id": "01a1a307-b97c-73a6-99ea-eb004fceb13a", "status": "draft", "batchId": "BATCH-SS26-003", "version": 1, "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "createdAt": "2026-11-03T20:09:14.876201919Z", "materials": [], "qrCodeUrl": null, "updatedAt": "2026-11-03T20:09:14.876202580Z", "lintResult": {"assessedAt": "2026-11-03T20:09:14.879321282Z", "packVersion": "1.1.0"}, "co2ePerUnit": null, "productName": "Glamor Sport Jacket", "publishedAt": null, "jwsSignature": null, "manufacturer": {"name": "Glamor Clothing", "address": "DE", "country": "DE", "didWebUrl": null}, "productGroup": "textile", "schemaVersion": "1.4.0", "retentionLocked": false, "complianceResult": {"co2eScore": null, "assessedAt": "2026-11-03T20:09:14.879318011Z", "complianceStatus": "PASSTHROUGH_NO_VALIDATION", "recycledContentPct": null, "repairabilityIndex": null}, "productGroupData": {"productGroup": "textile", "countryOfOrigin": "DE", "careInstructions": "Machine wash 30°C. Do not tumble dry.", "fibreComposition": [{"pct": 85.0, "fibre": "polyester"}, {"pct": 15.0, "fibre": "elastane"}], "productIdentifier": {"gtin": "38012345679134", "scheme": "gs1"}, "chemicalComplianceStandard": "OEKO-TEX Standard 100"}, "operatorIdentifier": "DE123456789", "repairabilityScore": null, "applicableInstruments": [{"recorded": "catalog", "instrument": "espr"}]}', '2026-11-03 20:09:15.186861+00', '2026-11-03 20:09:15.184636+00'); + + +-- +-- Data for Name: personal_data_record; Type: TABLE DATA; Schema: odal; Owner: - +-- + + + +-- +-- Data for Name: qr_render; Type: TABLE DATA; Schema: odal; Owner: - +-- + + + +-- +-- Data for Name: registry_identity_audit; Type: TABLE DATA; Schema: odal; Owner: - +-- + +INSERT INTO odal.registry_identity_audit VALUES ('01a1a307-b4a1-757e-90ce-6d02ccd55395', 'self_hosted', 'facility', '01a1a307-b491-71ca-99f2-400f95e6a462', 'added', 'api-key', '{"id": "01a1a307-b491-71ca-99f2-400f95e6a462", "name": "Demo Plant", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE", "createdAt": "2026-11-03T20:09:13.617033Z", "isDefault": true, "identifierValue": "4012345000009", "identifierScheme": "gln"}', '2026-11-03 20:09:13.633166+00'); +INSERT INTO odal.registry_identity_audit VALUES ('01a1a307-b4dd-746f-a18f-40e4046acf08', 'self_hosted', 'operator_identifier', '01a1a307-b4d7-73f2-95bf-3b2ddd1f6473', 'added', 'api-key', '{"id": "01a1a307-b4d7-73f2-95bf-3b2ddd1f6473", "value": "DE123456789", "scheme": "vat", "createdAt": "2026-11-03T20:09:13.687031Z", "isPrimary": true}', '2026-11-03 20:09:13.693081+00'); +INSERT INTO odal.registry_identity_audit VALUES ('01a1a307-b9f4-7088-8fa4-3e253785b623', 'self_hosted', 'facility', '01a1a307-b9f0-7352-8855-da4204d950cf', 'added', 'api-key', '{"id": "01a1a307-b9f0-7352-8855-da4204d950cf", "name": "Second Plant", "country": "DE", "createdAt": "2026-11-03T20:09:14.992137Z", "isDefault": false, "identifierValue": "4012345000016", "identifierScheme": "gln"}', '2026-11-03 20:09:14.996333+00'); + + +-- +-- Data for Name: registry_sync; Type: TABLE DATA; Schema: odal; Owner: - +-- + +INSERT INTO odal.registry_sync VALUES ('df85e350-1f88-42d1-9e3b-2ccf125e9fc5', '01a1a307-b510-75c3-b5cc-1ccbddeb9787', NULL, 'pending', '{"batchId": "ODL-IND-NMC-2026-Q2-001", "modelId": "GC-IND-NMC48-320", "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "requestId": "01a1a307-b561-7445-bde3-3d2e85134fa2", "itemSerial": "75c3b5cc1ccbddeb9787", "passportId": "01a1a307-b510-75c3-b5cc-1ccbddeb9787", "granularity": "item", "publishedAt": "2026-11-03T20:09:13.789298757Z", "jwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELU5NQy0yMDI2LVEyLTAwMSIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjc0NzkzMTk5N1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNi0wMS4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy43NDQzNzgwMzRaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjUxMC03NWMzLWI1Y2MtMWNjYmRkZWI5Nzg3IiwiandzU2lnbmF0dXJlIjpudWxsLCJsaW50UmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjc0NzkzMzk5MFoiLCJwYWNrVmVyc2lvbiI6IjEuMS4wIn0sIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiUHJlbnpsYXVlciBCZXJnIDEyLCAxMDQwNSBCZXJsaW4sIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly9iYXR0ZXJpZXMuZXhhbXBsZS8ud2VsbC1rbm93bi9kaWQuanNvbiIsIm5hbWUiOiJFeGFtcGxlIEJhdHRlcmllcyBHbWJIIn0sIm1hdGVyaWFscyI6W3siY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiTGl0aGl1bSBOaWNrZWwgTWFuZ2FuZXNlIENvYmFsdCBPeGlkZSIsInJlY3ljbGVkUGN0IjoxMSwid2VpZ2h0S2ciOjE0LjJ9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiR3JhcGhpdGUiLCJyZWN5Y2xlZFBjdCI6NSwid2VpZ2h0S2ciOjYuNH0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJBbHVtaW5pdW0gZW5jbG9zdXJlIiwicmVjeWNsZWRQY3QiOjc4LCJ3ZWlnaHRLZyI6OC4xfV0sIm9wZXJhdG9ySWRlbnRpZmllciI6IkRFMTIzNDU2Nzg5IiwicGxhY2VkT25NYXJrZXREYXRlIjoiMjAyNi0wNi0wMSIsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImFub2RlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6Ijc3ODItNDItNSIsIm5hbWUiOiJHcmFwaGl0ZSIsIndlaWdodFBjdCI6OTV9LHsiY2FzTnVtYmVyIjoiOTAwMy01NS04IiwibmFtZSI6IlNCUi9DTUMgYmluZGVyIiwid2VpZ2h0UGN0IjozfSx7ImNhc051bWJlciI6IjEzMzMtODYtNCIsIm5hbWUiOiJDb25kdWN0aXZlIGNhcmJvbiBibGFjayIsIndlaWdodFBjdCI6Mn1dLCJiYXR0ZXJ5Q2hlbWlzdHJ5IjoiTk1DIiwiYmF0dGVyeU1vZGVsSWQiOiJHQy1JTkQtTk1DNDgtMzIwIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDE0Ok9ETC1JTkQtTk1DLTIwMjYtUTItMDAxIiwiYmF0dGVyeVN0YXR1cyI6Im9yaWdpbmFsIiwiYmF0dGVyeVR5cGUiOiJpbmR1c3RyaWFsIiwiYmF0dGVyeVdlaWdodEtnIjozMiwiY2F0aG9kZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiIzNDY0MTctOTctOCIsIm5hbWUiOiJMaU5pMC42TW4wLjJDbzAuMk8yIiwid2VpZ2h0UGN0Ijo5Nn0seyJjYXNOdW1iZXIiOiIyNDkzNy03OS05IiwibmFtZSI6IlBWREYgYmluZGVyIiwid2VpZ2h0UGN0IjoyfSx7ImNhc051bWJlciI6IjEzMzMtODYtNCIsIm5hbWUiOiJDb25kdWN0aXZlIGNhcmJvbiBibGFjayIsIndlaWdodFBjdCI6Mn1dLCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjEyMCwiY29tcG9uZW50UGFydE51bWJlcnMiOlsiR0MtQ0VMTC0yMTcwLU5NQyIsIkdDLUJNUy00OC1WMyIsIkdDLVBBQ0stRU5DTC0zMjAiXSwiY3JpdGljYWxSYXdNYXRlcmlhbHMiOlt7ImNhc051bWJlciI6Ijc0NDAtNDgtNCIsImNvdW50cnlPZk9yaWdpbiI6IkNEIiwibmFtZSI6IkNvYmFsdCIsIndlaWdodEdyYW1zIjoxNDUwfSx7ImNhc051bWJlciI6Ijc0MzktOTMtMiIsImNvdW50cnlPZk9yaWdpbiI6IkNMIiwibmFtZSI6IkxpdGhpdW0iLCJ3ZWlnaHRHcmFtcyI6MjEwMH0seyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDTiIsIm5hbWUiOiJOYXR1cmFsIGdyYXBoaXRlIiwid2VpZ2h0R3JhbXMiOjY0MDB9XSwiY3ljbGVMaWZlVGVzdENSYXRlIjowLjUsImRpc2Fzc2VtYmx5SW5zdHJ1Y3Rpb25zVXJsIjoiaHR0cHM6Ly9iYXR0ZXJpZXMuZXhhbXBsZS9zZXJ2aWNlL2luZC1ubWM0OC9kaXNhc3NlbWJseSIsImR5bmFtaWNQZXJmb3JtYW5jZSI6eyJjYXBhY2l0eUZhZGVQY3QiOjAuNywiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6Mjc5MCwiaW50ZXJuYWxSZXNpc3RhbmNlTW9obSI6MTQuMiwicmF0ZWRDYXBhY2l0eUFoIjo4OS40LCJyb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5NC42fSwiZWxlY3Ryb2x5dGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiOTYtNDktMSIsIm5hbWUiOiJFdGh5bGVuZSBjYXJib25hdGUiLCJ3ZWlnaHRQY3QiOjQwfSx7ImNhc051bWJlciI6IjYxNi0zOC02IiwibmFtZSI6IkRpbWV0aHlsIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6NDd9LHsiY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUiLCJ3ZWlnaHRQY3QiOjEzfV0sImV1RGVjbGFyYXRpb25PZkNvbmZvcm1pdHkiOiJFVS1Eb0MgR0MtMjAyNi1JTkQtMDA5MSBkYXRlZCAyMDI2LTA0LTIwLCBwZXIgUmVndWxhdGlvbiAoRVUpIDIwMjMvMTU0MiBBcnQuIDE4IiwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6MjgwMCwiZXhwZWN0ZWRMaWZldGltZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjE0MjctMToyMDEzLCAyNSBkZWdDLCA4MCUgRG9EIiwiaGF6YXJkb3VzU3Vic3RhbmNlcyI6W3siY2FzTnVtYmVyIjoiNzc4Ni04MS00IiwiY29uY2VudHJhdGlvblBjdCI6MS4yLCJuYW1lIjoiTmlja2VsIHN1bGZhdGUifSx7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJjb25jZW50cmF0aW9uUGN0IjowLjgsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUifV0sImluaXRpYWxSb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5NSwiaW50ZXJuYWxDZWxsUmVzaXN0YW5jZU1vaG0iOjAuOSwiaW50ZXJuYWxQYWNrUmVzaXN0YW5jZU1vaG0iOjE0LCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDQtMTVUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiQmVybGluLCBERSAocGxhbnQgQkxOLTEpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgKGNyb3NzZWQtb3V0IHdoZWVsZWQgYmluKSBwZXIgQXJ0LiAxMyg0KTsgY2FwYWNpdHkgOTAgQWggLyA0LjMyIGtXaCIsIm1heGltdW1Wb2x0YWdlViI6NTQuNiwibWluaW1hbFZvbHRhZ2VWIjo0Miwibm9taW5hbENhcGFjaXR5QWgiOjkwLCJub21pbmFsVm9sdGFnZVYiOjQ4LCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NDUsIm1pbkMiOi0yMH0sIm5vdEluVXNlVGVtcGVyYXR1cmVSZWZlcmVuY2VUZXN0IjoiSUVDIDYyNjYwLTE6MjAxOCBzdG9yYWdlIHByb2ZpbGUiLCJvcmlnaW5hbFBvd2VyQ2FwYWJpbGl0eVciOjQ2MDAsInBvd2VyTGltaXRNYXhXIjo2MDAwLCJwb3dlckxpbWl0TWluVyI6MjAwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDE0Iiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOjQuMzIsInJhdGVkRW5lcmd5V2giOjQzMjAsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRSZXBvcnRpbmdZZWFyIjoyMDI2LCJyZW5ld2FibGVDb250ZW50UGN0IjoxOCwicm91bmRUcmlwRWZmaWNpZW5jeUF0SGFsZkN5Y2xlTGlmZVBjdCI6ODksInNhZmV0eU1lYXN1cmVzIjoiRGUtZW5lcmdpc2UgYW5kIHdhaXQgMTUgbWluIGJlZm9yZSBvcGVuaW5nLiBDbGFzcyAwIGdsb3Zlcy4gTm8gY29uZHVjdGl2ZSB0b29scy4iLCJzcGFyZVBhcnRzQ29udGFjdHMiOiJzcGFyZXNAYmF0dGVyaWVzLmV4YW1wbGU7ICs0OSAzMCA1NTUwIDEyMDAiLCJzdGF0ZU9mSGVhbHRoIjp7Im9obWljUmVzaXN0YW5jZU1vaG0iOjE0LjIsInBhcmFtZXRlclNldCI6InN0YXRpb25hcnlPckxtdCIsInJlbWFpbmluZ0NhcGFjaXR5UGN0Ijo5OS4zLCJyZW1haW5pbmdQb3dlckNhcGFiaWxpdHlQY3QiOjk5LCJyZW1haW5pbmdSb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5OC41LCJzZWxmRGlzY2hhcmdlUmF0ZVBjdFBlck1vbnRoIjoyLjF9LCJzdGF0ZU9mSGVhbHRoUGN0IjpudWxsLCJ0ZXN0UmVwb3J0UmVzdWx0cyI6IklFQyA2MjYxOToyMDIyIHBhc3M7IFVOIDM4LjMgcGFzczsgcmVwb3J0IEdDLVRSLTIwMjYtMDQxMiIsInVzYWJsZUV4dGluZ3Vpc2hpbmdBZ2VudCI6IldhdGVyIG1pc3Qgb3IgQUJDIGRyeSBwb3dkZXI7IGRvIG5vdCB1c2UgQ08yIiwidXNhZ2VIaXN0b3J5Ijp7ImNoYXJnZURpc2NoYXJnZUN5Y2xlcyI6MTIsIm5lZ2F0aXZlRXZlbnRzIjpbXX0sIndhc3RlQmF0dGVyeUluZm9ybWF0aW9uIjoiUmV0dXJuIHRvIGFuIGF1dGhvcmlzZWQgY29sbGVjdGlvbiBwb2ludCBvciBpbnN0YWxsZXIuIERvIG5vdCBzaHJlZC4gQ29udGFpbnMgbGl0aGl1bTsgZmlyZSByaXNrIGlmIGRhbWFnZWQuIFByb2R1Y2VyIHJlZ2lzdHJhdGlvbiBERS1CQVRULTAwMTIzLiJ9LCJwcm9kdWN0TmFtZSI6Ik9kYWwgSW5kdXN0cmlhbCBCYXR0ZXJ5IE5NQy00OCIsInB1Ymxpc2hlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy43ODkyOTg3NTdaIiwicXJDb2RlVXJsIjoiaHR0cDovLzEyNy4wLjAuMToxLzAxLzA5NTkwMDAwMDAwMDE0LzIxLzc1YzNiNWNjMWNjYmRkZWI5Nzg3IiwicmVwYWlyYWJpbGl0eVNjb3JlIjp7Im92ZXJhbGwiOjcuNX0sInJldGVudGlvbkxvY2tlZCI6dHJ1ZSwicmV0ZW50aW9uVW50aWwiOiIyMDM2LTExLTAzVDIwOjA5OjEzLjc4OTI5ODc1N1oiLCJzY2hlbWFWZXJzaW9uIjoiMi44LjAiLCJzdGF0dXMiOiJhY3RpdmUiLCJ1cGRhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjc4OTI5ODc1N1oiLCJ2ZXJzaW9uIjoxfQ.dAaMwYQYGxuJl7K3m8A19ZMa1XYXVZrjfI6cpYK2C01nDyOvcG1Rl2qFVH7guz4DhZ_7RvxefF11rwnwnOnEAw", "commodityCode": "85076000", "schemaVersion": "2.8.0", "dataCarrierUri": "http://127.0.0.1:1/01/09590000000014/21/75c3b5cc1ccbddeb9787", "productCategory": "battery", "productIdentifier": {"gtin": "09590000000014", "scheme": "gs1"}, "facilityIdentifier": "4012345000009", "operatorIdentifier": "DE123456789", "operatorIdentifierScheme": "vat"}', NULL, 0, '2026-11-03 20:09:13.82635+00', NULL, NULL, '2026-11-03 20:09:13.82635+00', '2026-11-03 20:09:13.82635+00', NULL, NULL); +INSERT INTO odal.registry_sync VALUES ('ad87a4a4-a303-4157-b034-c66de8b80d0c', '01a1a307-b5bd-71ac-9c86-4696540c3b21', NULL, 'pending', '{"batchId": "ODL-IND-LFP-2026-Q2-014", "modelId": "GC-ESS-LFP512-100", "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "requestId": "01a1a307-b633-7057-b277-68df9e28e830", "itemSerial": "71ac9c864696540c3b21", "passportId": "01a1a307-b5bd-71ac-9c86-4696540c3b21", "granularity": "item", "publishedAt": "2026-11-03T20:09:13.987954878Z", "jwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELUxGUC0yMDI2LVEyLTAxNCIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjkyMTcwNDA0OVoiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNS0yMC4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45MTcxMzU1MzFaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjViZC03MWFjLTljODYtNDY5NjU0MGMzYjIxIiwiandzU2lnbmF0dXJlIjpudWxsLCJsaW50UmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjkyMTcwNzYxMVoiLCJwYWNrVmVyc2lvbiI6IjEuMS4wIn0sIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiUHJlbnpsYXVlciBCZXJnIDEyLCAxMDQwNSBCZXJsaW4sIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly9iYXR0ZXJpZXMuZXhhbXBsZS8ud2VsbC1rbm93bi9kaWQuanNvbiIsIm5hbWUiOiJFeGFtcGxlIEJhdHRlcmllcyBHbWJIIn0sIm1hdGVyaWFscyI6W3siY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiTGl0aGl1bSBJcm9uIFBob3NwaGF0ZSIsInJlY3ljbGVkUGN0Ijo4LCJ3ZWlnaHRLZyI6MjJ9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiR3JhcGhpdGUiLCJyZWN5Y2xlZFBjdCI6Niwid2VpZ2h0S2ciOjkuNX0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJTdGVlbCBlbmNsb3N1cmUiLCJyZWN5Y2xlZFBjdCI6ODIsIndlaWdodEtnIjoxMn1dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInBsYWNlZE9uTWFya2V0RGF0ZSI6IjIwMjYtMDUtMjAiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJhbm9kZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJuYW1lIjoiR3JhcGhpdGUiLCJ3ZWlnaHRQY3QiOjk2fSx7ImNhc051bWJlciI6IjkwMDMtNTUtOCIsIm5hbWUiOiJTQlIvQ01DIGJpbmRlciIsIndlaWdodFBjdCI6M30seyJjYXNOdW1iZXIiOiIxMzMzLTg2LTQiLCJuYW1lIjoiQ29uZHVjdGl2ZSBjYXJib24gYmxhY2siLCJ3ZWlnaHRQY3QiOjF9XSwiYmF0dGVyeUNoZW1pc3RyeSI6IkxGUCIsImJhdHRlcnlNb2RlbElkIjoiR0MtRVNTLUxGUDUxMi0xMDAiLCJiYXR0ZXJ5UGFzc3BvcnROdW1iZXIiOiJVUk46T0RMOkJBVFQ6MDk1OTAwMDAwMDAwMjE6T0RMLUlORC1MRlAtMjAyNi1RMi0wMTQiLCJiYXR0ZXJ5U3RhdHVzIjoib3JpZ2luYWwiLCJiYXR0ZXJ5VHlwZSI6ImluZHVzdHJpYWwiLCJiYXR0ZXJ5V2VpZ2h0S2ciOjQ2LjUsImNhdGhvZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiMTUzNjUtMTQtNyIsIm5hbWUiOiJMaUZlUE80Iiwid2VpZ2h0UGN0Ijo5NX0seyJjYXNOdW1iZXIiOiIyNDkzNy03OS05IiwibmFtZSI6IlBWREYgYmluZGVyIiwid2VpZ2h0UGN0IjozfSx7ImNhc051bWJlciI6IjEzMzMtODYtNCIsIm5hbWUiOiJDb25kdWN0aXZlIGNhcmJvbiBibGFjayIsIndlaWdodFBjdCI6Mn1dLCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjE0NCwiY29tcG9uZW50UGFydE51bWJlcnMiOlsiR0MtQ0VMTC0zMjcwMC1MRlAiLCJHQy1CTVMtMTZTLVYyIiwiR0MtRVNTLUVOQ0wtNTEyIl0sImNyaXRpY2FsUmF3TWF0ZXJpYWxzIjpbeyJjYXNOdW1iZXIiOiI3NDM5LTkzLTIiLCJjb3VudHJ5T2ZPcmlnaW4iOiJBVSIsIm5hbWUiOiJMaXRoaXVtIiwid2VpZ2h0R3JhbXMiOjI2MDB9LHsiY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwiY291bnRyeU9mT3JpZ2luIjoiQ04iLCJuYW1lIjoiTmF0dXJhbCBncmFwaGl0ZSIsIndlaWdodEdyYW1zIjo5NTAwfV0sImN5Y2xlTGlmZVRlc3RDUmF0ZSI6MC4zMywiZGlzYXNzZW1ibHlJbnN0cnVjdGlvbnNVcmwiOiJodHRwczovL2JhdHRlcmllcy5leGFtcGxlL3NlcnZpY2UvZXNzLWxmcDUxMi9kaXNhc3NlbWJseSIsImR5bmFtaWNQZXJmb3JtYW5jZSI6eyJjYXBhY2l0eUZhZGVQY3QiOjAuNCwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6NTk4MCwiaW50ZXJuYWxSZXNpc3RhbmNlTW9obSI6OS4xLCJyYXRlZENhcGFjaXR5QWgiOjk5LjYsInJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk2LjJ9LCJlbGVjdHJvbHl0ZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI5Ni00OS0xIiwibmFtZSI6IkV0aHlsZW5lIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6Mzh9LHsiY2FzTnVtYmVyIjoiNjIzLTUzLTAiLCJuYW1lIjoiRXRoeWwgbWV0aHlsIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6NDl9LHsiY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUiLCJ3ZWlnaHRQY3QiOjEzfV0sImV1RGVjbGFyYXRpb25PZkNvbmZvcm1pdHkiOiJFVS1Eb0MgR0MtMjAyNi1JTkQtMDA2NyBkYXRlZCAyMDI2LTAzLTEwLCBwZXIgUmVndWxhdGlvbiAoRVUpIDIwMjMvMTU0MiBBcnQuIDE4IiwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6NjAwMCwiZXhwZWN0ZWRMaWZldGltZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjE0MjctMToyMDEzLCAyNSBkZWdDLCA5MCUgRG9EIiwiaGF6YXJkb3VzU3Vic3RhbmNlcyI6W3siY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsImNvbmNlbnRyYXRpb25QY3QiOjAuNywibmFtZSI6IkxpdGhpdW0gaGV4YWZsdW9yb3Bob3NwaGF0ZSJ9XSwiaW5pdGlhbFJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk2LjUsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjowLjYsImludGVybmFsUGFja1Jlc2lzdGFuY2VNb2htIjo5LCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDMtMDJUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiQmVybGluLCBERSAocGxhbnQgQkxOLTIpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDEwMCBBaCAvIDUuMTIga1doOyBjaGVtaXN0cnkgTEZQIiwibWF4aW11bVZvbHRhZ2VWIjo1OC40LCJtaW5pbWFsVm9sdGFnZVYiOjQwLCJub21pbmFsQ2FwYWNpdHlBaCI6MTAwLCJub21pbmFsVm9sdGFnZVYiOjUxLjIsIm5vdEluVXNlVGVtcGVyYXR1cmVSYW5nZSI6eyJtYXhDIjo1MCwibWluQyI6LTEwfSwibm90SW5Vc2VUZW1wZXJhdHVyZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjI2NjAtMToyMDE4IHN0b3JhZ2UgcHJvZmlsZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NTAwMCwicG93ZXJMaW1pdE1heFciOjc1MDAsInBvd2VyTGltaXRNaW5XIjoxNTAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwMjEiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6NS4xMiwicmF0ZWRFbmVyZ3lXaCI6NTEyMCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjIyLCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo5Miwic2FmZXR5TWVhc3VyZXMiOiJJc29sYXRlIERDIGJyZWFrZXIgYmVmb3JlIHNlcnZpY2luZy4gSW5zdWxhdGVkIHRvb2xzIG9ubHkuIFZlbnRpbGF0ZSBlbmNsb3N1cmUgYmVmb3JlIG9wZW5pbmcuIiwic3BhcmVQYXJ0c0NvbnRhY3RzIjoic3BhcmVzQGJhdHRlcmllcy5leGFtcGxlOyArNDkgMzAgNTU1MCAxMjAwIiwic3RhdGVPZkhlYWx0aCI6eyJvaG1pY1Jlc2lzdGFuY2VNb2htIjo5LjEsInBhcmFtZXRlclNldCI6InN0YXRpb25hcnlPckxtdCIsInJlbWFpbmluZ0NhcGFjaXR5UGN0Ijo5OS42LCJyZW1haW5pbmdQb3dlckNhcGFiaWxpdHlQY3QiOjk5LjQsInJlbWFpbmluZ1JvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk5LjEsInNlbGZEaXNjaGFyZ2VSYXRlUGN0UGVyTW9udGgiOjEuNH0sInN0YXRlT2ZIZWFsdGhQY3QiOm51bGwsInRlc3RSZXBvcnRSZXN1bHRzIjoiSUVDIDYyNjE5OjIwMjIgcGFzczsgSUVDIDYzMDU2OjIwMjAgcGFzczsgVU4gMzguMyBwYXNzOyByZXBvcnQgR0MtVFItMjAyNi0wMzAxIiwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIgb3IgQUJDIGRyeSBwb3dkZXI7IGxhcmdlIHZvbHVtZXMgb2Ygd2F0ZXIgZm9yIHRoZXJtYWwgcnVuYXdheSIsInVzYWdlSGlzdG9yeSI6eyJjaGFyZ2VEaXNjaGFyZ2VDeWNsZXMiOjUsIm5lZ2F0aXZlRXZlbnRzIjpbXX0sIndhc3RlQmF0dGVyeUluZm9ybWF0aW9uIjoiUmV0dXJuIHRvIGFuIGF1dGhvcmlzZWQgY29sbGVjdGlvbiBwb2ludC4gRG8gbm90IGluY2luZXJhdGUgb3Igc2hyZWQuIFByb2R1Y2VyIHJlZ2lzdHJhdGlvbiBERS1CQVRULTAwMTIzLiJ9LCJwcm9kdWN0TmFtZSI6Ik9kYWwgU3RhdGlvbmFyeSBTdG9yYWdlIEJhdHRlcnkgTEZQLTUxLjIiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTMuOTg3OTU0ODc4WiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDAyMS8yMS83MWFjOWM4NjQ2OTY1NDBjM2IyMSIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjo4LjF9LCJyZXRlbnRpb25Mb2NrZWQiOnRydWUsInJldGVudGlvblVudGlsIjoiMjAzNi0xMS0wM1QyMDowOToxMy45ODc5NTQ4NzhaIiwic2NoZW1hVmVyc2lvbiI6IjIuOC4wIiwic3RhdHVzIjoiYWN0aXZlIiwidXBkYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy45ODc5NTQ4NzhaIiwidmVyc2lvbiI6MX0.inyiRNW7RsdQojuWFAIjW07Rr6HezmP9kWPsCPYLA50HXEpLHhUYEXWReJH9D2lYoALCO20rUyU-mgcStk9GDQ", "commodityCode": "85076000", "schemaVersion": "2.8.0", "dataCarrierUri": "http://127.0.0.1:1/01/09590000000021/21/71ac9c864696540c3b21", "productCategory": "battery", "productIdentifier": {"gtin": "09590000000021", "scheme": "gs1"}, "facilityIdentifier": "4012345000009", "operatorIdentifier": "DE123456789", "operatorIdentifierScheme": "vat"}', NULL, 0, '2026-11-03 20:09:14.03597+00', NULL, NULL, '2026-11-03 20:09:14.03597+00', '2026-11-03 20:09:14.03597+00', NULL, NULL); +INSERT INTO odal.registry_sync VALUES ('8ea8f014-7ed4-42f9-a32b-f2ddc373d64c', '01a1a307-b67c-706a-a55e-689712fcd122', NULL, 'pending', '{"batchId": "ODL-IND-PB-2026-Q1-007", "modelId": "RP-VRLA-12-200", "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "requestId": "01a1a307-b6ec-705d-a285-a14c31185cd2", "itemSerial": "706aa55e689712fcd122", "passportId": "01a1a307-b67c-706a-a55e-689712fcd122", "granularity": "item", "publishedAt": "2026-11-03T20:09:14.173554734Z", "jwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELVBCLTIwMjYtUTEtMDA3IiwiY28yZVBlclVuaXQiOm51bGwsImNvbW1vZGl0eUNvZGUiOiI4NTA3MjAwMCIsImNvbXBsaWFuY2VSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMTEyNTI2OTQ1WiIsImNvMmVTY29yZSI6bnVsbCwiY29tcGxpYW5jZVN0YXR1cyI6IlBBU1NUSFJPVUdIX05PX1ZBTElEQVRJT04iLCJyZWN5Y2xlZENvbnRlbnRQY3QiOm51bGwsInJlcGFpcmFiaWxpdHlJbmRleCI6bnVsbCwid2FybmluZ3MiOlt7ImNvZGUiOiJiYXR0ZXJ5LnJlY3ljbGVkX2NvbnRlbnQubm90X3lldF9iaW5kaW5nIiwiZmllbGQiOiIvcGxhY2VkT25NYXJrZXREYXRlIiwibWVzc2FnZSI6Ik5vIEFydC4gOCBtaW5pbXVtIHJlY3ljbGVkIHNoYXJlIGJpbmRzIGEgYmF0dGVyeSBwbGFjZWQgb24gdGhlIEVVIG1hcmtldCBvbiAyMDI2LTAyLTEwLiBSdWxlc2V0ICdiYXR0ZXJ5LXJlY3ljbGVkLWNvbnRlbnQtYXJ0OC0yJyBhcHBsaWVzIGZyb20gMjAzMS0wOC0xOC4ifV19LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjEwODA4NTQyN1oiLCJmYWNpbGl0eSI6eyJhZGRyZXNzIjoiV2Vya3N0cmFzc2UgMiwgMTAxMTUgQmVybGluIiwiY291bnRyeSI6IkRFIiwibmFtZSI6IkRlbW8gUGxhbnQiLCJzY2hlbWUiOiJnbG4iLCJ2YWx1ZSI6IjQwMTIzNDUwMDAwMDkifSwiaWQiOiIwMWExYTMwNy1iNjdjLTcwNmEtYTU1ZS02ODk3MTJmY2QxMjIiLCJqd3NTaWduYXR1cmUiOm51bGwsImxpbnRSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMTEyNTMxMDI2WiIsInBhY2tWZXJzaW9uIjoiMS4xLjAifSwibWFudWZhY3R1cmVyIjp7ImFkZHJlc3MiOiJJbmR1c3RyaWVzdHJhc3NlIDgsIDQ3MDU5IER1aXNidXJnLCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vcmhlaW5wb3dlci5leGFtcGxlLy53ZWxsLWtub3duL2RpZC5qc29uIiwibmFtZSI6IlJoZWlucG93ZXIgQmF0dGVyaWVuIEdtYkgifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJMZWFkIiwicmVjeWNsZWRQY3QiOjgyLCJ3ZWlnaHRLZyI6Mzh9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiU3VsZnVyaWMgYWNpZCBlbGVjdHJvbHl0ZSIsInJlY3ljbGVkUGN0IjowLCJ3ZWlnaHRLZyI6OX0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJQb2x5cHJvcHlsZW5lIGNhc2UiLCJyZWN5Y2xlZFBjdCI6NDUsIndlaWdodEtnIjozLjJ9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTAyLTEwIiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYW5vZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiNzQzOS05Mi0xIiwibmFtZSI6IlNwb25nZSBsZWFkIChuZWdhdGl2ZSBhY3RpdmUgbWF0ZXJpYWwpIiwid2VpZ2h0UGN0IjoxMDB9XSwiYmF0dGVyeUNoZW1pc3RyeSI6ImxlYWQtYWNpZCIsImJhdHRlcnlNb2RlbElkIjoiUlAtVlJMQS0xMi0yMDAiLCJiYXR0ZXJ5UGFzc3BvcnROdW1iZXIiOiJVUk46T0RMOkJBVFQ6MDk1OTAwMDAwMDAwMzg6T0RMLUlORC1QQi0yMDI2LVExLTAwNyIsImJhdHRlcnlTdGF0dXMiOiJvcmlnaW5hbCIsImJhdHRlcnlUeXBlIjoiaW5kdXN0cmlhbCIsImJhdHRlcnlXZWlnaHRLZyI6NTIsImNhdGhvZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiMTMwOS02MC0wIiwibmFtZSI6IkxlYWQgZGlveGlkZSAocG9zaXRpdmUgYWN0aXZlIG1hdGVyaWFsKSIsIndlaWdodFBjdCI6MTAwfV0sImNvbW1lcmNpYWxXYXJyYW50eVBlcmlvZE1vbnRocyI6NjAsImNvbXBvbmVudFBhcnROdW1iZXJzIjpbIlJQLVBMQVRFLVBPUy0yMDAiLCJSUC1QTEFURS1ORUctMjAwIiwiUlAtQ0FTRS0xMi0yMDAiLCJSUC1WQUxWRS1WUkxBIl0sImNyaXRpY2FsUmF3TWF0ZXJpYWxzIjpbeyJjYXNOdW1iZXIiOiI3NDQwLTM2LTAiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDTiIsIm5hbWUiOiJBbnRpbW9ueSIsIndlaWdodEdyYW1zIjozODB9XSwiY3ljbGVMaWZlVGVzdENSYXRlIjowLjEsImRpc2Fzc2VtYmx5SW5zdHJ1Y3Rpb25zVXJsIjoiaHR0cHM6Ly9yaGVpbnBvd2VyLmV4YW1wbGUvc2VydmljZS92cmxhLTEyLTIwMC9kaXNhc3NlbWJseSIsImVsZWN0cm9seXRlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6Ijc2NjQtOTMtOSIsIm5hbWUiOiJTdWxmdXJpYyBhY2lkIChhcXVlb3VzLCB-MzclKSIsIndlaWdodFBjdCI6Mzd9LHsiY2FzTnVtYmVyIjoiNzczMi0xOC01IiwibmFtZSI6IldhdGVyIiwid2VpZ2h0UGN0Ijo2M31dLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIFJQLTIwMjYtSU5ELTAwMTQgZGF0ZWQgMjAyNi0wMS0yMCwgcGVyIFJlZ3VsYXRpb24gKEVVKSAyMDIzLzE1NDIgQXJ0LiAxOCIsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjEyMDAsImV4cGVjdGVkTGlmZXRpbWVSZWZlcmVuY2VUZXN0IjoiSUVDIDYwODk2LTIxLzIyLCAyNSBkZWdDLCA1MCUgRG9EIiwiaGF6YXJkU3ltYm9sIjoibGVhZCIsImhhemFyZG91c1N1YnN0YW5jZXMiOlt7ImNhc051bWJlciI6Ijc0MzktOTItMSIsImNvbmNlbnRyYXRpb25QY3QiOjY4LCJuYW1lIjoiTGVhZCJ9LHsiY2FzTnVtYmVyIjoiNzY2NC05My05IiwiY29uY2VudHJhdGlvblBjdCI6MTcsIm5hbWUiOiJTdWxmdXJpYyBhY2lkIn1dLCJpbml0aWFsUm91bmRUcmlwRWZmaWNpZW5jeVBjdCI6ODIsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjozLjUsImludGVybmFsUGFja1Jlc2lzdGFuY2VNb2htIjo0LjIsIm1hbnVmYWN0dXJpbmdEYXRlIjoiMjAyNi0wMS0wOFQwMDowMDowMFoiLCJtYW51ZmFjdHVyaW5nUGxhY2UiOiJEdWlzYnVyZywgREUgKHBsYW50IERVLTMpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IFBiIHN5bWJvbCBwZXIgQXJ0LiAxMyg1KTsgY2FwYWNpdHkgMjAwIEFoIC8gMTIgViIsIm1heGltdW1Wb2x0YWdlViI6MTQuNCwibWluaW1hbFZvbHRhZ2VWIjoxMC41LCJub21pbmFsQ2FwYWNpdHlBaCI6MjAwLCJub21pbmFsVm9sdGFnZVYiOjEyLCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NDAsIm1pbkMiOi0xNX0sIm5vdEluVXNlVGVtcGVyYXR1cmVSZWZlcmVuY2VUZXN0IjoiSUVDIDYwODk2LTIxOjIwMDQgc3RvcmFnZSBjbGF1c2UiLCJvcmlnaW5hbFBvd2VyQ2FwYWJpbGl0eVciOjE4MDAsInBvd2VyTGltaXRNYXhXIjoyNDAwLCJwb3dlckxpbWl0TWluVyI6NTAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwMzgiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6Mi40LCJyYXRlZEVuZXJneVdoIjoyNDAwLCJyZWN5Y2xlZENvbnRlbnRDb2JhbHRQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudExpdGhpdW1QY3QiOm51bGwsInJlY3ljbGVkQ29udGVudE5pY2tlbFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50UmVwb3J0aW5nWWVhciI6MjAyNiwicmVuZXdhYmxlQ29udGVudFBjdCI6Miwicm91bmRUcmlwRWZmaWNpZW5jeUF0SGFsZkN5Y2xlTGlmZVBjdCI6NzQsInNhZmV0eU1lYXN1cmVzIjoiQWNpZCBoYXphcmQuIEZhY2Ugc2hpZWxkIGFuZCBhY2lkLXJlc2lzdGFudCBnbG92ZXMuIERvIG5vdCB0aXAuIFZlbnQgaHlkcm9nZW4gYmVmb3JlIHNlcnZpY2luZy4iLCJzcGFyZVBhcnRzQ29udGFjdHMiOiJzZXJ2aWNlQHJoZWlucG93ZXIuZXhhbXBsZTsgKzQ5IDIwMyA1NTUwIDgwMCIsInN0YXRlT2ZIZWFsdGhQY3QiOm51bGwsInRlc3RSZXBvcnRSZXN1bHRzIjoiSUVDIDYwODk2LTIxLzIyOjIwMDQgcGFzczsgVU4gMjgwMCAobm9uLXNwaWxsYWJsZSkgcGFzczsgcmVwb3J0IFJQLVRSLTIwMjYtMDAyMSIsInVzYWJsZUV4dGluZ3Vpc2hpbmdBZ2VudCI6IkNPMiwgQUJDIGRyeSBwb3dkZXIsIG9yIHdhdGVyOyBuZXV0cmFsaXNlIGFjaWQgc3BpbGxzIHdpdGggc29kYSBhc2giLCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IkxlYWQtYWNpZCBiYXR0ZXJ5IC0gcmV0dXJuIHRvIHJldGFpbGVyIG9yIGF1dGhvcmlzZWQgY29sbGVjdG9yIGZvciByZWN5Y2xpbmcuIE5ldmVyIGRpc3Bvc2UgaW4gaG91c2Vob2xkIHdhc3RlLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDQ1Ny4ifSwicHJvZHVjdE5hbWUiOiJPZGFsIEluZHVzdHJpYWwgU3RhbmRieSBCYXR0ZXJ5IDEyVi0yMDBBaCAoVlJMQSkiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMTczNTU0NzM0WiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDAzOC8yMS83MDZhYTU1ZTY4OTcxMmZjZDEyMiIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjozfSwicmV0ZW50aW9uTG9ja2VkIjp0cnVlLCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuMTczNTU0NzM0WiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMTczNTU0NzM0WiIsInZlcnNpb24iOjF9.G9Xwozbdcl8A1vQTUkgncQp2iTrIGx_pl1x_Ed74t4i9DqXrfM6JYyaTQdCohp1T2LK-73yC2IxW3vW2FfPmCQ", "commodityCode": "85072000", "schemaVersion": "2.8.0", "dataCarrierUri": "http://127.0.0.1:1/01/09590000000038/21/706aa55e689712fcd122", "productCategory": "battery", "productIdentifier": {"gtin": "09590000000038", "scheme": "gs1"}, "facilityIdentifier": "4012345000009", "operatorIdentifier": "DE123456789", "operatorIdentifierScheme": "vat"}', NULL, 0, '2026-11-03 20:09:14.221426+00', NULL, NULL, '2026-11-03 20:09:14.221426+00', '2026-11-03 20:09:14.221426+00', NULL, NULL); +INSERT INTO odal.registry_sync VALUES ('3f87c4ca-eda9-4fae-998b-a1318cdba68f', '01a1a307-b70c-70c1-b4a3-a616d161e997', NULL, 'pending', '{"batchId": "VBX-EV-2026-0421", "modelId": "VBX-EV75-NMC811-P4S96", "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "requestId": "01a1a307-b74b-777d-8295-99a8c1a332d5", "itemSerial": "70c1b4a3a616d161e997", "passportId": "01a1a307-b70c-70c1-b4a3-a616d161e997", "granularity": "item", "publishedAt": "2026-11-03T20:09:14.279493731Z", "jwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJWQlgtRVYtMjAyNi0wNDIxIiwiY28yZVBlclVuaXQiOm51bGwsImNvbW1vZGl0eUNvZGUiOiI4NTA3NjAwMCIsImNvbXBsaWFuY2VSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMjU1MjE0NDE2WiIsImNvMmVTY29yZSI6bnVsbCwiY29tcGxpYW5jZVN0YXR1cyI6IlBBU1NUSFJPVUdIX05PX1ZBTElEQVRJT04iLCJyZWN5Y2xlZENvbnRlbnRQY3QiOm51bGwsInJlcGFpcmFiaWxpdHlJbmRleCI6bnVsbCwid2FybmluZ3MiOlt7ImNvZGUiOiJiYXR0ZXJ5LnJlY3ljbGVkX2NvbnRlbnQubm90X3lldF9iaW5kaW5nIiwiZmllbGQiOiIvcGxhY2VkT25NYXJrZXREYXRlIiwibWVzc2FnZSI6Ik5vIEFydC4gOCBtaW5pbXVtIHJlY3ljbGVkIHNoYXJlIGJpbmRzIGEgYmF0dGVyeSBwbGFjZWQgb24gdGhlIEVVIG1hcmtldCBvbiAyMDI2LTA3LTAxLiBSdWxlc2V0ICdiYXR0ZXJ5LXJlY3ljbGVkLWNvbnRlbnQtYXJ0OC0yJyBhcHBsaWVzIGZyb20gMjAzMS0wOC0xOC4ifV19LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjI1Mjk0MjA0M1oiLCJmYWNpbGl0eSI6eyJhZGRyZXNzIjoiV2Vya3N0cmFzc2UgMiwgMTAxMTUgQmVybGluIiwiY291bnRyeSI6IkRFIiwibmFtZSI6IkRlbW8gUGxhbnQiLCJzY2hlbWUiOiJnbG4iLCJ2YWx1ZSI6IjQwMTIzNDUwMDAwMDkifSwiaWQiOiIwMWExYTMwNy1iNzBjLTcwYzEtYjRhMy1hNjE2ZDE2MWU5OTciLCJqd3NTaWduYXR1cmUiOm51bGwsImxpbnRSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMjU1MjE2MTY3WiIsInBhY2tWZXJzaW9uIjoiMS4xLjAifSwibWFudWZhY3R1cmVyIjp7ImFkZHJlc3MiOiJXZXJrc3RyYXNzZSA1LCA4NTA1NSBJbmdvbHN0YWR0LCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vdm9sdGV4LmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiVm9sdGV4IEJhdHRlcnkgR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkxpdGhpdW0gTmlja2VsIE1hbmdhbmVzZSBDb2JhbHQgT3hpZGUgKE5NQyA4MTEpIiwicmVjeWNsZWRQY3QiOjEzLCJ3ZWlnaHRLZyI6MjEwfSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkdyYXBoaXRlL3NpbGljb24gYW5vZGUiLCJyZWN5Y2xlZFBjdCI6NCwid2VpZ2h0S2ciOjk1fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkFsdW1pbml1bSBwYWNrIGhvdXNpbmciLCJyZWN5Y2xlZFBjdCI6NzEsIndlaWdodEtnIjo4NX1dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInBsYWNlZE9uTWFya2V0RGF0ZSI6IjIwMjYtMDctMDEiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJhbm9kZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJuYW1lIjoiR3JhcGhpdGUiLCJ3ZWlnaHRQY3QiOjkwfSx7ImNhc051bWJlciI6IjEwMDk3LTI4LTYiLCJuYW1lIjoiU2lsaWNvbiBveGlkZSIsIndlaWdodFBjdCI6Nn0seyJjYXNOdW1iZXIiOiI5MDAzLTU1LTgiLCJuYW1lIjoiU0JSL0NNQyBiaW5kZXIiLCJ3ZWlnaHRQY3QiOjR9XSwiYmF0dGVyeUNoZW1pc3RyeSI6Ik5NQyIsImJhdHRlcnlNb2RlbElkIjoiVkJYLUVWNzUtTk1DODExLVA0Uzk2IiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDQ1OlZCWC1FVi0yMDI2LTA0MjEiLCJiYXR0ZXJ5U3RhdHVzIjoib3JpZ2luYWwiLCJiYXR0ZXJ5VHlwZSI6ImV2IiwiYmF0dGVyeVdlaWdodEtnIjo0NjgsImNhcGFjaXR5VGhyZXNob2xkRm9yRXhoYXVzdGlvblBjdCI6NzAsImNhdGhvZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiMzQ2NDE3LTk3LTgiLCJuYW1lIjoiTGlOaTAuOE1uMC4xQ28wLjFPMiIsIndlaWdodFBjdCI6OTYuNX0seyJjYXNOdW1iZXIiOiIyNDkzNy03OS05IiwibmFtZSI6IlBWREYgYmluZGVyIiwid2VpZ2h0UGN0IjoyfSx7ImNhc051bWJlciI6IjEzMzMtODYtNCIsIm5hbWUiOiJDb25kdWN0aXZlIGNhcmJvbiBibGFjayIsIndlaWdodFBjdCI6MS41fV0sImNvbW1lcmNpYWxXYXJyYW50eVBlcmlvZE1vbnRocyI6OTYsImNvbXBvbmVudFBhcnROdW1iZXJzIjpbIlZCWC1DRUxMLTQ2ODAtTk1DODExIiwiVkJYLU1PRFVMRS0yNFMiLCJWQlgtQk1TLU1BU1RFUi1WNCIsIlZCWC1QQUNLLUhWLTc1Il0sImNyaXRpY2FsUmF3TWF0ZXJpYWxzIjpbeyJjYXNOdW1iZXIiOiI3NDQwLTQ4LTQiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDRCIsIm5hbWUiOiJDb2JhbHQiLCJ3ZWlnaHRHcmFtcyI6NTIwMH0seyJjYXNOdW1iZXIiOiI3NDM5LTkzLTIiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDTCIsIm5hbWUiOiJMaXRoaXVtIiwid2VpZ2h0R3JhbXMiOjgxMDB9LHsiY2FzTnVtYmVyIjoiNzQ0MC0wMi0wIiwiY291bnRyeU9mT3JpZ2luIjoiSUQiLCJuYW1lIjoiTmlja2VsIiwid2VpZ2h0R3JhbXMiOjMyMDAwfSx7ImNhc051bWJlciI6Ijc3ODItNDItNSIsImNvdW50cnlPZk9yaWdpbiI6Ik1aIiwibmFtZSI6Ik5hdHVyYWwgZ3JhcGhpdGUiLCJ3ZWlnaHRHcmFtcyI6NzQwMDB9XSwiY3ljbGVMaWZlVGVzdENSYXRlIjoxLCJkaXNhc3NlbWJseUluc3RydWN0aW9uc1VybCI6Imh0dHBzOi8vdm9sdGV4LmV4YW1wbGUvc2VydmljZS9ldjc1L2Rpc2Fzc2VtYmx5IiwiZHluYW1pY1BlcmZvcm1hbmNlIjp7ImNhcGFjaXR5RmFkZVBjdCI6MC45LCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjoxNzg1LCJleHBlY3RlZExpZmV0aW1lWWVhcnMiOjkuNSwiaW50ZXJuYWxSZXNpc3RhbmNlSW5jcmVhc2VQY3QiOjEsImludGVybmFsUmVzaXN0YW5jZU1vaG0iOjQyLjQsInBvd2VyRmFkZVBjdCI6MC42LCJwb3dlclciOjE0ODAwMCwicmF0ZWRDYXBhY2l0eUFoIjoyMDksInJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjkzLjZ9LCJlbGVjdHJvbHl0ZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI5Ni00OS0xIiwibmFtZSI6IkV0aHlsZW5lIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6MzB9LHsiY2FzTnVtYmVyIjoiNjIzLTUzLTAiLCJuYW1lIjoiRXRoeWwgbWV0aHlsIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6NTV9LHsiY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUiLCJ3ZWlnaHRQY3QiOjEzfSx7ImNhc051bWJlciI6Ijg3Mi0zNi02IiwibmFtZSI6IlZpbnlsZW5lIGNhcmJvbmF0ZSBhZGRpdGl2ZSIsIndlaWdodFBjdCI6Mn1dLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIFZCWC0yMDI2LUVWLTAxNDIgZGF0ZWQgMjAyNi0wNS0xOCwgcGVyIFJlZ3VsYXRpb24gKEVVKSAyMDIzLzE1NDIgQXJ0LiAxOCIsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjE4MDAsImV4cGVjdGVkTGlmZXRpbWVSZWZlcmVuY2VUZXN0IjoiVU5FQ0UgR1RSIDIyIC8gSUVDIDYyNjYwLTE6MjAxOCwgMjUgZGVnQyIsImhhemFyZG91c1N1YnN0YW5jZXMiOlt7ImNhc051bWJlciI6Ijc3ODYtODEtNCIsImNvbmNlbnRyYXRpb25QY3QiOjIuNCwibmFtZSI6Ik5pY2tlbCBzdWxmYXRlIn0seyJjYXNOdW1iZXIiOiIxMDEyNC00My0zIiwiY29uY2VudHJhdGlvblBjdCI6MC45LCJuYW1lIjoiQ29iYWx0IHN1bGZhdGUifSx7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJjb25jZW50cmF0aW9uUGN0IjowLjksIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUifV0sImluaXRpYWxSb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5NCwiaW50ZXJuYWxDZWxsUmVzaXN0YW5jZU1vaG0iOjEuMSwiaW50ZXJuYWxQYWNrUmVzaXN0YW5jZU1vaG0iOjQyLCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDUtMTFUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiSW5nb2xzdGFkdCwgREUgKHBsYW50IElORy0xKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIHBlciBBcnQuIDEzKDQpOyBjYXBhY2l0eSA3NSBrV2g7IEhWIHdhcm5pbmcgbGFiZWwiLCJtYXhpbXVtVm9sdGFnZVYiOjQwMy4yLCJtaW5pbWFsVm9sdGFnZVYiOjI3MCwibm9taW5hbENhcGFjaXR5QWgiOjIxMSwibm9taW5hbFZvbHRhZ2VWIjozNTUsIm5vdEluVXNlVGVtcGVyYXR1cmVSYW5nZSI6eyJtYXhDIjo1NSwibWluQyI6LTMwfSwibm90SW5Vc2VUZW1wZXJhdHVyZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjI2NjAtMToyMDE4IHN0b3JhZ2UgcHJvZmlsZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6MTUwMDAwLCJwb3dlckxpbWl0TWF4VyI6MjIwMDAwLCJwb3dlckxpbWl0TWluVyI6NTAwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDQ1Iiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOjc1LCJyYXRlZEVuZXJneVdoIjo3NTAwMCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjE2LCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo4OCwic2FmZXR5TWVhc3VyZXMiOiJIaWdoLXZvbHRhZ2UgKD42MCBWIERDKS4gSFYtcXVhbGlmaWVkIHBlcnNvbm5lbCBvbmx5LiBTZXJ2aWNlIGRpc2Nvbm5lY3QgcGx1ZyByZW1vdmVkIGFuZCBsb2NrZWQgb3V0LiBJbnN1bGF0ZWQgUFBFIGNsYXNzIDAwLiIsInNwYXJlUGFydHNDb250YWN0cyI6Imh2LXNlcnZpY2VAdm9sdGV4LmV4YW1wbGU7ICs0OSA4NDEgNTU1MCA0MDAgKGNlcnRpZmllZCBIViB3b3Jrc2hvcHMgb25seSkiLCJzdGF0ZU9mSGVhbHRoIjp7InBhcmFtZXRlclNldCI6ImVsZWN0cmljVmVoaWNsZSIsInNvY2VQY3QiOjk5LjF9LCJzdGF0ZU9mSGVhbHRoUGN0IjpudWxsLCJ0ZXN0UmVwb3J0UmVzdWx0cyI6IklFQyA2MjY2MC0yOjIwMTggcGFzczsgSVNPIDY0NjktMToyMDE5IHBhc3M7IFVOIDM4LjMgcGFzczsgcmVwb3J0IFZCWC1UUi0yMDI2LTA1MDciLCJ1c2FibGVFeHRpbmd1aXNoaW5nQWdlbnQiOiJMYXJnZSB2b2x1bWVzIG9mIHdhdGVyIGZvciBjb29saW5nOyBkbyBub3QgdXNlIENPMiBvbiBjZWxsIGZpcmUiLCJ1c2FnZUhpc3RvcnkiOnsiY2hhcmdlRGlzY2hhcmdlQ3ljbGVzIjo4LCJuZWdhdGl2ZUV2ZW50cyI6W10sInN0YXRlT2ZDaGFyZ2UiOlt7InJlY29yZGVkQXQiOiIyMDI2LTA3LTE1VDA5OjAwOjAwWiIsInN0YXRlT2ZDaGFyZ2VQY3QiOjgyfSx7InJlY29yZGVkQXQiOiIyMDI2LTA3LTIyVDA5OjAwOjAwWiIsInN0YXRlT2ZDaGFyZ2VQY3QiOjY0fV19LCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IkVWIGJhdHRlcnkgLSBoYW5kbGVkIGJ5IHRoZSB2ZWhpY2xlIG1hbnVmYWN0dXJlciB0YWtlLWJhY2sgc2NoZW1lLiBEbyBub3Qgb3BlbiBvciBkaXNjaGFyZ2UuIENvbnRhY3QgaHYtc2VydmljZUB2b2x0ZXguZXhhbXBsZS4gUHJvZHVjZXIgcmVnaXN0cmF0aW9uIERFLUJBVFQtMDA5ODEuIn0sInByb2R1Y3ROYW1lIjoiVm9sdGV4IEVWIFRyYWN0aW9uIEJhdHRlcnkgNzUga1doIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjI3OTQ5MzczMVoiLCJxckNvZGVVcmwiOiJodHRwOi8vMTI3LjAuMC4xOjEvMDEvMDk1OTAwMDAwMDAwNDUvMjEvNzBjMWI0YTNhNjE2ZDE2MWU5OTciLCJyZXBhaXJhYmlsaXR5U2NvcmUiOnsib3ZlcmFsbCI6NS41fSwicmV0ZW50aW9uTG9ja2VkIjp0cnVlLCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuMjc5NDkzNzMxWiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMjc5NDkzNzMxWiIsInZlcnNpb24iOjF9.LIfiNtturpbmDIHCfCRaAxGyQUrBS8P8r83iZiR0hWhcKGeimVyYBCYrLWjy0wS6hdZBse-dnrTGsgtdrOQSBg", "commodityCode": "85076000", "schemaVersion": "2.8.0", "dataCarrierUri": "http://127.0.0.1:1/01/09590000000045/21/70c1b4a3a616d161e997", "productCategory": "battery", "productIdentifier": {"gtin": "09590000000045", "scheme": "gs1"}, "facilityIdentifier": "4012345000009", "operatorIdentifier": "DE123456789", "operatorIdentifierScheme": "vat"}', NULL, 0, '2026-11-03 20:09:14.316459+00', NULL, NULL, '2026-11-03 20:09:14.316459+00', '2026-11-03 20:09:14.316459+00', NULL, NULL); +INSERT INTO odal.registry_sync VALUES ('21556703-f16a-4364-9848-5f1cb6adfe50', '01a1a307-b792-7764-a50c-b10fe65bf932', NULL, 'pending', '{"batchId": "VBX-LMT-2026-0188", "modelId": "VBX-LMT36-14-10S", "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "requestId": "01a1a307-b7dc-7605-8f6b-6ab6f187a056", "itemSerial": "7764a50cb10fe65bf932", "passportId": "01a1a307-b792-7764-a50c-b10fe65bf932", "granularity": "item", "publishedAt": "2026-11-03T20:09:14.420275202Z", "jwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJWQlgtTE1ULTIwMjYtMDE4OCIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjM4OTU1NTc0M1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNi0xNS4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMycgYXBwbGllcyBmcm9tIDIwMzYtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC4zODYzNzA1MjdaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjc5Mi03NzY0LWE1MGMtYjEwZmU2NWJmOTMyIiwiandzU2lnbmF0dXJlIjpudWxsLCJsaW50UmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjM4OTU1ODI5NVoiLCJwYWNrVmVyc2lvbiI6IjEuMS4wIn0sIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiV2Vya3N0cmFzc2UgNSwgODUwNTUgSW5nb2xzdGFkdCwgREUiLCJjb3VudHJ5IjpudWxsLCJkaWRXZWJVcmwiOiJodHRwczovL3ZvbHRleC5leGFtcGxlLy53ZWxsLWtub3duL2RpZC5qc29uIiwibmFtZSI6IlZvbHRleCBCYXR0ZXJ5IEdtYkgifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJMaXRoaXVtIElyb24gUGhvc3BoYXRlIiwicmVjeWNsZWRQY3QiOjcsIndlaWdodEtnIjoxLjR9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiR3JhcGhpdGUiLCJyZWN5Y2xlZFBjdCI6Mywid2VpZ2h0S2ciOjAuNn0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJBQlMvUEMgaG91c2luZyIsInJlY3ljbGVkUGN0IjozMCwid2VpZ2h0S2ciOjAuNX1dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInBsYWNlZE9uTWFya2V0RGF0ZSI6IjIwMjYtMDYtMTUiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJhbm9kZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJuYW1lIjoiR3JhcGhpdGUiLCJ3ZWlnaHRQY3QiOjk2fSx7ImNhc051bWJlciI6IjkwMDMtNTUtOCIsIm5hbWUiOiJTQlIvQ01DIGJpbmRlciIsIndlaWdodFBjdCI6NH1dLCJiYXR0ZXJ5Q2hlbWlzdHJ5IjoiTEZQIiwiYmF0dGVyeU1vZGVsSWQiOiJWQlgtTE1UMzYtMTQtMTBTIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDUyOlZCWC1MTVQtMjAyNi0wMTg4IiwiYmF0dGVyeVN0YXR1cyI6Im9yaWdpbmFsIiwiYmF0dGVyeVR5cGUiOiJsbXQiLCJiYXR0ZXJ5V2VpZ2h0S2ciOjIuNiwiY2F0aG9kZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiIxNTM2NS0xNC03IiwibmFtZSI6IkxpRmVQTzQiLCJ3ZWlnaHRQY3QiOjk1fSx7ImNhc051bWJlciI6IjI0OTM3LTc5LTkiLCJuYW1lIjoiUFZERiBiaW5kZXIiLCJ3ZWlnaHRQY3QiOjN9LHsiY2FzTnVtYmVyIjoiMTMzMy04Ni00IiwibmFtZSI6IkNvbmR1Y3RpdmUgY2FyYm9uIGJsYWNrIiwid2VpZ2h0UGN0IjoyfV0sImNvbW1lcmNpYWxXYXJyYW50eVBlcmlvZE1vbnRocyI6MjQsImNvbXBvbmVudFBhcnROdW1iZXJzIjpbIlZCWC1DRUxMLTIxNzAwLUxGUCIsIlZCWC1CTVMtMTBTLVYyIiwiVkJYLUxNVC1FTkNMLTM2Il0sImNyaXRpY2FsUmF3TWF0ZXJpYWxzIjpbeyJjYXNOdW1iZXIiOiI3NDM5LTkzLTIiLCJjb3VudHJ5T2ZPcmlnaW4iOiJBVSIsIm5hbWUiOiJMaXRoaXVtIiwid2VpZ2h0R3JhbXMiOjE2NX0seyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDTiIsIm5hbWUiOiJOYXR1cmFsIGdyYXBoaXRlIiwid2VpZ2h0R3JhbXMiOjYwMH1dLCJjeWNsZUxpZmVUZXN0Q1JhdGUiOjEsImRpc2Fzc2VtYmx5SW5zdHJ1Y3Rpb25zVXJsIjoiaHR0cHM6Ly92b2x0ZXguZXhhbXBsZS9zZXJ2aWNlL2xtdDM2LTE0L2Rpc2Fzc2VtYmx5IiwiZHluYW1pY1BlcmZvcm1hbmNlIjp7ImNhcGFjaXR5RmFkZVBjdCI6MC43LCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjo5OTUsImludGVybmFsUmVzaXN0YW5jZU1vaG0iOjk1LjQsInJhdGVkQ2FwYWNpdHlBaCI6MTMuOSwicm91bmRUcmlwRWZmaWNpZW5jeVBjdCI6OTQuN30sImVsZWN0cm9seXRlTWF0ZXJpYWwiOlt7ImNhc051bWJlciI6Ijk2LTQ5LTEiLCJuYW1lIjoiRXRoeWxlbmUgY2FyYm9uYXRlIiwid2VpZ2h0UGN0IjozOH0seyJjYXNOdW1iZXIiOiI2MTYtMzgtNiIsIm5hbWUiOiJEaW1ldGh5bCBjYXJib25hdGUiLCJ3ZWlnaHRQY3QiOjQ5fSx7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIiwid2VpZ2h0UGN0IjoxM31dLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIFZCWC0yMDI2LUxNVC0wMDg4IGRhdGVkIDIwMjYtMDUtMTIsIHBlciBSZWd1bGF0aW9uIChFVSkgMjAyMy8xNTQyIEFydC4gMTgiLCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjoxMDAwLCJleHBlY3RlZExpZmV0aW1lUmVmZXJlbmNlVGVzdCI6IkVOIDUwNjA0LTE6MjAxNiwgMjUgZGVnQywgMTAwJSBEb0QiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiIyMTMyNC00MC0zIiwiY29uY2VudHJhdGlvblBjdCI6MC42LCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIn1dLCJpbml0aWFsUm91bmRUcmlwRWZmaWNpZW5jeVBjdCI6OTUsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjo4LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6OTUsIm1hbnVmYWN0dXJpbmdEYXRlIjoiMjAyNi0wNS0wNVQwMDowMDowMFoiLCJtYW51ZmFjdHVyaW5nUGxhY2UiOiJJbmdvbHN0YWR0LCBERSAocGxhbnQgSU5HLTIpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDE0IEFoIC8gNTA0IFdoOyAzNiBWIExNVCIsIm1heGltdW1Wb2x0YWdlViI6NDIsIm1pbmltYWxWb2x0YWdlViI6MzAsIm5vbWluYWxDYXBhY2l0eUFoIjoxNCwibm9taW5hbFZvbHRhZ2VWIjozNiwibm90SW5Vc2VUZW1wZXJhdHVyZVJhbmdlIjp7Im1heEMiOjQ1LCJtaW5DIjotMTB9LCJub3RJblVzZVRlbXBlcmF0dXJlUmVmZXJlbmNlVGVzdCI6IkVOIDUwNjA0LTE6MjAxNiBzdG9yYWdlIGNsYXVzZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NTAwLCJwb3dlckxpbWl0TWF4VyI6NzUwLCJwb3dlckxpbWl0TWluVyI6MTAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwNTIiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6MC41MDQsInJhdGVkRW5lcmd5V2giOjUwNCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjEyLCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo5MCwic2FmZXR5TWVhc3VyZXMiOiJEbyBub3Qgb3BlbiB0aGUgcGFjay4gUmVwbGFjZSB2aWEgYXV0aG9yaXNlZCBkZWFsZXIuIEtlZXAgYXdheSBmcm9tIGhlYXQgYWJvdmUgNjAgZGVnQy4iLCJzcGFyZVBhcnRzQ29udGFjdHMiOiJzcGFyZXNAdm9sdGV4LmV4YW1wbGU7ICs0OSA4NDEgNTU1MCA0MTAiLCJzdGF0ZU9mSGVhbHRoIjp7InBhcmFtZXRlclNldCI6InN0YXRpb25hcnlPckxtdCIsInJlbWFpbmluZ0NhcGFjaXR5UGN0Ijo5OS4zLCJyZW1haW5pbmdQb3dlckNhcGFiaWxpdHlQY3QiOjk4LjgsInNlbGZEaXNjaGFyZ2VSYXRlUGN0UGVyTW9udGgiOjN9LCJzdGF0ZU9mSGVhbHRoUGN0IjpudWxsLCJ0ZXN0UmVwb3J0UmVzdWx0cyI6IkVOIDUwNjA0LTE6MjAxNiBwYXNzOyBVTiAzOC4zIHBhc3M7IHJlcG9ydCBWQlgtVFItMjAyNi0wMzExIiwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIgb3IgQUJDIGRyeSBwb3dkZXI7IHN1Ym1lcmdlIGluIHdhdGVyIGlmIHNhZmUgdG8gZG8gc28iLCJ1c2FnZUhpc3RvcnkiOnsiY2hhcmdlRGlzY2hhcmdlQ3ljbGVzIjo2fSwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJSZXR1cm4gdGhlIExNVCBiYXR0ZXJ5IHRvIGFueSBiaWN5Y2xlIHJldGFpbGVyIG9yIG11bmljaXBhbCBjb2xsZWN0aW9uIHBvaW50LiBEbyBub3QgcGxhY2UgaW4gaG91c2Vob2xkIHdhc3RlLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDk4MS4ifSwicHJvZHVjdE5hbWUiOiJWb2x0ZXggZS1CaWtlIEJhdHRlcnkgMzZWIDE0QWgiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNDIwMjc1MjAyWiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDA1Mi8yMS83NzY0YTUwY2IxMGZlNjViZjkzMiIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjo2fSwicmV0ZW50aW9uTG9ja2VkIjp0cnVlLCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuNDIwMjc1MjAyWiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNDIwMjc1MjAyWiIsInZlcnNpb24iOjF9.7Gb05PPET9E6Le1xshL4REUbMbrxVjYZZb_BhBUxVHOSCfwACrojwS3Qw55igxO8TzuetARKPDu1Y4tK5xlCAg", "commodityCode": "85076000", "schemaVersion": "2.8.0", "dataCarrierUri": "http://127.0.0.1:1/01/09590000000052/21/7764a50cb10fe65bf932", "productCategory": "battery", "productIdentifier": {"gtin": "09590000000052", "scheme": "gs1"}, "facilityIdentifier": "4012345000009", "operatorIdentifier": "DE123456789", "operatorIdentifierScheme": "vat"}', NULL, 0, '2026-11-03 20:09:14.460817+00', NULL, NULL, '2026-11-03 20:09:14.460817+00', '2026-11-03 20:09:14.460817+00', NULL, NULL); +INSERT INTO odal.registry_sync VALUES ('f77ce1dc-036e-4572-a244-0a39e1a4153a', '01a1a307-b7f5-72ed-abdb-036d686a2932', NULL, 'pending', '{"batchId": "ODL-PORT-2026-0342", "modelId": "RP-AA-NIMH-2000-4", "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "requestId": "01a1a307-b81e-74ed-8ac2-ee1743edd959", "itemSerial": "72edabdb036d686a2932", "passportId": "01a1a307-b7f5-72ed-abdb-036d686a2932", "granularity": "item", "publishedAt": "2026-11-03T20:09:14.508496352Z", "jwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtUE9SVC0yMDI2LTAzNDIiLCJjbzJlUGVyVW5pdCI6eyJ2YWx1ZUtnIjowLjQyfSwiY29tbW9kaXR5Q29kZSI6Ijg1MDczMDAwIiwiY29tcGxpYW5jZVJlc3VsdCI6eyJhc3Nlc3NlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODc4MTAwOTVaIiwiY28yZVNjb3JlIjowLjQyLCJjb21wbGlhbmNlU3RhdHVzIjoiUEFTU1RIUk9VR0hfTk9fVkFMSURBVElPTiIsInJlY3ljbGVkQ29udGVudFBjdCI6bnVsbCwicmVwYWlyYWJpbGl0eUluZGV4IjpudWxsfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40ODU4NzQxOTBaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjdmNS03MmVkLWFiZGItMDM2ZDY4NmEyOTMyIiwiandzU2lnbmF0dXJlIjpudWxsLCJsaW50UmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjQ4NzgxMjE5N1oiLCJwYWNrVmVyc2lvbiI6IjEuMS4wIn0sIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiSW5kdXN0cmllc3RyYXNzZSA4LCA0NzA1OSBEdWlzYnVyZywgREUiLCJjb3VudHJ5IjpudWxsLCJkaWRXZWJVcmwiOiJodHRwczovL3JoZWlucG93ZXIuZXhhbXBsZS8ud2VsbC1rbm93bi9kaWQuanNvbiIsIm5hbWUiOiJSaGVpbnBvd2VyIEJhdHRlcmllbiBHbWJIIn0sIm1hdGVyaWFscyI6W3siY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiTmlja2VsIiwicmVjeWNsZWRQY3QiOjIwLCJ3ZWlnaHRLZyI6MC4wNDV9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiU3RlZWwgY2FuIiwicmVjeWNsZWRQY3QiOjYwLCJ3ZWlnaHRLZyI6MC4wM31dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInBsYWNlZE9uTWFya2V0RGF0ZSI6IjIwMjYtMDQtMDEiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJiYXR0ZXJ5Q2hlbWlzdHJ5IjoiTmlNSCIsImJhdHRlcnlNb2RlbElkIjoiUlAtQUEtTklNSC0yMDAwLTQiLCJiYXR0ZXJ5U3RhdHVzIjoib3JpZ2luYWwiLCJiYXR0ZXJ5VHlwZSI6InBvcnRhYmxlIiwiYmF0dGVyeVdlaWdodEtnIjowLjEyLCJjbzJlUGVyVW5pdEtnIjowLjQyLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiIxMzEwLTU4LTMiLCJjb25jZW50cmF0aW9uUGN0IjozLCJuYW1lIjoiUG90YXNzaXVtIGh5ZHJveGlkZSJ9XSwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTAyLTIwVDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkR1aXNidXJnLCBERSAocGxhbnQgRFUtMSkiLCJtYXJraW5nSW5mb3JtYXRpb24iOiJTZXBhcmF0ZS1jb2xsZWN0aW9uIHN5bWJvbCBwZXIgQXJ0LiAxMyg0KTsgY2FwYWNpdHkgMjAwMCBtQWg7IHJlY2hhcmdlYWJsZSIsIm5vbWluYWxDYXBhY2l0eUFoIjoyLCJub21pbmFsVm9sdGFnZVYiOjEuMiwib3BlcmF0aW5nVGVtcE1heEMiOjQ1LCJvcGVyYXRpbmdUZW1wTWluQyI6LTEwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDY5Iiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOm51bGwsInJhdGVkRW5lcmd5V2giOjIuNCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjowLCJyZWN5Y2xlZENvbnRlbnRMZWFkUGN0IjowLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjowLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOjE4LCJzdGF0ZU9mSGVhbHRoUGN0IjpudWxsLCJ1c2FibGVFeHRpbmd1aXNoaW5nQWdlbnQiOiJXYXRlciwgQ08yLCBvciBkcnkgcG93ZGVyIiwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJSZXR1cm4gc3BlbnQgY2VsbHMgdG8gYW55IHJldGFpbGVyIGNvbGxlY3Rpb24gYm94LiBEbyBub3QgaW5jaW5lcmF0ZS4gUHJvZHVjZXIgcmVnaXN0cmF0aW9uIERFLUJBVFQtMDA0NTcuIn0sInByb2R1Y3ROYW1lIjoiT2RhbCBQb3J0YWJsZSBQb3dlciBQYWNrIEFBIE5pTUggKDQtcGFjaykiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNTA4NDk2MzUyWiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDA2OS8yMS83MmVkYWJkYjAzNmQ2ODZhMjkzMiIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjoxfSwicmV0ZW50aW9uTG9ja2VkIjp0cnVlLCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuNTA4NDk2MzUyWiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNTA4NDk2MzUyWiIsInZlcnNpb24iOjF9.j3mIBOcpst6hdk-oskF3Fe891VAWjN_Th78F8zv0jxl6oOIRH4o6YCkVtnw3-hY8Db4ICsweUisPJx-qfqVTDw", "commodityCode": "85073000", "schemaVersion": "2.8.0", "dataCarrierUri": "http://127.0.0.1:1/01/09590000000069/21/72edabdb036d686a2932", "productCategory": "battery", "productIdentifier": {"gtin": "09590000000069", "scheme": "gs1"}, "facilityIdentifier": "4012345000009", "operatorIdentifier": "DE123456789", "operatorIdentifierScheme": "vat"}', NULL, 0, '2026-11-03 20:09:14.527408+00', NULL, NULL, '2026-11-03 20:09:14.527408+00', '2026-11-03 20:09:14.550274+00', 'suspended', NULL); +INSERT INTO odal.registry_sync VALUES ('48e09763-aa09-4671-9b2f-df7fae7c3b8d', '01a1a307-b84c-7245-8576-ea67194809ea', NULL, 'pending', '{"batchId": "ODL-IND-LFP-2026-Q2-014", "modelId": "GC-ESS-LFP512-100", "facility": {"name": "Demo Plant", "value": "4012345000009", "scheme": "gln", "address": "Werkstrasse 2, 10115 Berlin", "country": "DE"}, "requestId": "01a1a307-b881-776a-b3b1-bbb7f6871ff8", "itemSerial": "71ac9c864696540c3b21", "passportId": "01a1a307-b84c-7245-8576-ea67194809ea", "granularity": "item", "publishedAt": "2026-11-03T20:09:14.585049272Z", "jwsSignature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELUxGUC0yMDI2LVEyLTAxNCIsImNhcnJpZXJTZXJpYWwiOiI3MWFjOWM4NjQ2OTY1NDBjM2IyMSIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjU3MzQ3NDc0N1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNS0yMC4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC41NzIwNTE3MzRaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjg0Yy03MjQ1LTg1NzYtZWE2NzE5NDgwOWVhIiwiandzU2lnbmF0dXJlIjpudWxsLCJsaW50UmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjU3MzQ3NzE4NVoiLCJwYWNrVmVyc2lvbiI6IjEuMS4wIn0sIm1hbnVmYWN0dXJlciI6eyJhZGRyZXNzIjoiUHJlbnpsYXVlciBCZXJnIDEyLCAxMDQwNSBCZXJsaW4sIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly9iYXR0ZXJpZXMuZXhhbXBsZS8ud2VsbC1rbm93bi9kaWQuanNvbiIsIm5hbWUiOiJFeGFtcGxlIEJhdHRlcmllcyBHbWJIIn0sIm1hdGVyaWFscyI6W3siY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiTGl0aGl1bSBJcm9uIFBob3NwaGF0ZSIsInJlY3ljbGVkUGN0Ijo4LCJ3ZWlnaHRLZyI6MjJ9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiR3JhcGhpdGUiLCJyZWN5Y2xlZFBjdCI6Niwid2VpZ2h0S2ciOjkuNX0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJTdGVlbCBlbmNsb3N1cmUiLCJyZWN5Y2xlZFBjdCI6ODIsIndlaWdodEtnIjoxMn1dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInBsYWNlZE9uTWFya2V0RGF0ZSI6IjIwMjYtMDUtMjAiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJhbm9kZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJuYW1lIjoiR3JhcGhpdGUiLCJ3ZWlnaHRQY3QiOjk2fSx7ImNhc051bWJlciI6IjkwMDMtNTUtOCIsIm5hbWUiOiJTQlIvQ01DIGJpbmRlciIsIndlaWdodFBjdCI6M30seyJjYXNOdW1iZXIiOiIxMzMzLTg2LTQiLCJuYW1lIjoiQ29uZHVjdGl2ZSBjYXJib24gYmxhY2siLCJ3ZWlnaHRQY3QiOjF9XSwiYmF0dGVyeUNoZW1pc3RyeSI6IkxGUCIsImJhdHRlcnlNb2RlbElkIjoiR0MtRVNTLUxGUDUxMi0xMDAiLCJiYXR0ZXJ5UGFzc3BvcnROdW1iZXIiOiJVUk46T0RMOkJBVFQ6MDk1OTAwMDAwMDAwMjE6T0RMLUlORC1MRlAtMjAyNi1RMi0wMTQiLCJiYXR0ZXJ5U3RhdHVzIjoib3JpZ2luYWwiLCJiYXR0ZXJ5VHlwZSI6ImluZHVzdHJpYWwiLCJiYXR0ZXJ5V2VpZ2h0S2ciOjQ2LjUsImNhdGhvZGVNYXRlcmlhbCI6W3siY2FzTnVtYmVyIjoiMTUzNjUtMTQtNyIsIm5hbWUiOiJMaUZlUE80Iiwid2VpZ2h0UGN0Ijo5NX0seyJjYXNOdW1iZXIiOiIyNDkzNy03OS05IiwibmFtZSI6IlBWREYgYmluZGVyIiwid2VpZ2h0UGN0IjozfSx7ImNhc051bWJlciI6IjEzMzMtODYtNCIsIm5hbWUiOiJDb25kdWN0aXZlIGNhcmJvbiBibGFjayIsIndlaWdodFBjdCI6Mn1dLCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjE0NCwiY29tcG9uZW50UGFydE51bWJlcnMiOlsiR0MtQ0VMTC0zMjcwMC1MRlAiLCJHQy1CTVMtMTZTLVYyIiwiR0MtRVNTLUVOQ0wtNTEyIl0sImNyaXRpY2FsUmF3TWF0ZXJpYWxzIjpbeyJjYXNOdW1iZXIiOiI3NDM5LTkzLTIiLCJjb3VudHJ5T2ZPcmlnaW4iOiJBVSIsIm5hbWUiOiJMaXRoaXVtIiwid2VpZ2h0R3JhbXMiOjI2MDB9LHsiY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwiY291bnRyeU9mT3JpZ2luIjoiQ04iLCJuYW1lIjoiTmF0dXJhbCBncmFwaGl0ZSIsIndlaWdodEdyYW1zIjo5NTAwfV0sImN5Y2xlTGlmZVRlc3RDUmF0ZSI6MC4zMywiZGlzYXNzZW1ibHlJbnN0cnVjdGlvbnNVcmwiOiJodHRwczovL2JhdHRlcmllcy5leGFtcGxlL3NlcnZpY2UvZXNzLWxmcDUxMi9kaXNhc3NlbWJseSIsImR5bmFtaWNQZXJmb3JtYW5jZSI6eyJjYXBhY2l0eUZhZGVQY3QiOjAuNCwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6NTk4MCwiaW50ZXJuYWxSZXNpc3RhbmNlTW9obSI6OS4xLCJyYXRlZENhcGFjaXR5QWgiOjk5LjYsInJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk2LjJ9LCJlbGVjdHJvbHl0ZU1hdGVyaWFsIjpbeyJjYXNOdW1iZXIiOiI5Ni00OS0xIiwibmFtZSI6IkV0aHlsZW5lIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6Mzh9LHsiY2FzTnVtYmVyIjoiNjIzLTUzLTAiLCJuYW1lIjoiRXRoeWwgbWV0aHlsIGNhcmJvbmF0ZSIsIndlaWdodFBjdCI6NDl9LHsiY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUiLCJ3ZWlnaHRQY3QiOjEzfV0sImV1RGVjbGFyYXRpb25PZkNvbmZvcm1pdHkiOiJFVS1Eb0MgR0MtMjAyNi1JTkQtMDA2NyBkYXRlZCAyMDI2LTAzLTEwLCBwZXIgUmVndWxhdGlvbiAoRVUpIDIwMjMvMTU0MiBBcnQuIDE4IiwiZXhwZWN0ZWRMaWZldGltZUN5Y2xlcyI6NjAwMCwiZXhwZWN0ZWRMaWZldGltZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjE0MjctMToyMDEzLCAyNSBkZWdDLCA5MCUgRG9EIiwiaGF6YXJkb3VzU3Vic3RhbmNlcyI6W3siY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsImNvbmNlbnRyYXRpb25QY3QiOjAuNywibmFtZSI6IkxpdGhpdW0gaGV4YWZsdW9yb3Bob3NwaGF0ZSJ9XSwiaW5pdGlhbFJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk2LjUsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjowLjYsImludGVybmFsUGFja1Jlc2lzdGFuY2VNb2htIjo5LCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDMtMDJUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiQmVybGluLCBERSAocGxhbnQgQkxOLTIpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDEwMCBBaCAvIDUuMTIga1doOyBjaGVtaXN0cnkgTEZQIiwibWF4aW11bVZvbHRhZ2VWIjo1OC40LCJtaW5pbWFsVm9sdGFnZVYiOjQwLCJub21pbmFsQ2FwYWNpdHlBaCI6MTAwLCJub21pbmFsVm9sdGFnZVYiOjUxLjIsIm5vdEluVXNlVGVtcGVyYXR1cmVSYW5nZSI6eyJtYXhDIjo1MCwibWluQyI6LTEwfSwibm90SW5Vc2VUZW1wZXJhdHVyZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjI2NjAtMToyMDE4IHN0b3JhZ2UgcHJvZmlsZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NTAwMCwicG93ZXJMaW1pdE1heFciOjc1MDAsInBvd2VyTGltaXRNaW5XIjoxNTAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwMjEiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6NS4xMiwicmF0ZWRFbmVyZ3lXaCI6NTEyMCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjIyLCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo5Miwic2FmZXR5TWVhc3VyZXMiOiJJc29sYXRlIERDIGJyZWFrZXIgYmVmb3JlIHNlcnZpY2luZy4gSW5zdWxhdGVkIHRvb2xzIG9ubHkuIFZlbnRpbGF0ZSBlbmNsb3N1cmUgYmVmb3JlIG9wZW5pbmcuIiwic3BhcmVQYXJ0c0NvbnRhY3RzIjoic3BhcmVzQGJhdHRlcmllcy5leGFtcGxlOyArNDkgMzAgNTU1MCAxMjAwIiwic3RhdGVPZkhlYWx0aCI6eyJvaG1pY1Jlc2lzdGFuY2VNb2htIjo5LjEsInBhcmFtZXRlclNldCI6InN0YXRpb25hcnlPckxtdCIsInJlbWFpbmluZ0NhcGFjaXR5UGN0Ijo5OS42LCJyZW1haW5pbmdQb3dlckNhcGFiaWxpdHlQY3QiOjk5LjQsInJlbWFpbmluZ1JvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk5LjEsInNlbGZEaXNjaGFyZ2VSYXRlUGN0UGVyTW9udGgiOjEuNH0sInN0YXRlT2ZIZWFsdGhQY3QiOm51bGwsInRlc3RSZXBvcnRSZXN1bHRzIjoiSUVDIDYyNjE5OjIwMjIgcGFzczsgSUVDIDYzMDU2OjIwMjAgcGFzczsgVU4gMzguMyBwYXNzOyByZXBvcnQgR0MtVFItMjAyNi0wMzAxIiwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIgb3IgQUJDIGRyeSBwb3dkZXI7IGxhcmdlIHZvbHVtZXMgb2Ygd2F0ZXIgZm9yIHRoZXJtYWwgcnVuYXdheSIsInVzYWdlSGlzdG9yeSI6eyJjaGFyZ2VEaXNjaGFyZ2VDeWNsZXMiOjUsIm5lZ2F0aXZlRXZlbnRzIjpbXX0sIndhc3RlQmF0dGVyeUluZm9ybWF0aW9uIjoiUmV0dXJuIHRvIGFuIGF1dGhvcmlzZWQgY29sbGVjdGlvbiBwb2ludC4gRG8gbm90IGluY2luZXJhdGUgb3Igc2hyZWQuIFByb2R1Y2VyIHJlZ2lzdHJhdGlvbiBERS1CQVRULTAwMTIzLiJ9LCJwcm9kdWN0TmFtZSI6IkxGUCBzdGF0aW9uYXJ5IHBhY2ssIGNvcnJlY3RlZCIsInB1Ymxpc2hlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC41ODUwNDkyNzJaIiwicXJDb2RlVXJsIjoiaHR0cDovLzEyNy4wLjAuMToxLzAxLzA5NTkwMDAwMDAwMDIxLzIxLzcxYWM5Yzg2NDY5NjU0MGMzYjIxIiwicmVwYWlyYWJpbGl0eVNjb3JlIjp7Im92ZXJhbGwiOjguMX0sInJldGVudGlvbkxvY2tlZCI6dHJ1ZSwicmV0ZW50aW9uVW50aWwiOiIyMDM2LTExLTAzVDIwOjA5OjE0LjU4NTA0OTI3MloiLCJzY2hlbWFWZXJzaW9uIjoiMi44LjAiLCJzdGF0dXMiOiJhY3RpdmUiLCJzdXBlcnNlZGVzSWQiOiIwMWExYTMwNy1iNWJkLTcxYWMtOWM4Ni00Njk2NTQwYzNiMjEiLCJ1cGRhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjU4NTA0OTI3MloiLCJ2ZXJzaW9uIjoyfQ.MPlNiIgt_OdDPjIwg44_t9ksmhr8o5F_WfkKHppMgwbkYPKPMw-dhtLBtnPmMJfekc0QWJnzdrMqv0Tg6sNCCQ", "commodityCode": "85076000", "schemaVersion": "2.8.0", "dataCarrierUri": "http://127.0.0.1:1/01/09590000000021/21/71ac9c864696540c3b21", "productCategory": "battery", "productIdentifier": {"gtin": "09590000000021", "scheme": "gs1"}, "facilityIdentifier": "4012345000009", "operatorIdentifier": "DE123456789", "operatorIdentifierScheme": "vat"}', NULL, 0, '2026-11-03 20:09:14.626531+00', NULL, NULL, '2026-11-03 20:09:14.626531+00', '2026-11-03 20:09:14.626531+00', NULL, NULL); + + +-- +-- Data for Name: registry_transfer; Type: TABLE DATA; Schema: odal; Owner: - +-- + + + +-- +-- Data for Name: scan_telemetry; Type: TABLE DATA; Schema: odal; Owner: - +-- + + + +-- +-- Data for Name: seal_audit_state; Type: TABLE DATA; Schema: odal; Owner: - +-- + + + +-- +-- Data for Name: seal_outbox; Type: TABLE DATA; Schema: odal; Owner: - +-- + + + +-- +-- Data for Name: snapshot_outbox; Type: TABLE DATA; Schema: odal; Owner: - +-- + + + +-- +-- Data for Name: trusted_list_cache; Type: TABLE DATA; Schema: odal; Owner: - +-- + + + +-- +-- Data for Name: unsold_goods_report; Type: TABLE DATA; Schema: odal; Owner: - +-- + + + +-- +-- Data for Name: webhook_delivery; Type: TABLE DATA; Schema: odal; Owner: - +-- + + + +-- +-- Data for Name: webhook_subscription; Type: TABLE DATA; Schema: odal; Owner: - +-- + + + +-- +-- Data for Name: _sqlx_migrations; Type: TABLE DATA; Schema: public; Owner: - +-- + +INSERT INTO public._sqlx_migrations VALUES (1, 'extensions roles schemas', '2026-11-03 20:09:11.435688+00', true, '\x7d3cfc198510d35358290bee547846dbfc11deabe6526014e8551939e40a162a1a2de0e39b9c4100d0bbd0f4c48d571d', 15759246); +INSERT INTO public._sqlx_migrations VALUES (2, 'operator', '2026-11-03 20:09:11.45466+00', true, '\x7d1166f7a8e138b707795480190cc28c96ad9ba1aad87d407a6a575335f67f653a766bffe3a49dcdf7fd1dc4abcfb095', 8315145); +INSERT INTO public._sqlx_migrations VALUES (3, 'api key', '2026-11-03 20:09:11.465862+00', true, '\x792bf1e6a6da6e4f66f8320ae8012af786f584f4de272784bb3fcf18b1faf097cf45cb8bca2c64f004f01615f7bffb1b', 3897050); +INSERT INTO public._sqlx_migrations VALUES (4, 'passport', '2026-11-03 20:09:11.471482+00', true, '\x604d09249966ccdb8b1fb9f613503bd87f39099054f040dea0738f49ac73082fd4b529832cf57d2924a7c4d48a0fe3f0', 6447658); +INSERT INTO public._sqlx_migrations VALUES (5, 'passport audit', '2026-11-03 20:09:11.479752+00', true, '\xf89949020d1fa605e88592f39ebd1f82ed0ec955518cdbd4b1951751d5242be2bbd6ab9a230af0788c70274ff80d8017', 4365377); +INSERT INTO public._sqlx_migrations VALUES (6, 'registry sync', '2026-11-03 20:09:11.487083+00', true, '\xe4aef736a387ab05c7c08e8f507c5707d6966ed4da31dbf0ef98984657b1f1324ad9eea6818a2cbfd96ddb1eb37fa3eb', 5320781); +INSERT INTO public._sqlx_migrations VALUES (7, 'import job', '2026-11-03 20:09:11.495322+00', true, '\x9f9ef70edef4448efe9095117ff83c306a5769f1bb55031699ec5b78c0e28512da74f1fce472b589df637f097521c452', 4733344); +INSERT INTO public._sqlx_migrations VALUES (8, 'unsold goods report', '2026-11-03 20:09:11.50277+00', true, '\x11f306e511ccadd4f4f5e817525a6ff8482f63212da10a4beaeae32a9978ec4da72dbabf30200fc61544ec307210b080', 4912593); +INSERT INTO public._sqlx_migrations VALUES (9, 'identity', '2026-11-03 20:09:11.510404+00', true, '\x91448824eb27b442c8244029ddc9af3f56ec7a51289da5a3890b8c868d809f7c8961befe5806b88fcd3f100a4749d359', 6787050); +INSERT INTO public._sqlx_migrations VALUES (10, 'grants', '2026-11-03 20:09:11.519104+00', true, '\xd7d145cabd732ed0ed4e0b482a6c7a2f6ef2bc836318a4c5542391afea87f185b2e887a688cf3879e3275b0560d3f3a6', 3827086); +INSERT INTO public._sqlx_migrations VALUES (11, 'public jws mutable', '2026-11-03 20:09:11.525806+00', true, '\x967cb3777fc36f75b8a12054dc5f8c4fc723349c6a67dd4ed36c22514688210e9f1235d75e458bb78f1ea325919e5f23', 5459532); +INSERT INTO public._sqlx_migrations VALUES (12, 'registry identity grants', '2026-11-03 20:09:11.533975+00', true, '\x3b24351dbf3c62658ac583991f6642bea0dfe6da353ab004ce4d4375ddaddb6a4977e89dec2b34a2be938c7c6aebda5f', 4777450); +INSERT INTO public._sqlx_migrations VALUES (13, 'facility retire', '2026-11-03 20:09:11.541278+00', true, '\x59aa874881e66ea9a41111354dcd9960b234b1785a0781cdb14151ea6c31fdf0d2da51779db9bbbf087895df35cde417', 16265598); +INSERT INTO public._sqlx_migrations VALUES (14, 'operator identifier retire', '2026-11-03 20:09:11.561185+00', true, '\x5818d5545941548e43156731386cf675dccc860de92fd4f82ed0dcb547a2c96de40961e6471f339336d7f6a2736e07b8', 8710853); +INSERT INTO public._sqlx_migrations VALUES (15, 'audit hash chain', '2026-11-03 20:09:11.573519+00', true, '\x5b1fd525affcca7b18aa78b18b94ca34aee8c49c529ae5f437d74d5946a121f22c727e4ab39ac94d8bd2a747398ff09f', 6354240); +INSERT INTO public._sqlx_migrations VALUES (16, 'deactivated state', '2026-11-03 20:09:11.583712+00', true, '\xd4cc9eff8a3d8dc55b811afa7539e5176cdd9756347aafcc6dd1093991898527e69a0fa830913a8c50417aec9d86a4db', 7562970); +INSERT INTO public._sqlx_migrations VALUES (17, 'passport transfer', '2026-11-03 20:09:11.595022+00', true, '\x50dbd33b7e7700089fe781c3e1045efdf1e864f720aeb47fc96b69716d4f437962e520cc389d31b25bf2e726fbb0c7c2', 10120062); +INSERT INTO public._sqlx_migrations VALUES (18, 'lint result mutable', '2026-11-03 20:09:11.607761+00', true, '\xd05b474eee06fda643cb64def793aaf2e8e6dd4731f18a67ffebfc76285c2b93765db5e7e3818c72b9e6fcfbcd1d0e52', 5169441); +INSERT INTO public._sqlx_migrations VALUES (19, 'passport identity index', '2026-11-03 20:09:11.615351+00', true, '\x15c313e9acfdf9bdbb0e9a6ef83bfe3a839665d35a92126b35ab5392f71f83273ff4f31d118fa4d997ddce4cef4d24df', 6661478); +INSERT INTO public._sqlx_migrations VALUES (20, 'import job report', '2026-11-03 20:09:11.624693+00', true, '\x17d78c3b1342e2c2dc8cf8b2d24b8d95c15a68f51ed48de5ffefd14f0aa5599e3bf402ef36bd8348406b89aa535d6363', 6307391); +INSERT INTO public._sqlx_migrations VALUES (21, 'evidence dossier', '2026-11-03 20:09:11.633659+00', true, '\xaa35a41d68d552095b8c5e80efb6948c9ed57fd00eb55bca93fcfa28185b87516f101e306617bd5bd2f40e565885412e', 12651441); +INSERT INTO public._sqlx_migrations VALUES (22, 'webhooks', '2026-11-03 20:09:11.649817+00', true, '\x213514c352f43591fe572dbc528a944a6112c113babeb8917b848a05091b8b5c25bd8aa80cf3b95c1d7a903422f8a057', 17971068); +INSERT INTO public._sqlx_migrations VALUES (23, 'snapshot outbox', '2026-11-03 20:09:11.671279+00', true, '\xfba28f646649b6a304d9498a8782d001b8701dd6ff0838968df1bc269d549b271eb03423b9701295002b60587dd949a3', 14008608); +INSERT INTO public._sqlx_migrations VALUES (24, 'registry sync status intent', '2026-11-03 20:09:11.689017+00', true, '\x6f01020b683ed36a92ed2ed1a2d04106c77460b5e259bc8e7e1f12da80a46aca753a7e76d1681f93e8385b92cc5edbe8', 8130999); +INSERT INTO public._sqlx_migrations VALUES (25, 'scan telemetry', '2026-11-03 20:09:11.700345+00', true, '\x8e826b4f09f4fee69a7ff328a12cdada4d56c1bf1c39ab617fc34a5dca4097b1eac58d27341e7826b92934f0b5b1f8ed', 9741476); +INSERT INTO public._sqlx_migrations VALUES (26, 'credentialed read audit', '2026-11-03 20:09:11.711961+00', true, '\xd188292c956ee5857236ed47892cad9c94610cb25f83a8c18fb4fde55b8a8c4c65c5e728a89e4fdd803b0eecfc72b496', 2869454); +INSERT INTO public._sqlx_migrations VALUES (27, 'disclosure signatures mutable', '2026-11-03 20:09:11.716609+00', true, '\x910aaa13a55198b490ca81e2836d015b4f5b723943d3fe581601861cf7dcb2d3529092991c731f328511cd9d63aaed2c', 2557381); +INSERT INTO public._sqlx_migrations VALUES (28, 'seal outbox', '2026-11-03 20:09:11.720971+00', true, '\x7ae4fe1174aa5e82f23030a4540850b96e7b65be5a9e6b4c777a8f2aed4386740fb9b4980edf46795fd0e91c201895fe', 7021136); +INSERT INTO public._sqlx_migrations VALUES (29, 'registry transfer', '2026-11-03 20:09:11.730697+00', true, '\x9e938e77c9a0246f7db5733744b323563c064ef9eca3e855f138a5423d51c8691854d9c21cad62fd4838fd8bde38be8a', 6149207); +INSERT INTO public._sqlx_migrations VALUES (30, 'registry sync submitted', '2026-11-03 20:09:11.739876+00', true, '\xb4a77133009db45257d959e2e3b27a757ad20146a694a32e6b5812855a41079ff58196f47f30d6ce613f58747eafab86', 4861498); +INSERT INTO public._sqlx_migrations VALUES (31, 'operator registry verification', '2026-11-03 20:09:11.747595+00', true, '\x1b582822cbcdeb873dfe666ac1ac94555b3261f72e69d1d83d53fc71db66f05fa03bc0e58791891b3cfb649817c5c83c', 3645236); +INSERT INTO public._sqlx_migrations VALUES (32, 'product group rename', '2026-11-03 20:09:11.753128+00', true, '\xcf29f87e3e9759f1afb2c002c52d4d272f788ba726ab8b14da6cbbd637b7d2acc3a4a779f5b7479c524a6a1b01e331d3', 3109096); +INSERT INTO public._sqlx_migrations VALUES (33, 'snapshot refresh index', '2026-11-03 20:09:11.757941+00', true, '\xc629233f7ebe566ca0450aa703e784c0b8db3324025b57b34dff1d2a5c8319243533ca4fd94f606ac9554369a3a19329', 2561466); +INSERT INTO public._sqlx_migrations VALUES (34, 'superseded audit action', '2026-11-03 20:09:11.762259+00', true, '\x54329aa02762e1d98cd47771f632627b5d3d589d86bc73d7472c03899bde4f3bba5ab1a08dd676fe06b8abe9ce870138', 2372132); +INSERT INTO public._sqlx_migrations VALUES (35, 'drop unmodelled passport columns', '2026-11-03 20:09:11.766348+00', true, '\xcbbb8dfa4b7a7cbf66773651e91f8833c3749240543438f8be137a1f315179554ec9e5609b7cc22d1fbbd75b65eb9944', 2395844); +INSERT INTO public._sqlx_migrations VALUES (36, 'idempotency key', '2026-11-03 20:09:11.771867+00', true, '\x9fa532cc616c58c448b777899f75e57d5705da18fd0752c72b68ed00709cc12cb5f0e1b0f7247352191eb16242c91e94', 5348621); +INSERT INTO public._sqlx_migrations VALUES (37, 'unsold goods unit count', '2026-11-03 20:09:11.781248+00', true, '\x546191dbb57658443260f3adec56f19f593f98e1a2ba9690b7c722cd377589178e55e5026f03c1e15cb401be8b629a5d', 6597682); +INSERT INTO public._sqlx_migrations VALUES (38, 'seal audit state', '2026-11-03 20:09:11.7916+00', true, '\xc4e3c78db64b623186cc18f3d52bbd70eef3b0a15bc0eb25a05b6ef592e9f3b6b6bcae8f2f7a1914fb0e18fdad513331', 10534854); +INSERT INTO public._sqlx_migrations VALUES (39, 'trusted list cache', '2026-11-03 20:09:11.805765+00', true, '\x4070b042426fb994c4a0b2a7a0362e639a4fdc744a9b69e7b483b6835480711461c930033d46b5161cc09ea67a8f4754', 10618625); +INSERT INTO public._sqlx_migrations VALUES (40, 'passport version', '2026-11-03 20:09:11.819029+00', true, '\x9f911363e8c5aa94144745ed300117a26e458e2fdc9b577e54915337dd0b5fddf69edccb07eb9adc88fa7da138ff1e70', 11192628); +INSERT INTO public._sqlx_migrations VALUES (41, 'retired status', '2026-11-03 20:09:11.832863+00', true, '\x1e6bc0f709a895c3633ed031c0439cbfeee7646a3ef595ed67be029f92b749adfb0bc7dd8416284e30c9cb8f782b3e1a', 8718891); +INSERT INTO public._sqlx_migrations VALUES (42, 'passport identifier index', '2026-11-03 20:09:11.844243+00', true, '\x0b3a58ae130b623b06ca61e8727d309f0d0b3c57b54c8cd78ee401d912366fbd219cd222fc2fd7d0736cead230db50c5', 11388810); +INSERT INTO public._sqlx_migrations VALUES (43, 'personal data record', '2026-11-03 20:09:11.858165+00', true, '\x39cff6333d611630b6ecfc66cab24ef1e6a746b50ce212f174340786c1101a2e065eda958a9036c1a40acc304030c49b', 12052022); +INSERT INTO public._sqlx_migrations VALUES (44, 'idempotency claim token', '2026-11-03 20:09:11.873363+00', true, '\xbad4f08f0500dcfc937397223208d110ec83db7ebaa538ed7e9cde6f9026cb7af347692564d6f5c165d84d7055f7dff4', 10646260); + + +-- +-- Name: did_document did_document_did_key; Type: CONSTRAINT; Schema: identity; Owner: - +-- + +ALTER TABLE ONLY identity.did_document + ADD CONSTRAINT did_document_did_key UNIQUE (did); + + +-- +-- Name: did_document did_document_operator_id_key; Type: CONSTRAINT; Schema: identity; Owner: - +-- + +ALTER TABLE ONLY identity.did_document + ADD CONSTRAINT did_document_operator_id_key UNIQUE (operator_id); + + +-- +-- Name: did_document did_document_pkey; Type: CONSTRAINT; Schema: identity; Owner: - +-- + +ALTER TABLE ONLY identity.did_document + ADD CONSTRAINT did_document_pkey PRIMARY KEY (id); + + +-- +-- Name: key_pair key_pair_operator_id_key_id_key; Type: CONSTRAINT; Schema: identity; Owner: - +-- + +ALTER TABLE ONLY identity.key_pair + ADD CONSTRAINT key_pair_operator_id_key_id_key UNIQUE (operator_id, key_id); + + +-- +-- Name: key_pair key_pair_pkey; Type: CONSTRAINT; Schema: identity; Owner: - +-- + +ALTER TABLE ONLY identity.key_pair + ADD CONSTRAINT key_pair_pkey PRIMARY KEY (id); + + +-- +-- Name: api_key api_key_key_prefix_key; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.api_key + ADD CONSTRAINT api_key_key_prefix_key UNIQUE (key_prefix); + + +-- +-- Name: api_key api_key_name_key; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.api_key + ADD CONSTRAINT api_key_name_key UNIQUE (name); + + +-- +-- Name: api_key api_key_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.api_key + ADD CONSTRAINT api_key_pkey PRIMARY KEY (id); + + +-- +-- Name: evidence_dossier evidence_dossier_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.evidence_dossier + ADD CONSTRAINT evidence_dossier_pkey PRIMARY KEY (id); + + +-- +-- Name: facility facility_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.facility + ADD CONSTRAINT facility_pkey PRIMARY KEY (id); + + +-- +-- Name: idempotency_key idempotency_key_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.idempotency_key + ADD CONSTRAINT idempotency_key_pkey PRIMARY KEY (principal, method, path, idem_key); + + +-- +-- Name: import_job import_job_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.import_job + ADD CONSTRAINT import_job_pkey PRIMARY KEY (id); + + +-- +-- Name: operator_config operator_config_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.operator_config + ADD CONSTRAINT operator_config_pkey PRIMARY KEY (operator_id); + + +-- +-- Name: operator_identifier operator_identifier_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.operator_identifier + ADD CONSTRAINT operator_identifier_pkey PRIMARY KEY (id); + + +-- +-- Name: passport_audit passport_audit_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.passport_audit + ADD CONSTRAINT passport_audit_pkey PRIMARY KEY (id); + + +-- +-- Name: passport passport_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.passport + ADD CONSTRAINT passport_pkey PRIMARY KEY (id); + + +-- +-- Name: passport_transfer passport_transfer_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.passport_transfer + ADD CONSTRAINT passport_transfer_pkey PRIMARY KEY (passport_id); + + +-- +-- Name: passport_version passport_version_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.passport_version + ADD CONSTRAINT passport_version_pkey PRIMARY KEY (id); + + +-- +-- Name: personal_data_record personal_data_record_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.personal_data_record + ADD CONSTRAINT personal_data_record_pkey PRIMARY KEY (id); + + +-- +-- Name: qr_render qr_render_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.qr_render + ADD CONSTRAINT qr_render_pkey PRIMARY KEY (dpp_id, day); + + +-- +-- Name: registry_identity_audit registry_identity_audit_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.registry_identity_audit + ADD CONSTRAINT registry_identity_audit_pkey PRIMARY KEY (id); + + +-- +-- Name: registry_sync registry_sync_passport_id_key; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.registry_sync + ADD CONSTRAINT registry_sync_passport_id_key UNIQUE (passport_id); + + +-- +-- Name: registry_sync registry_sync_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.registry_sync + ADD CONSTRAINT registry_sync_pkey PRIMARY KEY (id); + + +-- +-- Name: registry_transfer registry_transfer_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.registry_transfer + ADD CONSTRAINT registry_transfer_pkey PRIMARY KEY (transfer_id); + + +-- +-- Name: scan_telemetry scan_telemetry_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.scan_telemetry + ADD CONSTRAINT scan_telemetry_pkey PRIMARY KEY (dpp_id, day, variant); + + +-- +-- Name: seal_audit_state seal_audit_state_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.seal_audit_state + ADD CONSTRAINT seal_audit_state_pkey PRIMARY KEY (id); + + +-- +-- Name: seal_outbox seal_outbox_passport_id_payload_hash_key; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.seal_outbox + ADD CONSTRAINT seal_outbox_passport_id_payload_hash_key UNIQUE (passport_id, payload_hash); + + +-- +-- Name: seal_outbox seal_outbox_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.seal_outbox + ADD CONSTRAINT seal_outbox_pkey PRIMARY KEY (id); + + +-- +-- Name: snapshot_outbox snapshot_outbox_passport_id_key; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.snapshot_outbox + ADD CONSTRAINT snapshot_outbox_passport_id_key UNIQUE (passport_id); + + +-- +-- Name: snapshot_outbox snapshot_outbox_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.snapshot_outbox + ADD CONSTRAINT snapshot_outbox_pkey PRIMARY KEY (id); + + +-- +-- Name: trusted_list_cache trusted_list_cache_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.trusted_list_cache + ADD CONSTRAINT trusted_list_cache_pkey PRIMARY KEY (territory); + + +-- +-- Name: unsold_goods_report unsold_goods_report_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.unsold_goods_report + ADD CONSTRAINT unsold_goods_report_pkey PRIMARY KEY (id); + + +-- +-- Name: webhook_delivery webhook_delivery_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.webhook_delivery + ADD CONSTRAINT webhook_delivery_pkey PRIMARY KEY (id); + + +-- +-- Name: webhook_subscription webhook_subscription_pkey; Type: CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.webhook_subscription + ADD CONSTRAINT webhook_subscription_pkey PRIMARY KEY (id); + + +-- +-- Name: _sqlx_migrations _sqlx_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public._sqlx_migrations + ADD CONSTRAINT _sqlx_migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: idx_key_pair_active; Type: INDEX; Schema: identity; Owner: - +-- + +CREATE INDEX idx_key_pair_active ON identity.key_pair USING btree (operator_id, is_active); + + +-- +-- Name: idx_api_key_prefix_active; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_api_key_prefix_active ON odal.api_key USING btree (key_prefix) WHERE is_active; + + +-- +-- Name: idx_audit_passport; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_audit_passport ON odal.passport_audit USING btree (passport_id); + + +-- +-- Name: idx_audit_passport_time; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_audit_passport_time ON odal.passport_audit USING btree (passport_id, ts); + + +-- +-- Name: idx_evidence_dossier_passport; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_evidence_dossier_passport ON odal.evidence_dossier USING btree (passport_id, created_at); + + +-- +-- Name: idx_facility_operator; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_facility_operator ON odal.facility USING btree (operator_id); + + +-- +-- Name: idx_idempotency_expiry; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_idempotency_expiry ON odal.idempotency_key USING btree (expires_at); + + +-- +-- Name: idx_import_job_status; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_import_job_status ON odal.import_job USING btree (status); + + +-- +-- Name: idx_opid_scheme_value; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_opid_scheme_value ON odal.operator_identifier USING btree (scheme, value); + + +-- +-- Name: idx_passport_batch_trgm; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_passport_batch_trgm ON odal.passport USING gin (((doc ->> 'batchId'::text)) public.gin_trgm_ops); + + +-- +-- Name: idx_passport_carrier_serial; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_passport_carrier_serial ON odal.passport USING btree (COALESCE((doc ->> 'carrierSerial'::text), "right"(replace((id)::text, '-'::text, ''::text), 20))); + + +-- +-- Name: idx_passport_identifier; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_passport_identifier ON odal.passport USING btree (COALESCE((((doc -> 'productGroupData'::text) -> 'productIdentifier'::text) ->> 'gtin'::text), (((doc -> 'productGroupData'::text) -> 'productIdentifier'::text) ->> 'url'::text), (((doc -> 'productGroupData'::text) -> 'productIdentifier'::text) ->> 'did'::text), ((doc -> 'productGroupData'::text) ->> 'gtin'::text))); + + +-- +-- Name: idx_passport_identity; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_passport_identity ON odal.passport USING btree (product_group, COALESCE((((doc -> 'productGroupData'::text) -> 'productIdentifier'::text) ->> 'gtin'::text), (((doc -> 'productGroupData'::text) -> 'productIdentifier'::text) ->> 'url'::text), (((doc -> 'productGroupData'::text) -> 'productIdentifier'::text) ->> 'did'::text), ((doc -> 'productGroupData'::text) ->> 'gtin'::text)), ((doc ->> 'batchId'::text))) WHERE (status = ANY (ARRAY['draft'::text, 'active'::text])); + + +-- +-- Name: idx_passport_name_trgm; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_passport_name_trgm ON odal.passport USING gin (((doc ->> 'productName'::text)) public.gin_trgm_ops); + + +-- +-- Name: idx_passport_product_group; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_passport_product_group ON odal.passport USING btree (product_group); + + +-- +-- Name: idx_passport_published; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_passport_published ON odal.passport USING btree (published_at); + + +-- +-- Name: idx_passport_status; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_passport_status ON odal.passport USING btree (status); + + +-- +-- Name: idx_passport_supersedes; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_passport_supersedes ON odal.passport USING btree (supersedes_id); + + +-- +-- Name: idx_passport_version_asof; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_passport_version_asof ON odal.passport_version USING btree (passport_id, superseded_at); + + +-- +-- Name: idx_personal_data_record_passport; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_personal_data_record_passport ON odal.personal_data_record USING btree (passport_id, stored_at, id); + + +-- +-- Name: idx_qr_render_day; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_qr_render_day ON odal.qr_render USING btree (day); + + +-- +-- Name: idx_regsync_due; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_regsync_due ON odal.registry_sync USING btree (next_attempt_at) WHERE (status = ANY (ARRAY['pending'::text, 'submitted'::text])); + + +-- +-- Name: idx_regtransfer_due; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_regtransfer_due ON odal.registry_transfer USING btree (next_attempt_at) WHERE (status = 'pending'::text); + + +-- +-- Name: idx_regtransfer_passport; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_regtransfer_passport ON odal.registry_transfer USING btree (passport_id); + + +-- +-- Name: idx_ri_audit_entity; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_ri_audit_entity ON odal.registry_identity_audit USING btree (entity_type, entity_id, ts); + + +-- +-- Name: idx_scan_telemetry_day; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_scan_telemetry_day ON odal.scan_telemetry USING btree (day); + + +-- +-- Name: idx_seal_outbox_due; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_seal_outbox_due ON odal.seal_outbox USING btree (next_attempt_at) WHERE (status = 'pending'::text); + + +-- +-- Name: idx_snapshot_outbox_due; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_snapshot_outbox_due ON odal.snapshot_outbox USING btree (next_attempt_at) WHERE (status = 'pending'::text); + + +-- +-- Name: idx_snapshot_outbox_refresh; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_snapshot_outbox_refresh ON odal.snapshot_outbox USING btree (reconciled_at) WHERE (status = 'reconciled'::text); + + +-- +-- Name: idx_unsold_operator_period; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_unsold_operator_period ON odal.unsold_goods_report USING btree (operator_id, reporting_period); + + +-- +-- Name: idx_webhook_delivery_due; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE INDEX idx_webhook_delivery_due ON odal.webhook_delivery USING btree (next_attempt_at) WHERE (status = 'pending'::text); + + +-- +-- Name: uq_facility_identifier_live; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE UNIQUE INDEX uq_facility_identifier_live ON odal.facility USING btree (identifier_scheme, identifier_value) WHERE (retired_at IS NULL); + + +-- +-- Name: uq_operator_identifier_live; Type: INDEX; Schema: odal; Owner: - +-- + +CREATE UNIQUE INDEX uq_operator_identifier_live ON odal.operator_identifier USING btree (operator_id, scheme, value) WHERE (retired_at IS NULL); + + +-- +-- Name: passport_audit audit_immutable; Type: TRIGGER; Schema: odal; Owner: - +-- + +CREATE TRIGGER audit_immutable BEFORE DELETE OR UPDATE ON odal.passport_audit FOR EACH ROW EXECUTE FUNCTION odal.audit_append_only(); + + +-- +-- Name: evidence_dossier evidence_dossier_immutable; Type: TRIGGER; Schema: odal; Owner: - +-- + +CREATE TRIGGER evidence_dossier_immutable BEFORE DELETE OR UPDATE ON odal.evidence_dossier FOR EACH ROW EXECUTE FUNCTION odal.evidence_append_only(); + + +-- +-- Name: passport passport_retention; Type: TRIGGER; Schema: odal; Owner: - +-- + +CREATE TRIGGER passport_retention BEFORE DELETE OR UPDATE ON odal.passport FOR EACH ROW EXECUTE FUNCTION odal.passport_retention_guard(); + + +-- +-- Name: passport_version passport_version_immutable; Type: TRIGGER; Schema: odal; Owner: - +-- + +CREATE TRIGGER passport_version_immutable BEFORE DELETE OR UPDATE ON odal.passport_version FOR EACH ROW EXECUTE FUNCTION odal.passport_version_append_only(); + + +-- +-- Name: personal_data_record personal_data_record_guard; Type: TRIGGER; Schema: odal; Owner: - +-- + +CREATE TRIGGER personal_data_record_guard BEFORE UPDATE ON odal.personal_data_record FOR EACH ROW EXECUTE FUNCTION odal.personal_data_record_guard(); + + +-- +-- Name: registry_identity_audit ri_audit_immutable; Type: TRIGGER; Schema: odal; Owner: - +-- + +CREATE TRIGGER ri_audit_immutable BEFORE DELETE OR UPDATE ON odal.registry_identity_audit FOR EACH ROW EXECUTE FUNCTION odal.audit_append_only(); + + +-- +-- Name: evidence_dossier evidence_dossier_passport_id_fkey; Type: FK CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.evidence_dossier + ADD CONSTRAINT evidence_dossier_passport_id_fkey FOREIGN KEY (passport_id) REFERENCES odal.passport(id); + + +-- +-- Name: facility facility_operator_id_fkey; Type: FK CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.facility + ADD CONSTRAINT facility_operator_id_fkey FOREIGN KEY (operator_id) REFERENCES odal.operator_config(operator_id); + + +-- +-- Name: import_job import_job_parent_job_id_fkey; Type: FK CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.import_job + ADD CONSTRAINT import_job_parent_job_id_fkey FOREIGN KEY (parent_job_id) REFERENCES odal.import_job(id); + + +-- +-- Name: operator_identifier operator_identifier_operator_id_fkey; Type: FK CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.operator_identifier + ADD CONSTRAINT operator_identifier_operator_id_fkey FOREIGN KEY (operator_id) REFERENCES odal.operator_config(operator_id); + + +-- +-- Name: passport passport_supersedes_id_fkey; Type: FK CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.passport + ADD CONSTRAINT passport_supersedes_id_fkey FOREIGN KEY (supersedes_id) REFERENCES odal.passport(id); + + +-- +-- Name: passport_transfer passport_transfer_passport_id_fkey; Type: FK CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.passport_transfer + ADD CONSTRAINT passport_transfer_passport_id_fkey FOREIGN KEY (passport_id) REFERENCES odal.passport(id); + + +-- +-- Name: personal_data_record personal_data_record_passport_id_fkey; Type: FK CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.personal_data_record + ADD CONSTRAINT personal_data_record_passport_id_fkey FOREIGN KEY (passport_id) REFERENCES odal.passport(id); + + +-- +-- Name: qr_render qr_render_dpp_id_fkey; Type: FK CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.qr_render + ADD CONSTRAINT qr_render_dpp_id_fkey FOREIGN KEY (dpp_id) REFERENCES odal.passport(id); + + +-- +-- Name: registry_sync registry_sync_passport_id_fkey; Type: FK CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.registry_sync + ADD CONSTRAINT registry_sync_passport_id_fkey FOREIGN KEY (passport_id) REFERENCES odal.passport(id); + + +-- +-- Name: registry_transfer registry_transfer_passport_id_fkey; Type: FK CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.registry_transfer + ADD CONSTRAINT registry_transfer_passport_id_fkey FOREIGN KEY (passport_id) REFERENCES odal.passport(id); + + +-- +-- Name: scan_telemetry scan_telemetry_dpp_id_fkey; Type: FK CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.scan_telemetry + ADD CONSTRAINT scan_telemetry_dpp_id_fkey FOREIGN KEY (dpp_id) REFERENCES odal.passport(id); + + +-- +-- Name: seal_outbox seal_outbox_passport_id_fkey; Type: FK CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.seal_outbox + ADD CONSTRAINT seal_outbox_passport_id_fkey FOREIGN KEY (passport_id) REFERENCES odal.passport(id); + + +-- +-- Name: snapshot_outbox snapshot_outbox_passport_id_fkey; Type: FK CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.snapshot_outbox + ADD CONSTRAINT snapshot_outbox_passport_id_fkey FOREIGN KEY (passport_id) REFERENCES odal.passport(id); + + +-- +-- Name: webhook_delivery webhook_delivery_subscription_id_fkey; Type: FK CONSTRAINT; Schema: odal; Owner: - +-- + +ALTER TABLE ONLY odal.webhook_delivery + ADD CONSTRAINT webhook_delivery_subscription_id_fkey FOREIGN KEY (subscription_id) REFERENCES odal.webhook_subscription(id); + + +-- +-- Name: SCHEMA identity; Type: ACL; Schema: -; Owner: - +-- + +GRANT USAGE ON SCHEMA identity TO odal_app; + + +-- +-- Name: SCHEMA odal; Type: ACL; Schema: -; Owner: - +-- + +GRANT USAGE ON SCHEMA odal TO odal_app; + + +-- +-- Name: TABLE did_document; Type: ACL; Schema: identity; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE identity.did_document TO odal_app; + + +-- +-- Name: TABLE key_pair; Type: ACL; Schema: identity; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE identity.key_pair TO odal_app; + + +-- +-- Name: TABLE api_key; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE odal.api_key TO odal_app; + + +-- +-- Name: TABLE evidence_dossier; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT ON TABLE odal.evidence_dossier TO odal_app; + + +-- +-- Name: TABLE facility; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE odal.facility TO odal_app; + + +-- +-- Name: TABLE idempotency_key; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE odal.idempotency_key TO odal_app; + + +-- +-- Name: TABLE import_job; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE odal.import_job TO odal_app; + + +-- +-- Name: TABLE operator_config; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE odal.operator_config TO odal_app; + + +-- +-- Name: TABLE operator_identifier; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE odal.operator_identifier TO odal_app; + + +-- +-- Name: TABLE passport; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE odal.passport TO odal_app; + + +-- +-- Name: TABLE passport_audit; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE odal.passport_audit TO odal_app; + + +-- +-- Name: TABLE passport_transfer; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE odal.passport_transfer TO odal_app; + + +-- +-- Name: TABLE passport_version; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT ON TABLE odal.passport_version TO odal_app; + + +-- +-- Name: TABLE personal_data_record; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT ON TABLE odal.personal_data_record TO odal_app; + + +-- +-- Name: COLUMN personal_data_record.content; Type: ACL; Schema: odal; Owner: - +-- + +GRANT UPDATE(content) ON TABLE odal.personal_data_record TO odal_app; + + +-- +-- Name: COLUMN personal_data_record.erased_at; Type: ACL; Schema: odal; Owner: - +-- + +GRANT UPDATE(erased_at) ON TABLE odal.personal_data_record TO odal_app; + + +-- +-- Name: TABLE qr_render; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE odal.qr_render TO odal_app; + + +-- +-- Name: TABLE registry_identity_audit; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT ON TABLE odal.registry_identity_audit TO odal_app; + + +-- +-- Name: TABLE registry_sync; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE odal.registry_sync TO odal_app; + + +-- +-- Name: TABLE registry_transfer; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE odal.registry_transfer TO odal_app; + + +-- +-- Name: TABLE scan_telemetry; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE odal.scan_telemetry TO odal_app; + + +-- +-- Name: TABLE seal_audit_state; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE odal.seal_audit_state TO odal_app; + + +-- +-- Name: TABLE seal_outbox; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE odal.seal_outbox TO odal_app; + + +-- +-- Name: TABLE snapshot_outbox; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE odal.snapshot_outbox TO odal_app; + + +-- +-- Name: TABLE trusted_list_cache; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE odal.trusted_list_cache TO odal_app; + + +-- +-- Name: TABLE unsold_goods_report; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE odal.unsold_goods_report TO odal_app; + + +-- +-- Name: TABLE webhook_delivery; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE odal.webhook_delivery TO odal_app; + + +-- +-- Name: TABLE webhook_subscription; Type: ACL; Schema: odal; Owner: - +-- + +GRANT SELECT,INSERT,UPDATE ON TABLE odal.webhook_subscription TO odal_app; + + +-- +-- PostgreSQL database dump complete +-- + + diff --git a/crates/dpp-node/tests/fixtures/upgrade/v0.15.0/public.json b/crates/dpp-node/tests/fixtures/upgrade/v0.15.0/public.json new file mode 100644 index 00000000..d814747c --- /dev/null +++ b/crates/dpp-node/tests/fixtures/upgrade/v0.15.0/public.json @@ -0,0 +1,8 @@ +{ + "01a1a307-b510-75c3-b5cc-1ccbddeb9787": "{\"applicableInstruments\":[{\"instrument\":\"battery-reg-2023-1542\",\"recorded\":\"catalog\"}],\"batchId\":\"ODL-IND-NMC-2026-Q2-001\",\"co2ePerUnit\":null,\"commodityCode\":\"85076000\",\"complianceResult\":{\"assessedAt\":\"2026-11-03T20:09:13.747931997Z\",\"co2eScore\":null,\"complianceStatus\":\"PASSTHROUGH_NO_VALIDATION\",\"recycledContentPct\":null,\"repairabilityIndex\":null,\"warnings\":[{\"code\":\"battery.recycled_content.not_yet_binding\",\"field\":\"/placedOnMarketDate\",\"message\":\"No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-06-01. Ruleset 'battery-recycled-content-art8-2' applies from 2031-08-18.\"}]},\"createdAt\":\"2026-11-03T20:09:13.744378034Z\",\"facility\":{\"address\":\"Werkstrasse 2, 10115 Berlin\",\"country\":\"DE\",\"name\":\"Demo Plant\",\"scheme\":\"gln\",\"value\":\"4012345000009\"},\"id\":\"01a1a307-b510-75c3-b5cc-1ccbddeb9787\",\"manufacturer\":{\"address\":\"Prenzlauer Berg 12, 10405 Berlin, DE\",\"country\":null,\"didWebUrl\":\"https://batteries.example/.well-known/did.json\",\"name\":\"Example Batteries GmbH\"},\"materials\":[{\"countryOfOrigin\":\"DE\",\"name\":\"Lithium Nickel Manganese Cobalt Oxide\",\"recycledPct\":11,\"weightKg\":14.2},{\"countryOfOrigin\":\"DE\",\"name\":\"Graphite\",\"recycledPct\":5,\"weightKg\":6.4},{\"countryOfOrigin\":\"DE\",\"name\":\"Aluminium enclosure\",\"recycledPct\":78,\"weightKg\":8.1}],\"operatorIdentifier\":\"DE123456789\",\"placedOnMarketDate\":\"2026-06-01\",\"productGroup\":\"battery\",\"productGroupData\":{\"batteryChemistry\":\"NMC\",\"batteryModelId\":\"GC-IND-NMC48-320\",\"batteryPassportNumber\":\"URN:ODL:BATT:09590000000014:ODL-IND-NMC-2026-Q2-001\",\"batteryType\":\"industrial\",\"batteryWeightKg\":32,\"commercialWarrantyPeriodMonths\":120,\"criticalRawMaterials\":[{\"casNumber\":\"7440-48-4\",\"countryOfOrigin\":\"CD\",\"name\":\"Cobalt\",\"weightGrams\":1450},{\"casNumber\":\"7439-93-2\",\"countryOfOrigin\":\"CL\",\"name\":\"Lithium\",\"weightGrams\":2100},{\"casNumber\":\"7782-42-5\",\"countryOfOrigin\":\"CN\",\"name\":\"Natural graphite\",\"weightGrams\":6400}],\"cycleLifeTestCRate\":0.5,\"euDeclarationOfConformity\":\"EU-DoC GC-2026-IND-0091 dated 2026-04-20, per Regulation (EU) 2023/1542 Art. 18\",\"expectedLifetimeCycles\":2800,\"expectedLifetimeReferenceTest\":\"IEC 61427-1:2013, 25 degC, 80% DoD\",\"hazardousSubstances\":[{\"casNumber\":\"7786-81-4\",\"concentrationPct\":1.2,\"name\":\"Nickel sulfate\"},{\"casNumber\":\"21324-40-3\",\"concentrationPct\":0.8,\"name\":\"Lithium hexafluorophosphate\"}],\"initialRoundTripEfficiencyPct\":95,\"internalCellResistanceMohm\":0.9,\"internalPackResistanceMohm\":14,\"manufacturingDate\":\"2026-04-15T00:00:00Z\",\"manufacturingPlace\":\"Berlin, DE (plant BLN-1)\",\"markingInformation\":\"Separate-collection symbol (crossed-out wheeled bin) per Art. 13(4); capacity 90 Ah / 4.32 kWh\",\"maximumVoltageV\":54.6,\"minimalVoltageV\":42,\"nominalCapacityAh\":90,\"nominalVoltageV\":48,\"notInUseTemperatureRange\":{\"maxC\":45,\"minC\":-20},\"notInUseTemperatureReferenceTest\":\"IEC 62660-1:2018 storage profile\",\"originalPowerCapabilityW\":4600,\"powerLimitMaxW\":6000,\"powerLimitMinW\":200,\"productGroup\":\"battery\",\"productIdentifier\":{\"gtin\":\"09590000000014\",\"scheme\":\"gs1\"},\"ratedCapacityKwh\":4.32,\"ratedEnergyWh\":4320,\"recycledContentCobaltPct\":null,\"recycledContentLithiumPct\":null,\"recycledContentNickelPct\":null,\"recycledContentReportingYear\":2026,\"renewableContentPct\":18,\"roundTripEfficiencyAtHalfCycleLifePct\":89,\"usableExtinguishingAgent\":\"Water mist or ABC dry powder; do not use CO2\",\"wasteBatteryInformation\":\"Return to an authorised collection point or installer. Do not shred. Contains lithium; fire risk if damaged. Producer registration DE-BATT-00123.\"},\"productName\":\"Odal Industrial Battery NMC-48\",\"publicJwsSignature\":\"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELU5NQy0yMDI2LVEyLTAwMSIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjEzLjc0NzkzMTk5N1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNi0wMS4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxMy43NDQzNzgwMzRaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjUxMC03NWMzLWI1Y2MtMWNjYmRkZWI5Nzg3IiwibWFudWZhY3R1cmVyIjp7ImFkZHJlc3MiOiJQcmVuemxhdWVyIEJlcmcgMTIsIDEwNDA1IEJlcmxpbiwgREUiLCJjb3VudHJ5IjpudWxsLCJkaWRXZWJVcmwiOiJodHRwczovL2JhdHRlcmllcy5leGFtcGxlLy53ZWxsLWtub3duL2RpZC5qc29uIiwibmFtZSI6IkV4YW1wbGUgQmF0dGVyaWVzIEdtYkgifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJMaXRoaXVtIE5pY2tlbCBNYW5nYW5lc2UgQ29iYWx0IE94aWRlIiwicmVjeWNsZWRQY3QiOjExLCJ3ZWlnaHRLZyI6MTQuMn0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJHcmFwaGl0ZSIsInJlY3ljbGVkUGN0Ijo1LCJ3ZWlnaHRLZyI6Ni40fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkFsdW1pbml1bSBlbmNsb3N1cmUiLCJyZWN5Y2xlZFBjdCI6NzgsIndlaWdodEtnIjo4LjF9XSwib3BlcmF0b3JJZGVudGlmaWVyIjoiREUxMjM0NTY3ODkiLCJwbGFjZWRPbk1hcmtldERhdGUiOiIyMDI2LTA2LTAxIiwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RHcm91cERhdGEiOnsiYmF0dGVyeUNoZW1pc3RyeSI6Ik5NQyIsImJhdHRlcnlNb2RlbElkIjoiR0MtSU5ELU5NQzQ4LTMyMCIsImJhdHRlcnlQYXNzcG9ydE51bWJlciI6IlVSTjpPREw6QkFUVDowOTU5MDAwMDAwMDAxNDpPREwtSU5ELU5NQy0yMDI2LVEyLTAwMSIsImJhdHRlcnlUeXBlIjoiaW5kdXN0cmlhbCIsImJhdHRlcnlXZWlnaHRLZyI6MzIsImNvbW1lcmNpYWxXYXJyYW50eVBlcmlvZE1vbnRocyI6MTIwLCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQ0MC00OC00IiwiY291bnRyeU9mT3JpZ2luIjoiQ0QiLCJuYW1lIjoiQ29iYWx0Iiwid2VpZ2h0R3JhbXMiOjE0NTB9LHsiY2FzTnVtYmVyIjoiNzQzOS05My0yIiwiY291bnRyeU9mT3JpZ2luIjoiQ0wiLCJuYW1lIjoiTGl0aGl1bSIsIndlaWdodEdyYW1zIjoyMTAwfSx7ImNhc051bWJlciI6Ijc3ODItNDItNSIsImNvdW50cnlPZk9yaWdpbiI6IkNOIiwibmFtZSI6Ik5hdHVyYWwgZ3JhcGhpdGUiLCJ3ZWlnaHRHcmFtcyI6NjQwMH1dLCJjeWNsZUxpZmVUZXN0Q1JhdGUiOjAuNSwiZXVEZWNsYXJhdGlvbk9mQ29uZm9ybWl0eSI6IkVVLURvQyBHQy0yMDI2LUlORC0wMDkxIGRhdGVkIDIwMjYtMDQtMjAsIHBlciBSZWd1bGF0aW9uIChFVSkgMjAyMy8xNTQyIEFydC4gMTgiLCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjoyODAwLCJleHBlY3RlZExpZmV0aW1lUmVmZXJlbmNlVGVzdCI6IklFQyA2MTQyNy0xOjIwMTMsIDI1IGRlZ0MsIDgwJSBEb0QiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiI3Nzg2LTgxLTQiLCJjb25jZW50cmF0aW9uUGN0IjoxLjIsIm5hbWUiOiJOaWNrZWwgc3VsZmF0ZSJ9LHsiY2FzTnVtYmVyIjoiMjEzMjQtNDAtMyIsImNvbmNlbnRyYXRpb25QY3QiOjAuOCwibmFtZSI6IkxpdGhpdW0gaGV4YWZsdW9yb3Bob3NwaGF0ZSJ9XSwiaW5pdGlhbFJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjk1LCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6MC45LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6MTQsIm1hbnVmYWN0dXJpbmdEYXRlIjoiMjAyNi0wNC0xNVQwMDowMDowMFoiLCJtYW51ZmFjdHVyaW5nUGxhY2UiOiJCZXJsaW4sIERFIChwbGFudCBCTE4tMSkiLCJtYXJraW5nSW5mb3JtYXRpb24iOiJTZXBhcmF0ZS1jb2xsZWN0aW9uIHN5bWJvbCAoY3Jvc3NlZC1vdXQgd2hlZWxlZCBiaW4pIHBlciBBcnQuIDEzKDQpOyBjYXBhY2l0eSA5MCBBaCAvIDQuMzIga1doIiwibWF4aW11bVZvbHRhZ2VWIjo1NC42LCJtaW5pbWFsVm9sdGFnZVYiOjQyLCJub21pbmFsQ2FwYWNpdHlBaCI6OTAsIm5vbWluYWxWb2x0YWdlViI6NDgsIm5vdEluVXNlVGVtcGVyYXR1cmVSYW5nZSI6eyJtYXhDIjo0NSwibWluQyI6LTIwfSwibm90SW5Vc2VUZW1wZXJhdHVyZVJlZmVyZW5jZVRlc3QiOiJJRUMgNjI2NjAtMToyMDE4IHN0b3JhZ2UgcHJvZmlsZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NDYwMCwicG93ZXJMaW1pdE1heFciOjYwMDAsInBvd2VyTGltaXRNaW5XIjoyMDAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwMTQiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6NC4zMiwicmF0ZWRFbmVyZ3lXaCI6NDMyMCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjE4LCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo4OSwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIgbWlzdCBvciBBQkMgZHJ5IHBvd2RlcjsgZG8gbm90IHVzZSBDTzIiLCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IlJldHVybiB0byBhbiBhdXRob3Jpc2VkIGNvbGxlY3Rpb24gcG9pbnQgb3IgaW5zdGFsbGVyLiBEbyBub3Qgc2hyZWQuIENvbnRhaW5zIGxpdGhpdW07IGZpcmUgcmlzayBpZiBkYW1hZ2VkLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDEyMy4ifSwicHJvZHVjdE5hbWUiOiJPZGFsIEluZHVzdHJpYWwgQmF0dGVyeSBOTUMtNDgiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTMuNzg5Mjk4NzU3WiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDAxNC8yMS83NWMzYjVjYzFjY2JkZGViOTc4NyIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjo3LjV9LCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTMuNzg5Mjk4NzU3WiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTMuNzg5Mjk4NzU3WiIsInZlcnNpb24iOjF9.9TX5ElGt90F54ac4WbhRXEisQKqA27m7ovL9k6ptKJwirMiAUmCxK2zQ1WQfAIeQp-XMXKO6e_qsRjVJ4nNPCg\",\"publishedAt\":\"2026-11-03T20:09:13.789298757Z\",\"qrCodeUrl\":\"http://127.0.0.1:1/01/09590000000014/21/75c3b5cc1ccbddeb9787\",\"repairabilityScore\":{\"overall\":7.5},\"retentionUntil\":\"2036-11-03T20:09:13.789298757Z\",\"schemaVersion\":\"2.8.0\",\"status\":\"active\",\"updatedAt\":\"2026-11-03T20:09:13.789298757Z\",\"version\":1}", + "01a1a307-b5bd-71ac-9c86-4696540c3b21": "{\"applicableInstruments\":[{\"instrument\":\"battery-reg-2023-1542\",\"recorded\":\"catalog\"}],\"batchId\":\"ODL-IND-LFP-2026-Q2-014\",\"carrierSerial\":\"71ac9c864696540c3b21\",\"co2ePerUnit\":null,\"commodityCode\":\"85076000\",\"complianceResult\":{\"assessedAt\":\"2026-11-03T20:09:14.573474747Z\",\"co2eScore\":null,\"complianceStatus\":\"PASSTHROUGH_NO_VALIDATION\",\"recycledContentPct\":null,\"repairabilityIndex\":null,\"warnings\":[{\"code\":\"battery.recycled_content.not_yet_binding\",\"field\":\"/placedOnMarketDate\",\"message\":\"No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-05-20. Ruleset 'battery-recycled-content-art8-2' applies from 2031-08-18.\"}]},\"createdAt\":\"2026-11-03T20:09:14.572051734Z\",\"facility\":{\"address\":\"Werkstrasse 2, 10115 Berlin\",\"country\":\"DE\",\"name\":\"Demo Plant\",\"scheme\":\"gln\",\"value\":\"4012345000009\"},\"id\":\"01a1a307-b84c-7245-8576-ea67194809ea\",\"manufacturer\":{\"address\":\"Prenzlauer Berg 12, 10405 Berlin, DE\",\"country\":null,\"didWebUrl\":\"https://batteries.example/.well-known/did.json\",\"name\":\"Example Batteries GmbH\"},\"materials\":[{\"countryOfOrigin\":\"DE\",\"name\":\"Lithium Iron Phosphate\",\"recycledPct\":8,\"weightKg\":22},{\"countryOfOrigin\":\"DE\",\"name\":\"Graphite\",\"recycledPct\":6,\"weightKg\":9.5},{\"countryOfOrigin\":\"DE\",\"name\":\"Steel enclosure\",\"recycledPct\":82,\"weightKg\":12}],\"operatorIdentifier\":\"DE123456789\",\"placedOnMarketDate\":\"2026-05-20\",\"productGroup\":\"battery\",\"productGroupData\":{\"batteryChemistry\":\"LFP\",\"batteryModelId\":\"GC-ESS-LFP512-100\",\"batteryPassportNumber\":\"URN:ODL:BATT:09590000000021:ODL-IND-LFP-2026-Q2-014\",\"batteryType\":\"industrial\",\"batteryWeightKg\":46.5,\"commercialWarrantyPeriodMonths\":144,\"criticalRawMaterials\":[{\"casNumber\":\"7439-93-2\",\"countryOfOrigin\":\"AU\",\"name\":\"Lithium\",\"weightGrams\":2600},{\"casNumber\":\"7782-42-5\",\"countryOfOrigin\":\"CN\",\"name\":\"Natural graphite\",\"weightGrams\":9500}],\"cycleLifeTestCRate\":0.33,\"euDeclarationOfConformity\":\"EU-DoC GC-2026-IND-0067 dated 2026-03-10, per Regulation (EU) 2023/1542 Art. 18\",\"expectedLifetimeCycles\":6000,\"expectedLifetimeReferenceTest\":\"IEC 61427-1:2013, 25 degC, 90% DoD\",\"hazardousSubstances\":[{\"casNumber\":\"21324-40-3\",\"concentrationPct\":0.7,\"name\":\"Lithium hexafluorophosphate\"}],\"initialRoundTripEfficiencyPct\":96.5,\"internalCellResistanceMohm\":0.6,\"internalPackResistanceMohm\":9,\"manufacturingDate\":\"2026-03-02T00:00:00Z\",\"manufacturingPlace\":\"Berlin, DE (plant BLN-2)\",\"markingInformation\":\"Separate-collection symbol per Art. 13(4); capacity 100 Ah / 5.12 kWh; chemistry LFP\",\"maximumVoltageV\":58.4,\"minimalVoltageV\":40,\"nominalCapacityAh\":100,\"nominalVoltageV\":51.2,\"notInUseTemperatureRange\":{\"maxC\":50,\"minC\":-10},\"notInUseTemperatureReferenceTest\":\"IEC 62660-1:2018 storage profile\",\"originalPowerCapabilityW\":5000,\"powerLimitMaxW\":7500,\"powerLimitMinW\":150,\"productGroup\":\"battery\",\"productIdentifier\":{\"gtin\":\"09590000000021\",\"scheme\":\"gs1\"},\"ratedCapacityKwh\":5.12,\"ratedEnergyWh\":5120,\"recycledContentCobaltPct\":null,\"recycledContentLithiumPct\":null,\"recycledContentNickelPct\":null,\"recycledContentReportingYear\":2026,\"renewableContentPct\":22,\"roundTripEfficiencyAtHalfCycleLifePct\":92,\"usableExtinguishingAgent\":\"Water or ABC dry powder; large volumes of water for thermal runaway\",\"wasteBatteryInformation\":\"Return to an authorised collection point. Do not incinerate or shred. Producer registration DE-BATT-00123.\"},\"productName\":\"LFP stationary pack, corrected\",\"publicJwsSignature\":\"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELUxGUC0yMDI2LVEyLTAxNCIsImNhcnJpZXJTZXJpYWwiOiI3MWFjOWM4NjQ2OTY1NDBjM2IyMSIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjU3MzQ3NDc0N1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNS0yMC4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC41NzIwNTE3MzRaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjg0Yy03MjQ1LTg1NzYtZWE2NzE5NDgwOWVhIiwibWFudWZhY3R1cmVyIjp7ImFkZHJlc3MiOiJQcmVuemxhdWVyIEJlcmcgMTIsIDEwNDA1IEJlcmxpbiwgREUiLCJjb3VudHJ5IjpudWxsLCJkaWRXZWJVcmwiOiJodHRwczovL2JhdHRlcmllcy5leGFtcGxlLy53ZWxsLWtub3duL2RpZC5qc29uIiwibmFtZSI6IkV4YW1wbGUgQmF0dGVyaWVzIEdtYkgifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJMaXRoaXVtIElyb24gUGhvc3BoYXRlIiwicmVjeWNsZWRQY3QiOjgsIndlaWdodEtnIjoyMn0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJHcmFwaGl0ZSIsInJlY3ljbGVkUGN0Ijo2LCJ3ZWlnaHRLZyI6OS41fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IlN0ZWVsIGVuY2xvc3VyZSIsInJlY3ljbGVkUGN0Ijo4Miwid2VpZ2h0S2ciOjEyfV0sIm9wZXJhdG9ySWRlbnRpZmllciI6IkRFMTIzNDU2Nzg5IiwicGxhY2VkT25NYXJrZXREYXRlIjoiMjAyNi0wNS0yMCIsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImJhdHRlcnlDaGVtaXN0cnkiOiJMRlAiLCJiYXR0ZXJ5TW9kZWxJZCI6IkdDLUVTUy1MRlA1MTItMTAwIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDIxOk9ETC1JTkQtTEZQLTIwMjYtUTItMDE0IiwiYmF0dGVyeVR5cGUiOiJpbmR1c3RyaWFsIiwiYmF0dGVyeVdlaWdodEtnIjo0Ni41LCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjE0NCwiY3JpdGljYWxSYXdNYXRlcmlhbHMiOlt7ImNhc051bWJlciI6Ijc0MzktOTMtMiIsImNvdW50cnlPZk9yaWdpbiI6IkFVIiwibmFtZSI6IkxpdGhpdW0iLCJ3ZWlnaHRHcmFtcyI6MjYwMH0seyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDTiIsIm5hbWUiOiJOYXR1cmFsIGdyYXBoaXRlIiwid2VpZ2h0R3JhbXMiOjk1MDB9XSwiY3ljbGVMaWZlVGVzdENSYXRlIjowLjMzLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIEdDLTIwMjYtSU5ELTAwNjcgZGF0ZWQgMjAyNi0wMy0xMCwgcGVyIFJlZ3VsYXRpb24gKEVVKSAyMDIzLzE1NDIgQXJ0LiAxOCIsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjYwMDAsImV4cGVjdGVkTGlmZXRpbWVSZWZlcmVuY2VUZXN0IjoiSUVDIDYxNDI3LTE6MjAxMywgMjUgZGVnQywgOTAlIERvRCIsImhhemFyZG91c1N1YnN0YW5jZXMiOlt7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJjb25jZW50cmF0aW9uUGN0IjowLjcsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUifV0sImluaXRpYWxSb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5Ni41LCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6MC42LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6OSwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTAzLTAyVDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkJlcmxpbiwgREUgKHBsYW50IEJMTi0yKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIHBlciBBcnQuIDEzKDQpOyBjYXBhY2l0eSAxMDAgQWggLyA1LjEyIGtXaDsgY2hlbWlzdHJ5IExGUCIsIm1heGltdW1Wb2x0YWdlViI6NTguNCwibWluaW1hbFZvbHRhZ2VWIjo0MCwibm9taW5hbENhcGFjaXR5QWgiOjEwMCwibm9taW5hbFZvbHRhZ2VWIjo1MS4yLCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NTAsIm1pbkMiOi0xMH0sIm5vdEluVXNlVGVtcGVyYXR1cmVSZWZlcmVuY2VUZXN0IjoiSUVDIDYyNjYwLTE6MjAxOCBzdG9yYWdlIHByb2ZpbGUiLCJvcmlnaW5hbFBvd2VyQ2FwYWJpbGl0eVciOjUwMDAsInBvd2VyTGltaXRNYXhXIjo3NTAwLCJwb3dlckxpbWl0TWluVyI6MTUwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDIxIiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOjUuMTIsInJhdGVkRW5lcmd5V2giOjUxMjAsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRSZXBvcnRpbmdZZWFyIjoyMDI2LCJyZW5ld2FibGVDb250ZW50UGN0IjoyMiwicm91bmRUcmlwRWZmaWNpZW5jeUF0SGFsZkN5Y2xlTGlmZVBjdCI6OTIsInVzYWJsZUV4dGluZ3Vpc2hpbmdBZ2VudCI6IldhdGVyIG9yIEFCQyBkcnkgcG93ZGVyOyBsYXJnZSB2b2x1bWVzIG9mIHdhdGVyIGZvciB0aGVybWFsIHJ1bmF3YXkiLCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IlJldHVybiB0byBhbiBhdXRob3Jpc2VkIGNvbGxlY3Rpb24gcG9pbnQuIERvIG5vdCBpbmNpbmVyYXRlIG9yIHNocmVkLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDEyMy4ifSwicHJvZHVjdE5hbWUiOiJMRlAgc3RhdGlvbmFyeSBwYWNrLCBjb3JyZWN0ZWQiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNTg1MDQ5MjcyWiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDAyMS8yMS83MWFjOWM4NjQ2OTY1NDBjM2IyMSIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjo4LjF9LCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuNTg1MDQ5MjcyWiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInN1cGVyc2VkZXNJZCI6IjAxYTFhMzA3LWI1YmQtNzFhYy05Yzg2LTQ2OTY1NDBjM2IyMSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNTg1MDQ5MjcyWiIsInZlcnNpb24iOjJ9.eUpdL2Rti-tkqYKFpz3AmYCthWqDEKBVFoZq44jXJAhIv14G-G1KVxZrjWcizjVLyMtTj04p-IN7IdttyodNCA\",\"publishedAt\":\"2026-11-03T20:09:14.585049272Z\",\"qrCodeUrl\":\"http://127.0.0.1:1/01/09590000000021/21/71ac9c864696540c3b21\",\"repairabilityScore\":{\"overall\":8.1},\"retentionUntil\":\"2036-11-03T20:09:14.585049272Z\",\"schemaVersion\":\"2.8.0\",\"status\":\"active\",\"supersedesId\":\"01a1a307-b5bd-71ac-9c86-4696540c3b21\",\"updatedAt\":\"2026-11-03T20:09:14.585049272Z\",\"version\":2}", + "01a1a307-b67c-706a-a55e-689712fcd122": "{\"applicableInstruments\":[{\"instrument\":\"battery-reg-2023-1542\",\"recorded\":\"catalog\"}],\"batchId\":\"ODL-IND-PB-2026-Q1-007\",\"co2ePerUnit\":null,\"commodityCode\":\"85072000\",\"complianceResult\":{\"assessedAt\":\"2026-11-03T20:09:14.112526945Z\",\"co2eScore\":null,\"complianceStatus\":\"PASSTHROUGH_NO_VALIDATION\",\"recycledContentPct\":null,\"repairabilityIndex\":null,\"warnings\":[{\"code\":\"battery.recycled_content.not_yet_binding\",\"field\":\"/placedOnMarketDate\",\"message\":\"No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-02-10. Ruleset 'battery-recycled-content-art8-2' applies from 2031-08-18.\"}]},\"createdAt\":\"2026-11-03T20:09:14.108085427Z\",\"facility\":{\"address\":\"Werkstrasse 2, 10115 Berlin\",\"country\":\"DE\",\"name\":\"Demo Plant\",\"scheme\":\"gln\",\"value\":\"4012345000009\"},\"id\":\"01a1a307-b67c-706a-a55e-689712fcd122\",\"manufacturer\":{\"address\":\"Industriestrasse 8, 47059 Duisburg, DE\",\"country\":null,\"didWebUrl\":\"https://rheinpower.example/.well-known/did.json\",\"name\":\"Rheinpower Batterien GmbH\"},\"materials\":[{\"countryOfOrigin\":\"DE\",\"name\":\"Lead\",\"recycledPct\":82,\"weightKg\":38},{\"countryOfOrigin\":\"DE\",\"name\":\"Sulfuric acid electrolyte\",\"recycledPct\":0,\"weightKg\":9},{\"countryOfOrigin\":\"DE\",\"name\":\"Polypropylene case\",\"recycledPct\":45,\"weightKg\":3.2}],\"operatorIdentifier\":\"DE123456789\",\"placedOnMarketDate\":\"2026-02-10\",\"productGroup\":\"battery\",\"productGroupData\":{\"batteryChemistry\":\"lead-acid\",\"batteryModelId\":\"RP-VRLA-12-200\",\"batteryPassportNumber\":\"URN:ODL:BATT:09590000000038:ODL-IND-PB-2026-Q1-007\",\"batteryType\":\"industrial\",\"batteryWeightKg\":52,\"commercialWarrantyPeriodMonths\":60,\"criticalRawMaterials\":[{\"casNumber\":\"7440-36-0\",\"countryOfOrigin\":\"CN\",\"name\":\"Antimony\",\"weightGrams\":380}],\"cycleLifeTestCRate\":0.1,\"euDeclarationOfConformity\":\"EU-DoC RP-2026-IND-0014 dated 2026-01-20, per Regulation (EU) 2023/1542 Art. 18\",\"expectedLifetimeCycles\":1200,\"expectedLifetimeReferenceTest\":\"IEC 60896-21/22, 25 degC, 50% DoD\",\"hazardSymbol\":\"lead\",\"hazardousSubstances\":[{\"casNumber\":\"7439-92-1\",\"concentrationPct\":68,\"name\":\"Lead\"},{\"casNumber\":\"7664-93-9\",\"concentrationPct\":17,\"name\":\"Sulfuric acid\"}],\"initialRoundTripEfficiencyPct\":82,\"internalCellResistanceMohm\":3.5,\"internalPackResistanceMohm\":4.2,\"manufacturingDate\":\"2026-01-08T00:00:00Z\",\"manufacturingPlace\":\"Duisburg, DE (plant DU-3)\",\"markingInformation\":\"Separate-collection symbol per Art. 13(4); Pb symbol per Art. 13(5); capacity 200 Ah / 12 V\",\"maximumVoltageV\":14.4,\"minimalVoltageV\":10.5,\"nominalCapacityAh\":200,\"nominalVoltageV\":12,\"notInUseTemperatureRange\":{\"maxC\":40,\"minC\":-15},\"notInUseTemperatureReferenceTest\":\"IEC 60896-21:2004 storage clause\",\"originalPowerCapabilityW\":1800,\"powerLimitMaxW\":2400,\"powerLimitMinW\":50,\"productGroup\":\"battery\",\"productIdentifier\":{\"gtin\":\"09590000000038\",\"scheme\":\"gs1\"},\"ratedCapacityKwh\":2.4,\"ratedEnergyWh\":2400,\"recycledContentCobaltPct\":null,\"recycledContentLithiumPct\":null,\"recycledContentNickelPct\":null,\"recycledContentReportingYear\":2026,\"renewableContentPct\":2,\"roundTripEfficiencyAtHalfCycleLifePct\":74,\"usableExtinguishingAgent\":\"CO2, ABC dry powder, or water; neutralise acid spills with soda ash\",\"wasteBatteryInformation\":\"Lead-acid battery - return to retailer or authorised collector for recycling. Never dispose in household waste. Producer registration DE-BATT-00457.\"},\"productName\":\"Odal Industrial Standby Battery 12V-200Ah (VRLA)\",\"publicJwsSignature\":\"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELVBCLTIwMjYtUTEtMDA3IiwiY28yZVBlclVuaXQiOm51bGwsImNvbW1vZGl0eUNvZGUiOiI4NTA3MjAwMCIsImNvbXBsaWFuY2VSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMTEyNTI2OTQ1WiIsImNvMmVTY29yZSI6bnVsbCwiY29tcGxpYW5jZVN0YXR1cyI6IlBBU1NUSFJPVUdIX05PX1ZBTElEQVRJT04iLCJyZWN5Y2xlZENvbnRlbnRQY3QiOm51bGwsInJlcGFpcmFiaWxpdHlJbmRleCI6bnVsbCwid2FybmluZ3MiOlt7ImNvZGUiOiJiYXR0ZXJ5LnJlY3ljbGVkX2NvbnRlbnQubm90X3lldF9iaW5kaW5nIiwiZmllbGQiOiIvcGxhY2VkT25NYXJrZXREYXRlIiwibWVzc2FnZSI6Ik5vIEFydC4gOCBtaW5pbXVtIHJlY3ljbGVkIHNoYXJlIGJpbmRzIGEgYmF0dGVyeSBwbGFjZWQgb24gdGhlIEVVIG1hcmtldCBvbiAyMDI2LTAyLTEwLiBSdWxlc2V0ICdiYXR0ZXJ5LXJlY3ljbGVkLWNvbnRlbnQtYXJ0OC0yJyBhcHBsaWVzIGZyb20gMjAzMS0wOC0xOC4ifV19LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjEwODA4NTQyN1oiLCJmYWNpbGl0eSI6eyJhZGRyZXNzIjoiV2Vya3N0cmFzc2UgMiwgMTAxMTUgQmVybGluIiwiY291bnRyeSI6IkRFIiwibmFtZSI6IkRlbW8gUGxhbnQiLCJzY2hlbWUiOiJnbG4iLCJ2YWx1ZSI6IjQwMTIzNDUwMDAwMDkifSwiaWQiOiIwMWExYTMwNy1iNjdjLTcwNmEtYTU1ZS02ODk3MTJmY2QxMjIiLCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IkluZHVzdHJpZXN0cmFzc2UgOCwgNDcwNTkgRHVpc2J1cmcsIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly9yaGVpbnBvd2VyLmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiUmhlaW5wb3dlciBCYXR0ZXJpZW4gR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkxlYWQiLCJyZWN5Y2xlZFBjdCI6ODIsIndlaWdodEtnIjozOH0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJTdWxmdXJpYyBhY2lkIGVsZWN0cm9seXRlIiwicmVjeWNsZWRQY3QiOjAsIndlaWdodEtnIjo5fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IlBvbHlwcm9weWxlbmUgY2FzZSIsInJlY3ljbGVkUGN0Ijo0NSwid2VpZ2h0S2ciOjMuMn1dLCJvcGVyYXRvcklkZW50aWZpZXIiOiJERTEyMzQ1Njc4OSIsInBsYWNlZE9uTWFya2V0RGF0ZSI6IjIwMjYtMDItMTAiLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdEdyb3VwRGF0YSI6eyJiYXR0ZXJ5Q2hlbWlzdHJ5IjoibGVhZC1hY2lkIiwiYmF0dGVyeU1vZGVsSWQiOiJSUC1WUkxBLTEyLTIwMCIsImJhdHRlcnlQYXNzcG9ydE51bWJlciI6IlVSTjpPREw6QkFUVDowOTU5MDAwMDAwMDAzODpPREwtSU5ELVBCLTIwMjYtUTEtMDA3IiwiYmF0dGVyeVR5cGUiOiJpbmR1c3RyaWFsIiwiYmF0dGVyeVdlaWdodEtnIjo1MiwiY29tbWVyY2lhbFdhcnJhbnR5UGVyaW9kTW9udGhzIjo2MCwiY3JpdGljYWxSYXdNYXRlcmlhbHMiOlt7ImNhc051bWJlciI6Ijc0NDAtMzYtMCIsImNvdW50cnlPZk9yaWdpbiI6IkNOIiwibmFtZSI6IkFudGltb255Iiwid2VpZ2h0R3JhbXMiOjM4MH1dLCJjeWNsZUxpZmVUZXN0Q1JhdGUiOjAuMSwiZXVEZWNsYXJhdGlvbk9mQ29uZm9ybWl0eSI6IkVVLURvQyBSUC0yMDI2LUlORC0wMDE0IGRhdGVkIDIwMjYtMDEtMjAsIHBlciBSZWd1bGF0aW9uIChFVSkgMjAyMy8xNTQyIEFydC4gMTgiLCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjoxMjAwLCJleHBlY3RlZExpZmV0aW1lUmVmZXJlbmNlVGVzdCI6IklFQyA2MDg5Ni0yMS8yMiwgMjUgZGVnQywgNTAlIERvRCIsImhhemFyZFN5bWJvbCI6ImxlYWQiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiI3NDM5LTkyLTEiLCJjb25jZW50cmF0aW9uUGN0Ijo2OCwibmFtZSI6IkxlYWQifSx7ImNhc051bWJlciI6Ijc2NjQtOTMtOSIsImNvbmNlbnRyYXRpb25QY3QiOjE3LCJuYW1lIjoiU3VsZnVyaWMgYWNpZCJ9XSwiaW5pdGlhbFJvdW5kVHJpcEVmZmljaWVuY3lQY3QiOjgyLCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6My41LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6NC4yLCJtYW51ZmFjdHVyaW5nRGF0ZSI6IjIwMjYtMDEtMDhUMDA6MDA6MDBaIiwibWFudWZhY3R1cmluZ1BsYWNlIjoiRHVpc2J1cmcsIERFIChwbGFudCBEVS0zKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIHBlciBBcnQuIDEzKDQpOyBQYiBzeW1ib2wgcGVyIEFydC4gMTMoNSk7IGNhcGFjaXR5IDIwMCBBaCAvIDEyIFYiLCJtYXhpbXVtVm9sdGFnZVYiOjE0LjQsIm1pbmltYWxWb2x0YWdlViI6MTAuNSwibm9taW5hbENhcGFjaXR5QWgiOjIwMCwibm9taW5hbFZvbHRhZ2VWIjoxMiwibm90SW5Vc2VUZW1wZXJhdHVyZVJhbmdlIjp7Im1heEMiOjQwLCJtaW5DIjotMTV9LCJub3RJblVzZVRlbXBlcmF0dXJlUmVmZXJlbmNlVGVzdCI6IklFQyA2MDg5Ni0yMToyMDA0IHN0b3JhZ2UgY2xhdXNlIiwib3JpZ2luYWxQb3dlckNhcGFiaWxpdHlXIjoxODAwLCJwb3dlckxpbWl0TWF4VyI6MjQwMCwicG93ZXJMaW1pdE1pblciOjUwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDM4Iiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOjIuNCwicmF0ZWRFbmVyZ3lXaCI6MjQwMCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjIsInJvdW5kVHJpcEVmZmljaWVuY3lBdEhhbGZDeWNsZUxpZmVQY3QiOjc0LCJ1c2FibGVFeHRpbmd1aXNoaW5nQWdlbnQiOiJDTzIsIEFCQyBkcnkgcG93ZGVyLCBvciB3YXRlcjsgbmV1dHJhbGlzZSBhY2lkIHNwaWxscyB3aXRoIHNvZGEgYXNoIiwid2FzdGVCYXR0ZXJ5SW5mb3JtYXRpb24iOiJMZWFkLWFjaWQgYmF0dGVyeSAtIHJldHVybiB0byByZXRhaWxlciBvciBhdXRob3Jpc2VkIGNvbGxlY3RvciBmb3IgcmVjeWNsaW5nLiBOZXZlciBkaXNwb3NlIGluIGhvdXNlaG9sZCB3YXN0ZS4gUHJvZHVjZXIgcmVnaXN0cmF0aW9uIERFLUJBVFQtMDA0NTcuIn0sInByb2R1Y3ROYW1lIjoiT2RhbCBJbmR1c3RyaWFsIFN0YW5kYnkgQmF0dGVyeSAxMlYtMjAwQWggKFZSTEEpIiwicHVibGlzaGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjE3MzU1NDczNFoiLCJxckNvZGVVcmwiOiJodHRwOi8vMTI3LjAuMC4xOjEvMDEvMDk1OTAwMDAwMDAwMzgvMjEvNzA2YWE1NWU2ODk3MTJmY2QxMjIiLCJyZXBhaXJhYmlsaXR5U2NvcmUiOnsib3ZlcmFsbCI6M30sInJldGVudGlvblVudGlsIjoiMjAzNi0xMS0wM1QyMDowOToxNC4xNzM1NTQ3MzRaIiwic2NoZW1hVmVyc2lvbiI6IjIuOC4wIiwic3RhdHVzIjoiYWN0aXZlIiwidXBkYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC4xNzM1NTQ3MzRaIiwidmVyc2lvbiI6MX0.XgFJkY8XptJaEsJoInGkAlWHMfr81JbdNxNQ0LF4PDw7ik2h_m4prtUaaWzqCc05O5McEunL7-tf-J5vZfHzDA\",\"publishedAt\":\"2026-11-03T20:09:14.173554734Z\",\"qrCodeUrl\":\"http://127.0.0.1:1/01/09590000000038/21/706aa55e689712fcd122\",\"repairabilityScore\":{\"overall\":3},\"retentionUntil\":\"2036-11-03T20:09:14.173554734Z\",\"schemaVersion\":\"2.8.0\",\"status\":\"active\",\"updatedAt\":\"2026-11-03T20:09:14.173554734Z\",\"version\":1}", + "01a1a307-b70c-70c1-b4a3-a616d161e997": "{\"applicableInstruments\":[{\"instrument\":\"battery-reg-2023-1542\",\"recorded\":\"catalog\"}],\"batchId\":\"VBX-EV-2026-0421\",\"co2ePerUnit\":null,\"commodityCode\":\"85076000\",\"complianceResult\":{\"assessedAt\":\"2026-11-03T20:09:14.255214416Z\",\"co2eScore\":null,\"complianceStatus\":\"PASSTHROUGH_NO_VALIDATION\",\"recycledContentPct\":null,\"repairabilityIndex\":null,\"warnings\":[{\"code\":\"battery.recycled_content.not_yet_binding\",\"field\":\"/placedOnMarketDate\",\"message\":\"No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-07-01. Ruleset 'battery-recycled-content-art8-2' applies from 2031-08-18.\"}]},\"createdAt\":\"2026-11-03T20:09:14.252942043Z\",\"facility\":{\"address\":\"Werkstrasse 2, 10115 Berlin\",\"country\":\"DE\",\"name\":\"Demo Plant\",\"scheme\":\"gln\",\"value\":\"4012345000009\"},\"id\":\"01a1a307-b70c-70c1-b4a3-a616d161e997\",\"manufacturer\":{\"address\":\"Werkstrasse 5, 85055 Ingolstadt, DE\",\"country\":null,\"didWebUrl\":\"https://voltex.example/.well-known/did.json\",\"name\":\"Voltex Battery GmbH\"},\"materials\":[{\"countryOfOrigin\":\"DE\",\"name\":\"Lithium Nickel Manganese Cobalt Oxide (NMC 811)\",\"recycledPct\":13,\"weightKg\":210},{\"countryOfOrigin\":\"DE\",\"name\":\"Graphite/silicon anode\",\"recycledPct\":4,\"weightKg\":95},{\"countryOfOrigin\":\"DE\",\"name\":\"Aluminium pack housing\",\"recycledPct\":71,\"weightKg\":85}],\"operatorIdentifier\":\"DE123456789\",\"placedOnMarketDate\":\"2026-07-01\",\"productGroup\":\"battery\",\"productGroupData\":{\"batteryChemistry\":\"NMC\",\"batteryModelId\":\"VBX-EV75-NMC811-P4S96\",\"batteryPassportNumber\":\"URN:ODL:BATT:09590000000045:VBX-EV-2026-0421\",\"batteryType\":\"ev\",\"batteryWeightKg\":468,\"capacityThresholdForExhaustionPct\":70,\"commercialWarrantyPeriodMonths\":96,\"criticalRawMaterials\":[{\"casNumber\":\"7440-48-4\",\"countryOfOrigin\":\"CD\",\"name\":\"Cobalt\",\"weightGrams\":5200},{\"casNumber\":\"7439-93-2\",\"countryOfOrigin\":\"CL\",\"name\":\"Lithium\",\"weightGrams\":8100},{\"casNumber\":\"7440-02-0\",\"countryOfOrigin\":\"ID\",\"name\":\"Nickel\",\"weightGrams\":32000},{\"casNumber\":\"7782-42-5\",\"countryOfOrigin\":\"MZ\",\"name\":\"Natural graphite\",\"weightGrams\":74000}],\"cycleLifeTestCRate\":1,\"euDeclarationOfConformity\":\"EU-DoC VBX-2026-EV-0142 dated 2026-05-18, per Regulation (EU) 2023/1542 Art. 18\",\"expectedLifetimeCycles\":1800,\"expectedLifetimeReferenceTest\":\"UNECE GTR 22 / IEC 62660-1:2018, 25 degC\",\"hazardousSubstances\":[{\"casNumber\":\"7786-81-4\",\"concentrationPct\":2.4,\"name\":\"Nickel sulfate\"},{\"casNumber\":\"10124-43-3\",\"concentrationPct\":0.9,\"name\":\"Cobalt sulfate\"},{\"casNumber\":\"21324-40-3\",\"concentrationPct\":0.9,\"name\":\"Lithium hexafluorophosphate\"}],\"initialRoundTripEfficiencyPct\":94,\"internalCellResistanceMohm\":1.1,\"internalPackResistanceMohm\":42,\"manufacturingDate\":\"2026-05-11T00:00:00Z\",\"manufacturingPlace\":\"Ingolstadt, DE (plant ING-1)\",\"markingInformation\":\"Separate-collection symbol per Art. 13(4); capacity 75 kWh; HV warning label\",\"maximumVoltageV\":403.2,\"minimalVoltageV\":270,\"nominalCapacityAh\":211,\"nominalVoltageV\":355,\"notInUseTemperatureRange\":{\"maxC\":55,\"minC\":-30},\"notInUseTemperatureReferenceTest\":\"IEC 62660-1:2018 storage profile\",\"originalPowerCapabilityW\":150000,\"powerLimitMaxW\":220000,\"powerLimitMinW\":500,\"productGroup\":\"battery\",\"productIdentifier\":{\"gtin\":\"09590000000045\",\"scheme\":\"gs1\"},\"ratedCapacityKwh\":75,\"ratedEnergyWh\":75000,\"recycledContentCobaltPct\":null,\"recycledContentLithiumPct\":null,\"recycledContentNickelPct\":null,\"recycledContentReportingYear\":2026,\"renewableContentPct\":16,\"roundTripEfficiencyAtHalfCycleLifePct\":88,\"usableExtinguishingAgent\":\"Large volumes of water for cooling; do not use CO2 on cell fire\",\"wasteBatteryInformation\":\"EV battery - handled by the vehicle manufacturer take-back scheme. Do not open or discharge. Contact hv-service@voltex.example. Producer registration DE-BATT-00981.\"},\"productName\":\"Voltex EV Traction Battery 75 kWh\",\"publicJwsSignature\":\"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJWQlgtRVYtMjAyNi0wNDIxIiwiY28yZVBlclVuaXQiOm51bGwsImNvbW1vZGl0eUNvZGUiOiI4NTA3NjAwMCIsImNvbXBsaWFuY2VSZXN1bHQiOnsiYXNzZXNzZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMjU1MjE0NDE2WiIsImNvMmVTY29yZSI6bnVsbCwiY29tcGxpYW5jZVN0YXR1cyI6IlBBU1NUSFJPVUdIX05PX1ZBTElEQVRJT04iLCJyZWN5Y2xlZENvbnRlbnRQY3QiOm51bGwsInJlcGFpcmFiaWxpdHlJbmRleCI6bnVsbCwid2FybmluZ3MiOlt7ImNvZGUiOiJiYXR0ZXJ5LnJlY3ljbGVkX2NvbnRlbnQubm90X3lldF9iaW5kaW5nIiwiZmllbGQiOiIvcGxhY2VkT25NYXJrZXREYXRlIiwibWVzc2FnZSI6Ik5vIEFydC4gOCBtaW5pbXVtIHJlY3ljbGVkIHNoYXJlIGJpbmRzIGEgYmF0dGVyeSBwbGFjZWQgb24gdGhlIEVVIG1hcmtldCBvbiAyMDI2LTA3LTAxLiBSdWxlc2V0ICdiYXR0ZXJ5LXJlY3ljbGVkLWNvbnRlbnQtYXJ0OC0yJyBhcHBsaWVzIGZyb20gMjAzMS0wOC0xOC4ifV19LCJjcmVhdGVkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjI1Mjk0MjA0M1oiLCJmYWNpbGl0eSI6eyJhZGRyZXNzIjoiV2Vya3N0cmFzc2UgMiwgMTAxMTUgQmVybGluIiwiY291bnRyeSI6IkRFIiwibmFtZSI6IkRlbW8gUGxhbnQiLCJzY2hlbWUiOiJnbG4iLCJ2YWx1ZSI6IjQwMTIzNDUwMDAwMDkifSwiaWQiOiIwMWExYTMwNy1iNzBjLTcwYzEtYjRhMy1hNjE2ZDE2MWU5OTciLCJtYW51ZmFjdHVyZXIiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDUsIDg1MDU1IEluZ29sc3RhZHQsIERFIiwiY291bnRyeSI6bnVsbCwiZGlkV2ViVXJsIjoiaHR0cHM6Ly92b2x0ZXguZXhhbXBsZS8ud2VsbC1rbm93bi9kaWQuanNvbiIsIm5hbWUiOiJWb2x0ZXggQmF0dGVyeSBHbWJIIn0sIm1hdGVyaWFscyI6W3siY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiTGl0aGl1bSBOaWNrZWwgTWFuZ2FuZXNlIENvYmFsdCBPeGlkZSAoTk1DIDgxMSkiLCJyZWN5Y2xlZFBjdCI6MTMsIndlaWdodEtnIjoyMTB9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiR3JhcGhpdGUvc2lsaWNvbiBhbm9kZSIsInJlY3ljbGVkUGN0Ijo0LCJ3ZWlnaHRLZyI6OTV9LHsiY291bnRyeU9mT3JpZ2luIjoiREUiLCJuYW1lIjoiQWx1bWluaXVtIHBhY2sgaG91c2luZyIsInJlY3ljbGVkUGN0Ijo3MSwid2VpZ2h0S2ciOjg1fV0sIm9wZXJhdG9ySWRlbnRpZmllciI6IkRFMTIzNDU2Nzg5IiwicGxhY2VkT25NYXJrZXREYXRlIjoiMjAyNi0wNy0wMSIsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImJhdHRlcnlDaGVtaXN0cnkiOiJOTUMiLCJiYXR0ZXJ5TW9kZWxJZCI6IlZCWC1FVjc1LU5NQzgxMS1QNFM5NiIsImJhdHRlcnlQYXNzcG9ydE51bWJlciI6IlVSTjpPREw6QkFUVDowOTU5MDAwMDAwMDA0NTpWQlgtRVYtMjAyNi0wNDIxIiwiYmF0dGVyeVR5cGUiOiJldiIsImJhdHRlcnlXZWlnaHRLZyI6NDY4LCJjYXBhY2l0eVRocmVzaG9sZEZvckV4aGF1c3Rpb25QY3QiOjcwLCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjk2LCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQ0MC00OC00IiwiY291bnRyeU9mT3JpZ2luIjoiQ0QiLCJuYW1lIjoiQ29iYWx0Iiwid2VpZ2h0R3JhbXMiOjUyMDB9LHsiY2FzTnVtYmVyIjoiNzQzOS05My0yIiwiY291bnRyeU9mT3JpZ2luIjoiQ0wiLCJuYW1lIjoiTGl0aGl1bSIsIndlaWdodEdyYW1zIjo4MTAwfSx7ImNhc051bWJlciI6Ijc0NDAtMDItMCIsImNvdW50cnlPZk9yaWdpbiI6IklEIiwibmFtZSI6Ik5pY2tlbCIsIndlaWdodEdyYW1zIjozMjAwMH0seyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJjb3VudHJ5T2ZPcmlnaW4iOiJNWiIsIm5hbWUiOiJOYXR1cmFsIGdyYXBoaXRlIiwid2VpZ2h0R3JhbXMiOjc0MDAwfV0sImN5Y2xlTGlmZVRlc3RDUmF0ZSI6MSwiZXVEZWNsYXJhdGlvbk9mQ29uZm9ybWl0eSI6IkVVLURvQyBWQlgtMjAyNi1FVi0wMTQyIGRhdGVkIDIwMjYtMDUtMTgsIHBlciBSZWd1bGF0aW9uIChFVSkgMjAyMy8xNTQyIEFydC4gMTgiLCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjoxODAwLCJleHBlY3RlZExpZmV0aW1lUmVmZXJlbmNlVGVzdCI6IlVORUNFIEdUUiAyMiAvIElFQyA2MjY2MC0xOjIwMTgsIDI1IGRlZ0MiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiI3Nzg2LTgxLTQiLCJjb25jZW50cmF0aW9uUGN0IjoyLjQsIm5hbWUiOiJOaWNrZWwgc3VsZmF0ZSJ9LHsiY2FzTnVtYmVyIjoiMTAxMjQtNDMtMyIsImNvbmNlbnRyYXRpb25QY3QiOjAuOSwibmFtZSI6IkNvYmFsdCBzdWxmYXRlIn0seyJjYXNOdW1iZXIiOiIyMTMyNC00MC0zIiwiY29uY2VudHJhdGlvblBjdCI6MC45LCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIn1dLCJpbml0aWFsUm91bmRUcmlwRWZmaWNpZW5jeVBjdCI6OTQsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjoxLjEsImludGVybmFsUGFja1Jlc2lzdGFuY2VNb2htIjo0MiwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTA1LTExVDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkluZ29sc3RhZHQsIERFIChwbGFudCBJTkctMSkiLCJtYXJraW5nSW5mb3JtYXRpb24iOiJTZXBhcmF0ZS1jb2xsZWN0aW9uIHN5bWJvbCBwZXIgQXJ0LiAxMyg0KTsgY2FwYWNpdHkgNzUga1doOyBIViB3YXJuaW5nIGxhYmVsIiwibWF4aW11bVZvbHRhZ2VWIjo0MDMuMiwibWluaW1hbFZvbHRhZ2VWIjoyNzAsIm5vbWluYWxDYXBhY2l0eUFoIjoyMTEsIm5vbWluYWxWb2x0YWdlViI6MzU1LCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NTUsIm1pbkMiOi0zMH0sIm5vdEluVXNlVGVtcGVyYXR1cmVSZWZlcmVuY2VUZXN0IjoiSUVDIDYyNjYwLTE6MjAxOCBzdG9yYWdlIHByb2ZpbGUiLCJvcmlnaW5hbFBvd2VyQ2FwYWJpbGl0eVciOjE1MDAwMCwicG93ZXJMaW1pdE1heFciOjIyMDAwMCwicG93ZXJMaW1pdE1pblciOjUwMCwicHJvZHVjdEdyb3VwIjoiYmF0dGVyeSIsInByb2R1Y3RJZGVudGlmaWVyIjp7Imd0aW4iOiIwOTU5MDAwMDAwMDA0NSIsInNjaGVtZSI6ImdzMSJ9LCJyYXRlZENhcGFjaXR5S3doIjo3NSwicmF0ZWRFbmVyZ3lXaCI6NzUwMDAsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRSZXBvcnRpbmdZZWFyIjoyMDI2LCJyZW5ld2FibGVDb250ZW50UGN0IjoxNiwicm91bmRUcmlwRWZmaWNpZW5jeUF0SGFsZkN5Y2xlTGlmZVBjdCI6ODgsInVzYWJsZUV4dGluZ3Vpc2hpbmdBZ2VudCI6IkxhcmdlIHZvbHVtZXMgb2Ygd2F0ZXIgZm9yIGNvb2xpbmc7IGRvIG5vdCB1c2UgQ08yIG9uIGNlbGwgZmlyZSIsIndhc3RlQmF0dGVyeUluZm9ybWF0aW9uIjoiRVYgYmF0dGVyeSAtIGhhbmRsZWQgYnkgdGhlIHZlaGljbGUgbWFudWZhY3R1cmVyIHRha2UtYmFjayBzY2hlbWUuIERvIG5vdCBvcGVuIG9yIGRpc2NoYXJnZS4gQ29udGFjdCBodi1zZXJ2aWNlQHZvbHRleC5leGFtcGxlLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDk4MS4ifSwicHJvZHVjdE5hbWUiOiJWb2x0ZXggRVYgVHJhY3Rpb24gQmF0dGVyeSA3NSBrV2giLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMjc5NDkzNzMxWiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDA0NS8yMS83MGMxYjRhM2E2MTZkMTYxZTk5NyIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjo1LjV9LCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuMjc5NDkzNzMxWiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuMjc5NDkzNzMxWiIsInZlcnNpb24iOjF9.IcMAcCj2y2pu9zIc89MDC0kcO5KrS_l29thCxduNJn5PClbtwiRDLkd1FgNOcqGaxPZwrHrFFrQE7o2aj50KAQ\",\"publishedAt\":\"2026-11-03T20:09:14.279493731Z\",\"qrCodeUrl\":\"http://127.0.0.1:1/01/09590000000045/21/70c1b4a3a616d161e997\",\"repairabilityScore\":{\"overall\":5.5},\"retentionUntil\":\"2036-11-03T20:09:14.279493731Z\",\"schemaVersion\":\"2.8.0\",\"status\":\"active\",\"updatedAt\":\"2026-11-03T20:09:14.279493731Z\",\"version\":1}", + "01a1a307-b792-7764-a50c-b10fe65bf932": "{\"applicableInstruments\":[{\"instrument\":\"battery-reg-2023-1542\",\"recorded\":\"catalog\"}],\"batchId\":\"VBX-LMT-2026-0188\",\"co2ePerUnit\":null,\"commodityCode\":\"85076000\",\"complianceResult\":{\"assessedAt\":\"2026-11-03T20:09:14.389555743Z\",\"co2eScore\":null,\"complianceStatus\":\"PASSTHROUGH_NO_VALIDATION\",\"recycledContentPct\":null,\"repairabilityIndex\":null,\"warnings\":[{\"code\":\"battery.recycled_content.not_yet_binding\",\"field\":\"/placedOnMarketDate\",\"message\":\"No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-06-15. Ruleset 'battery-recycled-content-art8-3' applies from 2036-08-18.\"}]},\"createdAt\":\"2026-11-03T20:09:14.386370527Z\",\"facility\":{\"address\":\"Werkstrasse 2, 10115 Berlin\",\"country\":\"DE\",\"name\":\"Demo Plant\",\"scheme\":\"gln\",\"value\":\"4012345000009\"},\"id\":\"01a1a307-b792-7764-a50c-b10fe65bf932\",\"manufacturer\":{\"address\":\"Werkstrasse 5, 85055 Ingolstadt, DE\",\"country\":null,\"didWebUrl\":\"https://voltex.example/.well-known/did.json\",\"name\":\"Voltex Battery GmbH\"},\"materials\":[{\"countryOfOrigin\":\"DE\",\"name\":\"Lithium Iron Phosphate\",\"recycledPct\":7,\"weightKg\":1.4},{\"countryOfOrigin\":\"DE\",\"name\":\"Graphite\",\"recycledPct\":3,\"weightKg\":0.6},{\"countryOfOrigin\":\"DE\",\"name\":\"ABS/PC housing\",\"recycledPct\":30,\"weightKg\":0.5}],\"operatorIdentifier\":\"DE123456789\",\"placedOnMarketDate\":\"2026-06-15\",\"productGroup\":\"battery\",\"productGroupData\":{\"batteryChemistry\":\"LFP\",\"batteryModelId\":\"VBX-LMT36-14-10S\",\"batteryPassportNumber\":\"URN:ODL:BATT:09590000000052:VBX-LMT-2026-0188\",\"batteryType\":\"lmt\",\"batteryWeightKg\":2.6,\"commercialWarrantyPeriodMonths\":24,\"criticalRawMaterials\":[{\"casNumber\":\"7439-93-2\",\"countryOfOrigin\":\"AU\",\"name\":\"Lithium\",\"weightGrams\":165},{\"casNumber\":\"7782-42-5\",\"countryOfOrigin\":\"CN\",\"name\":\"Natural graphite\",\"weightGrams\":600}],\"cycleLifeTestCRate\":1,\"euDeclarationOfConformity\":\"EU-DoC VBX-2026-LMT-0088 dated 2026-05-12, per Regulation (EU) 2023/1542 Art. 18\",\"expectedLifetimeCycles\":1000,\"expectedLifetimeReferenceTest\":\"EN 50604-1:2016, 25 degC, 100% DoD\",\"hazardousSubstances\":[{\"casNumber\":\"21324-40-3\",\"concentrationPct\":0.6,\"name\":\"Lithium hexafluorophosphate\"}],\"initialRoundTripEfficiencyPct\":95,\"internalCellResistanceMohm\":8,\"internalPackResistanceMohm\":95,\"manufacturingDate\":\"2026-05-05T00:00:00Z\",\"manufacturingPlace\":\"Ingolstadt, DE (plant ING-2)\",\"markingInformation\":\"Separate-collection symbol per Art. 13(4); capacity 14 Ah / 504 Wh; 36 V LMT\",\"maximumVoltageV\":42,\"minimalVoltageV\":30,\"nominalCapacityAh\":14,\"nominalVoltageV\":36,\"notInUseTemperatureRange\":{\"maxC\":45,\"minC\":-10},\"notInUseTemperatureReferenceTest\":\"EN 50604-1:2016 storage clause\",\"originalPowerCapabilityW\":500,\"powerLimitMaxW\":750,\"powerLimitMinW\":10,\"productGroup\":\"battery\",\"productIdentifier\":{\"gtin\":\"09590000000052\",\"scheme\":\"gs1\"},\"ratedCapacityKwh\":0.504,\"ratedEnergyWh\":504,\"recycledContentCobaltPct\":null,\"recycledContentLithiumPct\":null,\"recycledContentNickelPct\":null,\"recycledContentReportingYear\":2026,\"renewableContentPct\":12,\"roundTripEfficiencyAtHalfCycleLifePct\":90,\"usableExtinguishingAgent\":\"Water or ABC dry powder; submerge in water if safe to do so\",\"wasteBatteryInformation\":\"Return the LMT battery to any bicycle retailer or municipal collection point. Do not place in household waste. Producer registration DE-BATT-00981.\"},\"productName\":\"Voltex e-Bike Battery 36V 14Ah\",\"publicJwsSignature\":\"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJWQlgtTE1ULTIwMjYtMDE4OCIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjM4OTU1NTc0M1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNi0xNS4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMycgYXBwbGllcyBmcm9tIDIwMzYtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC4zODYzNzA1MjdaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjc5Mi03NzY0LWE1MGMtYjEwZmU2NWJmOTMyIiwibWFudWZhY3R1cmVyIjp7ImFkZHJlc3MiOiJXZXJrc3RyYXNzZSA1LCA4NTA1NSBJbmdvbHN0YWR0LCBERSIsImNvdW50cnkiOm51bGwsImRpZFdlYlVybCI6Imh0dHBzOi8vdm9sdGV4LmV4YW1wbGUvLndlbGwta25vd24vZGlkLmpzb24iLCJuYW1lIjoiVm9sdGV4IEJhdHRlcnkgR21iSCJ9LCJtYXRlcmlhbHMiOlt7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkxpdGhpdW0gSXJvbiBQaG9zcGhhdGUiLCJyZWN5Y2xlZFBjdCI6Nywid2VpZ2h0S2ciOjEuNH0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJHcmFwaGl0ZSIsInJlY3ljbGVkUGN0IjozLCJ3ZWlnaHRLZyI6MC42fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IkFCUy9QQyBob3VzaW5nIiwicmVjeWNsZWRQY3QiOjMwLCJ3ZWlnaHRLZyI6MC41fV0sIm9wZXJhdG9ySWRlbnRpZmllciI6IkRFMTIzNDU2Nzg5IiwicGxhY2VkT25NYXJrZXREYXRlIjoiMjAyNi0wNi0xNSIsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImJhdHRlcnlDaGVtaXN0cnkiOiJMRlAiLCJiYXR0ZXJ5TW9kZWxJZCI6IlZCWC1MTVQzNi0xNC0xMFMiLCJiYXR0ZXJ5UGFzc3BvcnROdW1iZXIiOiJVUk46T0RMOkJBVFQ6MDk1OTAwMDAwMDAwNTI6VkJYLUxNVC0yMDI2LTAxODgiLCJiYXR0ZXJ5VHlwZSI6ImxtdCIsImJhdHRlcnlXZWlnaHRLZyI6Mi42LCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjI0LCJjcml0aWNhbFJhd01hdGVyaWFscyI6W3siY2FzTnVtYmVyIjoiNzQzOS05My0yIiwiY291bnRyeU9mT3JpZ2luIjoiQVUiLCJuYW1lIjoiTGl0aGl1bSIsIndlaWdodEdyYW1zIjoxNjV9LHsiY2FzTnVtYmVyIjoiNzc4Mi00Mi01IiwiY291bnRyeU9mT3JpZ2luIjoiQ04iLCJuYW1lIjoiTmF0dXJhbCBncmFwaGl0ZSIsIndlaWdodEdyYW1zIjo2MDB9XSwiY3ljbGVMaWZlVGVzdENSYXRlIjoxLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIFZCWC0yMDI2LUxNVC0wMDg4IGRhdGVkIDIwMjYtMDUtMTIsIHBlciBSZWd1bGF0aW9uIChFVSkgMjAyMy8xNTQyIEFydC4gMTgiLCJleHBlY3RlZExpZmV0aW1lQ3ljbGVzIjoxMDAwLCJleHBlY3RlZExpZmV0aW1lUmVmZXJlbmNlVGVzdCI6IkVOIDUwNjA0LTE6MjAxNiwgMjUgZGVnQywgMTAwJSBEb0QiLCJoYXphcmRvdXNTdWJzdGFuY2VzIjpbeyJjYXNOdW1iZXIiOiIyMTMyNC00MC0zIiwiY29uY2VudHJhdGlvblBjdCI6MC42LCJuYW1lIjoiTGl0aGl1bSBoZXhhZmx1b3JvcGhvc3BoYXRlIn1dLCJpbml0aWFsUm91bmRUcmlwRWZmaWNpZW5jeVBjdCI6OTUsImludGVybmFsQ2VsbFJlc2lzdGFuY2VNb2htIjo4LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6OTUsIm1hbnVmYWN0dXJpbmdEYXRlIjoiMjAyNi0wNS0wNVQwMDowMDowMFoiLCJtYW51ZmFjdHVyaW5nUGxhY2UiOiJJbmdvbHN0YWR0LCBERSAocGxhbnQgSU5HLTIpIiwibWFya2luZ0luZm9ybWF0aW9uIjoiU2VwYXJhdGUtY29sbGVjdGlvbiBzeW1ib2wgcGVyIEFydC4gMTMoNCk7IGNhcGFjaXR5IDE0IEFoIC8gNTA0IFdoOyAzNiBWIExNVCIsIm1heGltdW1Wb2x0YWdlViI6NDIsIm1pbmltYWxWb2x0YWdlViI6MzAsIm5vbWluYWxDYXBhY2l0eUFoIjoxNCwibm9taW5hbFZvbHRhZ2VWIjozNiwibm90SW5Vc2VUZW1wZXJhdHVyZVJhbmdlIjp7Im1heEMiOjQ1LCJtaW5DIjotMTB9LCJub3RJblVzZVRlbXBlcmF0dXJlUmVmZXJlbmNlVGVzdCI6IkVOIDUwNjA0LTE6MjAxNiBzdG9yYWdlIGNsYXVzZSIsIm9yaWdpbmFsUG93ZXJDYXBhYmlsaXR5VyI6NTAwLCJwb3dlckxpbWl0TWF4VyI6NzUwLCJwb3dlckxpbWl0TWluVyI6MTAsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0SWRlbnRpZmllciI6eyJndGluIjoiMDk1OTAwMDAwMDAwNTIiLCJzY2hlbWUiOiJnczEifSwicmF0ZWRDYXBhY2l0eUt3aCI6MC41MDQsInJhdGVkRW5lcmd5V2giOjUwNCwicmVjeWNsZWRDb250ZW50Q29iYWx0UGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRMaXRoaXVtUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnROaWNrZWxQY3QiOm51bGwsInJlY3ljbGVkQ29udGVudFJlcG9ydGluZ1llYXIiOjIwMjYsInJlbmV3YWJsZUNvbnRlbnRQY3QiOjEyLCJyb3VuZFRyaXBFZmZpY2llbmN5QXRIYWxmQ3ljbGVMaWZlUGN0Ijo5MCwidXNhYmxlRXh0aW5ndWlzaGluZ0FnZW50IjoiV2F0ZXIgb3IgQUJDIGRyeSBwb3dkZXI7IHN1Ym1lcmdlIGluIHdhdGVyIGlmIHNhZmUgdG8gZG8gc28iLCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IlJldHVybiB0aGUgTE1UIGJhdHRlcnkgdG8gYW55IGJpY3ljbGUgcmV0YWlsZXIgb3IgbXVuaWNpcGFsIGNvbGxlY3Rpb24gcG9pbnQuIERvIG5vdCBwbGFjZSBpbiBob3VzZWhvbGQgd2FzdGUuIFByb2R1Y2VyIHJlZ2lzdHJhdGlvbiBERS1CQVRULTAwOTgxLiJ9LCJwcm9kdWN0TmFtZSI6IlZvbHRleCBlLUJpa2UgQmF0dGVyeSAzNlYgMTRBaCIsInB1Ymxpc2hlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC40MjAyNzUyMDJaIiwicXJDb2RlVXJsIjoiaHR0cDovLzEyNy4wLjAuMToxLzAxLzA5NTkwMDAwMDAwMDUyLzIxLzc3NjRhNTBjYjEwZmU2NWJmOTMyIiwicmVwYWlyYWJpbGl0eVNjb3JlIjp7Im92ZXJhbGwiOjZ9LCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuNDIwMjc1MjAyWiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNDIwMjc1MjAyWiIsInZlcnNpb24iOjF9.mttt33dSVCirL2JM213ygXJq_v8_efhV2-zOQqE_-4rKWYeWqomq_icn6aYTPEUjBCzKg_ldjFPCwA_0XGbPDQ\",\"publishedAt\":\"2026-11-03T20:09:14.420275202Z\",\"qrCodeUrl\":\"http://127.0.0.1:1/01/09590000000052/21/7764a50cb10fe65bf932\",\"repairabilityScore\":{\"overall\":6},\"retentionUntil\":\"2036-11-03T20:09:14.420275202Z\",\"schemaVersion\":\"2.8.0\",\"status\":\"active\",\"updatedAt\":\"2026-11-03T20:09:14.420275202Z\",\"version\":1}", + "01a1a307-b84c-7245-8576-ea67194809ea": "{\"applicableInstruments\":[{\"instrument\":\"battery-reg-2023-1542\",\"recorded\":\"catalog\"}],\"batchId\":\"ODL-IND-LFP-2026-Q2-014\",\"carrierSerial\":\"71ac9c864696540c3b21\",\"co2ePerUnit\":null,\"commodityCode\":\"85076000\",\"complianceResult\":{\"assessedAt\":\"2026-11-03T20:09:14.573474747Z\",\"co2eScore\":null,\"complianceStatus\":\"PASSTHROUGH_NO_VALIDATION\",\"recycledContentPct\":null,\"repairabilityIndex\":null,\"warnings\":[{\"code\":\"battery.recycled_content.not_yet_binding\",\"field\":\"/placedOnMarketDate\",\"message\":\"No Art. 8 minimum recycled share binds a battery placed on the EU market on 2026-05-20. Ruleset 'battery-recycled-content-art8-2' applies from 2031-08-18.\"}]},\"createdAt\":\"2026-11-03T20:09:14.572051734Z\",\"facility\":{\"address\":\"Werkstrasse 2, 10115 Berlin\",\"country\":\"DE\",\"name\":\"Demo Plant\",\"scheme\":\"gln\",\"value\":\"4012345000009\"},\"id\":\"01a1a307-b84c-7245-8576-ea67194809ea\",\"manufacturer\":{\"address\":\"Prenzlauer Berg 12, 10405 Berlin, DE\",\"country\":null,\"didWebUrl\":\"https://batteries.example/.well-known/did.json\",\"name\":\"Example Batteries GmbH\"},\"materials\":[{\"countryOfOrigin\":\"DE\",\"name\":\"Lithium Iron Phosphate\",\"recycledPct\":8,\"weightKg\":22},{\"countryOfOrigin\":\"DE\",\"name\":\"Graphite\",\"recycledPct\":6,\"weightKg\":9.5},{\"countryOfOrigin\":\"DE\",\"name\":\"Steel enclosure\",\"recycledPct\":82,\"weightKg\":12}],\"operatorIdentifier\":\"DE123456789\",\"placedOnMarketDate\":\"2026-05-20\",\"productGroup\":\"battery\",\"productGroupData\":{\"batteryChemistry\":\"LFP\",\"batteryModelId\":\"GC-ESS-LFP512-100\",\"batteryPassportNumber\":\"URN:ODL:BATT:09590000000021:ODL-IND-LFP-2026-Q2-014\",\"batteryType\":\"industrial\",\"batteryWeightKg\":46.5,\"commercialWarrantyPeriodMonths\":144,\"criticalRawMaterials\":[{\"casNumber\":\"7439-93-2\",\"countryOfOrigin\":\"AU\",\"name\":\"Lithium\",\"weightGrams\":2600},{\"casNumber\":\"7782-42-5\",\"countryOfOrigin\":\"CN\",\"name\":\"Natural graphite\",\"weightGrams\":9500}],\"cycleLifeTestCRate\":0.33,\"euDeclarationOfConformity\":\"EU-DoC GC-2026-IND-0067 dated 2026-03-10, per Regulation (EU) 2023/1542 Art. 18\",\"expectedLifetimeCycles\":6000,\"expectedLifetimeReferenceTest\":\"IEC 61427-1:2013, 25 degC, 90% DoD\",\"hazardousSubstances\":[{\"casNumber\":\"21324-40-3\",\"concentrationPct\":0.7,\"name\":\"Lithium hexafluorophosphate\"}],\"initialRoundTripEfficiencyPct\":96.5,\"internalCellResistanceMohm\":0.6,\"internalPackResistanceMohm\":9,\"manufacturingDate\":\"2026-03-02T00:00:00Z\",\"manufacturingPlace\":\"Berlin, DE (plant BLN-2)\",\"markingInformation\":\"Separate-collection symbol per Art. 13(4); capacity 100 Ah / 5.12 kWh; chemistry LFP\",\"maximumVoltageV\":58.4,\"minimalVoltageV\":40,\"nominalCapacityAh\":100,\"nominalVoltageV\":51.2,\"notInUseTemperatureRange\":{\"maxC\":50,\"minC\":-10},\"notInUseTemperatureReferenceTest\":\"IEC 62660-1:2018 storage profile\",\"originalPowerCapabilityW\":5000,\"powerLimitMaxW\":7500,\"powerLimitMinW\":150,\"productGroup\":\"battery\",\"productIdentifier\":{\"gtin\":\"09590000000021\",\"scheme\":\"gs1\"},\"ratedCapacityKwh\":5.12,\"ratedEnergyWh\":5120,\"recycledContentCobaltPct\":null,\"recycledContentLithiumPct\":null,\"recycledContentNickelPct\":null,\"recycledContentReportingYear\":2026,\"renewableContentPct\":22,\"roundTripEfficiencyAtHalfCycleLifePct\":92,\"usableExtinguishingAgent\":\"Water or ABC dry powder; large volumes of water for thermal runaway\",\"wasteBatteryInformation\":\"Return to an authorised collection point. Do not incinerate or shred. Producer registration DE-BATT-00123.\"},\"productName\":\"LFP stationary pack, corrected\",\"publicJwsSignature\":\"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6MTI3LjAuMC4xJTNBNTgwOTEjdXJuOmlldGY6cGFyYW1zOm9hdXRoOmp3ay10aHVtYnByaW50OnNoYS0yNTY6bUl4WkJxak5JZEJSU0h2QURxRGNWd3RIaDhOUXBIeWREOUw1TmhPUk9xMCJ9.eyJhcHBsaWNhYmxlSW5zdHJ1bWVudHMiOlt7Imluc3RydW1lbnQiOiJiYXR0ZXJ5LXJlZy0yMDIzLTE1NDIiLCJyZWNvcmRlZCI6ImNhdGFsb2cifV0sImJhdGNoSWQiOiJPREwtSU5ELUxGUC0yMDI2LVEyLTAxNCIsImNhcnJpZXJTZXJpYWwiOiI3MWFjOWM4NjQ2OTY1NDBjM2IyMSIsImNvMmVQZXJVbml0IjpudWxsLCJjb21tb2RpdHlDb2RlIjoiODUwNzYwMDAiLCJjb21wbGlhbmNlUmVzdWx0Ijp7ImFzc2Vzc2VkQXQiOiIyMDI2LTExLTAzVDIwOjA5OjE0LjU3MzQ3NDc0N1oiLCJjbzJlU2NvcmUiOm51bGwsImNvbXBsaWFuY2VTdGF0dXMiOiJQQVNTVEhST1VHSF9OT19WQUxJREFUSU9OIiwicmVjeWNsZWRDb250ZW50UGN0IjpudWxsLCJyZXBhaXJhYmlsaXR5SW5kZXgiOm51bGwsIndhcm5pbmdzIjpbeyJjb2RlIjoiYmF0dGVyeS5yZWN5Y2xlZF9jb250ZW50Lm5vdF95ZXRfYmluZGluZyIsImZpZWxkIjoiL3BsYWNlZE9uTWFya2V0RGF0ZSIsIm1lc3NhZ2UiOiJObyBBcnQuIDggbWluaW11bSByZWN5Y2xlZCBzaGFyZSBiaW5kcyBhIGJhdHRlcnkgcGxhY2VkIG9uIHRoZSBFVSBtYXJrZXQgb24gMjAyNi0wNS0yMC4gUnVsZXNldCAnYmF0dGVyeS1yZWN5Y2xlZC1jb250ZW50LWFydDgtMicgYXBwbGllcyBmcm9tIDIwMzEtMDgtMTguIn1dfSwiY3JlYXRlZEF0IjoiMjAyNi0xMS0wM1QyMDowOToxNC41NzIwNTE3MzRaIiwiZmFjaWxpdHkiOnsiYWRkcmVzcyI6IldlcmtzdHJhc3NlIDIsIDEwMTE1IEJlcmxpbiIsImNvdW50cnkiOiJERSIsIm5hbWUiOiJEZW1vIFBsYW50Iiwic2NoZW1lIjoiZ2xuIiwidmFsdWUiOiI0MDEyMzQ1MDAwMDA5In0sImlkIjoiMDFhMWEzMDctYjg0Yy03MjQ1LTg1NzYtZWE2NzE5NDgwOWVhIiwibWFudWZhY3R1cmVyIjp7ImFkZHJlc3MiOiJQcmVuemxhdWVyIEJlcmcgMTIsIDEwNDA1IEJlcmxpbiwgREUiLCJjb3VudHJ5IjpudWxsLCJkaWRXZWJVcmwiOiJodHRwczovL2JhdHRlcmllcy5leGFtcGxlLy53ZWxsLWtub3duL2RpZC5qc29uIiwibmFtZSI6IkV4YW1wbGUgQmF0dGVyaWVzIEdtYkgifSwibWF0ZXJpYWxzIjpbeyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJMaXRoaXVtIElyb24gUGhvc3BoYXRlIiwicmVjeWNsZWRQY3QiOjgsIndlaWdodEtnIjoyMn0seyJjb3VudHJ5T2ZPcmlnaW4iOiJERSIsIm5hbWUiOiJHcmFwaGl0ZSIsInJlY3ljbGVkUGN0Ijo2LCJ3ZWlnaHRLZyI6OS41fSx7ImNvdW50cnlPZk9yaWdpbiI6IkRFIiwibmFtZSI6IlN0ZWVsIGVuY2xvc3VyZSIsInJlY3ljbGVkUGN0Ijo4Miwid2VpZ2h0S2ciOjEyfV0sIm9wZXJhdG9ySWRlbnRpZmllciI6IkRFMTIzNDU2Nzg5IiwicGxhY2VkT25NYXJrZXREYXRlIjoiMjAyNi0wNS0yMCIsInByb2R1Y3RHcm91cCI6ImJhdHRlcnkiLCJwcm9kdWN0R3JvdXBEYXRhIjp7ImJhdHRlcnlDaGVtaXN0cnkiOiJMRlAiLCJiYXR0ZXJ5TW9kZWxJZCI6IkdDLUVTUy1MRlA1MTItMTAwIiwiYmF0dGVyeVBhc3Nwb3J0TnVtYmVyIjoiVVJOOk9ETDpCQVRUOjA5NTkwMDAwMDAwMDIxOk9ETC1JTkQtTEZQLTIwMjYtUTItMDE0IiwiYmF0dGVyeVR5cGUiOiJpbmR1c3RyaWFsIiwiYmF0dGVyeVdlaWdodEtnIjo0Ni41LCJjb21tZXJjaWFsV2FycmFudHlQZXJpb2RNb250aHMiOjE0NCwiY3JpdGljYWxSYXdNYXRlcmlhbHMiOlt7ImNhc051bWJlciI6Ijc0MzktOTMtMiIsImNvdW50cnlPZk9yaWdpbiI6IkFVIiwibmFtZSI6IkxpdGhpdW0iLCJ3ZWlnaHRHcmFtcyI6MjYwMH0seyJjYXNOdW1iZXIiOiI3NzgyLTQyLTUiLCJjb3VudHJ5T2ZPcmlnaW4iOiJDTiIsIm5hbWUiOiJOYXR1cmFsIGdyYXBoaXRlIiwid2VpZ2h0R3JhbXMiOjk1MDB9XSwiY3ljbGVMaWZlVGVzdENSYXRlIjowLjMzLCJldURlY2xhcmF0aW9uT2ZDb25mb3JtaXR5IjoiRVUtRG9DIEdDLTIwMjYtSU5ELTAwNjcgZGF0ZWQgMjAyNi0wMy0xMCwgcGVyIFJlZ3VsYXRpb24gKEVVKSAyMDIzLzE1NDIgQXJ0LiAxOCIsImV4cGVjdGVkTGlmZXRpbWVDeWNsZXMiOjYwMDAsImV4cGVjdGVkTGlmZXRpbWVSZWZlcmVuY2VUZXN0IjoiSUVDIDYxNDI3LTE6MjAxMywgMjUgZGVnQywgOTAlIERvRCIsImhhemFyZG91c1N1YnN0YW5jZXMiOlt7ImNhc051bWJlciI6IjIxMzI0LTQwLTMiLCJjb25jZW50cmF0aW9uUGN0IjowLjcsIm5hbWUiOiJMaXRoaXVtIGhleGFmbHVvcm9waG9zcGhhdGUifV0sImluaXRpYWxSb3VuZFRyaXBFZmZpY2llbmN5UGN0Ijo5Ni41LCJpbnRlcm5hbENlbGxSZXNpc3RhbmNlTW9obSI6MC42LCJpbnRlcm5hbFBhY2tSZXNpc3RhbmNlTW9obSI6OSwibWFudWZhY3R1cmluZ0RhdGUiOiIyMDI2LTAzLTAyVDAwOjAwOjAwWiIsIm1hbnVmYWN0dXJpbmdQbGFjZSI6IkJlcmxpbiwgREUgKHBsYW50IEJMTi0yKSIsIm1hcmtpbmdJbmZvcm1hdGlvbiI6IlNlcGFyYXRlLWNvbGxlY3Rpb24gc3ltYm9sIHBlciBBcnQuIDEzKDQpOyBjYXBhY2l0eSAxMDAgQWggLyA1LjEyIGtXaDsgY2hlbWlzdHJ5IExGUCIsIm1heGltdW1Wb2x0YWdlViI6NTguNCwibWluaW1hbFZvbHRhZ2VWIjo0MCwibm9taW5hbENhcGFjaXR5QWgiOjEwMCwibm9taW5hbFZvbHRhZ2VWIjo1MS4yLCJub3RJblVzZVRlbXBlcmF0dXJlUmFuZ2UiOnsibWF4QyI6NTAsIm1pbkMiOi0xMH0sIm5vdEluVXNlVGVtcGVyYXR1cmVSZWZlcmVuY2VUZXN0IjoiSUVDIDYyNjYwLTE6MjAxOCBzdG9yYWdlIHByb2ZpbGUiLCJvcmlnaW5hbFBvd2VyQ2FwYWJpbGl0eVciOjUwMDAsInBvd2VyTGltaXRNYXhXIjo3NTAwLCJwb3dlckxpbWl0TWluVyI6MTUwLCJwcm9kdWN0R3JvdXAiOiJiYXR0ZXJ5IiwicHJvZHVjdElkZW50aWZpZXIiOnsiZ3RpbiI6IjA5NTkwMDAwMDAwMDIxIiwic2NoZW1lIjoiZ3MxIn0sInJhdGVkQ2FwYWNpdHlLd2giOjUuMTIsInJhdGVkRW5lcmd5V2giOjUxMjAsInJlY3ljbGVkQ29udGVudENvYmFsdFBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50TGl0aGl1bVBjdCI6bnVsbCwicmVjeWNsZWRDb250ZW50Tmlja2VsUGN0IjpudWxsLCJyZWN5Y2xlZENvbnRlbnRSZXBvcnRpbmdZZWFyIjoyMDI2LCJyZW5ld2FibGVDb250ZW50UGN0IjoyMiwicm91bmRUcmlwRWZmaWNpZW5jeUF0SGFsZkN5Y2xlTGlmZVBjdCI6OTIsInVzYWJsZUV4dGluZ3Vpc2hpbmdBZ2VudCI6IldhdGVyIG9yIEFCQyBkcnkgcG93ZGVyOyBsYXJnZSB2b2x1bWVzIG9mIHdhdGVyIGZvciB0aGVybWFsIHJ1bmF3YXkiLCJ3YXN0ZUJhdHRlcnlJbmZvcm1hdGlvbiI6IlJldHVybiB0byBhbiBhdXRob3Jpc2VkIGNvbGxlY3Rpb24gcG9pbnQuIERvIG5vdCBpbmNpbmVyYXRlIG9yIHNocmVkLiBQcm9kdWNlciByZWdpc3RyYXRpb24gREUtQkFUVC0wMDEyMy4ifSwicHJvZHVjdE5hbWUiOiJMRlAgc3RhdGlvbmFyeSBwYWNrLCBjb3JyZWN0ZWQiLCJwdWJsaXNoZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNTg1MDQ5MjcyWiIsInFyQ29kZVVybCI6Imh0dHA6Ly8xMjcuMC4wLjE6MS8wMS8wOTU5MDAwMDAwMDAyMS8yMS83MWFjOWM4NjQ2OTY1NDBjM2IyMSIsInJlcGFpcmFiaWxpdHlTY29yZSI6eyJvdmVyYWxsIjo4LjF9LCJyZXRlbnRpb25VbnRpbCI6IjIwMzYtMTEtMDNUMjA6MDk6MTQuNTg1MDQ5MjcyWiIsInNjaGVtYVZlcnNpb24iOiIyLjguMCIsInN0YXR1cyI6ImFjdGl2ZSIsInN1cGVyc2VkZXNJZCI6IjAxYTFhMzA3LWI1YmQtNzFhYy05Yzg2LTQ2OTY1NDBjM2IyMSIsInVwZGF0ZWRBdCI6IjIwMjYtMTEtMDNUMjA6MDk6MTQuNTg1MDQ5MjcyWiIsInZlcnNpb24iOjJ9.eUpdL2Rti-tkqYKFpz3AmYCthWqDEKBVFoZq44jXJAhIv14G-G1KVxZrjWcizjVLyMtTj04p-IN7IdttyodNCA\",\"publishedAt\":\"2026-11-03T20:09:14.585049272Z\",\"qrCodeUrl\":\"http://127.0.0.1:1/01/09590000000021/21/71ac9c864696540c3b21\",\"repairabilityScore\":{\"overall\":8.1},\"retentionUntil\":\"2036-11-03T20:09:14.585049272Z\",\"schemaVersion\":\"2.8.0\",\"status\":\"active\",\"supersedesId\":\"01a1a307-b5bd-71ac-9c86-4696540c3b21\",\"updatedAt\":\"2026-11-03T20:09:14.585049272Z\",\"version\":2}" +} From 65ab4750a27bb5798c58f16344ac8fe8dbb6b2e1 Mon Sep 17 00:00:00 2001 From: chillin_since_26 Date: Tue, 3 Nov 2026 21:34:35 +0100 Subject: [PATCH 05/12] chore: pin dpp-core 0.23.0 --- CHANGELOG.md | 10 +++++----- Cargo.toml | 18 +++++++++--------- crates/dpp-node/tests/openapi_contract.rs | 2 +- .../tests/fixtures/abi-echo/Cargo.toml | 2 +- fuzz/Cargo.toml | 4 ++-- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e52173f..25fc0071 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,7 @@ under the pre-1.0 conventions in [VERSIONING.md](docs/governance/VERSIONING.md): **Migration:** make an LEI or a D-U-N-S number the primary operator identifier (`POST /vault/api/v1/operator-identifiers/{id}/primary`). A registration already queued under a VAT or EORI number is rejected by the drain the first time it is - tried (see **Fixed**). + tried (see **Fixed**). (#561) - **`dpp-core` 0.23.0: an EV, LMT or industrial battery publishes only with its placing date, and from 18 August 2027 with its due diligence declaration.** @@ -46,14 +46,14 @@ under the pre-1.0 conventions in [VERSIONING.md](docs/governance/VERSIONING.md): **Migration:** send `placedOnMarketDate` on create, or in the import column of that name. The battery CSV templates have no column for `dueDiligenceUrl` or `dueDiligenceOperatorScope` yet, so a battery placed from 18 August 2027 cannot - be imported into a passport that publishes. + be imported into a passport that publishes. (#561) - **`dpp-core` 0.23.0: an anonymous reader no longer receives a critical raw material's weight or country of origin** from a battery passport written at schema 2.9.0 or later, or an electronics passport written at 1.5.0 or later. Core labels both fields `restricted` from those versions. A passport written against an earlier version keeps its classes and the bytes its signatures - cover; a new one is written against the new version. + cover; a new one is written against the new version. (#561) ### Fixed @@ -77,13 +77,13 @@ under the pre-1.0 conventions in [VERSIONING.md](docs/governance/VERSIONING.md): route to ask has been seen. A refusal is recorded with the registry's reasons. Before, a reply reporting the record suspended or deactivated closed the row as notified. The transfer route's reply is unobserved, so only `registered` is read - as an acceptance. + as an acceptance. (#561) - **The resolver's JSON-LD defines a term for every product group field.** It carries `dpp-core` 0.23.0's context, so a JSON-LD processor no longer drops a product group's data when it expands the document. The context names withheld fields too, as terms; their values are not served, and the tests that scan the - body for a withheld field now read it without its `@context`. + body for a withheld field now read it without its `@context`. (#561) ## [0.15.0] - 2026-11-03 diff --git a/Cargo.toml b/Cargo.toml index 1a98b093..a1c82834 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,20 +52,20 @@ debug = "line-tables-only" # Core crates — consumed from crates.io (dpp-core is published independently; # see the pins below for the version actually in use, and README.md's sync # note for the other places that must agree on a bump). -dpp-domain = "0.22.0" +dpp-domain = "0.23.0" # Engine-wide types (tenant, auth, audit, API keys) — engine-internal crate. dpp-types = { path = "crates/dpp-types" } -dpp-crypto = "0.22.0" -dpp-vc = "0.22.0" +dpp-crypto = "0.23.0" +dpp-vc = "0.23.0" # KeyStore lives in dpp-core/dpp-crypto; the identity service crate is dpp-identity # (dir: crates/dpp-identity, pkg: dpp-identity). -dpp-digital-link = "0.22.0" -dpp-aas = "0.22.0" -dpp-plugin-traits = "0.22.0" -dpp-registry = "0.22.0" -dpp-calc = "0.22.0" -dpp-rules = { version = "0.22.0", features = ["bundle"] } +dpp-digital-link = "0.23.0" +dpp-aas = "0.23.0" +dpp-plugin-traits = "0.23.0" +dpp-registry = "0.23.0" +dpp-calc = "0.23.0" +dpp-rules = { version = "0.23.0", features = ["bundle"] } # Web framework axum = { version = "0.8", features = ["macros", "multipart"] } diff --git a/crates/dpp-node/tests/openapi_contract.rs b/crates/dpp-node/tests/openapi_contract.rs index 2209a685..88a961a0 100644 --- a/crates/dpp-node/tests/openapi_contract.rs +++ b/crates/dpp-node/tests/openapi_contract.rs @@ -861,7 +861,7 @@ fn deactivation_reason_kinds() -> Vec { /// Bump only after re-checking the enums listed below against the released /// crate. Bumping it to make a red build green is the one thing that breaks /// this gate. -const CORE_VERSION_VERIFIED: &str = "0.22.0"; +const CORE_VERSION_VERIFIED: &str = "0.23.0"; /// Enums whose variants this test cannot enumerate, and so cannot gate. /// diff --git a/crates/dpp-plugin-host/tests/fixtures/abi-echo/Cargo.toml b/crates/dpp-plugin-host/tests/fixtures/abi-echo/Cargo.toml index 9cddb771..e2a0c3f3 100644 --- a/crates/dpp-plugin-host/tests/fixtures/abi-echo/Cargo.toml +++ b/crates/dpp-plugin-host/tests/fixtures/abi-echo/Cargo.toml @@ -20,7 +20,7 @@ path = "src/lib.rs" # the guest is built against the same contract the host links. Bump the two # together. A path into a sibling dpp-core checkout built whatever that working # tree held, and resolved only from a checkout sitting next to it. -dpp-plugin-sdk = "0.22.0" +dpp-plugin-sdk = "0.23.0" serde_json = "1" [profile.release] diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 8b05a5b0..0031f53b 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -24,8 +24,8 @@ dpp-seal = { path = "../crates/dpp-seal" } # Core, at the version the workspace pins in its root `Cargo.toml`. A repin moves # these with it: the credential target implements dpp-vault's traits over # dpp-vc's types, and two versions of one crate do not share a type. -dpp-domain = "0.22.0" -dpp-vc = "0.22.0" +dpp-domain = "0.23.0" +dpp-vc = "0.23.0" async-trait = "0.1" base64 = "0.23" chrono = "0.4" From 82f420850b9daa9d5a25558300ddb2ae49c46575 Mon Sep 17 00:00:00 2001 From: chillin_since_26 Date: Tue, 3 Nov 2026 22:01:23 +0100 Subject: [PATCH 06/12] fix(vault): let pre-upgrade passports re-activate --- CHANGELOG.md | 8 +- .../dpp-vault/src/domain/service/publish.rs | 23 +++- .../tests/facility_operator_stamping.rs | 116 ++++++++++++++++++ 3 files changed, 140 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25fc0071..7370ae76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,9 +23,11 @@ under the pre-1.0 conventions in [VERSIONING.md](docs/governance/VERSIONING.md): `unregistrable_identity`, naming the scheme and the fix, and elsewhere it publishes without a registration, as it does when the identity is missing. Queued, such a registration would have been refused on every attempt, because - its payload is fixed at publish. A VAT or EORI number is still accepted and - stamped as the operator identifier. Facilities are added under a GLN and are - not affected. + its payload is fixed at publish. Only a passport that has never queued a + registration is refused: one published before this release keeps its row, and + its identifier cannot change, so a suspended one can still be re-activated. A + VAT or EORI number is still accepted and stamped as the operator identifier. + Facilities are added under a GLN and are not affected. **Migration:** make an LEI or a D-U-N-S number the primary operator identifier (`POST /vault/api/v1/operator-identifiers/{id}/primary`). A registration already diff --git a/crates/dpp-vault/src/domain/service/publish.rs b/crates/dpp-vault/src/domain/service/publish.rs index f8b73e8f..1a0ffe79 100644 --- a/crates/dpp-vault/src/domain/service/publish.rs +++ b/crates/dpp-vault/src/domain/service/publish.rs @@ -473,7 +473,7 @@ impl PassportService { // under its own reason: see `unregistrable_identity`. let registration = match &self.registry_outbox { None => None, - Some(_) => { + Some(outbox) => { // The scheme is only assertable for the value the passport // actually carries. If that value did not come from the current // primary identifier — the primary was changed after the draft @@ -485,9 +485,24 @@ impl PassportService { .filter(|(_, value)| Some(value) == passport.operator_identifier.as_ref()) .map(|(scheme, _)| scheme.as_str()) .unwrap_or_default(); - let built = match unregistrable_identity(identifier_scheme, &passport) { - Some(why) => Err((REASON_UNREGISTRABLE_IDENTITY, why.into())), - None => RegistrationRequest::from_published_passport( + let unregistrable = unregistrable_identity(identifier_scheme, &passport); + // An identity the registry cannot take is refused only for a + // passport that has never queued a registration. One that has + // keeps its row, whatever became of it: the identifier it was + // signed with cannot change, so refusing a re-publish would fix + // nothing and only keep a suspended passport from being + // re-activated. Its registration is queued as before, which the + // outbox leaves alone unless the row was rejected, and a rejected + // one the drain rejects again with its reason. This is how an + // operator who published under a VAT number before it was + // refused re-activates a passport after upgrading. + let refused = match &unregistrable { + Some(_) => outbox.pending_for(id).await?.is_none(), + None => false, + }; + let built = match unregistrable { + Some(why) if refused => Err((REASON_UNREGISTRABLE_IDENTITY, why.into())), + _ => RegistrationRequest::from_published_passport( &passport, RegisteringOperator { legal_name: &self.operator.legal_name, diff --git a/crates/dpp-vault/tests/facility_operator_stamping.rs b/crates/dpp-vault/tests/facility_operator_stamping.rs index 4ab89a6b..a5e7add1 100644 --- a/crates/dpp-vault/tests/facility_operator_stamping.rs +++ b/crates/dpp-vault/tests/facility_operator_stamping.rs @@ -287,3 +287,119 @@ async fn a_vat_primary_identifier_is_refused_at_publish_where_a_registration_is_ "and none is queued" ); } + +/// An identity the registry cannot take is refused only for a passport that has +/// never queued a registration (the test above). One that has keeps its row: +/// the identifier it was signed with cannot change, so refusing would fix +/// nothing and only keep a suspended passport from being re-activated. An +/// operator who published under a VAT number before it was refused has exactly +/// such rows, `pending` until the drain reaches them and `rejected` after. +/// Both re-publish. A pending row is left as it is, and a rejected one is +/// queued again, for the drain to reject again with its reason. +/// +/// The row is written directly, as a node from before the refusal wrote it, and +/// publish meets it by the path a re-activation takes. `operator_paths.rs` runs +/// the real case end to end: a suspended passport in a database v0.14.0 wrote. +#[tokio::test(flavor = "multi_thread")] +async fn an_unregistrable_identity_is_refused_only_before_a_registration_was_queued() { + use dpp_dal::pg::{PgRegistrySyncRepo, sqlx}; + use dpp_domain::passport::PassportId; + use dpp_types::{RegistrySyncOutbox, RegistrySyncStatus}; + + let pg = start_postgres().await; + seed_operator_config(&pg.dal).await; + let vault_url = start_vault(pg.dal.clone()).await; + let token = make_jwt("00000000-0000-0000-0000-000000000001"); + let client = TestClient::new(&vault_url, &token); + let outbox = PgRegistrySyncRepo::new(pg.dal.clone()); + + let resp = client + .post_json( + "/api/v1/facilities", + serde_json::json!({ + "name": "Default Plant", + "identifierScheme": "gln", + "identifierValue": "4012345000009", + "country": "DE", + "isDefault": true + }), + ) + .await; + assert_eq!(resp.status(), 201); + let resp = client + .post_json( + "/api/v1/operator-identifiers", + serde_json::json!({ "scheme": "vat", "value": "DE123456789", "isPrimary": true }), + ) + .await; + assert_eq!(resp.status(), 201); + + for (row_status, gtin) in [ + (RegistrySyncStatus::Pending, "09506000134376"), + (RegistrySyncStatus::Rejected, "09506000134383"), + ] { + let status = row_status.as_db(); + let resp = client + .post_json( + "/api/v1/dpp", + serde_json::json!({ + "productName": "Portable Cell", + "manufacturer": { "name": "Example Batteries GmbH", "address": "Berlin, DE" }, + "productGroupData": { + "productGroup": "battery", + "productIdentifier": { "scheme": "gs1", "gtin": gtin }, + "batteryChemistry": "LFP", + "batteryType": "portable", + "nominalVoltageV": 3.7, + "nominalCapacityAh": 2.5, + "co2ePerUnitKg": 1.8 + } + }), + ) + .await; + assert_eq!(resp.status(), 201); + let battery = resp.json::().await.unwrap()["id"] + .as_str() + .expect("created passport has an id") + .to_owned(); + let battery_id = PassportId(uuid::Uuid::parse_str(&battery).unwrap()); + sqlx::query( + "INSERT INTO odal.registry_sync (passport_id, payload, status) \ + VALUES ($1, '{\"queued\": \"before the upgrade\"}', $2)", + ) + .bind(battery_id.0) + .bind(status) + .execute(pg.dal.pool()) + .await + .expect("write the row an earlier node queued"); + + let resp = client + .post_json( + &format!("/api/v1/dpp/{battery}/publish"), + serde_json::json!({}), + ) + .await; + assert_eq!( + resp.status(), + 200, + "publish over a `{status}` registration: {}", + resp.text().await.unwrap_or_default() + ); + let row = outbox.pending_for(battery_id).await.unwrap().unwrap(); + let before = Some(serde_json::json!({ "queued": "before the upgrade" })); + match row_status { + RegistrySyncStatus::Pending => { + assert_eq!(row.status, RegistrySyncStatus::Pending); + assert_eq!(row.payload, before, "a pending row is left as it was"); + } + _ => { + assert_eq!( + row.status, + RegistrySyncStatus::Pending, + "a rejected row is queued again" + ); + assert_ne!(row.payload, before, "with this publish's registration"); + } + } + } +} From 8877d688aee6e2551603db9934c1af781a7bf17f Mon Sep 17 00:00:00 2001 From: chillin_since_26 Date: Tue, 3 Nov 2026 22:18:12 +0100 Subject: [PATCH 07/12] fix(vault): stamp a draft's identifier at publish --- CHANGELOG.md | 17 ++- .../dpp-vault/src/domain/service/publish.rs | 13 ++- .../tests/facility_operator_stamping.rs | 105 ++++++++++++++++++ 3 files changed, 131 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7370ae76..7a90b939 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,9 +30,11 @@ under the pre-1.0 conventions in [VERSIONING.md](docs/governance/VERSIONING.md): Facilities are added under a GLN and are not affected. **Migration:** make an LEI or a D-U-N-S number the primary operator identifier - (`POST /vault/api/v1/operator-identifiers/{id}/primary`). A registration already - queued under a VAT or EORI number is rejected by the drain the first time it is - tried (see **Fixed**). (#561) + (`POST /vault/api/v1/operator-identifiers/{id}/primary`). Drafts, and the + successor an amendment publishes, then take it; a published passport keeps the + identifier it was signed with. A registration already queued under a VAT or + EORI number is rejected by the drain the first time it is tried (see + **Fixed**). (#561) - **`dpp-core` 0.23.0: an EV, LMT or industrial battery publishes only with its placing date, and from 18 August 2027 with its due diligence declaration.** @@ -59,6 +61,15 @@ under the pre-1.0 conventions in [VERSIONING.md](docs/governance/VERSIONING.md): ### Fixed +- **A draft is published under the operator's primary identifier as it stands + at publish.** It kept the one stamped when it was created, and an amendment's + successor the one its predecessor was signed with. So an operator that changed + its primary, as moving off a VAT number requires, went on publishing under the + old one, and a registration built from a value the primary no longer had named + no scheme, which the registry was bound to refuse. A draft is not signed yet + and create takes no identifier, so nothing an operator chose is overwritten. A + re-publish keeps the identifier the passport was signed with. (#561) + - **A registration the adapter refuses before sending is rejected, not retried for ever.** The registration drain read every error as transient, so a payload the adapter's own checks refused backed off and was tried again with no end. diff --git a/crates/dpp-vault/src/domain/service/publish.rs b/crates/dpp-vault/src/domain/service/publish.rs index 1a0ffe79..b3425928 100644 --- a/crates/dpp-vault/src/domain/service/publish.rs +++ b/crates/dpp-vault/src/domain/service/publish.rs @@ -205,7 +205,18 @@ impl PassportService { .await .unwrap_or(None); } - if passport.operator_identifier.is_none() { + // A draft takes the primary identifier as it stands now, not as it + // stood when the draft was created or, for an amendment, when its + // predecessor was: an operator that changed its primary, as moving + // off a VAT number requires, publishes under the new one. A draft is + // not signed yet, and create takes no identifier, so nothing an + // operator chose is overwritten. A re-publish keeps the identifier + // the passport was signed with. + if passport.status == PassportStatus::Draft { + if let Some((_, value)) = &primary_identifier { + passport.operator_identifier = Some(value.clone()); + } + } else if passport.operator_identifier.is_none() { passport.operator_identifier = primary_identifier.as_ref().map(|(_, value)| value.clone()); } diff --git a/crates/dpp-vault/tests/facility_operator_stamping.rs b/crates/dpp-vault/tests/facility_operator_stamping.rs index a5e7add1..840fb592 100644 --- a/crates/dpp-vault/tests/facility_operator_stamping.rs +++ b/crates/dpp-vault/tests/facility_operator_stamping.rs @@ -403,3 +403,108 @@ async fn an_unregistrable_identity_is_refused_only_before_a_registration_was_que } } } + +/// A draft is published under the operator's primary identifier as it stands at +/// publish, not as it stood when the draft was created or, for an amendment, +/// when its predecessor was. A draft is not signed yet, and nothing an operator +/// sent is overwritten: create takes no identifier. Without this, an operator +/// that moved its primary from a VAT number to an LEI, as the migration for the +/// clause 6 refusal says to, still published under the VAT number, with a +/// registration that names no scheme and that the registry was bound to refuse. +#[tokio::test(flavor = "multi_thread")] +async fn a_draft_is_published_under_the_operators_current_primary_identifier() { + use dpp_dal::pg::PgRegistrySyncRepo; + use dpp_domain::passport::PassportId; + use dpp_types::RegistrySyncOutbox; + + let pg = start_postgres().await; + seed_operator_config(&pg.dal).await; + let vault_url = start_vault(pg.dal.clone()).await; + let token = make_jwt("00000000-0000-0000-0000-000000000001"); + let client = TestClient::new(&vault_url, &token); + let outbox = PgRegistrySyncRepo::new(pg.dal.clone()); + + let resp = client + .post_json( + "/api/v1/facilities", + serde_json::json!({ + "name": "Default Plant", + "identifierScheme": "gln", + "identifierValue": "4012345000009", + "country": "DE", + "isDefault": true + }), + ) + .await; + assert_eq!(resp.status(), 201); + let resp = client + .post_json( + "/api/v1/operator-identifiers", + serde_json::json!({ "scheme": "vat", "value": "DE123456789", "isPrimary": true }), + ) + .await; + assert_eq!(resp.status(), 201); + + let resp = client + .post_json( + "/api/v1/dpp", + serde_json::json!({ + "productName": "Portable Cell", + "manufacturer": { "name": "Example Batteries GmbH", "address": "Berlin, DE" }, + "productGroupData": { + "productGroup": "battery", + "productIdentifier": { "scheme": "gs1", "gtin": "09506000134390" }, + "batteryChemistry": "LFP", + "batteryType": "portable", + "nominalVoltageV": 3.7, + "nominalCapacityAh": 2.5, + "co2ePerUnitKg": 1.8 + } + }), + ) + .await; + assert_eq!(resp.status(), 201); + let draft = resp.json::().await.unwrap(); + assert_eq!( + draft["operatorIdentifier"], "DE123456789", + "create stamps the primary identifier of the moment" + ); + let battery = draft["id"].as_str().expect("an id").to_owned(); + + // The migration: an LEI becomes the primary identifier. + let resp = client + .post_json( + "/api/v1/operator-identifiers", + serde_json::json!({ "scheme": "lei", "value": "529900T8BM49AURSDO55", "isPrimary": true }), + ) + .await; + assert_eq!(resp.status(), 201); + + let resp = client + .post_json( + &format!("/api/v1/dpp/{battery}/publish"), + serde_json::json!({}), + ) + .await; + assert_eq!( + resp.status(), + 200, + "{}", + resp.text().await.unwrap_or_default() + ); + let published = resp.json::().await.unwrap(); + assert_eq!( + published["operatorIdentifier"], "529900T8BM49AURSDO55", + "the draft is published under the primary identifier of the publish" + ); + let row = outbox + .pending_for(PassportId(uuid::Uuid::parse_str(&battery).unwrap())) + .await + .unwrap() + .expect("a registration is queued"); + let payload = row.payload.expect("with its payload").to_string(); + assert!( + payload.contains("529900T8BM49AURSDO55") && !payload.contains("DE123456789"), + "the registration names the LEI and not the VAT number: {payload}" + ); +} From 3b7fa413ee556b09c4fc952b940bb87163729855 Mon Sep 17 00:00:00 2001 From: chillin_since_26 Date: Tue, 3 Nov 2026 22:18:13 +0100 Subject: [PATCH 08/12] test(node): apply the migration before amending --- crates/dpp-node/tests/operator_paths.rs | 62 +++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/crates/dpp-node/tests/operator_paths.rs b/crates/dpp-node/tests/operator_paths.rs index ab89fb09..fe255992 100644 --- a/crates/dpp-node/tests/operator_paths.rs +++ b/crates/dpp-node/tests/operator_paths.rs @@ -516,6 +516,8 @@ async fn upgrade_from(fixture: &Path) { ); } + let primary = apply_documented_migrations(&node, release).await; + let rows: Vec<(String, String)> = sqlx::query_as("SELECT id::text, status FROM odal.passport ORDER BY id") .fetch_all(&admin) @@ -532,7 +534,7 @@ async fn upgrade_from(fixture: &Path) { assert_eq!(code, StatusCode::OK, "{release}: GET {read}: {body}"); } match status.as_str() { - "active" => published_lifecycle(&node, &admin, release, &id).await, + "active" => published_lifecycle(&node, &admin, release, &id, &primary).await, "suspended" => { let (code, body) = node .admin(Method::POST, &format!("{path}/publish"), None) @@ -566,9 +568,59 @@ async fn upgrade_from(fixture: &Path) { } } +/// The changes the CHANGELOG's **Breaking** entries ask an operator to make, +/// made through the API an operator uses, before anything else is asked of the +/// upgraded node. Without them a documented break would read as an upgrade +/// defect. Returns the primary operator identifier they leave in place. +/// +/// - A registration names the operator only under a scheme with an EN 18219 +/// clause 6 route (dpp-core 0.23.0), so an operator whose primary identifier +/// is a VAT or EORI number makes an LEI primary. Its passports keep the +/// identifier they were signed with; a draft, an amendment's successor +/// among them, is published under the LEI. +async fn apply_documented_migrations(node: &Node, release: &str) -> String { + const PATH: &str = "/vault/api/v1/operator-identifiers"; + let (code, identifiers) = node.admin(Method::GET, PATH, None).await; + assert_eq!( + code, + StatusCode::OK, + "{release}: list operator identifiers: {identifiers}" + ); + let primary = identifiers + .as_array() + .into_iter() + .flatten() + .find(|identifier| identifier["isPrimary"] == true) + .unwrap_or_else(|| panic!("{release}: the fixture has no primary identifier")); + if !matches!(primary["scheme"].as_str(), Some("vat" | "eori")) { + return primary["value"].as_str().expect("a value").to_owned(); + } + let lei = "529900T8BM49AURSDO55"; + let (code, body) = node + .admin( + Method::POST, + PATH, + Some(json!({ "scheme": "lei", "value": lei, "isPrimary": true })), + ) + .await; + assert_eq!( + code, + StatusCode::CREATED, + "{release}: make an LEI the primary identifier: {body}" + ); + lei.to_owned() +} + /// Suspend, re-activate and amend a passport the release published: the -/// changes an operator makes to one, which must all still work. -async fn published_lifecycle(node: &Node, admin: &sqlx::PgPool, release: &str, id: &str) { +/// changes an operator makes to one, which must all still work. The successor +/// is published under `primary`, the operator's identifier at the time. +async fn published_lifecycle( + node: &Node, + admin: &sqlx::PgPool, + release: &str, + id: &str, + primary: &str, +) { let path = format!("/vault/api/v1/dpp/{id}"); let (code, body) = node .admin( @@ -605,6 +657,10 @@ async fn published_lifecycle(node: &Node, admin: &sqlx::PgPool, release: &str, i successor["productName"], "Corrected after the upgrade", "{release}: the successor carries the correction it was amended with" ); + assert_eq!( + successor["operatorIdentifier"], primary, + "{release}: the successor is published under the operator's identifier of the day" + ); let status: String = sqlx::query_scalar("SELECT status FROM odal.passport WHERE id = $1::uuid") .bind(id) .fetch_one(admin) From 840c1d7d9ccd20c1e8cb2da6a48d584fca95f13e Mon Sep 17 00:00:00 2001 From: chillin_since_26 Date: Tue, 3 Nov 2026 22:38:44 +0100 Subject: [PATCH 09/12] fix: patch the plugin SDK in the core override --- .cargo/config.toml.example | 4 ++++ CHANGELOG.md | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/.cargo/config.toml.example b/.cargo/config.toml.example index 98c8c4b3..7d949b1f 100644 --- a/.cargo/config.toml.example +++ b/.cargo/config.toml.example @@ -27,3 +27,7 @@ dpp-plugin-traits = { path = "../dpp-core/crates/dpp-plugin-traits" } dpp-registry = { path = "../dpp-core/crates/dpp-registry" } dpp-calc = { path = "../dpp-core/crates/dpp-calc" } dpp-rules = { path = "../dpp-core/crates/dpp-rules" } +# Not in this workspace's graph, so cargo reports it unused here. The plugin SDK +# ABI guest (crates/dpp-plugin-host/tests/fixtures/abi-echo) depends on it, and +# without it a guest pinned to an unpublished release cannot resolve. +dpp-plugin-sdk = { path = "../dpp-core/crates/dpp-plugin-sdk" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a90b939..f91a7076 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,13 @@ under the pre-1.0 conventions in [VERSIONING.md](docs/governance/VERSIONING.md): and create takes no identifier, so nothing an operator chose is overwritten. A re-publish keeps the identifier the passport was signed with. (#561) +- **The core candidate run checks the plugin SDK ABI against the candidate's + SDK.** `.cargo/config.toml.example`, which `just core-local` and the run + apply, did not patch `dpp-plugin-sdk`, which only the ABI guest uses. So the + guest resolved the published SDK, and the run checked that one while 0.15.0's + notes said it checked the candidate's, until a guest pinned to an unpublished + release could not resolve at all. The override now patches it. (#561) + - **A registration the adapter refuses before sending is rejected, not retried for ever.** The registration drain read every error as transient, so a payload the adapter's own checks refused backed off and was tried again with no end. From fc8a8e3b898c6b78c84c2a671906489df82ad15c Mon Sep 17 00:00:00 2001 From: chillin_since_26 Date: Tue, 3 Nov 2026 23:55:42 +0100 Subject: [PATCH 10/12] chore: relock dpp-core 0.23.0 from crates.io --- Cargo.lock | 57 +++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f8a4b4f7..9603d631 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2159,9 +2159,9 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] name = "dpp-aas" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c0957eeb9edaee056135bed203e98207d381630ce752135be38e1c4cd5bf0f5" +checksum = "f30acb24178aff806266e3eabd98e77d68c29544b3c6e7c693c87b9d3562032f" dependencies = [ "dpp-domain", "dpp-vocab", @@ -2171,9 +2171,9 @@ dependencies = [ [[package]] name = "dpp-calc" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a76a50d0c01e3d9bf051c22cf21eb9e75aa62157c1957a78c3681898e001e8" +checksum = "fd577b3ae6d42ac3fac2a9d8bf332d3c8613ef0cb43156a6e5588fde37508ef5" dependencies = [ "chrono", "dpp-rules", @@ -2244,9 +2244,9 @@ dependencies = [ [[package]] name = "dpp-crypto" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ebeac7dee3982e5c2b12b36f96c10315c137857acb3ce5df0f8aad51fdaa3da" +checksum = "f38eff0e9afbbde450ae8e6607fe053db39c31f79661b504323b9c3bdd9d1a44" dependencies = [ "aes-gcm", "anyhow", @@ -2297,9 +2297,9 @@ dependencies = [ [[package]] name = "dpp-digital-link" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2362fbca86fc9895bba086a94cc4fb7e7db7972b673d59733e35c8fe1b636920" +checksum = "46ae50e9267b797d8976dafc8335a69fad3a099cabec837ac1260956a2d717e1" dependencies = [ "dpp-domain", "dpp-rules", @@ -2310,9 +2310,9 @@ dependencies = [ [[package]] name = "dpp-domain" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b770df4f4835914af417fca35a8d5c804c5ee144fd0d25e9c26768040e3afe17" +checksum = "287ce22b7a08c7b9ad153218b4c27f212ccb0dee8ad2628baa3145964fb4b203" dependencies = [ "async-trait", "chrono", @@ -2489,9 +2489,9 @@ dependencies = [ [[package]] name = "dpp-plugin-traits" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f767576abb9e08c1bede58d2a0a874e6ac79abb9714ca9f900e53bef33b54c9" +checksum = "8e5bf7037651760cedb40e9a80adb765eec6198a62ec7f89575bd4dff9d2d81b" dependencies = [ "semver", "serde", @@ -2501,9 +2501,9 @@ dependencies = [ [[package]] name = "dpp-registry" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a024fb2a4c3a6f5718deb9f123315ffadfbba6e6cabe709f521554578e50ef11" +checksum = "56becfd3198e2d3cd810b867c632cc522cd534ad28a801377b9734e0ce2a984a" dependencies = [ "chrono", "dpp-domain", @@ -2567,9 +2567,9 @@ dependencies = [ [[package]] name = "dpp-rules" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3b8ed43e1ccf5406630ef0a21a382405123006a2c3463d10971c1244b42348" +checksum = "1c256813b31e63cc14fa8c720ce21eeb4eeb2cf274a5eaebfeffd92ebc8b8ef3" dependencies = [ "base64 0.23.1", "chrono", @@ -2681,9 +2681,9 @@ dependencies = [ [[package]] name = "dpp-vc" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa7a2b467cbf6f3a657465a105dd5f84f20d8a294424c0ab879ed05741991669" +checksum = "27d0ee8d5d7ac269ca2c8c57c9e7c96d89eaf0ae86188bfbdadfaf0fd2a7e6ed" dependencies = [ "anyhow", "async-trait", @@ -2704,9 +2704,9 @@ dependencies = [ [[package]] name = "dpp-vocab" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6e9629ed973689c67a3153e65db810e34c3845655b6a3a41da10506d728cb50" +checksum = "9e655c42afe4f56857936fcf4452e7e41198126f64c0738239cdf108efd6def3" dependencies = [ "serde", "serde_json", @@ -4061,9 +4061,9 @@ dependencies = [ [[package]] name = "jsonschema" -version = "0.58.6" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f622c574a29fb3034a0a90645afee194dfedc75fc124228801b6e77041075bd1" +checksum = "a42a11b47492e6a2cc76e9c7bcc8077776cb31dac444e0795d7f61bcc6441c08" dependencies = [ "ahash", "bytecount", @@ -4090,23 +4090,24 @@ dependencies = [ [[package]] name = "jsonschema-regex" -version = "0.58.6" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a968fffd387b2ab005bf00c1851f5d26e90df05b6f7ad609ed0d620033cca5e7" +checksum = "107aeb6ff14b8deb5610096a8ce5c554ec89806b521147eaeb3cae05b07edb7a" dependencies = [ "regex-syntax", ] [[package]] name = "jsonschema-value" -version = "0.58.6" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39b7670345e5ce87371339d13705dbb9276b7e3e934bbe1efa2c6734a1f65a96" +checksum = "e281d6b177bb4834f2ffd6f92aa0443cffe29c996b251c8d94eb6f456186feba" dependencies = [ "ahash", "bytecount", "fraction", "getrandom 0.3.4", + "memchr", "num-cmp", "num-traits", "serde_json", @@ -5570,9 +5571,9 @@ dependencies = [ [[package]] name = "referencing" -version = "0.58.6" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558f3855529c0a7e78a48e409fa653456e55f1b167eab4109c16a255b7727e3f" +checksum = "f42818036d3b37cda6bd4c494543158a36e431cb717015ef7c93acc6ba214d3b" dependencies = [ "ahash", "fluent-uri", From c8ae75ab26e2927273526ff065db064a8471256a Mon Sep 17 00:00:00 2001 From: chillin_since_26 Date: Wed, 4 Nov 2026 00:01:49 +0100 Subject: [PATCH 11/12] test(node): give each upgrade fixture its own test --- crates/dpp-node/tests/operator_paths.rs | 34 ++++++++--------- .../tests/upgrade_fixture_coverage.rs | 37 +++++++++++++++++++ docs/governance/RELEASE.md | 5 ++- 3 files changed, 57 insertions(+), 19 deletions(-) diff --git a/crates/dpp-node/tests/operator_paths.rs b/crates/dpp-node/tests/operator_paths.rs index ab89fb09..3a6a2f5c 100644 --- a/crates/dpp-node/tests/operator_paths.rs +++ b/crates/dpp-node/tests/operator_paths.rs @@ -26,6 +26,12 @@ //! `upgrade_fixture_coverage.rs` fails a release pull request whose predecessor //! has none. //! +//! Each fixture has a test of its own, `v___upgrades_to_this_build`, so +//! each upgrade is held to the slow-test budget alone and a failure names the +//! release. One test over every fixture grew by a node boot per release and went +//! over the budget at the second. `upgrade_fixture_coverage.rs` fails when a +//! fixture has no test here. +//! //! # 🚨 `odal` is another package's binary //! //! Cargo only exposes a package's own binaries to its tests, so the onboarding @@ -406,15 +412,9 @@ async fn a_fresh_node_is_onboarded_with_odal() { // ── Upgrading a node that holds passports ─────────────────────────────────── -/// Every captured release, oldest first. -fn fixtures() -> Vec { - let mut dirs: Vec = fs::read_dir(manifest_dir().join("tests/fixtures/upgrade")) - .expect("read tests/fixtures/upgrade") - .map(|entry| entry.expect("dir entry").path()) - .filter(|path| path.is_dir()) - .collect(); - dirs.sort(); - dirs +/// The fixture a release left, `tests/fixtures/upgrade//`. +fn fixture(release: &str) -> PathBuf { + manifest_dir().join("tests/fixtures/upgrade").join(release) } /// The newest migration this build carries, by its number. @@ -430,15 +430,13 @@ fn latest_migration() -> i64 { } #[tokio::test(flavor = "multi_thread")] -async fn every_captured_release_upgrades_to_this_build() { - let fixtures = fixtures(); - assert!( - !fixtures.is_empty(), - "no upgrade fixtures under tests/fixtures/upgrade" - ); - for fixture in fixtures { - upgrade_from(&fixture).await; - } +async fn v0_14_0_upgrades_to_this_build() { + upgrade_from(&fixture("v0.14.0")).await; +} + +#[tokio::test(flavor = "multi_thread")] +async fn v0_15_0_upgrades_to_this_build() { + upgrade_from(&fixture("v0.15.0")).await; } async fn upgrade_from(fixture: &Path) { diff --git a/crates/dpp-node/tests/upgrade_fixture_coverage.rs b/crates/dpp-node/tests/upgrade_fixture_coverage.rs index ae746cb1..c5a96582 100644 --- a/crates/dpp-node/tests/upgrade_fixture_coverage.rs +++ b/crates/dpp-node/tests/upgrade_fixture_coverage.rs @@ -127,3 +127,40 @@ fn the_version_parser_reads_releases_and_refuses_the_rest() { assert_eq!(parse("0.15.0.1"), None); assert_eq!(parse("0.15.0-rc.1"), None); } + +/// Each captured release is upgraded by a test of its own in `operator_paths.rs`, +/// `v___upgrades_to_this_build`, so each is held to the slow-test budget +/// alone and a failure names the release. A fixture with no test would be +/// captured and never upgraded, and a test with no fixture would fail only in the +/// Docker tier; both are caught here. +#[test] +fn every_fixture_has_its_own_upgrade_test() { + let tests = include_str!("operator_paths.rs"); + let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/upgrade"); + for entry in fs::read_dir(&root).expect("read tests/fixtures/upgrade") { + let name = entry.expect("dir entry").file_name(); + let name = name.to_str().unwrap_or_default(); + let test = format!( + "async fn {}_upgrades_to_this_build()", + name.replace('.', "_") + ); + assert!( + tests.contains(&test), + "{name} has no upgrade test: add `{test}` to operator_paths.rs, as the others are" + ); + } + for line in tests.lines() { + let Some(test) = line + .trim() + .strip_prefix("async fn ") + .and_then(|rest| rest.strip_suffix("_upgrades_to_this_build() {")) + else { + continue; + }; + let release = test.replace('_', "."); + assert!( + root.join(&release).is_dir(), + "operator_paths.rs upgrades from {release}, which has no fixture" + ); + } +} diff --git a/docs/governance/RELEASE.md b/docs/governance/RELEASE.md index 620e118f..35d87a12 100644 --- a/docs/governance/RELEASE.md +++ b/docs/governance/RELEASE.md @@ -132,7 +132,10 @@ the Dockerfiles' build command as a supply-chain change. 4. `odal bootstrap` works against the released images. 5. **The upgrade fixture is captured**, so the next release is tested upgrading from this one: `just capture-upgrade-fixture `, then commit - `crates/dpp-node/tests/fixtures/upgrade/v/` in a pull request. It builds the + `crates/dpp-node/tests/fixtures/upgrade/v/` in a pull request, with its own + test in `crates/dpp-node/tests/operator_paths.rs` + (`v___upgrades_to_this_build`, like the others), which + `upgrade_fixture_coverage.rs` requires. It builds the tag's own binaries, because a fixture written by later code would agree with that code by construction. Skipping it fails the next release's pull request. 6. The release is announced. From bf39228617975b9dac480efb6bae4fd0b6511427 Mon Sep 17 00:00:00 2001 From: chillin_since_26 Date: Wed, 4 Nov 2026 00:45:36 +0100 Subject: [PATCH 12/12] fix(node): apply the review of the repin --- CHANGELOG.md | 17 ++-- README.md | 2 +- cli/README.md | 2 +- crates/dpp-node/src/infra/registry_drain.rs | 18 +++- crates/dpp-node/src/infra/transfer_drain.rs | 65 ++++++++++++++- crates/dpp-node/tests/registry_outbox.rs | 91 +++++++++++++++++++++ crates/dpp-node/tests/smoke.rs | 2 +- docs/guides/DEMO-RUNBOOK.md | 2 +- docs/guides/DEVELOPER-GUIDE.md | 2 +- docs/guides/OPERATOR-SETUP.md | 3 +- scripts/capture-upgrade-fixture.sh | 5 +- 11 files changed, 189 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f91a7076..fe7b41b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,13 +77,16 @@ under the pre-1.0 conventions in [VERSIONING.md](docs/governance/VERSIONING.md): notes said it checked the candidate's, until a guest pinned to an unpublished release could not resolve at all. The override now patches it. (#561) -- **A registration the adapter refuses before sending is rejected, not retried - for ever.** The registration drain read every error as transient, so a payload - the adapter's own checks refused backed off and was tried again with no end. - It can never pass: the payload is fixed when the passport is published. Such a - row is now marked rejected, with the reason, as a corrupt payload already was. - Only the adapter's checks report a `Validation` error; a registry that cannot be - reached, or that answers with a failure, is still retried. (#562) +- **A registration or transfer notification the adapter refuses before sending + is rejected, not retried for ever.** Both registry drains read every error as + transient, so a payload the adapter's own checks refused backed off and was + tried again with no end. It can never pass: a registration's payload is fixed + when the passport is published, and a notification's when the transfer is + accepted. Such a row is now marked rejected, with the reason, as a corrupt + payload already was; if that write fails, the row backs off rather than being + checked again on every pass. Only the adapter's checks report a `Validation` + error; a registry that cannot be reached, or that answers with a failure, is + still retried. (#562, #561) - **A transfer notification is sent under a stable key, and only an answer that says the registry took it closes it.** `dpp-core` 0.23.0's registry port diff --git a/README.md b/README.md index 42c211fb..082ac5cc 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ set -a; . ./.env; set +a odal bootstrap --legal-name "Example Batteries GmbH" --country DE \ --address "Musterstrasse 1, 10115 Berlin" --contact-email ops@example.com odal facility add --name "Berlin plant" --value 4012345000009 --country DE --default -odal operator-id add --scheme vat --value DE123456789 --primary +odal operator-id add --scheme lei --value 984500ODALEXAMPLE069 --primary ``` `bootstrap` prints an API key once and saves it to `~/.config/odal/credentials.toml`. diff --git a/cli/README.md b/cli/README.md index 7cb83c5b..17dfcf5c 100644 --- a/cli/README.md +++ b/cli/README.md @@ -96,7 +96,7 @@ set -a; . ./.env; set +a # odal reads ADMIN_USERNAME / ADMIN_PASSWOR odal bootstrap --legal-name "Example GmbH" --country DE \ --address "Musterstrasse 1, 10115 Berlin" --contact-email ops@example.com odal facility add --name "Main plant" --value 4012345000009 --country DE --default -odal operator-id add --scheme vat --value DE123456789 --primary +odal operator-id add --scheme lei --value 984500ODALEXAMPLE069 --primary odal passport import ops/demo/samples/battery-full.json # creates a battery draft odal passport validate # checks the stored drafts diff --git a/crates/dpp-node/src/infra/registry_drain.rs b/crates/dpp-node/src/infra/registry_drain.rs index 4e85f464..284042e6 100644 --- a/crates/dpp-node/src/infra/registry_drain.rs +++ b/crates/dpp-node/src/infra/registry_drain.rs @@ -188,10 +188,20 @@ async fn apply_acknowledgement( // payload is. Every failure to reach or hear from the registry arrives as // another variant and is retried below. Err(dpp_domain::DppError::Validation(e)) => { - let _ = outbox - .mark_rejected(pid, format!("refused before sending: {e}")) - .await; - stats.skipped += 1; + let reason = format!("refused before sending: {e}"); + if let Err(write) = outbox.mark_rejected(pid, reason.clone()).await { + // Not closed, so it backs off like any failure rather than being + // checked again on every pass. + tracing::warn!( + passport_id = %pid, + error = %write, + "a refused registration could not be recorded as rejected" + ); + let _ = outbox.mark_attempt_failed(pid, reason).await; + stats.retried += 1; + } else { + stats.skipped += 1; + } } Err(e) => { // Transient/unreachable — back off and retry. The row stays. diff --git a/crates/dpp-node/src/infra/transfer_drain.rs b/crates/dpp-node/src/infra/transfer_drain.rs index 19039536..f9856974 100644 --- a/crates/dpp-node/src/infra/transfer_drain.rs +++ b/crates/dpp-node/src/infra/transfer_drain.rs @@ -24,7 +24,8 @@ pub struct TransferDrainStats { pub rejected: u32, /// Rows that failed transiently and were backed off for retry. pub retried: u32, - /// Rows dropped from draining because their payload was corrupt. + /// Rows dropped from draining because their payload was corrupt, or refused + /// by the adapter's own checks before it was sent. pub skipped: u32, /// Rows held back because the passport's own registration has not been /// accepted by the registry yet, so there is no record to amend. @@ -113,7 +114,16 @@ pub async fn drain_once( // applied, and no route that would say so has been seen, so the row // closes on the registry's word that it holds the notification. Ok(TransferReport::Accepted | TransferReport::AlreadyHeld) => { - let _ = outbox.mark_notified(tid, registry_id.clone()).await; + // A failed write leaves the row pending, and the next pass sends + // it again under the same key, which the registry answers as + // already held. That recovers; this says why it had to. + if let Err(e) = outbox.mark_notified(tid, registry_id.clone()).await { + tracing::warn!( + transfer_id = %tid, + error = %e, + "the registry holds the transfer notification but the outbox write failed" + ); + } stats.notified += 1; } Ok(TransferReport::Refused { reason }) => { @@ -165,6 +175,28 @@ pub async fn drain_once( .await; stats.retried += 1; } + // The adapter's own checks refused the notification before sending + // it. Its payload was fixed when the transfer was accepted, so sending + // it again cannot change that answer: it is rejected with the reason, + // as `registry_drain` does with a registration. Every failure to + // reach or hear from the registry arrives as another variant and is + // retried below. + Err(dpp_domain::DppError::Validation(e)) => { + let reason = format!("refused before sending: {e}"); + if let Err(write) = outbox.mark_rejected(tid, reason.clone()).await { + // Not closed, so it backs off like any failure rather than + // being checked again on every pass. + tracing::warn!( + transfer_id = %tid, + error = %write, + "a refused transfer notification could not be recorded as rejected" + ); + let _ = outbox.mark_attempt_failed(tid, reason).await; + stats.retried += 1; + } else { + stats.skipped += 1; + } + } Err(e) => { // Transient/unreachable — back off and retry. The row stays. let _ = outbox.mark_attempt_failed(tid, e.to_string()).await; @@ -395,6 +427,8 @@ mod tests { /// A state the port has no name for. Unrecognised, Transient, + /// The adapter's own checks refused the notification before sending it. + RefusedBeforeSending, } struct FakePort { @@ -438,6 +472,11 @@ mod tests { state: "UNDER_REVIEW".into(), }), Outcome::Transient => Err(DppError::Internal("registry unreachable".into())), + Outcome::RefusedBeforeSending => Err(DppError::Validation( + "the incoming operator has no clause 6 route" + .to_owned() + .into(), + )), } } } @@ -593,6 +632,28 @@ mod tests { assert!(outbox.notified.lock().unwrap().is_empty()); } + /// A notification the adapter's own checks refuse before sending is rejected, + /// not retried. Its payload was fixed when the transfer was accepted, so + /// sending it again cannot change the answer, and retrying would back off and + /// try again with no end. The registration drain treats the same error the + /// same way. + #[tokio::test] + async fn a_notification_the_adapter_refuses_is_rejected_not_retried() { + let (outbox, port) = fakes(Outcome::RefusedBeforeSending, vec![row_from(&record())]); + + let stats = run(&outbox, &port).await; + + assert_eq!((stats.skipped, stats.retried), (1, 0)); + let rejected = outbox.rejected.lock().unwrap(); + assert_eq!(rejected.len(), 1, "the row is closed as rejected"); + assert!( + rejected[0].starts_with("refused before sending:"), + "with the adapter's reason: {}", + rejected[0] + ); + assert!(outbox.failed.lock().unwrap().is_empty()); + } + /// 🚨 A state this node has no name for is not an acceptance. Recording it as /// one would close the row for good on a notification the registry may not /// have taken. diff --git a/crates/dpp-node/tests/registry_outbox.rs b/crates/dpp-node/tests/registry_outbox.rs index d4efe75d..60dc3cc8 100644 --- a/crates/dpp-node/tests/registry_outbox.rs +++ b/crates/dpp-node/tests/registry_outbox.rs @@ -448,6 +448,97 @@ async fn a_payload_the_adapter_refuses_is_rejected_not_retried() { ); } +/// A refusal whose rejection cannot be written is recorded as a failed attempt +/// instead, so the row backs off like any failure. Left as it was, it stayed +/// due and was checked again, and failed again, on every pass. +#[tokio::test(flavor = "multi_thread")] +async fn a_refusal_whose_rejection_cannot_be_written_backs_off() { + /// The real outbox, except that recording a rejection fails. + struct RejectionFails(PgRegistrySyncRepo); + + #[async_trait] + impl RegistrySyncOutbox for RejectionFails { + async fn commit_publish( + &self, + passport: &Passport, + payload: serde_json::Value, + ) -> Result<(), DppError> { + self.0.commit_publish(passport, payload).await + } + async fn enqueue_status( + &self, + passport_id: PassportId, + intent: RegistryStatusIntent, + ) -> Result<(), DppError> { + self.0.enqueue_status(passport_id, intent).await + } + async fn due(&self, limit: i64) -> Result, DppError> { + self.0.due(limit).await + } + async fn mark_submitted( + &self, + id: PassportId, + registry_id: String, + ) -> Result<(), DppError> { + self.0.mark_submitted(id, registry_id).await + } + async fn mark_deactivated(&self, id: PassportId, message: String) -> Result<(), DppError> { + self.0.mark_deactivated(id, message).await + } + async fn mark_registered( + &self, + id: PassportId, + registry_id: String, + ) -> Result<(), DppError> { + self.0.mark_registered(id, registry_id).await + } + async fn mark_rejected(&self, _id: PassportId, _message: String) -> Result<(), DppError> { + Err(DppError::Internal("the database stopped answering".into())) + } + async fn mark_attempt_failed( + &self, + id: PassportId, + message: String, + ) -> Result<(), DppError> { + self.0.mark_attempt_failed(id, message).await + } + async fn pending_for( + &self, + id: PassportId, + ) -> Result, DppError> { + self.0.pending_for(id).await + } + async fn status_counts( + &self, + stall_threshold: i32, + ) -> Result { + self.0.status_counts(stall_threshold).await + } + async fn unregistered_published_count(&self) -> Result { + self.0.unregistered_published_count().await + } + } + + let _c = start_pg().await; + let dal = _c.dal.clone(); + let plain: Arc = Arc::new(PgRegistrySyncRepo::new(dal.clone())); + let id = create_and_publish(&dal, &plain).await; + let outbox: Arc = + Arc::new(RejectionFails(PgRegistrySyncRepo::new(dal.clone()))); + + let (port, _) = mock(Outcome::Invalid); + let stats = drain_once(&outbox, &port, None, 50).await; + assert_eq!((stats.skipped, stats.retried), (0, 1), "{stats:?}"); + + let row = plain.pending_for(id).await.unwrap().unwrap(); + assert_eq!(row.status, RegistrySyncStatus::Pending); + assert_eq!(row.attempts, 1, "a failed attempt, so it backs off"); + assert!( + row.next_attempt_at > Utc::now(), + "and is not due again on the next pass" + ); +} + // ─── 0024 heals rows the old write path already clobbered ───────────────────── /// Bring a fresh database up to the pre-0024 schema by applying every migration diff --git a/crates/dpp-node/tests/smoke.rs b/crates/dpp-node/tests/smoke.rs index b0aa7ae1..6f2d501e 100644 --- a/crates/dpp-node/tests/smoke.rs +++ b/crates/dpp-node/tests/smoke.rs @@ -1243,7 +1243,7 @@ async fn the_quick_start_battery_sample_creates_and_publishes() { let operator_id = client .post(format!("{base}/vault/api/v1/operator-identifiers")) .bearer_auth(&token) - .json(&serde_json::json!({ "scheme": "lei", "value": "529900T8BM49AURSDO55", "isPrimary": true })) + .json(&serde_json::json!({ "scheme": "lei", "value": "984500ODALEXAMPLE069", "isPrimary": true })) .send() .await .expect("operator-identifier request failed"); diff --git a/docs/guides/DEMO-RUNBOOK.md b/docs/guides/DEMO-RUNBOOK.md index 2680067c..2ac33123 100644 --- a/docs/guides/DEMO-RUNBOOK.md +++ b/docs/guides/DEMO-RUNBOOK.md @@ -91,7 +91,7 @@ odal bootstrap --legal-name "Demo GmbH" --country DE \ --address "Musterstrasse 1, 10115 Berlin" --contact-email demo@example.com odal facility add --name "Demo plant" --scheme gln --value 4012345000009 \ --country DE --default -odal operator-id add --scheme vat --value DE123456789 --primary +odal operator-id add --scheme lei --value 984500ODALEXAMPLE069 --primary ``` `bootstrap` prints the first API key once; keep it. The demo passports are batteries, diff --git a/docs/guides/DEVELOPER-GUIDE.md b/docs/guides/DEVELOPER-GUIDE.md index 48160444..9b8b3844 100644 --- a/docs/guides/DEVELOPER-GUIDE.md +++ b/docs/guides/DEVELOPER-GUIDE.md @@ -30,7 +30,7 @@ set -a; . ./.env; set +a just cli -- bootstrap --legal-name "Example GmbH" --country DE \ --address "Musterstrasse 1, 10115 Berlin" --contact-email ops@example.com just cli -- facility add --name "Main plant" --value 4012345000009 --country DE --default -just cli -- operator-id add --scheme vat --value DE123456789 --primary +just cli -- operator-id add --scheme lei --value 984500ODALEXAMPLE069 --primary just cli -- passport import ops/demo/samples/battery-full.json just cli -- passport validate just cli -- passport publish diff --git a/docs/guides/OPERATOR-SETUP.md b/docs/guides/OPERATOR-SETUP.md index 8451b2e7..06822918 100644 --- a/docs/guides/OPERATOR-SETUP.md +++ b/docs/guides/OPERATOR-SETUP.md @@ -70,7 +70,7 @@ odal --profile prod operator set --legal-name "Example GmbH" --country DE \ --address "Musterstrasse 1, 10115 Berlin" --contact-email ops@example.com odal --profile prod facility add --name "Berlin plant" --scheme gln \ --value 4012345000009 --country DE --default -odal --profile prod operator-id add --scheme vat --value DE123456789 --primary +odal --profile prod operator-id add --scheme lei --value 984500ODALEXAMPLE069 --primary ``` - `bootstrap` mints the first API key, prints it once and saves it to @@ -264,6 +264,7 @@ Serve the resolver (port 8003) on the `RESOLVER_BASE_URL` host. | Node stays `starting`; logs repeat *"password authentication failed for user odal_app"* | The role-provisioning hook was not mounted on first start | Make sure `ops/bootstrap/` is beside `docker/`, then recreate the database volume (the hook runs only on first initialisation) | | *"No profile configured yet"* | Nothing configured on this machine | `odal init` or `odal profile create --node-url ` | | Publish fails `422` *"missing required registry identity"* | No default facility or primary operator identifier | `odal facility add … --default`, `odal operator-id add … --primary` | +| Publish fails `422` *"the operator identifier cannot be registered"* | The primary operator identifier is a VAT or EORI number, which the EU registry does not take | Make an LEI or a D-U-N-S number primary: `odal operator-id add --scheme lei … --primary` | | Import rejects every row on `gtin` | 13-digit GTINs, or wrong check digits | Use GTIN-14; the error names the expected digit | | Retire fails *"retention policy forbids retiring before …"* | The retention window is still running | Suspend instead | | `odal verify `: *"Dossier not found"* | A passport id was passed | Pass a dossier id from `odal passport evidence ` | diff --git a/scripts/capture-upgrade-fixture.sh b/scripts/capture-upgrade-fixture.sh index 304889d8..e9a2198a 100644 --- a/scripts/capture-upgrade-fixture.sh +++ b/scripts/capture-upgrade-fixture.sh @@ -119,7 +119,10 @@ odal bootstrap --legal-name "Upgrade Fixture GmbH" --country DE \ --admin-user "$admin_user" > /dev/null odal facility add --name "Demo Plant" --scheme gln --value 4012345000009 \ --country DE --address "Werkstrasse 2, 10115 Berlin" --default > /dev/null -odal operator-id add --scheme vat --value DE123456789 --primary > /dev/null +# A registrable primary: from 0.16.0 a battery does not publish under a VAT +# number. The v0.14.0 and v0.15.0 fixtures were seeded with one, as those +# releases allowed, and the upgrade test makes the documented move to an LEI. +odal operator-id add --scheme lei --value 984500ODALEXAMPLE069 --primary > /dev/null key="$(sed -n 's/^default = "\(.*\)"$/\1/p' "$work/home/.config/odal/credentials.toml")" auth="authorization: Bearer $key" json='content-type: application/json'