@open-charging-cloud/wwcp-poi 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +1155 -836
  2. package/README.md +88 -32
  3. package/dist/common.d.ts +11 -0
  4. package/dist/common.d.ts.map +1 -1
  5. package/dist/evTaler/contractTerms.d.ts +19 -0
  6. package/dist/evTaler/contractTerms.d.ts.map +1 -1
  7. package/dist/history/apply.d.ts.map +1 -1
  8. package/dist/history/changeSet.d.ts +12 -2
  9. package/dist/history/changeSet.d.ts.map +1 -1
  10. package/dist/history/commit.d.ts +8 -2
  11. package/dist/history/commit.d.ts.map +1 -1
  12. package/dist/history/preconditions.d.ts +3 -2
  13. package/dist/history/preconditions.d.ts.map +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +835 -324
  17. package/dist/index.js.map +1 -1
  18. package/dist/network/evTaler.d.ts +6 -0
  19. package/dist/network/evTaler.d.ts.map +1 -1
  20. package/dist/network/poiJSON.d.ts +4 -1
  21. package/dist/network/poiJSON.d.ts.map +1 -1
  22. package/dist/network/poiValues.d.ts +9 -2
  23. package/dist/network/poiValues.d.ts.map +1 -1
  24. package/dist/network/reading.d.ts +13 -1
  25. package/dist/network/reading.d.ts.map +1 -1
  26. package/dist/network/references.d.ts +10 -3
  27. package/dist/network/references.d.ts.map +1 -1
  28. package/dist/network/roamingNetwork.d.ts +7 -1
  29. package/dist/network/roamingNetwork.d.ts.map +1 -1
  30. package/dist/network/writtenForm.d.ts +27 -1
  31. package/dist/network/writtenForm.d.ts.map +1 -1
  32. package/dist/proofs/keyStatement.d.ts.map +1 -1
  33. package/dist/verifier/grossRates.d.ts +11 -0
  34. package/dist/verifier/grossRates.d.ts.map +1 -0
  35. package/dist/verifier/sessionPrices.d.ts +3 -0
  36. package/dist/verifier/sessionPrices.d.ts.map +1 -1
  37. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -1,836 +1,1155 @@
1
- # Changelog
2
-
3
- ## Unreleased
4
-
5
-
6
- ## 0.3.0 (prepared; not yet published)
7
-
8
- WWCP_POI3 as the measure: a roaming network is read as WWCP_POI3 reads it and refused in its words, change sets apply
9
- what WWCP_POI3 applies (the entities a party embeds, element changes, preconditions, no lastChange going back),
10
- histories require the signature roles WWCP_POI requires and take a checkpoint with the signatures that verify, and EV
11
- Taler phase 2 checks the steps and the settlement of a session. Each of them is probed against WWCP_POI3, what differs
12
- named below, and the vectors of WWCPPOIConformanceTests `215023a` hold, none deviating. See [Migration](#migration)
13
- before upgrading: versions, change sets and signatures 0.2.0 took are refused, and the types of three properties change.
14
-
15
- - A roaming network is read as WWCP_POI3 reads it (`RoamingNetwork.TryParse` and the TryParse of every entity) and
16
- refused in its words and its order: JSON types, mandatory properties, values, identifications, names of enumerations,
17
- currencies, URLs, timestamps, texts in languages, data licenses, and what the constructor of each entity refuses of
18
- what it read (`src/network/reading.ts`, `poiJSON.ts`, `poiValues.ts`). Every kind of entity it reads: the roaming
19
- network and its operator; charging station operators with their pools, stations, EVSEs, connectors, energy meters,
20
- templates, groups, tariffs, tariff templates, groups and offers, brands and parking spaces; manufacturers of charging
21
- stations and energy meters with their models, transparency software manufacturers with their releases, conformity
22
- assessment bodies, e-mobility providers, grid operators with their grid connection points, parking operators.
23
- - The references the constructor of the roaming network checks, and the identifications it keeps unique among them,
24
- interleaved in its order (`references.ts`): manufacturers, models, hardware revisions and firmware versions, releases,
25
- certificates, grid connection points, charging stations, parking spaces and maintainers the network does not have, a
26
- station or an energy meter contradicting its model, a key of two parties. After the last change of a change set, bare,
27
- in the order of `RoamingNetwork.ChangeSets.cs`, a reference the version before resolves named as one the change set
28
- withdrew. They replace the separate checks of identifications, versions, certificates, EV Taler and keys of parties.
29
- - 1268 versions probed against WWCP_POI3 at 89d2e02 are taken and refused alike, in the same words, but for nine agreed:
30
- five identifications of the roaming network and its operator given as numbers, which keep the words of this library
31
- for WWCP_POI3 to take, and four messages of the CBOR and COSE libraries.
32
- - Values WWCP_POI3 writes otherwise than given are refused in the words of its written form (`writtenForm.ts`): a
33
- number, a boolean or a text given as another JSON type it reads it of (`"0.390"` as 0.390, `"TRUE"` as true, 5 as
34
- `"5"`), coordinates of texts and booleans, durations in seconds in whole ticks of 100 ns without trailing zeros (1.50
35
- as 1.5), the width and height of an image; opening times as Styx writes them, `24/7` always, hours `HH:mm`, periods
36
- UTC, empty lists and free texts left out; NUTS codes in upper case, a list written otherwise compared element by
37
- element with the one WWCP_POI3 writes; addresses with their countries by their alpha-3 codes, their street, postal
38
- code and time zone trimmed, their other texts as given and left out where empty, their languages in lower case,
39
- nothing else. A country is read as `Country.TryParse` of Styx reads it, of its 251 countries (`countries.ts`): by its
40
- alpha-2 or alpha-3 code in any case, its numeric code, its calling code or one of its names, and refused where it
41
- names none. Of 164 more versions probed against WWCP_POI3 at 89d2e02, 163 are taken and refused alike, in the same
42
- words; the one left gives a number with an exponent, which the probe writes otherwise before WWCP_POI3 reads it.
43
- - Two charging tariff offers of an operator must not overlap (`offers.ts`, `OverlappingOffers` of WWCP_POI3, SPECIFICATION.md
44
- 9.4): covering one EVSE, for some of the same sessions (ad hoc, or contracts of a provider both name, or one names
45
- none), at some of the same times within their `notBefore` and `notAfter`, both inclusive, and their opening times in the
46
- time zone of the EVSE's charging pool, with what its templates give it, else UTC; local times as `TimeZoneInfo`
47
- converts them, one a change to daylight saving time skips the first after it, one it gives twice the one of standard
48
- time; exceptional closings
49
- close, exceptional openings open. Refused in its words, at the first EVSE in the ordinal order, as the constructor of
50
- the operator refuses it, also after a change set. After a change set also two offers of an e-mobility provider the
51
- change set changed that overlap and did not in the version before (`NewProviderOfferOverlap`); by the changes of
52
- others they may. Of 64 versions probed against WWCP_POI3 at d1c41ca, all are taken and refused alike, in the same
53
- words, also those of time zones a charging pool may not name.
54
- - The time zone of a charging pool or a charging pool template is one of a region of the IANA time zone database, as
55
- the database writes it, which this platform knows (`timeZones.ts`, `POITimeZones` of WWCP_POI3 `d1c41ca`,
56
- SPECIFICATION.md 9.4, D17 of the conformance tests): `Europe/Berlin`, also an older name the database keeps for one,
57
- `Asia/Kolkata` as `Asia/Calcutta`, or of a part of a region, `America/Argentina/Buenos_Aires`. No name of another
58
- system (`W. Europe Standard Time`), none without a region (`CET`, `UTC`, `Etc/GMT-1`, `US/Eastern`), none in another
59
- case (`europe/berlin`), none the platform does not know: refused in the words of WWCP_POI3 as the constructor of the
60
- pool or template refuses it, trimmed, after its images, no longer taken as UTC, nor a Windows name as its IANA name.
61
- The case is the one Temporal gives (Node 26); without Temporal (Node 22 and 24) Intl gives it of a name, but of an
62
- older name only the name it stands for, so an older name is then taken in any case (`Asia/KOLKATA`). Of 92 versions
63
- probed against WWCP_POI3 at `d1c41ca`, 90 are taken and refused alike, in the same words; the two left name
64
- `America/Ciudad_Juarez` and `Antarctica/Troll`, which .NET on Windows does not find, and this library takes, as Node
65
- knows them: databases of different age know different names (SPECIFICATION.md 9.4). `poi-refusals.json` of
66
- WWCPPOIConformanceTests vendored again without the marker of D17: its 10 time zones are refused by their reasons.
67
- - A measurement public key is a key as BouncyCastle 2.7.0 makes one, as WWCP_POI3 checks it (`measurementPublicKey.ts`,
68
- `MeasurementPublicKey.TryCheck`), refused in its words and those of BouncyCastle: a SubjectPublicKeyInfo of DER
69
- (`ber.ts`), of RSA, also of PSS and OAEP, its modulus odd, of no prime factor up to 743, of 16384 bits at most and no
70
- prime, its exponent odd (`rsa.ts`); of EC on the 84 named curves BouncyCastle knows (`curves.ts`, taken from it), a point
71
- it decodes, compressed, uncompressed or hybrid, on its curve and of its order (`ecc.ts`); of Ed25519 and Ed448 a point
72
- of no small order; of X25519, X448, ML-DSA, ML-KEM and SLH-DSA of their lengths; of DSA in its group. Its certificates
73
- DER, as X509CertificateParser reads them, the first of the key, each one issued by the next one, its signature of
74
- ECDSA, DSA, RSA of PKCS #1 v1.5 or PSS, Ed25519, Ed448 or ML-DSA verified with the key of the next one
75
- (`certificates.ts`). Of 242 keys and chains of BouncyCastle and variants of them probed against WWCP_POI3 at 89d2e02,
76
- 237 are taken and refused alike, in the same words; taken as given, where WWCP_POI3 checks them: points on curves over
77
- binary fields, explicit curve parameters, keys of Diffie-Hellman, ElGamal, GOST and DSTU. Not verified, so refused: a
78
- certificate signed with GOST, SM2 or SLH-DSA.
79
- - Other words: decimals in messages invariant and line breaks `\n`, where WWCP_POI3 writes them in the culture of its
80
- machine and `\r\n`; timestamps `DateTimeOffset` reads and the written form refuses, refused as invalid; an error of an
81
- entity changed by a change set without the `Change N:` of WWCP_POI3.
82
- - A price statement reads its price status as WWCP_POI3 reads it (`ChargingPrices.TryParse`), in its words and order,
83
- under its "Invalid price status of the price statement:": the prices of each charging tariff once, the tariff named
84
- by its identification `{operatorId}*T{suffix}` (SPECIFICATION.md 2, 12.1, D16 of the conformance tests), their
85
- elements, at least one, as a tariff has them, no property or JSON-LD context besides. A statement naming another text
86
- is no price statement: the price evidence of a session holding one has invalid signatures, as WWCP_POI3 gives it
87
- (`sessionPrice-foreign-tariff`), not valid ones of a tariff outside the references of its EVSE.
88
- - The nine vectors of WWCPPOIConformanceTests `fde40c9` vendored, `poi-refusals.json` of 129 vectors (a version naming a
89
- charging tariff not as `{operatorId}*T{suffix}`), and `poi-verifier.json`, new here: 106 checks of the verifier
90
- extensions, meter keys, historical meter evidence, session prices, trust transitions, keys of charging documents,
91
- trust policy identifications, digests of key statements, offer hashes and publication receipts, against the POI
92
- fixtures of ChargyWebApp at `fc513cd`, vendored as `tests/vectors/chargy-fc513cd`. Run in `tests/conformance.test.ts`
93
- as WWCPPOIConformanceTests runs them against this library: a refusal after its control, by its reason; none deviates.
94
- - `poi-refusals.json` of WWCPPOIConformanceTests `7d43d4b` vendored (`586a339` before a rebase there), of 142 vectors:
95
- 13 price statements whose price status is none WWCP_POI3 reads (without one; its prices not an array; the prices of a
96
- tariff not an object; a property or a JSON-LD context besides, of the status or of the prices of a tariff; a tariff
97
- not named `{operatorId}*T{suffix}` or named twice, also in another spelling; its elements empty, one not an object or
98
- without price components), refused as both read them, in the words both give. None deviates.
99
- - `poi-refusals.json` of WWCPPOIConformanceTests `ed91741` vendored, of 273 vectors: 131 versions built from the tests
100
- of this library and read as WWCP_POI3 reads them, 59 of reading, 18 of references, 33 of written forms, 11 of offers,
101
- 10 of measurement public keys, refused in the words both give. None deviates. Two of them, offers of `Europe/Berlin`,
102
- WWCP_POI3 takes where .NET runs without ICU, as it then finds no IANA time zone and compares them in UTC (O5 there).
103
- - `@vanaheimr/cose` 0.14.0, the codec still `@vanaheimr/metrological-cbor` 0.12: a key of a party of key type RSA (3) is
104
- read as Styx reads it, identified by its RFC 9679 thumbprint over n and e, and taken as WWCP_POI3 takes it; 0.12.0
105
- refused it, "Invalid COSE key: The curve of a COSE key must be an integer!". POI names no signature algorithm of RSA
106
- (SPECIFICATION.md 4), so such a key signs nothing this library verifies, as before.
107
- - `@vanaheimr/cose` 0.15.0, the codec still `@vanaheimr/metrological-cbor` 0.12. It reads and verifies the
108
- countersignature of RFC 8152 and holds a key an X.509 certificate writes as `id-RSASSA-PSS` to RSASSA-PSS, as Styx
109
- `7642de3d` does. This library uses neither, and reads and verifies its keys and signatures as with 0.14.0. For
110
- development, Playwright 1.65.0 and Vite 8.3.6. TypeScript stays at 6.0: typescript-eslint 8.72.1 requires a version
111
- below 6.1, and its typed linting needs the compiler API that TypeScript 7 does not ship.
112
- - `poi-refusals.json` of WWCPPOIConformanceTests `d388929` vendored, of 283 vectors: 10 versions naming a time zone of a
113
- charging pool or a charging pool template that is none of a region of the IANA time zone database, refused by WWCP_POI3
114
- since `d1c41ca` and taken by this library, marked as its deviation (D17 there).
115
- - A change set changes the entities a party embeds, as WWCP_POI3 applies them (`apply.ts`; RoamingNetwork.OfferChanges.cs,
116
- ProviderChanges.cs, GridChanges.cs and ParkingChanges.cs): the charging tariff offers of charging station operators
117
- and e-mobility providers, the EVSE, charging station and charging pool groups of e-mobility providers, the grid
118
- connection points of grid operators, the parking garages, spaces, sensors, space templates, space groups and products
119
- of parking operators and the parking spaces of charging station operators. Added to the party an `Add` names as its
120
- parent, found by its identification as its type compares it, e.g. a parking product with case, the properties a change
121
- set may change, in the words of WWCP_POI3; with their timestamps, but a grid connection point and a parking product,
122
- which have none. Owned by the party embedding them (4.1), the offers by keys that may sign `tariffs`, the others
123
- `infrastructure`; a grid connection point by its maintainer, its maintainer changed by it, its grid operator or the
124
- identity keys of the roaming network operator; also after the earlier changes of the change set, which may add or
125
- remove them or their party. 0.2.0 refused them as not supported. Of 107 change sets probed against WWCP_POI3 at
126
- `d1c41ca` (`CreateChangeSet`), the 50 it takes lead to the same content here, and the 57 it refuses are refused here
127
- too, 34 in the same words; the others in the words of the entity read again without `Change N:`, of the change as
128
- WWCP_POI3 parses it, or of the written form, where WWCP_POI3 names a property of infrastructure a change set may not
129
- change.
130
- - The `effectiveLastChanges` of a snapshot are read as WWCP_POI3 reads them (`TryTakeEffectiveFloors`,
131
- POIEffectiveLastChange): absent or `null`, or an array of `{entityType, entityId, lastChange}`, a JSON-LD context only
132
- its own, the timestamp in a form `DateTimeOffset` reads; refused otherwise in its words, "The effective last changes of
133
- a snapshot must be a JSON array!", "Invalid effective last change '{…}' of a snapshot!". 0.2.0 took any. They are the
134
- floors of the `lastChange` of effective members POI_API keeps at runtime (SPECIFICATION.md 8.8, point 3), found again
135
- for what a change set changes; the effective tree takes the `lastChange` of an effective member without them (8.1),
136
- as WWCP_POI3 does (`EffectiveChargingPoolsOfTheContent`), so this library reads them and keeps none.
137
- - The preconditions of a change set hold, as WWCP_POI3 checks them (`preconditions.ts`; RoamingNetwork.Preconditions.cs,
138
- SPECIFICATION.md 4): each names the ETag an entity still has in the version the change set is applied to, after its
139
- `beforeETags` and before its changes, over the entity as the roaming network embeds it, with everything it embeds, its
140
- canonical JSON or its deterministic CBOR, in the hash algorithm it names. The entity is found by its type and
141
- identification as its type compares it, e.g. `DEGEFP1` as `DE*GEF*P1`, among the entities a change set addresses on
142
- their own (`Flatten`), not a model, which is unique within its manufacturer only, as in WWCP_POI3. Refused otherwise in
143
- its words, "The change set does not fit this version of the roaming network: Precondition 0: the ChargingPool
144
- 'DE*GEF*P1' changed, it no longer has the ETag json:sha256:hex:…!", and a precondition not read as it reads one in a
145
- change set, "Invalid precondition 1!". 0.2.0 refused change sets with preconditions as not supported. Of 79 change sets
146
- with preconditions probed against WWCP_POI3 at `d1c41ca`, ETags of 40 entities of every kind among them, all are taken
147
- and refused alike, 70 in the same words, the others refused when read, where the probe reads a precondition on its own.
148
- - A change set changes elements, as WWCP_POI3 applies them (`elements.ts`, `apply.ts`; POIElements.cs and
149
- RoamingNetwork.ElementChanges.cs): `AddElement`, `RemoveElement`, `ReplaceElement`, `UpdateElementProperty` and
150
- `RemoveElementProperty` address an element of a property of an entity by their element path, e.g.
151
- `chargingConnectors['1'].format`, an element of an array by its identification, as the type of the identifications of
152
- the property compares them, e.g. `EVSEIds` without case, any other ordinally; an added element where the ordinal order
153
- puts it, in `templateIds` at the end; an array left without elements removed. Each amounts to the `UpdateProperty` or
154
- `RemoveProperty` of the property its path starts with, checked and owned as that change (4.1), its old value compared
155
- with the element, or the property of the element, it addresses; keys and tree hash algorithms are changed as a whole
156
- only. Element changes WWCP_POI3 does not make are refused when the change set is read: an element change without an
157
- element path, another change with one, a new value of JSON `null`, of each kind of change what it does not take, e.g.
158
- an `AddElement` with an old value, a segment of a path without a property name or with an empty identification.
159
- 0.2.0 refused element changes as not supported. The properties of charging pools, stations, EVSEs, tariffs, groups and
160
- templates a change set may change are those of WWCP_POI3 (RoamingNetwork.InfrastructureChanges.cs), refused otherwise
161
- in its words, e.g. "the property 'chargingStations' of a charging pool can not be changed!", and an infrastructure
162
- entity is named as WWCP_POI3 names it, e.g. "the charging tariff 'DE*GEF*T_AC'". Of 108 element changes probed against
163
- WWCP_POI3 at `d1c41ca` (`CreateChangeSet`), the 47 it takes lead to the same content here, and the 61 it refuses are
164
- refused here too, 31 in the same words; the others in the words of the entity read again without `Change N:`, of an
165
- old value without the value found, or of a change as this library reads it. Of the 107 change sets of embedded
166
- entities probed before, 93 are now in the same words.
167
- - The elements of the prices of a charging tariff in a price statement are written as a charging tariff holds them
168
- (SPECIFICATION.md 12.1, WWCP_POI3 `5a72313`, D18 of the conformance tests), as a version holds the elements of a tariff:
169
- an element, a price component or restrictions naming a property their type does not define, a number given as a text,
170
- the days of the week out of their order, the JSON-LD context of an embedded object are refused before the signature,
171
- "'elements[0].restrictions.maxStateOfCharge' of the prices of the charging tariff 'DE*GEF*T_DC' is not written as a
172
- charging tariff holds it: the content leaves it out!" under "Invalid price status of the price statement: ". A reader
173
- not knowing a restriction would else price a session where its signer did not. The written form knows the
174
- restrictions of an element now (`startTime` to `reservation`, as ChargingTariffRestrictions writes them): a version
175
- naming another one, e.g. `maxStateOfCharge`, is refused, which 0.2.0 took. The price evidence of a session whose
176
- statement names a restriction of OCPI, e.g. `max_kwh`, has invalid signatures now (`sessionPrice-conditional-price`).
177
- `poi-refusals.json` and `poi-verifier.json` of WWCPPOIConformanceTests `c540c7f` vendored, 290 vectors, 7 of them new;
178
- none deviates.
179
- - `poi-histories.json` of WWCPPOIConformanceTests `bc4e5a0` vendored: the 13 replays this library left to WWCP_POI3
180
- (`notApplicable`) hold here as well and are replayed: the references a version holds, e.g. `station-*`, `meter-*`,
181
- `withdrawn-while-named-*`, checked since `813e1d7`, and the changes of the groups of a provider and of grid connection
182
- points, applied since `1946013`, each refused in the words of its reason. `tests/conformance.test.ts` 760.
183
- - `poi-histories.json` of WWCPPOIConformanceTests `70deea0` vendored: 14 replays of the entities a party embeds, as
184
- `1946013` applies them, in `history-parties`: offers of an operator, signed for `tariffs`, not overlapping; an offer
185
- and groups of a provider, signed by the provider, not by the operator of the same prefix; a grid connection point
186
- handed on and named in one change set, by its new maintainer (csharp-review 9); parking data of a parking operator
187
- and the parking space of a charging station operator, each signed by its own party. `tests/conformance.test.ts` 774.
188
- - `poi-verifier.json` of WWCPPOIConformanceTests `aef4b38` vendored, with the price evidence of sessions it makes itself
189
- (`tests/vectors/chargy/POIPrices`): the cases of `POIPrices` of ChargyWebApp `fc513cd`, every signature
190
- deterministic. 23 `sessionPrice-*` vectors check the same as before on them; `-foreign-tariff` and
191
- `-conditional-price` stay on the fixtures. Two new ones are taken: `-tariff-of-another-evse`, a genuine price of a
192
- tariff of the pattern `{operatorId}*T{suffix}` the EVSE does not name, of which none applies, and
193
- `-conditional-element`, an element restricted by `maxEnergy`, whose price is selected without a rate.
194
- `tests/conformance.test.ts` 776.
195
- - Transparency software releases and certificates are identified under their owners, as WWCP_POI3 `045e180` checks it
196
- (SPECIFICATION.md 4.1, POI3-ISSUES.md S36, `ForeignTransparencySoftware`, `ForeignConformityCertificate`,
197
- `IsIdentifiedUnder`): the `@id` of a release is the `@id` of its manufacturer, a colon and a name without one
198
- (`GraphDefined:chargy-1.5.0`), of a certificate the `@id` of the body issuing it alike (`PTB:2026-0042`), the prefix
199
- compared without case, both trimmed as read. A version holding one identified otherwise is refused before releases or
200
- certificates given twice, in its words: "The transparency software release 'chargy-1.5.0' of the manufacturer
201
- 'GraphDefined' is not identified as one of it: by 'GraphDefined:' and a name without a colon!"; so is a change set
202
- leading to one, after the unknown parking spaces and before the releases given twice. References name them as before.
203
- Of 38 versions and change sets probed against WWCP_POI3 at `045e180`, all are taken and refused alike, in the same
204
- words. `tests/releaseIdentifications.test.ts`.
205
- - The test vectors of a new run of the C# examples, `tests/vectors/csharp-045e180` in place of `csharp-33447ac`: WWCP_POI
206
- `045e180`, examples A1 to A15, MakeProofs at `2026-11-05T19:46:00.000Z` (`tests/at.ts`). Releases and certificates
207
- under their owners (`GraphDefined:chargy-1.5.0`, `PTB:2026-0017`, `PTB:2026-0042`); example A15 names the EV Taler
208
- merchant of DE*GEF, so the head of epoch 2 is revision 11, while `QA.json` stays revision 8, as example A14 left it.
209
- With them `poi-history.json` (100 vectors), `poi-refusals.json` (296, 6 of them of S36), `poi-histories.json` (75
210
- replays, 8 of them of S36, the histories naming releases and certificates under their owners) and `poi-qidigital.json`
211
- of WWCPPOIConformanceTests `2d0780f`. The tests on QA hold the values of the new run; those WWCP_POI3 calculated of
212
- changed versions (element and embedded changes, preconditions, EV Taler) are calculated again by WWCP_POI3 at
213
- `045e180`, and this library takes the very same versions. `tests/conformance.test.ts` 794.
214
- - Required signature roles, as WWCP_POI `12e993a` checks them (POI3-ISSUES.md S24): `requiredSignatureRoles` of a history,
215
- the roles a commit must be signed in, refused before it is applied where it has no signature in one ("The commit '…'
216
- is not signed in the role '…'!"), and once its signatures are verified where none of them is by a key the version it
217
- leads to lets sign in that role ("… by a key that may sign in it!", `keyMaySignIn`): in the role of the roaming
218
- network operator a signing key certified for it, in the role of a charging station operator a signing key of the
219
- operator its signer names; leaders and witnesses are the verifier's. So also the commit of a snapshot.
220
- `requiredCheckpointSignatureRoles`, the roles a checkpoint must be signed in ("The checkpoint '…' is not signed in the
221
- role '…'!"). A signed commit without a verifier is refused before it is applied, as WWCP_POI does. Signatures of one
222
- key in one role, e.g. the twin of an ECDSA signature (S11), are kept as given, each verified, and counted once by the
223
- holder or witness they are of, as WWCP_POI `f7b8643` reads them too (SPECIFICATION.md 5.4): it kept the first alone
224
- since `12e993a`, so a forged signature behind a genuine one of its key went unchecked. `tests/signatureRoles.test.ts`.
225
- - A checkpoint is taken with the signatures that verify here, as WWCP_POI `399b7c8` takes it (POI3-ISSUES.md S39,
226
- SPECIFICATION.md 10.2): `verifyCheckpoint` of a history returns those of its signatures that verify, and the history
227
- keeps the first of each key and role of them; one that does not verify, invalid or by a key not trusted here, is left
228
- out, not refused, so that nobody blocks a checkpoint by adding a signature, and a witness trusted elsewhere and not here
229
- keeps none from it. The roles a checkpoint must be signed in are counted among them: before, a role of a signature
230
- `verifyCheckpointSignatures` passed over counted ("The checkpoint '…' is not signed in the role '…' by a signature that
231
- verifies here!", and why the first one was left out). `takeCheckpoint` and a snapshot of a checkpoint hold it with
232
- those only (`Checkpoint.withSignaturesOnly`). `verifyCheckpointSignatures` returns them as `verified`. The type of
233
- `verifyCheckpoint` is now `(checkpoint) => readonly RoleSignature[] | undefined`: `undefined`, every signature verifies,
234
- as before; a callback of a block without `return` is typed `void` and is no longer one.
235
- - A signature of a commit or checkpoint is read as WWCP_POI3 reads it (`RoamingNetworkCommitSignature.TryParse`): its role,
236
- time, algorithm, key and value trimmed, its signer as given, and what it signs is what was read; refused when its role,
237
- signer, algorithm, key or value is empty or whitespace alone (`String.IsNullOrWhiteSpace`), and so is a signature of a
238
- change set whose algorithm, key or value is ("The … of a signature of … must not be empty!"). Before, this library took
239
- them, and a role padded with whitespace was not the role.
240
- - Required signature roles probed against WWCP_POI3 at `12e993a`, `f7b8643` and `399b7c8` (`CheckSignatures` of a
241
- snapshot, `TryAppend`, `TryAddCheckpoint`), on QA of epoch 2: 1879 cases of roles required of a snapshot, a commit and
242
- a checkpoint, signed in every role, by keys of the roaming network operator, of DE*GEF and of nobody, by signers named
243
- in every form, with and without a verifier; a second signature of one key in one role, the same, forged or signed
244
- later; checkpoints verified by a rule leaving signatures out, forged ones before genuine ones among them; signatures
245
- read with texts empty, padded and of other types. At `399b7c8` all are taken and refused alike, the checkpoints held with
246
- the same signatures, the roles in the same words, signatures read refused in the words of this library. Stricter than
247
- WWCP_POI3 there, which took them: a signer given as a number, an `@context` or another property in a signature of a
248
- commit or checkpoint, and in the commit of a snapshot an algorithm its epoch does not name. WWCP_POI `b43ff1a` refuses
249
- them too (POI3-ISSUES.md S40), and `7c6f91c` refuses a signature read in the words of this library, a snapshot naming
250
- why its commit is refused: probed again at `7c6f91c`, all 1879 alike, word for word.
251
- - `poi-refusals.json` and `poi-histories.json` of WWCPPOIConformanceTests `215023a` vendored, 806 vectors there, 13 of
252
- them new: 8 refusals of a signature of the genesis commit or of checkpoint 1 not in its one form (another property,
253
- `@context`, a signer as a number, a role of whitespace, a signedAt without milliseconds); a replay starting from the
254
- snapshot of the genesis of epoch 3, its commit signed with Ed448, which the epoch does not name; 4 snapshots of
255
- checkpoint 0 anchored with a signature that does not verify or a witness the trust anchor does not know (taken, S39), a
256
- key of one holder signing twice (refused) and with the other holder too (taken). None deviates.
257
- - What a proof gives of a child array, as WWCP_POI `d35c780` tells it (S29): `givesCompletely`, whether an entity as a proof
258
- gives it has a child array completely, as an array or none, or pruned or partially; `sizeOf`, how many children it has,
259
- also where the proof gives only some or none of them. A size or index beyond what this platform holds, and an object as
260
- the identification of the roaming network, were refused already.
261
- - No lastChange goes back, as WWCP_POI `12e993a` stamps a change set (SPECIFICATION.md 4, "Timestamps", POI3-ISSUES.md
262
- S5): with its createdAt, or the lastChange of the roaming network of the version it is applied to, where that is later,
263
- as the lastChange of the network, of what it changes, adds and embeds, and as the created of what it adds without one.
264
- Its keys are still checked at its createdAt; nobody is refused for it, only the ETags after it differ (`stampOf`). The
265
- after ETags of the element and embedded change sets and of the preconditions of the tests, created before QA, are
266
- those WWCP_POI3 at `12e993a` calculated for the very same changes (67 change sets, all alike). `poi-histories.json` of
267
- WWCPPOIConformanceTests `249d667` vendored: the three rebased change sets of `history-parties`, created at 08:06:30 on a
268
- head of 08:07:00, are stamped 08:07:00. `tests/lastChange.test.ts`: every change set of both epochs of QA, created
269
- before its version, stamped with its lastChange.
270
- - EV Taler, phase 2, as WWCP_POI `c2d39e5` checks it (concepts/EVTaler.md 5 and 7, `src/evTaler`): the contract terms of
271
- a step of a session (`EVTalerContractTerms`: version 0, an order, an amount and a maximum fee as GNU Taler writes them,
272
- the merchant, its exchanges, the times, an auto refund) and their step in `extra.wwcp` (`EVTalerStep`: the roaming
273
- network, the EVSE, the session, the step after the order before, the EV Taler tariff, the price statement, what it
274
- covers, from step 2 on the signed meter value at the start, an object as OCPP 2.1 sends it), checked against the proof
275
- of the station: the operator's merchant and exchanges in the currency of the EV Taler tariff of the EVSE, the price
276
- statement current when the session began, each step following the one before, the signed start value verified with
277
- the measurement key of the meter or the station the proof gives, by a transparency software passed in; the amount of a
278
- step recalculated where the session is priced by one element of the simple profile (`EVTalerRates`), as System.Decimal
279
- calculates it (`NetDecimal`: 96 bits, 28 places, rounded half to even), rounded up to the minor unit. A `taler://pay`
280
- URI (`EVTalerPayURI`) names the merchant of the operator. The settlement statement (`EVTalerSettlement`), signed by the
281
- operator with a key that may sign `payments`, refused without both signed meter values, of the steps of the session,
282
- its energy and times those of the values, its price recalculated, half away from zero. Read and verified, not signed.
283
- Of 178 sessions, steps, pay URIs and settlement statements probed against WWCP_POI3 at `c2d39e5`, on station proofs it
284
- made, all are taken and refused alike, in the same words. `tests/evTalerSessions.test.ts`, on the proof of example A15.
285
- - EV Taler: a step in minutes costs what it covers, rounded up to the cent, not a cent more, as WWCP_POI `a374855`
286
- calculates it: the quantity multiplied by the price of an hour before it is divided by 60. A decimal holds no rate of a
287
- minute such as 0.40 / 60 exactly, and rounding up took its last digit for a cent: 60 minutes at 0.40 EUR an hour were
288
- 0.41 EUR, 30 minutes 0.21 EUR. Found by the probe of this port. The 178 cases probed again against WWCP_POI3 with
289
- `a374855`: all alike.
290
- - `hermodURL` as Hermod reads a URL: no `_` in host names, an empty port, an IPv6 address checked.
291
- - Reading a version takes about two to three times as long as with 0.2.0, e.g. 1.0 to 1.3 s for 3204 charging pools of
292
- 12816 EVSEs, each energy meter with a P-256 key of its own, where 0.2.0 took 0.4 s. A check of a version finds the
293
- members of the operators in their order, their effective charging stations and energy meters and the charging stations
294
- parking spaces name once (`whileChecking`); the identifications read of texts and the measurement public keys checked,
295
- the last 65536 of each, are remembered for the next version too, as a history names most of them again. A key of P-256
296
- takes about 20 µs, a key of RSA about as long as one exponentiation with its modulus, a certificate of ECDSA a few
297
- milliseconds.
298
-
299
-
300
- ### Migration
301
-
302
- - Versions WWCP_POI3 refuses and 0.2.0 took are refused now, in the words of WWCP_POI3; of the probed versions and the
303
- test vectors, none WWCP_POI3 takes. Messages of refusals checked by their text change where 0.2.0 worded them on its
304
- own, e.g. a roaming network with keys of its own.
305
- - A price statement whose price status 0.2.0 took and WWCP_POI3 refuses, e.g. naming the charging tariff
306
- `DE*POI*FOREIGN`, is refused before its signature is looked at.
307
- - A charging pool or a charging pool template naming its time zone by a Windows name, an abbreviation or in another case
308
- than the IANA time zone database, which 0.2.0 took, is refused: it names it as the database writes it, e.g.
309
- `Europe/Berlin`.
310
- - `HistoryOptions.verifyCheckpoint` returns the signatures of the checkpoint that verified, `readonly RoleSignature[]`,
311
- or `undefined` for all of them, e.g. `checkpoint => verifyCheckpointSignatures(checkpoint, trust, options).verified`.
312
- A callback of a block without `return` no longer compiles; one that throws still refuses the checkpoint. A history,
313
- `takeCheckpoint` and a snapshot of a checkpoint hold it with those signatures only, its identification the same: what
314
- an app keeps of a checkpoint it took may hold fewer signatures than it was given (S39).
315
- - `Change.elementPath` is an array of `ElementPathSegment`, `{propertyName, elementId}`, in place of the JSON objects
316
- given, and `ChangeSet.preconditions` an array of `Precondition`, `{entityType, entityId, eTag}`, both read as WWCP_POI3
317
- reads them; the JSON of a change and of a change set stays in its `json`. Both types are exported, and so are
318
- `MerkleTreeHead`, `PrunedTree` and `TreeNodeKind`, which the API used already.
319
- - A signature of a commit or checkpoint is read in its one form: its role, signedAt, algorithm, key and value trimmed,
320
- and refused where one of them or its signer is empty or whitespace alone, where its signer is not a text, or where it
321
- names an `@context` or another property; so is a signature of a change set whose algorithm, key or value is empty.
322
- Producers write each signature in this form, as WWCP_POI `7c6f91c` reads it too.
323
- - A change set created before the `lastChange` of the version it is applied to is stamped with that `lastChange`, not
324
- with its `createdAt` (S5): the version it leads to, and so its after ETags, differ from those 0.2.0 calculated, and a
325
- producer calculates them as WWCP_POI `12e993a` does. A change set created after its version, the usual case, is stamped
326
- with its `createdAt` as before.
327
- - A transparency software release is identified under its manufacturer, `{manufacturer}:{name}`, and a certificate
328
- under the body issuing it, `{body}:{name}`, e.g. `GraphDefined:chargy-1.5.0` (S36): a version or change set
329
- identifying one otherwise, which 0.2.0 took, is refused.
330
- - The prices of a charging tariff in a price statement are written as a tariff holds them (D18): a statement naming a
331
- property their type does not define, e.g. the OCPI restriction `max_kwh`, is refused before its signature, and the
332
- price evidence of a session holding one has invalid signatures. A version naming a restriction outside `startTime` to
333
- `reservation`, e.g. `maxStateOfCharge`, is refused.
334
- - A snapshot whose `effectiveLastChanges` WWCP_POI3 does not read, which 0.2.0 took, is refused. Change sets 0.2.0
335
- refused as not supported, of the entities a party embeds, with element changes or preconditions, are applied now.
336
- - Reading a version takes two to three times as long as with 0.2.0, about a second for 12816 EVSEs. The package is
337
- larger, 3.7 MB unpacked where 0.2.0 was 2.2 MB, of it `dist/index.js` 0.9 MB and its source map 2.3 MB.
338
- - `@vanaheimr/cose` requires ^0.15.0 and `@vanaheimr/metrological-cbor` still ^0.12.0: keep both ranges aligned in
339
- consumers that use them directly. The minimum runtime remains Node 22.18, and the package remains ESM-only. Without
340
- Temporal, on Node 22 and 24, a time zone Intl names by another name is taken in any case, e.g. `Asia/KOLKATA`, which
341
- it names `Asia/Calcutta`; Node 26 takes it only as the database writes it.
342
-
343
-
344
- ## 0.2.0 (2026-11-02)
345
-
346
- Shared trust and session verification APIs, previously delivered as the unpublished 0.1.2-dev.0 development candidate.
347
- The experimental evidence and delegation profiles described below remain experimental in this release.
348
-
349
- - Shared association of an EVSE, its energy meter and its key, with an explicit normalization of SubjectPublicKeyInfo
350
- and COSE P-256 keys and a conservative freshness of the observation; no claim of a historical association.
351
- - Historical meter evidence per reading, and checkpoints verified under the current local authorization.
352
- - Prices bound to a session, their publication and their rates checked apart.
353
- - A shared comparison of authenticated versions: rollback and fork constraints the caller keeps durably, historical and
354
- current versions told apart, gaps filled, a change of the epoch refused unless verified.
355
- - The charging document keys of a CPO, an authority of their own; experimental delegation of a policy signed by two
356
- quorums, historical timelines of authorities, and verified epoch continuation and profiles.
357
- - Experimental receipts of a public retrieval of prices, a policy of witness organizations, networks and clocks of
358
- its own, restrictions of key usages and a conservative result before the signed start; no witness authorized
359
- automatically, no claim of the actual start or of the consent of the customer.
360
- - COSE and Metrological CBOR on ^0.12.0, so that direct and transitive consumers share one codec; existing proof and
361
- signature fixtures remain valid.
362
- - `trustPolicyId` is the SHA-256 of the canonical JSON of `{network, holders, minConsensus, urls}`, the URLs as a WHATWG
363
- URL serializes them without trailing slashes, in their ordinal order, as WWCP_POI3 calculates it
364
- (`TrustTransition.PolicyIdOf`); in place of their `JSON.stringify`, whose order of properties and escapes are those of
365
- JavaScript, so that every implementation calculates the same. Every policy identification changes: decisions an app
366
- keeps under one are made again, and transitions are signed again.
367
- - A commit is refused without its `json`, `cbor` and `effectiveTree` ETags (SPECIFICATION.md 8.3), as WWCP_POI3 refuses
368
- it; the epoch change of `tests/vectors/trust-transitions.json` names the ETags of its versions.
369
- - `trustTransitions.ts` and `documentKeys.ts` with the license header and in the layout of the other sources.
370
-
371
-
372
- ### Migration
373
-
374
- - Commits must name all three version ETags: `json`, `cbor` and `effectiveTree`. A commit accepted by 0.1.1 with
375
- only an effective-tree ETag is now refused. Producers must generate the complete ETags and sign the resulting commits
376
- and checkpoints again; do not edit previously signed documents or silently repair an archived original.
377
- - When moving from the unpublished 0.1.2-dev.0 candidate that used `JSON.stringify` for policy identity, recompute
378
- `trustPolicyId` from canonical JSON. Reconfirm local decisions under the new identity and regenerate and sign affected
379
- trust transitions. Retain old observations and original evidence; a changed policy identity must not erase known forks
380
- or rollbacks. Candidates already using canonical JSON keep their policy identities.
381
- - COSE and Metrological CBOR both require ^0.12.0. Keep both dependency ranges aligned in consumers that use them directly.
382
- - The minimum runtime remains Node 22.18, and the package remains ESM-only. Local trust decisions, durable observation
383
- storage and independently authorized witness policies remain the caller's responsibility. No production trust roots
384
- or witness service are bundled.
385
-
386
-
387
- ## 0.1.1 (2026-10-31)
388
-
389
- `TrustContext`, for apps that import a trust anchor before they trust its keys. `TrustContext.evaluate` checks a key
390
- statement against the identity keys of a trust anchor the app trusts locally and reports its signatures, signers, quorum
391
- and validity apart (`TrustEvaluation`); a context only when all of them hold, `TrustContext.create` throws otherwise.
392
- The context keeps the holders of the trust anchor and its quorum, and binds the roaming network, its operator, the
393
- signed key statement and the time of the evaluation. `OperatorTrust.of` and `fromOperatorJSON`, `verifyVersion`,
394
- `verifyChargingStation`, `ChargingStationProof.verify` and `verifyCheckpoint` take it in place of a trust anchor; given
395
- a trust anchor, they verify as before. The app keeps storing and showing its decisions and creates a context from its
396
- current decisions for every verification. Tested with genuine signatures, the proofs of the conformance tests and in
397
- Chromium.
398
-
399
- S31 and S32 of WWCP_POI `d6b0127`. A release keeps its version: a new version is a new release, so that a
400
- certificate naming a release names the version that was assessed. A change set updating or removing the `version` of a
401
- transparency software release is refused, and so is one removing a release and adding it again with another version,
402
- after its last change (`ChangedTransparencySoftwareVersion`; versions compared trimmed and without case, as
403
- `TransparencySoftwareVersion` compares them). The `notifiedBodyNumber` and `notifiedFor` of a conformity assessment
404
- body are the roaming network operator's, which vets the body, also once the body has keys: a body issuing a
405
- certificate under a legislation the same change set adds to its `notifiedFor` signs it together with the operator. A
406
- number names one body, in every version and after every change set (`DuplicateNotifiedBodyNumber`).
407
- `tests/releasesAndBodies.test.ts`, after `A_release_keeps_its_version` and
408
- `The_roaming_network_operator_says_what_a_body_is_notified_for`.
409
-
410
- A conformity assessment body is refused as its constructor in WWCP_POI refuses it, in its words and order: a
411
- `notifiedBodyNumber` not of four digits (nothing after them, also no line feed, since WWCP_POI `f53d220`), a legislation
412
- of `notifiedFor` empty or given twice (with case), a certificate issued under a legislation it is not notified for
413
- (exactly as written, its certificates in the ordinal order of their identifications), each wrapped as WWCP_POI wraps it,
414
- e.g. "The given JSON representation of a conformity assessment body is invalid: The certificate 'PTB-2026-0042' is issued
415
- under the legislation 'CRA', which the conformity assessment body 'PTB' is not notified for! (Parameter 'Certificates')";
416
- before them a `notifiedBodyNumber` that is no text and a `notifiedFor` that is no array of texts. A version is refused
417
- for what WWCP_POI refuses of an entity as it reads it before what it refuses of the network made of them, the
418
- identifications unique within the network last. Checked against WWCP_POI3 itself, on 33 versions of QA: the same, but
419
- for two refusals of one body at once, a key and its number. In a change set WWCP_POI names the change ("Change 0: …"),
420
- this library refuses the version it leads to.
421
-
422
- The four vectors of WWCPPOIConformanceTests `d4a4928` vendored, `poi-refusals.json` of 93 vectors: a key of a party
423
- whose `notAfter` is written with an offset, as a date only or as null, and one whose key is another CBOR of the same
424
- COSE key. WWCP_POI refuses them too since `a425530` (D13 there, SPECIFICATION.md 2, Written forms), as this library did;
425
- the key not in deterministic CBOR now in its words: "Invalid COSE key: Invalid deterministic CBOR: the key of the public
426
- key '…' is not written as the deterministic CBOR of its COSE key!"
427
-
428
-
429
- The four vectors of WWCPPOIConformanceTests `ad7c73e` vendored, `poi-refusals.json` of 97 vectors: a conformity
430
- assessment body as its constructor in WWCP_POI checks it, a certificate issued under a legislation it is not notified
431
- for, a notified body number of three digits, a legislation notified for empty, one given twice (D14 there, found against
432
- `1f914ac`, fixed by `4ff1fc2` here).
433
-
434
- The legislations a conformity assessment body is notified for are refused unless written in their ordinal order, as
435
- WWCP_POI writes them (Achim's decision of 2026-10-29, as D13 decided it for keys): WWCP_POI takes another order and
436
- writes it sorted, which written again would be another text than the one hashed. Refused as the body is read, before
437
- what its constructor checks, in the words of this library: "The legislations the conformity assessment body 'PTB' is
438
- notified for are written in their ordinal order!"
439
-
440
- The four vectors of WWCPPOIConformanceTests `012ce9d` vendored, `poi-refusals.json` of 101 vectors: a conformity
441
- assessment body as the content holds it, notified for legislations out of their ordinal order, with a notified body
442
- number that is no text, with a notifiedFor that is no array of texts, and with the number of another body. WWCP_POI
443
- refuses the order since `8108522`, in the words of this library.
444
-
445
- WWCP_POI `f53d220`: its regular expressions end with `\z`, no longer with `$`, which in .NET matched before a last line
446
- feed as well. A notified body number "0102" with a line feed is refused, which this library had taken as WWCP_POI took
447
- it; a certificate timestamp with a line feed after its offset was refused here already, as every check of this library
448
- ends at the end of its text.
449
-
450
- The content as WWCP_POI writes it (SPECIFICATION.md 2, Written forms): what WWCP_POI takes of a version and writes otherwise
451
- is refused, as written again it would be another text than the one hashed (Achim's decision of 2026-10-29, as D13
452
- decided it for keys and 9decdb5 for the legislations a body is notified for). Of every entity, `notWrittenAsHeld`, as a
453
- version is read, after everything WWCP_POI refuses of it, so that its words come first: what the content orders
454
- (its ordered lists, `POIJSON.OrderedIds`, entities, keys, exchanges) out of its ordinal order, the texts of an ordered list
455
- untrimmed, a list WWCP_POI writes only with an element given empty (about ninety; not the versions, releases and energy
456
- meters of a certificate, the elements of a price, the exchanges of a merchant, the identity keys of a key statement), a
457
- property given as null, an embedded object naming a JSON-LD context, a text in languages not naming each language as
458
- Styx writes it, in lower case, with a text, a timestamp not UTC to the millisecond; custom data held as given. Of a key of
459
- a party, a property WWCP_POI does not read; of an EV Taler merchant and its exchanges, such a property, a base URL not as
460
- Hermod writes it, trimmed and with its scheme, and a currency not by its code. In the words of this library, e.g.
461
- "'chargingStationOperators[0].chargingPools[1].paymentOptions' is not written as the content holds it: its elements are in the
462
- order the content holds them in!". The new values of a change set are checked by the same rules (`asGiven`), as
463
- WWCP_POI refuses a new value the content holds otherwise, "e.g. an empty array written as none". Found by probing
464
- WWCP_POI3 at 8108522 with 201 versions of QA: of the 20 it takes and writes otherwise this library took all, and
465
- refuses all now; every one it takes as given is taken, every one it refuses refused, and every version of the frozen run
466
- is written as the content holds it. `tests/writtenForm.test.ts`.
467
-
468
- What the constructor of the roaming network refuses, of a version, in the words of WWCP_POI: as an argument of its
469
- parameter, which its TryParse names, e.g. "The given JSON representation of a roaming network is invalid: The notified
470
- body number '0102' of the conformity assessment body 'PTB' is the one of the conformity assessment body 'BSI' already!
471
- (Parameter 'ConformityAssessmentBodies')", in its order (the groups of providers and the offers before the releases): the
472
- identifications unique within the network, and the key statement and the certificates of the roaming network operator
473
- (`KeyStatementError`, 'Operator'), its key statement of another network now before its certificates. After the last change
474
- of a change set they are named bare, in the order WWCP_POI checks them there (`RoamingNetwork.afterChangeSet`). The
475
- golden vectors held the inner words only. Checked against WWCP_POI3 at 8108522 on 10 versions of QA.
476
-
477
- The roaming network operator, its key statement and the certificates of its signing keys in the words and order of
478
- WWCP_POI3 (the TryParse of `RoamingNetworkOperator`, `RoamingNetworkKeyStatement` and
479
- `RoamingNetworkSigningKeyCertificate`), e.g. "A key statement requires its minConsensus, a whole number from 1 to 255!",
480
- "Invalid key in the identityKeys of a key statement!", "A signing key certificate requires the signingKey it certifies!";
481
- what this library refuses besides (properties it does not know, identity keys out of their order) after them. What the
482
- constructor of the operator refuses as an argument of it, "The given JSON representation of a roaming network operator is
483
- invalid: …": a certificate by the same identity key given twice, which this library took, a signing key certified in two
484
- forms, a key both a signing and an identity key, a quorum its keys do not have; the holder of a key as it names it ("the
485
- holder 'BetaOrg'"). The tree hash algorithms of a roaming network as `POITreeHashAlgorithms` names them, four refusals for
486
- the one of this library. An entity without its identification as Styx refuses it, "Missing JSON property '@id'!", which
487
- this library took but of the roaming network and its operator (`invalidIdentification`, first of all), and one of another
488
- JSON type in the words of the kinds WWCP_POI3 refuses it of ("Invalid 'charging pool identification'!"). Certificates and
489
- the versions of stations, meters, their templates and models without the prefix this library gave them ("The certificate
490
- '…': "), as WWCP_POI3 names them, with the parameter its constructor names ("(Parameter 'Versions')", 'HardwareRevisions',
491
- 'FirmwareVersions', 'Legislation', 'NotBefore', 'EnergyMeters'), a version twice in a model as an argument of the model; a
492
- certificate without its identification, of a certified meter a model of another JSON type, in its words.
493
-
494
- Of the written form also, as WWCP_POI3 takes and writes it otherwise: an entity without its lastChange, which it writes as
495
- its created; a party without its name, which it makes up of its kind and identification ("Charging Station Manufacturer
496
- 'GraphDefined'"); a text in languages without a language, but the name of a charging station operator, which it writes
497
- so; the releases of a certificate and the URLs of an open source license left out, which it writes also empty; the energy
498
- meters of a certificate given empty; an identification and the type of a charging connector given as a number.
499
-
500
- Probed against WWCP_POI3 at 8108522 with 617 versions of QA, of every kind of object a property it does not read, every
501
- property of it removed and given in another JSON type, the tree hash algorithms and the quorum of the operator, and 10
502
- of certificates and models: none it takes as given is refused here; of what it refuses, this library refuses all in its
503
- words but 20, of what it takes and writes otherwise all but the properties it does not read (25 kinds, which it leaves
504
- out). Not ported yet: properties of another JSON type or missing that WWCP_POI3 refuses (168, in the words of every kind)
505
- and the references it checks (23, of templates, tariffs, members, manufacturers, models, releases and certificates).
506
- `tests/operator.test.ts`, and in `writtenForm`, `identifications` and `certificates`.
507
-
508
- The 27 vectors of WWCPPOIConformanceTests `1c36c6d` vendored, `poi-refusals.json` of 128 vectors: versions not written as
509
- the content holds them (`version-held-*`), the 20 versions WWCP_POI3 at 8108522 took and wrote otherwise and more of their
510
- kinds (bodies, keys, payment options and exchanges out of their order, an untrimmed payment option, lists given empty, a
511
- property given as null, embedded JSON-LD contexts, texts in languages not as Styx writes them, a timestamp with an offset,
512
- properties of keys, merchants and exchanges not read, URLs and currencies not as written), ten with their eTags as
513
- published, their reasons in the words of this library, which refuses them since `5c534d4`. WWCP_POI refuses them as well
514
- since D15 of WWCPPOIConformanceTests, fixed there in `1c36c6d` (SPECIFICATION.md 2: a reader MUST refuse a version not
515
- written so).
516
-
517
- The written form as WWCP_POI3 refuses it since WWCP_POI `3de830a`, which writes a version again and walks both
518
- (RoamingNetwork.WrittenForm.cs, FirstNotWritten): `notWritten` walks a version in that order and in those words, the
519
- reasons of this library, which WWCP_POI took over, and two of its own. "the content leaves it out", of a property given
520
- that WWCP_POI does not write: of every entity a property its type in WWCP_POI3 neither writes (ToJSON) nor reads
521
- (PropertyKeys), drawn from it at `3de830a` for 33 kinds and checked against the 605 versions it wrote in the frozen run
522
- and the probes, of a key statement, its signatures, a certificate of a signing key, a coordinate, a price component and
523
- a public key of a meter as well; `dataLicenseIds`, which a version names completely (`dataLicenses`), a license it does not know
524
- in its words; a text in languages without a language WWCP_POI reads. "the content holds it as '…'", of a value it writes
525
- otherwise, by the path of that value: the lastChange of an entity naming none (its created), the name of a party naming
526
- none (not of grid and parking operators), the lists it writes also empty, an identification given as a number, texts
527
- its types write trimmed (identifications, references, currencies, versions, e-mail addresses, URLs), URLs with the
528
- scheme https, the identification of a charging station operator in upper case, the role of a meter, the legal status of
529
- a release and signature methods as they are named, coordinates as doubles (50.92710 as 50.9271; quantities keep their
530
- decimals). Of a release and its licenses, a certificate and the meters it was issued with, a property they do not read
531
- as WWCP_POI2 refuses it ("Unknown property 'x'."). The roaming network and its operator compared without case and
532
- trimmed, as their types read them, also of the key statement and the certificates, signed as read. Seven cases keep the
533
- words of this library, refused where it reads them, before the written form, as WWCP_POI takes them over (Achim's
534
- decision of 2026-10-30): a property of a key of a party, of an EV Taler merchant or of an exchange, the merchant base URL,
535
- the base URL and the currency of an exchange not as written. Probed against WWCP_POI3 at `3de830a` with 1257 versions of
536
- QA: none it takes is refused here; of what it refuses as written otherwise, all in its words but those seven.
537
-
538
-
539
- ## 0.1.0 (2026-10-29)
540
-
541
- The first milestone: the examples A1 to A14 of WWCP_POI (commit `33447ac`) checked in TypeScript.
542
-
543
- The findings of the review this port made of the C# implementation are cited as csharp-review N, N the number of the
544
- finding in
545
- [docs/csharp-review.md of WWCPPOIConformanceTests](https://github.com/OpenChargingCloud/WWCPPOIConformanceTests/blob/master/docs/csharp-review.md);
546
- `docs/` of this repository held it up to `b9c786e`.
547
-
548
- - Canonical JSON and deterministic CBOR of Styx; a lossless JSON parser keeping decimals as `System.Decimal` holds them.
549
- - COSE keys, RFC 9679 thumbprints, signatures ES256/384/512, ES256K, ESP*, ESB*, Ed25519, Ed448, ML-DSA-44/65/87.
550
- - Trust anchors (QR code, URL, JSON), key statements, certificates of signing keys; trust following the key statements
551
- of a history (`OperatorTrust.followedBy`).
552
- - Roaming networks with their ETags, effective members and the effective tree; proofs of charging stations, attestations,
553
- the manufacturers and models of the station and its meters (`manufacturer`, `energyMeterModels`), the transparency
554
- software and certificates of their energy meters.
555
- - Change sets with their owners' signatures, commits, linear histories, checkpoints, their proofs and signatures by the
556
- roaming network operator and by witnesses, archive segments; epochs, their final checkpoints and the genesis of the
557
- next one.
558
- - Charging station and energy meter manufacturers, parties stating their models; transparency software manufacturers and
559
- conformity assessment bodies, parties holding their releases and certificates; the key usages `models`, `software` and
560
- `certificates`; energy meter templates and groups.
561
- - Price statements, their chains, checkpoint trees and proofs; evidence of a charging session as the app of example A10
562
- keeps it (`parseEvidence`).
563
- - The runtime status: status updates, frames of the WebSocket of a history, the runtime status of a snapshot.
564
- - `POIClient`: the proof of a charging station and its prices, from the servers of a trust anchor.
565
-
566
- Moving from the examples at `2d9511d` to `bbd4cbd` also made the ownership of changes the one of the C# implementation
567
- where the port differed: identity keys of the roaming network operator counted for every change of a party, not only for
568
- its keys; where it signs in place of a party without keys, its signing keys counted when they may sign `party`, not
569
- when they may sign the usage of the change (csharp-review 13); its identity and signing keys counted alike for
570
- everything it owns; and the signing keys of a party could not rotate its signing keys. The values of change sets are
571
- kept in the order the C# implementation keeps them in (csharp-review 10).
572
-
573
- Moving on to `801bbce`, the manufacturers became parties: the roaming network operator adds and removes them, they change
574
- their own properties and state their models with a key that may sign `models`, every change of a model naming its
575
- manufacturer. A change set changes only the properties of a party the C# implementation lets it change, so that the
576
- models, releases and certificates of a party are not replaced as one of its properties by a key that may not sign them.
577
- `forThisModel` of the transparency software of a meter asks for the version of the certificate among the versions of the
578
- model as well.
579
-
580
- Node.js 22.18 is the minimum, as of ChargyCore.TS: Node 20 has reached its end of life, and tsup 9, Vitest 5 and
581
- ESLint 10 do not run on it. The code follows the ESLint rules of ChargyCore.TS; where a missing value used to be asserted
582
- away, it is refused with a `POIError` saying what is missing. Tests of the bundle as published (its imports, its
583
- declarations for TypeScript apps, the files `npm pack` takes) and of the library in Chromium join the tests in Node; GitHub
584
- Actions run all of them on every push, and every night against floating dependencies, the newest codecs of Vanaheimr, and
585
- the C# examples of WWCP_POI as they are.
586
-
587
- The test vectors moved to WWCPPOIConformanceTests, which generates golden vectors - canonical JSON and CBOR, encodings,
588
- the examples of section 6 of the specification, signed station proofs against a trust anchor, the documents of the run of
589
- the C# examples, and documents to refuse - and runs them against this library and the C# implementation alike. This
590
- repository vendors them in `tests/vectors` and runs them in `tests/conformance.test.ts`. The nightly check against the C#
591
- examples of WWCP_POI master moved there as well.
592
-
593
- The codecs of Vanaheimr are `@vanaheimr/cose` 0.10.0 and `@vanaheimr/metrological-cbor` 0.11.1: `@vanaheimr/cose` 0.9.0
594
- brought a CBOR codec 0.10 of its own next to the 0.11 this library uses, so that an app installed two copies of it; now
595
- there is one.
596
-
597
- The digest of a price statement leaves its signature value out, as WWCP_POI does since `dbf39b0` (SPECIFICATION.md 12.1):
598
- the SHA-256 of its signature input. The ECDSA twin `(r, n − s)` of a statement, which anybody can make of it, is the same
599
- statement, of the same digest (csharp-review 2); `PriceStatement.sameSignedContent` is gone. The test vectors
600
- are those WWCPPOIConformanceTests generates for it, with a new run of the C# examples at `dbf39b0`
601
- (`tests/vectors/csharp-dbf39b0`).
602
-
603
- The price statements of an entity are one chain in whichever spelling they name it, as WWCP_POI keys them since
604
- `4680d99` (SPECIFICATION.md 12.1 to 12.3, `RoamingNetworkHistory.ChainOf`): by its type and its identification without
605
- `*`, whitespace around it, or case (`priceStatementChainOf`, `PriceStatement.chain`), so that `DE*GEF*E1*1` and
606
- `DE*GEF*E11` are one EVSE and a split view between them is seen. `follows` compares chains; `verifyPriceStatements`
607
- refuses a statement naming an entity of the station otherwise than the roaming network does, before its signature is
608
- looked at; `verifyCheckpointPriceStatements` refuses a checkpoint naming two statements of one entity and sequence with
609
- different digests, in the order of its tree, both in the words of WWCP_POI. A sequence is at most 2^53 − 1
610
- (`MAX_PRICE_STATEMENT_SEQUENCE`), as it was read here already. Not ported, as they bound what a server takes: a statement
611
- more than 1,000,000 beyond the current one of its entity, and the statements a server keeps per operator for its next
612
- checkpoint. The golden vectors hold both refusals (`tests/vectors/poi-refusals.json`, 75 vectors): the
613
- statement of example A10 naming its EVSE as `DE*GEF*E11`, checked with the proof of its station
614
- (`entityPriceStatement`), and checkpoint 4 naming a fourth statement of one sequence, in the other spelling and of
615
- another price (`checkpointPriceStatements`, its control the checkpoint and its statements as published).
616
-
617
- The holders of a quorum are counted as the trust anchor names them, as WWCP_POI does since `3e32f48` (SPECIFICATION.md
618
- 4.1, 8.6; csharp-review 1): a trust anchor names the keys per holder, the `@id` of its key or the array of the
619
- `@id`s of its keys (`TrustAnchor.holders`; in the URL the holders separated by `,`, the keys of one holder by `;`).
620
- `KeyStatement.verifyHolders` counts the holders as the verifier knows them and refuses a statement naming the trusted
621
- keys otherwise; a station proof, `OperatorTrust` and the signatures of checkpoints count an attestation or a signature
622
- for the holder of the trusted identity key that certified its key. A change set naming a key of a party with another
623
- holder than before is refused.
624
-
625
- A change set is identified by its content, as WWCP_POI does since `bc3b1bd` (SPECIFICATION.md 4, 5.2;
626
- csharp-review 4): its `@id` is the SHA-256 of its canonical JSON with its `@context`, without `@id` and
627
- signatures (`changeSetIdentification`), so that nobody commits another change set in the place of one. `ChangeSet.parse`
628
- refuses a change set identified otherwise, and one naming change sets in `metadata.rebasedFrom` it was not rebased from;
629
- `Commit.parse` reads the change set of a commit as well and refuses a commit with one parent whose change set has no
630
- changes or the identification of a merge. The test vectors are those of a new run of the C# examples at `bc3b1bd`
631
- (`tests/vectors/csharp-bc3b1bd`).
632
-
633
- A change set gives its new values as the content holds them, as WWCP_POI requires since `80e4ccc` (SPECIFICATION.md 2,
634
- 4; csharp-review 10): a new value is applied as it is given, no longer written anew as the C# model would, and
635
- one that names a JSON-LD context in what it embeds (custom data aside, which keeps it), gives identified elements, keys,
636
- certificates or the lists of section 2 out of their order or twice, or a timestamp not UTC to the millisecond is
637
- refused. An old value is compared as it is given. The test vectors are those of a new run of the C# examples at
638
- `80e4ccc` (`tests/vectors/csharp-80e4ccc`).
639
-
640
- An attestation gives its `validUntil`, at most 24 hours after its `signedAt`, as WWCP_POI requires since `be07037`
641
- (SPECIFICATION.md 8.5, 8.6, 12.1; csharp-review 7): `Attestation.parse` refuses one without or valid longer
642
- (`MAX_ATTESTATION_VALIDITY`), so that none is valid forever. The `signedAt` of an attestation or a price statement is the
643
- choice of its signer: the signing key of an attestation counts when it is valid when it attested and at the time the proof
644
- is checked, and a price statement is current no longer than its key.
645
-
646
- A version naming twice an identification WWCP_POI keeps unique within the roaming network is refused, as WWCP_POI refuses
647
- it (`DuplicateGridConnectionPoint` to `DuplicateEnergyMeterTemplate` of RoamingNetwork.cs): grid connection points, energy
648
- meters, parking garages, spaces, sensors, space groups, space templates and products, transparency software releases and
649
- certificates, energy meter templates and groups, the EVSE, charging station and charging pool groups of operators and
650
- providers, and charging tariff offers, compared as their C# types compare them, without case but parking products, the
651
- groups by the prefix of their party, `DE-GDF` as `DE*GDF` (`duplicateIdentification`). `RoamingNetwork.parse` checks every
652
- version it reads, also the one a change set leads to: before, an operator could add a group of the identification a group
653
- of the provider of its prefix has, and its change set was taken with the after ETags this library calculates. The golden
654
- vectors `version-twice-*` of WWCPPOIConformanceTests hold both implementations to it, refused in the same words (`reason`).
655
-
656
- The roaming network operator signs in place of a party without signing keys, e.g. the certificates of a conformity
657
- assessment body or the grid connection points of a grid operator not taking part, by a signing key that may sign
658
- `party`, as the examples certify its keys, as WWCP_POI does since `36bb176` (SPECIFICATION.md 4.1; csharp-review 13):
659
- `checkOwnership` counts its signing keys that may sign `party` besides those that may sign the usage of the change.
660
- A change set taken before is taken still.
661
-
662
- A change set naming `rebasedFrom` with a change, but an `Add` or `AddElement`, without `oldValue` is refused, as WWCP_POI
663
- refuses it since `1717ee2` (SPECIFICATION.md 4; csharp-review 12): only old values tell that a change set
664
- committed meanwhile changed what it changes, which such a rebase may have overwritten. One not rebased is read as before.
665
- The golden vector `changeSet-rebased-without-old-value` holds both implementations to it.
666
-
667
- A model of charging stations or energy meters names its `hardwareRevisions` and `firmwareVersions`, and a charging
668
- station and an energy meter one `hardwareRevision` and one `firmwareVersion` of it, as WWCP_POI does since `7e0b0ac`
669
- (csharp-review 16); a certificate (`ConformityCertificate`, the `certificates` of a conformity assessment body)
670
- is for a model of charging stations or of energy meters, in the hardware revisions and firmware versions it covers, and
671
- names its components, the transparency software and, of a station model, the energy meters it was issued with; a
672
- station and a meter name the certificates of their models in `certificateIds`, and the `transparencySoftwareStatus` of
673
- a meter names a release and its legal status, no certificate, as WWCP_POI does since `987fa26` (csharp-review 17).
674
- Change sets change them, effective members take them of their templates, and a certificate is unique within the
675
- network. `verifyChargingStation` gives the `certificates` the station and its meters name, each with `forThisModel`:
676
- whether it is for the model, the hardware revision and the firmware version of what names it; the `transparencySoftware`
677
- of a meter gives the certificates naming its release, no longer a certificate of its own. The test vectors are those of
678
- a new run of the C# examples at `987fa26` (`tests/vectors/csharp-987fa26`).
679
-
680
- A charging station signing measurements itself names its `serialNumber`, `transparencySoftwareStatus`,
681
- `signatureMethods` and `publicKeys` as an energy meter does, and the `modelURL` of its model; an EVSE names its
682
- `iso15118Certificate`, as WWCP_POI does since `52f1172`. Change sets change them; an effective station takes the model
683
- URL, the releases and the signature methods of its templates besides its own, never the serial number, the public keys
684
- or the ISO 15118 certificate of the single device; and a change set giving the releases of a station or a meter out of
685
- the order of their identifications, or one twice, is refused. `verifyChargingStation` gives the releases the station
686
- names among its `transparencySoftware`, proven by the certificates of the station; each entry names what names it,
687
- `namedBy` as of a certificate, in place of `energyMeterId` (`NamedTransparencySoftware` in place of
688
- `MeterTransparencySoftware`). The test vectors stay those of the run at `987fa26`: no station of its examples signs
689
- measurements itself.
690
-
691
- The first checkpoint of an epoch takes signatures of the algorithms of its epoch and of the epoch before, no other, as
692
- WWCP_POI does since `65e658d` (SPECIFICATION.md 11.3; csharp-review 14): a history started by
693
- `startNextEpoch` knows the profiles of the epoch it closed, one started from a snapshot those its `epoch` names as
694
- `previousProfiles` (`History.previousEpochProfiles`), and those of the first epoch are the default ones; without them
695
- it takes the algorithms of its own epoch alone. A snapshot of the epoch 2 naming profiles of the epoch before is refused.
696
-
697
- A certificate the roaming network operator adds or changes in place of a conformity assessment body without signing keys
698
- says so, `"statedBy": "roamingNetworkOperator"`, as WWCP_POI requires since `65e658d` (SPECIFICATION.md 4.1;
699
- csharp-review 15): a change set adding or changing a certificate whose `statedBy` does not say who signs it, as
700
- the version before has the keys of the body, is refused. `verifyChargingStation` gives each certificate with `statedBy`,
701
- `ConformityAssessmentBody` or `RoamingNetworkOperator`, in place of `bodyHasKeys`, which could not tell them apart once
702
- the body had keys. The test vectors stay those of the run at `987fa26`: none of its examples has a third epoch or a
703
- certificate stated in place of a body.
704
-
705
- A version naming a hardware revision, firmware version or serial number WWCP_POI refuses is refused, read or led to by a
706
- change set, as WWCP_POI reads them since `c31e5e3` and `3672b06` (`HardwareRevision`, `FirmwareVersion`,
707
- `SerialNumber`): a text as it is given, not empty and neither beginning nor ending with whitespace as `Char.IsWhiteSpace`
708
- tells it, of charging stations, energy meters and their templates, of the models of their manufacturers, and of
709
- certificates and the energy meters they were issued with, each once in a model, a certificate and such an energy meter
710
- (`ModelVersions.Of`), in the words of WWCP_POI (`invalidVersionText`, which `RoamingNetwork.parse` checks with
711
- `duplicateIdentification`, and for certificates `invalidCertificates`, below). Whether one is of the model of what names
712
- it stays with the references, which this library
713
- leaves to WWCP_POI. The test vectors stay those of the run at `987fa26`, whose versions name none it refuses; the vectors
714
- `version-text-*` of WWCPPOIConformanceTests, its head so changed, hold both implementations to it, refused in the same
715
- words (`reason`).
716
-
717
- The golden vectors of WWCPPOIConformanceTests hold, besides the run of the C# examples, small histories it signs itself
718
- (`tests/vectors/poi-histories.json`), replayed step by step in `tests/conformance.test.ts`: who signs in place of a
719
- party without keys, who states a certificate, old values and rebases, keys expired and removed, new values as the
720
- content holds them, `lastChange` of what embeds a change, and three epochs (csharp-review 3, 8, 10, 12 to 15). Ten of
721
- them - the references a version holds, the groups of e-mobility providers, grid connection points - this library
722
- leaves to WWCP_POI, and does not run. They found a deviation of this library (D8 of WWCPPOIConformanceTests): a change
723
- set signed, besides by its owner, by a key of a party not valid at its `createdAt` was taken here, and refused by
724
- WWCP_POI. Every signature by a key of a party of the version is now by one valid at the `createdAt` of the change set,
725
- also one besides the signatures of the owners, refused otherwise in the words of WWCP_POI (`TryGetSigners`); only a new
726
- identity key, which proves its possession, may be one valid later (SPECIFICATION.md 4.1).
727
-
728
- A version naming a certificate WWCP_POI refuses to read is refused, read or led to by a change set, as
729
- `ConformityCertificate` and `CertifiedEnergyMeter` of WWCP_POI read them (`src/network/certificates.ts`,
730
- `invalidCertificates`, which `RoamingNetwork.parse` checks after `invalidVersionText`): for exactly one model, with its
731
- manufacturer; at least one hardware revision and one firmware version, also of each energy meter it was issued with, as
732
- an object of its manufacturer and its model; an absolute `documentURL`, a legislation not empty, timestamps with an
733
- explicit offset, `notBefore` not after `notAfter` to the tick; at least one release, each once (compared ordinally once
734
- trimmed, as WWCP_POI does), none both verified and compatible (compared without case); energy meters of a charging
735
- station model only, each model once, compared without case. Refused in the words and in the order of WWCP_POI: first
736
- what it reads as the arguments of a certificate, by their path, then what its constructor checks. Its versions as
737
- texts moved there from `invalidVersionText`, in the same words. The vectors `version-certificate-*` of
738
- WWCPPOIConformanceTests found that this library took all of them (D9) and hold both implementations to it. Whether a
739
- certificate's model, versions, releases and legislation are those of the network stays with the references, which this
740
- library leaves to WWCP_POI; an unknown property, also of a certificate, this library does not refuse.
741
-
742
- A history starts from a snapshot of another server only anchored in the trust anchor of the roaming network, fail
743
- closed, as a peer of WWCP_POI starts since `ff2c56f` (SPECIFICATION.md 10.5, *Anchored*; `History.fromSnapshot` with
744
- `SnapshotOptions`): a snapshot of a checkpoint by the checkpoint, signed in the role of the roaming network operator by
745
- signing keys of as many holders as the trust anchor and the key statement of its version require, each certified by a
746
- trusted identity key of its holder, valid when it signed and at `at` (`ChargingStationProof.verifyCheckpoint`, as
747
- `Verify(Checkpoint, TrustAnchor)` of WWCP_POI); a snapshot of the head by the `attestations` it carries since then (8.5),
748
- which must attest the root of its head as for a proof of the root (`ChargingStationProof.ofRoot`, as `VerifyHead` of
749
- WWCP_POI). Without a trust anchor no snapshot is taken, unless the caller takes it `unanchored` explicitly, e.g. a genesis
750
- whose key statement it verifies against the trust anchor itself; an archive segment replays its base so, anchored by the
751
- checkpoint of its end. Before, a snapshot of the head carrying attestations was refused, and every snapshot taken
752
- unanchored. An attestation of a snapshot that cannot be read is refused, not left out.
753
-
754
- A signed commit or checkpoint, also the commit of a snapshot, is refused without a verifier of its signatures, as
755
- WWCP_POI does (`CheckSignatures`): before, its signatures were taken unchecked when `HistoryOptions.verifyCommit` or
756
- `verifyCheckpoint` was left out (O3 of WWCPPOIConformanceTests). `verifyCommit` checks the commit of a snapshot as well,
757
- its version before unknown there (`before` undefined).
758
-
759
- QI-Digital documents, the proof of concept of WWCP_POI (`WWCP_POI3/RoamingNetwork/QIDigital`, as of `6a0f96e`, which
760
- example A14 uses), are read and checked, not signed (`src/qidigital/`): the digital calibration certificate of a charging
761
- station or an energy meter (`DigitalCalibrationCertificate`), the digital certificate of conformity of a model or, of a
762
- module of quality assurance, of the quality system of a manufacturer (`DigitalCertificateOfConformity`), and the digital
763
- declaration of conformity of a model (`DigitalDeclarationOfConformity`). Each is refused in the words and the order of
764
- WWCP_POI: only its own properties, its context, timestamps exactly as signed, numbers as numbers, what its constructor
765
- checks after its properties were read. Its signature input and its digest are what WWCP_POI writes again of what it read
766
- (`Content()`), not the text as given: numbers without trailing zeros, a whole one as an integer, identifications trimmed,
767
- a charging station as `DE*GEF*S1`, `mainSigner` false, an empty list or text and a revision 0 as WWCP_POI writes them.
768
- Checked against a roaming network as WWCP_POI checks them: the issuer a party of it, its key one that may sign the
769
- document and valid when it signed and at `at`; the notified body with its number, notified for the legislation; the
770
- model, its manufacturer, revisions and versions; the charging station or energy meter of a calibration certificate as it
771
- is at runtime, with what its templates give it, and with the serial number the roaming network gives it, not the one the
772
- certificate names (csharp-review 23). And against each other: a declaration and each certificate it names, a certificate
773
- and the item of a calibration certificate it covers. A text in languages of no language, one that is no text and a
774
- language given twice are refused, not left out, as WWCP_POI refuses them since `bbe8fb8`: left out, they would travel
775
- unsigned with a signed document.
776
- A URL is read as Hermod reads it as far as a document URL needs it: trimmed, https unless it names a scheme, of http or
777
- https with a host.
778
-
779
- The test vectors are those of a new run of the C# examples at `33447ac` (`tests/vectors/csharp-33447ac`), the first with
780
- example A14: the QI-Digital documents of the meter of DE*GEF*E2*1 as WWCP_POI signed them, with ML-DSA-65 by the PTB and
781
- EM-LAB and with ES256 by SAALE, are checked against QA as example A14 published it, refused changed in the words of
782
- WWCP_POI, and written again as WWCP_POI wrote them. EM-LAB is a member of QA since, so the head is revision 8 of epoch 2.
783
- `examples/validate-examples.ts` checks example A14 as its `Validate()` does, and starts from the snapshots of examples A4,
784
- A5 and A11 anchored in the trust anchor, from the genesis and the live snapshot of example A12 unanchored, as the
785
- examples do: since the anchored start it refused them.
786
-
787
- The golden vectors hold the QI-Digital documents of example A14 too (`tests/vectors/poi-qidigital.json`), run in
788
- `tests/conformance.test.ts` as WWCPPOIConformanceTests runs them against WWCP_POI: the four documents verified against
789
- QA, the declaration of conformity against the certificates it names, both certificates covering the meter of
790
- DE*GEF*E2*1, a calibration certificate of the charging station DE*GEF*S2, signed by the generator with a key QA patched
791
- gives EM-LAB, and 38 refusals, each with its control, of documents or of QA patched. Both implementations refuse a text
792
- of no language, or one that is no text (D10 there, fixed in WWCP_POI `bbe8fb8`), and the calibration certificate of a
793
- station against one of another serial number, or of none (csharp-review 23).
794
-
795
- EV Taler, phase 1 of the concept of WWCP_POI for paying for charging with GNU Taler (`concepts/EVTaler.md`), as of
796
- `ec3dace`: a charging station operator names its merchant at a GNU Taler merchant backend and the exchanges whose coins it
797
- accepts (`evTaler`, `evTalerMerchantOf`), refused where WWCP_POI refuses it and in its words: base URLs of HTTPS without
798
- user, query and fragment, ending in '/', read as Hermod reads them; keys as GNU Taler writes them (`isTalerPublicKey`);
799
- at least one exchange, each once, of an ISO 4217 currency of Styx and with a name. A change of it needs a signing key
800
- that may sign the new usage `payments`. Payment options are an open set of names: any nonempty text, none twice whatever
801
- its case, united with those of the templates of a charging pool without case; a charging pool or template naming EVTaler
802
- needs the merchant of its operator, after the last change of a change set. An app reads the EV Taler tariff of an EVSE
803
- and the exchanges in its currency from the proof of its station (`evTalerTariffOf`, `evTalerExchangesAt`), and what
804
- drivers see, "EV Taler (Example Exchange, EUR)" (`paymentOptionName`). The JSON, the CBOR and the tree of a version with
805
- a merchant are those WWCP_POI writes: `tests/evTaler.test.ts` holds the ETags WWCP_POI3 calculates of QA with the merchant
806
- of its EVTalerTests. Not yet: golden vectors of WWCPPOIConformanceTests, and a station proof with a merchant as WWCP_POI
807
- signs it (example A15 came after the frozen run). Nothing here takes or makes a payment.
808
-
809
- The keys of the parties of a roaming network are read where it is read, as WWCP_POI reads them wherever it reads a
810
- party (`POIPartyKeys.TryParse` and `Check`), in its words (D12 of WWCPPOIConformanceTests): before, a party's keys were
811
- only read where they were used, so a version naming an unknown usage of a party's key was taken. Every key as
812
- `POIPublicKey.parse` reads it, none twice, none both a signing and an identity key, no identity key naming usages; the
813
- parties in the order WWCP_POI reads them, the charging station operators last; the identity keys of the roaming network
814
- operator in the same words (`src/network/partyKeys.ts`). Checked against WWCP_POI3 at `ec3dace` itself, on 47 versions
815
- of QA with keys valid and not, of every kind of party: 40 are read as WWCP_POI3 reads them, in the same words; 5 both
816
- refuse, in other words of `POIPublicKey.parse` within the same wrapper (a key without identification or key, not
817
- Base64, expiring before it becomes valid, of another JSON-LD context); 2 name a `notAfter` WWCP_POI takes and writes
818
- otherwise (a date only, an offset), refused here as every timestamp not written as POI writes it. `poi-refusals.json` of
819
- WWCPPOIConformanceTests vendored, 89 vectors: the 13 of EV Taler and `version-key-usage-unknown`, D12 closed.
820
-
821
- A key is read in the words and order of `POIPublicKey.TryParse` of WWCP_POI (`POIPublicKey.parse`): its identification
822
- and key as texts, then its JSON-LD context, then both given ("A public key needs its identification and its key!"); a
823
- key .NET can not read as Base64 in the words of .NET, one it reads but writes otherwise as not canonical; "Invalid COSE
824
- key: …", "A public key must not contain private parts!", "The holder of a public key must be a text!"; a key expiring
825
- before it becomes valid where the constructor of WWCP_POI refuses it, after its holder and usages, before its
826
- thumbprint. Checked against WWCP_POI3 at `ec3dace` itself, on 45 more versions of QA with keys valid and not: 34 in its
827
- words. Still other words: a key of CBOR WWCP_POI3 can not read (the words of `@vanaheimr/cose` and
828
- `@vanaheimr/metrological-cbor`, not of their C# originals), a timestamp both refuse ("Invalid value for 'not before'!"
829
- there). Refused here, taken and written otherwise by WWCP_POI3: a key not in deterministic CBOR, a timestamp not written
830
- as POI writes it, a `notBefore` or `notAfter` of null.
831
-
832
- `poi-histories.json` of WWCPPOIConformanceTests `0734641` vendored: 8 replays of EV Taler in a history the generator
833
- signs, who may sign the merchant of DE*GEF (a signing key for `payments`; the roaming network operator only in place
834
- of a party without signing keys) and a charging pool naming EVTaler with and without it. A change needing signatures is
835
- named as WWCP_POI names it, with the property it changes, e.g. "Change 0: UpdateProperty of the
836
- ChargingStationOperator 'DE*GEF' ('evTaler') needs a signature by …".
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+
6
+ ## 0.4.0 (prepared; not yet published)
7
+
8
+ Change sets and commits as WWCP_POI3 writes them again, and EV Taler phase 3: a change is read, applied and refused as
9
+ WWCP_POI3 reads, applies and refuses it, each party read again right after the change that changed it; a change set and
10
+ its commit are identified and signed over what WWCP_POI3 writes of them again, not over them as given; a charging
11
+ station operator names where drivers pay ad hoc (`webPaymentURL`) and ask for offers (`offersURL`), and a charging pool
12
+ naming EV Taler the secure dynamic QR code of its stations; a profile of contracts binds the price of a session started
13
+ through an e-mobility provider. Each of them is probed against WWCP_POI3, what differs named below, and the vectors of
14
+ WWCPPOIConformanceTests `448fc73` hold, none deviating, on the run of WWCP_POI `76d3653`. See [Migration](#migration)
15
+ before upgrading: versions and change sets 0.3.0 took are refused, others it refused are taken, and the words of many
16
+ refusals change.
17
+
18
+ - `poi-evtaler.json` of WWCPPOIConformanceTests `448fc73` vendored byte for byte and run in
19
+ `tests/conformance.test.ts`: the amounts of EV Taler (concepts/EVTaler.md of WWCP_POI, section 9 and open point 3,
20
+ no part of SPECIFICATION.md), 57 vectors. The gross rates of the simple profile of a charging tariff or why it has none
21
+ (4 priced, 14 not), what a step costs, rounded up to the minor unit of the currency, or that the tariff does not price
22
+ it (17 and 2), and the price of a session, rounded to the nearest, half away from zero (20). No change of the
23
+ library: it calculates all of them as WWCP_POI3 does.
24
+
25
+ - The authentication mode SecureDynamicQRCode, as WWCP_POI `76d3653` reads it (EV Taler, decision D8 of
26
+ concepts/EVTaler.md): the channels the stations of a charging pool give the URL of their secure dynamic QR code over,
27
+ with its time-based one-time password of OCPP 2.1, `BLE`, `Display` and `NFC`, at least one, each once, by its exact
28
+ name and in the ordinal order of their names; no other type names channels. A charging pool naming the payment option
29
+ EVTaler, as it is at runtime with what its templates give it, names it too, its own or of a template, where its
30
+ operator has an EV Taler merchant: "The charging pool '…' names the payment option 'EVTaler' without the
31
+ authentication mode 'SecureDynamicQRCode': an EV Taler session names the one-time password its EVSE gives right
32
+ then!", checked as the merchant rule, when an operator is read and after the last change of a change set, so that a
33
+ change set adds the merchant, the payment option and the code in any order. Probed against WWCP_POI3 at `76d3653`: the
34
+ 43 versions of ZProbeNetworks alike in verdicts and words, one in a JToken text of C# with CRLF only. Example A15
35
+ prints the authentication modes of Jena Paradies as the C# example does.
36
+
37
+ - The frozen run is `csharp-76d3653` (WWCP_POI `76d3653`, Hermod `c5ff091b`, Styx `a6b9f9fb`, .NET 11), AT
38
+ `2026-11-12T21:55:00.000Z`, in place of `csharp-c4c3c5a`: revision 11 of epoch 2 is "Jena Paradies: EV Taler, with a
39
+ secure dynamic QR code" now, which the old run, the payment option alone, no longer is. Vendored byte for byte from
40
+ WWCPPOIConformanceTests `a81db5f` with it: `poi-history.json`, `poi-refusals.json` (6 versions of the code refused or
41
+ held otherwise), `poi-histories.json` (Jena Paradies of history-parties with the code; 3 replays new: the payment
42
+ option without the code refused, the code first, the code removed while named refused) and `poi-qidigital.json`.
43
+ The tests on QA hold the values of the new run, mapped by content; what WWCP_POI3 calculated of changed versions
44
+ (after ETags of the change sets of element, embedded, infrastructure and release changes, the ETags and
45
+ identifications of the preconditions and change sets, the versions with an EV Taler merchant) calculated again by
46
+ WWCP_POI3 at `76d3653` on the very same versions, all alike.
47
+
48
+ - `poi-histories.json` of WWCPPOIConformanceTests `d552a8a` vendored, its other vectors those of `674b173`: 4
49
+ replays of a change set WWCP_POI3 writes again, it and its commit identified and signed so (`791219c`): a
50
+ precondition with ` DE*GEF `, `preconditions: []` and `metadata: {}` taken; the precondition identified as given
51
+ refused, "is not identified by its content".
52
+
53
+ - `poi-refusals.json` and `poi-histories.json` of WWCPPOIConformanceTests `674b173` vendored, its other vectors those of
54
+ `3bb2928`: 11 vectors this library took and refused as given already, of where drivers ask for offers (`offersURL`,
55
+ refused and held as written otherwise, signed for `payments`) and of preconditions held, changed and of no entity.
56
+
57
+ - A commit is identified and signed over its change set as WWCP_POI3 writes it again (`RoamingNetworkCommit.TryParse`),
58
+ as its change set is since `7f5fbe5`: a change set with `metadata: {}`, `preconditions: []` or a precondition with
59
+ ` DE*GEF `, its commit identified over it as written again, this library refused, "The commit identification '…' is
60
+ not the one of its content, '…'!", where WWCP_POI3 takes it (reported by WWCPPOIConformanceTests). A change set is
61
+ read in the order WWCP_POI3 reads it, identified by its content last, so that one wrong in two ways is refused for
62
+ the same: its roaming network and createdAt first, then its changes, ETags, description, metadata, preconditions and
63
+ signatures. With the change proposed for WWCP_POI3 on top of `62390ca` (a change set names only what it is written
64
+ with, its createdAt and roaming network as it writes them again), WWCP_POI3 refuses what this library refuses, a
65
+ property it does not read, a `createdAt` written otherwise, a roaming network given as a number, in its words: the 88
66
+ change sets of ZProbePreconditions alike in verdicts and words.
67
+
68
+ - `poi-verifier.json` and `chargy/POIPrices` of WWCPPOIConformanceTests `3bb2928` vendored, its other vectors those of
69
+ `86dfc09`: 3 vectors of the profile of contracts (WWCP_POI `b414296`), `sessionPrice-contract-profile` bound,
70
+ `sessionPrice-adhoc-contract-profile` and `sessionPrice-subscription-mode` unsupported, and their documents
71
+ `contract-profile.chargy`, `adhoc-contract-profile.chargy` and `subscription-mode.chargy`.
72
+
73
+ - EV Taler, phase 3, as WWCP_POI `62390ca` takes it: the merchant of an operator (`evTaler`) names where drivers ask
74
+ for offers, if the operator makes them, its optional `offersURL` (`EVTalerMerchant.offersURL`), a base URL as its
75
+ merchant base URL is one: HTTPS, ending in '/', without user, query and fragment, "The offers URL '…' must be an HTTPS
76
+ URL! (Parameter 'OffersURL')"; read after the exchanges and checked after the merchant base URL, as WWCP_POI3 reads
77
+ and checks it; written as Hermod writes it, or refused, "The offers URL '…' is written as '…'!". Without it, a merchant
78
+ is written as before, byte for byte. `evTalerSessionPrices` gives what prices a session at an EVSE, as the backend of
79
+ an operator makes its steps (`EVTalerContractTerms.TryGetSessionPrices`): its EV Taler tariff, the price statement
80
+ current when the session begins that prices it, one of an EVSE before the station's before the pool's, and the
81
+ elements. Probed with 29 merchants on WWCP_POI3 at `62390ca` (Hermod `c2f4bdde`): the same ETags, refusals and
82
+ words, but for the line ends of a JSON object named in a refusal, which .NET writes as the platform does.
83
+ - A change set is identified by its content as WWCP_POI3 writes it again (`ToJSON`), not only by its changes: its
84
+ preconditions each with its entity type and identification trimmed, also to nothing, without its context and any
85
+ other property, none where there are none (`preconditions: []`); its roaming network trimmed; its description as Styx
86
+ reads a text in languages (each language in lower case, a text of no language it reads left out), none where it is
87
+ JSON null or has no text left; no metadata where they hold nothing. This library identified them as given: a change
88
+ set with ` DE*GEF*P1 ` in a precondition, identified so, it took, which WWCP_POI3 refuses as "not identified by its
89
+ content". Its signatures are verified over it as written again, as WWCP_POI3 signs and verifies them
90
+ (`ToJSON(Embedded: true, IncludeSignatures: false)`), not over it as given, also of the changes written again since
91
+ `eb607f9`. Read as WWCP_POI3 reads it: the texts of a precondition trimmed, empty ones read and refused when applied,
92
+ "Precondition 0: the ChargingPool '' does not exist!", where this library said "Invalid precondition 0!"; its
93
+ roaming network trimmed and compared without case (`RoamingNetwork_Id`), refused in the words of WWCP_POI3 where it
94
+ is missing, JSON null or blank ("Invalid 'roaming network identification'!"); a description or metadata that is no
95
+ JSON object refused ("JSON property 'description' is not an object!", "The metadata must be a JSON object!"), which
96
+ this library took. Probed with 88 change sets with ZProbePreconditions on WWCP_POI3 at `c4c3c5a`, each identified as
97
+ given and as WWCP_POI3 writes it: the same verdicts and words but for those this library refuses where WWCP_POI3
98
+ takes them: a property it does not know of a change set, a change or a precondition, which WWCP_POI3 leaves out; a
99
+ `createdAt` written otherwise than `yyyy-MM-ddTHH:mm:ss.fffZ`, which it writes so; a roaming network given as a
100
+ number, which it reads as its text.
101
+
102
+ - A profile of contracts, `chargy-contract-gross-rates/v1` (`CONTRACT_GROSS_PRICE_PROFILE`), binds the price of a
103
+ session started through an e-mobility provider, of a contract with it, as WWCP_POI `b414296` takes it: a price
104
+ agreement whose `chargingMode` is `contract` is `bound` of it, of the same rates as the simple profile, and its price
105
+ is the one the CPO charges the provider; what the provider charges its customer stays unproven. Contract mode of the
106
+ simple profile stays `unsupported`, as the vector `sessionPrice-contract-mode` has it ("Contract mode requires an
107
+ additional profile"), and so does `adhoc` of the profile of contracts.
108
+ - `examples/validate-examples.ts` checks example A16 as its `Validate()` does: from the file the app kept alone, against
109
+ the trust anchor of epoch 2, the station proof at the time the session began; both steps in their contract terms
110
+ against it and the price statement of the EVSE; the start and the end value in OCMF with the key of the meter the
111
+ proof gives, by an OCMF verifier of its own as `VerifyOCMF` of `exampleA16.cs`; and the settlement statement, signed
112
+ by `DE*GEF` with its key for payments. Refused: another price, the end value left out, step 1 of another merchant,
113
+ step 2 naming the end value as the start. What it prints is what the C# example printed in a run of WWCP_POI
114
+ `c4c3c5a`, line for line. `npm run examples` checks it on the run vendored here, `csharp-c4c3c5a` (below), the nightly
115
+ runs of WWCPPOIConformanceTests on a new run of WWCP_POI master. README lists every test file.
116
+ - The test vectors of a new run of the C# examples, `tests/vectors/csharp-c4c3c5a` in place of `csharp-045e180`:
117
+ WWCP_POI `c4c3c5a` with Hermod `850cab3d` and Styx `21d00134`, examples A1 to A16, MakeProofs at
118
+ `2026-11-10T05:54:00.000Z` (`tests/at.ts`). The head of epoch 2 is revision 11, as before; example A16 adds the file
119
+ the app keeps of its EV Taler session, `driver/evidence-DE_GEF_E1_1-evTaler.json`. With them, of
120
+ WWCPPOIConformanceTests `228041d`, byte for byte: `poi-history.json` (the price statement of that file new, 9),
121
+ `poi-refusals.json` (307, 33 of them with a reason now), `poi-histories.json` (89 replays, 13 new of the web payment
122
+ page signed for payments), `poi-qidigital.json`, `poi-verifier.json` (109, `sessionPrice-flat-fee` bound) and
123
+ `chargy/POIPrices` with `flat-fee.chargy`. The tests on QA hold the values of the new run, mapped by content, a key
124
+ a test picks by its position the key now there; those WWCP_POI3 calculated of changed versions (the after ETags of 75
125
+ element, embedded, infrastructure and release change sets, the ETags of preconditions, the versions with an EV Taler
126
+ merchant) are calculated again by WWCP_POI3 at `c4c3c5a`, and this library takes the very same versions.
127
+ `tests/conformance.test.ts` 825.
128
+ - `poi-histories.json` of WWCPPOIConformanceTests `86dfc09` vendored, its other vectors those of `228041d`: 4 replays of
129
+ the roles every commit must be signed in (POI3-ISSUES.md S24), `required-role-*`, naming `requiredSignatureRoles`,
130
+ which `tests/conformance.ts` now gives the options of the history of a replay: a commit signed in the role
131
+ `roamingNetworkOperator` only by a key of DE*GEF, which the version does not certify for it, or not in it at all, is
132
+ refused; one key that may sign in it is enough. And `offer-of-a-provider-overlapping`, two offers of an e-mobility
133
+ provider overlapping at its EVSE group, refused (9.4). 94 replays, `tests/conformance.test.ts` 830.
134
+ - For development, Vite 8.3.7 (Rolldown 1.2.18) and typescript-eslint 8.72.2. TypeScript stays at 6.0:
135
+ typescript-eslint 8.72.2 still requires a version below 6.1.
136
+ - The simple profile of gross rates (`chargy-gross-rates/v1`) takes a flat fee without a step size, as a FLAT price
137
+ component has none ("A FLAT price component has no step size!"), and as WWCP_POI `c4c3c5a` takes it: it asked every
138
+ component for a step size of at least one, so that a price agreement of a flat fee was `unsupported`, never `bound`,
139
+ with no gross rates. A flat fee with a step size, also `null`, is none of the profile; energy and time need one of at
140
+ least one, as before. `grossRates` is a module of its own, not exported, tested apart.
141
+ - A change is read as WWCP_POI3 reads it and refused in the words of its refusal after "Change N: ", as WWCP_POI3
142
+ refuses it with the change proposed for it on top of `8b0580f` (`RoamingNetworkChange.Refusal`, shared by its
143
+ constructor and `TryParse`; without it WWCP_POI3 says "Invalid change 0: …", and where its constructor refuses, the
144
+ words of an `ArgumentException`, e.g. "Value must not be empty. (Parameter 'EntityId')"). Its texts: anything but a
145
+ text refused, also JSON null, "Change 0: the kind must be a text!", where this library said "The kind of change 0 must
146
+ be a text!", in the order WWCP_POI3 reads them, all before the element path; the kind trimmed, also when nothing is
147
+ left, "Change 0: unknown kind ''!"; a parent type or identification empty, "Change 0: its parent entity type and
148
+ identification must not be empty!", which this library refused only when applied, as not the parent; the property
149
+ name of a segment of an element path trimmed. The context of a change is checked, "Change 0: the JSON-LD context of
150
+ the roaming network change must be '…'!", also JSON null, where this library took any. A change set is identified by
151
+ its changes as WWCP_POI3 writes them again (`ToJSON(Embedded: true)`): without the context of a change or of a
152
+ segment, without an empty element path, the property name of a segment trimmed; this library identified it by them as
153
+ given, taking a change set WWCP_POI3 refuses as not identified by its content. Probed with 77 change sets with
154
+ changes refused when read and the 630 of the earlier probes: the same verdicts as WWCP_POI `8b0580f`, the same words
155
+ as with the proposed change, but of a change or a segment naming a property it does not have: WWCP_POI3 does not
156
+ refuse it when read, and then refuses the change set as not identified by its content, this library "Change 0 must
157
+ not name the property 'foo'!".
158
+ - A new value not given as the content holds it is refused as WWCP_POI3 refuses it (`TryHeldAsGiven`): after the party
159
+ the change changed is read again, not before, and naming what the content holds. "Change 0: the property 'name' of the
160
+ charging pool 'DE*GEF*P1' is not given as the content holds it, '{"en":"x"}'!" of an `UpdateProperty`; "Change 0: the
161
+ EVSE group 'DEGEFEG9' to add is not given as the content holds it, '{"@id":"DE*GEF*EG9",…}'!" of an `Add`, the
162
+ entity without its lastChange; "Change 0: 'chargingConnectors['1']' of the EVSE 'DE*GEF*E1*1' is not given as the
163
+ content holds it, '…'!" of an element change, its element or the property of its element; "… is given as '[]', which
164
+ the content does not hold at all!" of a value it holds none of. This library named where it was written otherwise and
165
+ why, "Change 0, the charging pool 'DE*GEF*P1': 'name' is not given as the content holds it: a text in languages names
166
+ each language as Styx writes it, in lower case, with a text!", the place of an element by its index, before reading
167
+ the party again: of an `Add` with an identification written otherwise and an invalid content, it named the
168
+ identification, WWCP_POI3 the content. `notWritten` tells what the content holds where it is written otherwise,
169
+ `heldAs` makes a new value so. Also held as WWCP_POI3 holds them, where this library took them as given: flags,
170
+ `currentTypes` and `chargingModes`, in the order of their values, each once (`POIJSON.FlagsToJSON`); data licenses
171
+ given empty, which the content leaves out, of every entity; a hotline, trimmed; a language given by its number, also
172
+ with a sign, by its name. A new value of a property is checked as a value too, a URL or a currency, as within an
173
+ object, where a version written otherwise refused it. Probed against WWCP_POI `8b0580f` with 88 change sets on the QA
174
+ network, one with connectors on an EVSE: the same verdict and words for each, but a change refused as WWCP_POI3's
175
+ constructor refuses it ("Invalid change 0: …"; refused so since, see above), and, as before, who must sign identity keys of the roaming network
176
+ operator given twice. The earlier probes: the same verdicts, now also the words of the
177
+ spellings probe's `Add` with an identification written otherwise and an invalid content.
178
+ - A change whose old value is no longer there is refused naming the value it found, as WWCP_POI3 names it
179
+ (`POIJSON.ContentText`): its canonical JSON, "absent" for none. "Change 0: the property 'name' of the charging pool
180
+ 'DE*GEF*P1' is no longer the expected old value, but '{"en":"Jena Paradies"}'!", of every kind of entity, its
181
+ embedded objects and the roaming network operator; "…'chargingConnectors['3'].format' of the EVSE 'DE*GEF*E1*1' is no
182
+ longer the expected old value, but '"CABLE"'!" of an element change; "Change 0: the property 'name' is no longer the
183
+ expected old value, but '{"en":"Quality Assurance"}'!" of the roaming network itself, which is no entity named, as
184
+ WWCP_POI3 names it, where this library named it "the property 'name' of the roaming network 'QA'". A change of a
185
+ complete entity names, as before, only whether it exists. Probed against WWCP_POI `8b0580f` with 59 change sets on
186
+ the QA network, one with connectors on an EVSE, of every kind of entity, of properties there and absent, of an old
187
+ value expecting one absent, of the roaming network itself and of the second change of a change set: the same words
188
+ for each. The earlier probes: the same verdicts and words as before, now also the old values.
189
+ - What a change set changes is read again right after each change, as WWCP_POI3 reads it (`TryApplyRegistryChange`,
190
+ `TryApplyInfrastructureChange`, …): the party the change changed, a charging station operator for its
191
+ infrastructure, templates, groups, tariffs, offers, parking spaces and energy meter templates and groups, the party
192
+ embedding a model, a release, a certificate, a grid connection point or parking data, the roaming network operator.
193
+ A change making one a party WWCP_POI3 does not read is refused in its words, "Change 0: Invalid hotline phone number
194
+ '42'!", not after the last change without them; a change set making a party invalid and valid again is refused, as
195
+ WWCP_POI3 refuses it, where it was taken before; of two changes making a party invalid, the first is named, not what
196
+ is read first. The groups, offers and payment options of a charging station operator are still checked after the last
197
+ change, as WWCP_POI3 checks them there. The properties of the roaming network itself are taken as WWCP_POI3 takes
198
+ them: "Change 0: invalid name: A multi-language text must be a JSON object!", "the data source must be a nonempty
199
+ text!" (a blank one taken before), "the custom data must be a JSON object!", "a roaming network has no keys of its
200
+ own: they are the keys of its RoamingNetworkOperator!", "the property 'foo' does not exist!" for one it has not,
201
+ "Add of the roaming network itself is not supported yet!". Its operator: "the operator of a roaming network is
202
+ changed, but neither added nor removed!", "the roaming network operator 'XYZ' does not exist!", "a roaming network
203
+ operator of this roaming network belongs to RoamingNetwork 'QA', not to …". Probed against WWCP_POI `8b0580f` on the
204
+ QA network with 88 change sets, every kind of party and of what it embeds, invalid values, invalid values made valid
205
+ again, two of them in either order: the same verdict for each, the same words for each but two, which this library
206
+ words otherwise for every kind of entity, not as it reads them again: an old value no longer there, which WWCP_POI3
207
+ names, "…, but '{"en":"Quality Assurance"}'!" (named so since, see above), and a new value the content does not
208
+ hold (refused so since, see above). The 116 change sets of the
209
+ spellings probe: four more in WWCP_POI3's words, the rest as before. The reading again costs about 1 s of the 80-odd
210
+ seconds of the conformance tests.
211
+ - The web payment page of a charging station operator, a charging pool, a charging station and their templates, as
212
+ WWCP_POI `848cc52` has it (`webPaymentURL`): the URL where an EV driver pays ad hoc, without an e-mobility provider,
213
+ and starts a session, found in the static data before standing in front of a station; not the URL in the dynamic QR
214
+ code of a station, which is of one EVSE and never part of them. Read and written as WWCP_POI reads and writes it,
215
+ refused when no text or no URL ("Change 0: Invalid web payment URL '42'!"); taken by the members of a template
216
+ without their own (effective pools and stations); changed and removed by change sets. Whoever changes it redirects
217
+ payments: a change of it needs a signing key that may sign `payments`, and a pool, a station or a template added
218
+ with one, a pool by one of its stations as well, a key that may sign `infrastructure` and `payments`, "… that may
219
+ sign 'infrastructure' and 'payments'!". The usage of a change is now a set of usages a key names all of; the roaming
220
+ network operator still signs everything it owns by a key for `party`. Commits with a web payment page were refused
221
+ before. Probed against WWCP_POI `8b0580f` on the network of its `WebPaymentURLTests` and 23 change sets, unsigned and
222
+ signed by keys for `payments`, `infrastructure`, `party` and both: the same verdict and the same signers for each, the
223
+ same content after each of the 19 taken, the two invalid values in the same words since every change is read again.
224
+ - `examples/validate-examples.ts` checks example A15 as its `Validate()` does: the station proof of DE*GEF*E1*1 the app
225
+ gets, against the trust anchor of epoch 2, with the EV Taler merchant of DE*GEF; the EV Taler tariff of the EVSE and
226
+ the exchanges in its currency the app shows, those in another currency not offered there; the merchant base URL and
227
+ the merchant key every offer must name; and the proof refused once its merchant key is changed to the key of an
228
+ exchange, the root its attestations do not attest the one WWCP_POI names. What it prints is what the C# example
229
+ printed in the run `csharp-045e180`, line for line.
230
+ - A charging station, EVSE or energy meter given twice within one charging station operator is refused, as WWCP_POI
231
+ `e76e5d0` refuses it: "The charging station 'DE*GEF*S1' of the operator 'DE*GEF' must not be given twice!", the same
232
+ for an EVSE and an energy meter. Before, a station in two of its pools, or an EVSE in two of its stations, was taken,
233
+ by WWCP_POI3 too; proofs, price statements and evidence name them by their identifications alone. Three refusals of
234
+ `poi-refusals.json` of WWCPPOIConformanceTests `bfc8fa0` hold them, one of each, in the words both give.
235
+ - Data licenses that differ in case only are given twice (Hermod `f7b00ef2`): "OWN-1.0" and "own-1.0", "CC BY 4.0" and
236
+ "cc by 4.0".
237
+ - Refusals speak as WWCP_POI `74f1d8e` and Styx `21d00134` speak them: "An" before A, E, I and O, "A" before any other
238
+ letter ("A public key of …", "An EU vehicle category of …", "An ENERGY price component needs a positive step size!");
239
+ numbers without trailing zeros ("22000.5 W", not "22000.50 W"); "Invalid opening times: " once, not twice; "Besides
240
+ its grid operator, the grid connection point '…' can only be maintained by a charging station operator or a parking
241
+ operator, not by …!".
242
+ - The refusals of changes say their articles as WWCP_POI `8b0580f` says them: "the property 'maxPower' of an EVSE
243
+ group can not be changed!", "of an energy meter model", "of an EVSE template", not "of a EVSE group". A group, a
244
+ template, a tariff or a charging pool added below another parent than its charging station operator is refused in
245
+ WWCP_POI's words: "Change 0: an EVSE group 'DE*GEF*EG9' belongs to ChargingStationOperator 'DE*GEF', not to
246
+ ChargingPool 'DE*GEF*P1'!", not "the EVSE group … is added to its charging station operator 'DE*GEF', …". Probed
247
+ against WWCP_POI `8b0580f` on 29 change sets of every kind of entity: 27 in the same verdict and words. WWCP_POI
248
+ refuses one change when it reads the change set, in other words, and takes the parent 'de*gef' for 'DE*GEF' (see
249
+ below).
250
+ - A change names the infrastructure of a charging station operator in every spelling its identification type considers
251
+ equal, as WWCP_POI3 finds it (`StoredId`): `DEGEFP1`, `DEGEF*P1` or `degefP1` for the charging pool `DE*GEF*P1`,
252
+ `DE*GEF*E11` for the EVSE `DE*GEF*E1*1`, `de*gef` for its operator, as the entity, as its parent and as the parent it
253
+ is added to. The operator is the one its identification names, as its type reads it, and signs the change, whether it
254
+ exists or not ("… needs a signature by a signing key of the ChargingStationOperator 'DE*XXX'!"). Before, only the very
255
+ text it is kept with was found, and other spellings were refused. Refused in WWCP_POI3's words: "invalid charging pool
256
+ identification 'de*gef*p1'!", "the charging station operator 'DE*XXX' of the charging pool 'DE*XXX*P1' does not
257
+ exist!", "adding the charging station '…' needs its ChargingPool as its parent!", "the charging pool '…' of the
258
+ charging station '…' does not exist!", and an entity added under an identification its type writes otherwise, "the EVSE
259
+ group 'DEGEFEG9' to add is not given as the content holds it, '{"@id":"DE*GEF*EG9",…}'!".
260
+ - The kind, the entity type and the entity identification of a change are read trimmed, as WWCP_POI3 reads them
261
+ (`ParseMandatoryText`), and the identification of a change set is of its content so read: a change set naming
262
+ `' DE*GEF*P1'` is identified as one naming `'DE*GEF*P1'`, and refused, "is not identified by its content", when
263
+ identified by the text untrimmed.
264
+ - Probed against WWCP_POI `8b0580f` on 190 change sets, as `TryApplyPendingChangeSet` reads and applies them and
265
+ `TryCheckCoSigning` names their owners: the same verdict for each, the same content after each of the 94 taken (after
266
+ ETags of `CreateChangeSet`), the same words but for seven. Four have two faults at once, refused in another order (an
267
+ EVSE removed whose station then exceeds its maximum power and whose group names it; a tariff without elements added
268
+ under another spelling); a tariff without elements is refused without "Change 0: ", as this library reads the
269
+ operator once after all changes; and WWCP_POI refuses an empty identification and a parent type without its
270
+ identification when it reads a change set, as "Invalid change 0: …" (refused so since, see above). Who must sign a change of a party without
271
+ signing keys was named otherwise in four of them (see below).
272
+ - A change of a party without signing keys, which the roaming network operator signs in its place (`SignerOf`), is
273
+ refused unsigned as WWCP_POI3 refuses it (`TryCheckCoSigning`): naming the roaming network operator, by as many
274
+ holders as its quorum needs, not the party, which has no key to sign with, e.g. "… needs signatures by signing keys of
275
+ 2 holders of the roaming network operator 'OCC'!"; signed by a key of it that may sign neither the usage of the
276
+ change nor `party`, with both, "… needs a signature by a signing key of the roaming network operator 'DE*RNO' that
277
+ may sign 'certificates' or 'party'!". Of several owners, e.g. of a grid connection point, the roaming network
278
+ operator is named with its quorum too, its identity keys as well, once each, and "signatures" where one owner needs
279
+ more than one holder. Probed against WWCP_POI `8b0580f`: the 190 change sets above and 11 more name who must sign
280
+ them alike, word for word.
281
+ - The key statement of the roaming network operator is refused as its constructor in WWCP_POI3 refuses it: an identity
282
+ key given twice ("An identity key of a key statement must not be given twice!") before the order of its identity keys
283
+ is looked at, and a signature by one identity key given twice ("An identity key signs a key statement once."), which
284
+ was taken before; its validity in C#'s words. Probed against WWCP_POI `74f1d8e` on 36 versions of QA, the same
285
+ verdict and words for each. Four vectors of `poi-refusals.json` follow the new words.
286
+
287
+ ### Migration
288
+
289
+ - A charging pool naming the payment option `EVTaler`, its own or of a template, whose operator has an EV Taler
290
+ merchant, names the authentication mode `SecureDynamicQRCode` with at least one of the channels `BLE`, `Display` and
291
+ `NFC`: a version or change set without it, which 0.3.0 took, is refused. No other authentication mode names channels.
292
+ - A change set is identified, and its signatures are verified, over it as WWCP_POI3 writes it again, and so is its
293
+ commit: one identified over what it was given as, with an untrimmed text, a context or another property in a
294
+ precondition, an empty `preconditions`, `metadata` or element path, a description in languages written otherwise,
295
+ which 0.3.0 took, is refused, "is not identified by its content"; one identified as WWCP_POI3 writes it, which 0.3.0
296
+ refused, is taken. Producers identify and sign change sets and commits as WWCP_POI3 does.
297
+ - A change set is refused where a change makes a party one WWCP_POI3 does not read, also when a later change makes it
298
+ valid again, which 0.3.0 took; the change is named, "Change N: …", in the words of WWCP_POI3. A change naming a
299
+ JSON-LD context other than its own, a description or metadata that is no JSON object, or a new value the content does
300
+ not hold as given is refused as WWCP_POI3 refuses it.
301
+ - Messages of refusals checked by their text change where 0.3.0 worded them on its own: a change refused when read
302
+ ("Change 0: the kind must be a text!"), an old value no longer there, which now names the value found, a new value
303
+ not given as the content holds it, who must sign a change of a party without signing keys, the articles of entities
304
+ ("An EVSE group", "of an energy meter model") and numbers without trailing zeros.
305
+ - A charging station, an EVSE or an energy meter given twice within one charging station operator, and data licenses
306
+ that differ in case only, which 0.3.0 took, are refused. So is a key statement of the roaming network operator with
307
+ two signatures by one identity key.
308
+ - A change names the infrastructure of a charging station operator in every spelling its identification type considers
309
+ equal, `DEGEFP1` for `DE*GEF*P1`: change sets 0.3.0 refused for another spelling are taken, and an entity added under
310
+ an identification its type writes otherwise is refused.
311
+ - Versions and commits with a web payment page (`webPaymentURL`) or an offers URL of an EV Taler merchant (`offersURL`),
312
+ which 0.3.0 refused, are taken. A change of a web payment page needs a signing key that may sign `payments`, and an
313
+ entity added with one a key that may sign `infrastructure` and `payments`.
314
+ - A price agreement of a flat fee, which 0.3.0 gave as `unsupported`, is `bound` of the simple profile of gross rates
315
+ where its FLAT component has no step size; one with a step size, also `null`, is none of the profile. A price
316
+ agreement whose `chargingMode` is `contract` is `bound` of the new profile `chargy-contract-gross-rates/v1`
317
+ (`CONTRACT_GROSS_PRICE_PROFILE`), and stays `unsupported` of the simple profile.
318
+ - New in the API, nothing removed: `evTalerSessionPrices` and its type `EVTalerSessionPrices`, the `offersURL` of an
319
+ EV Taler merchant, `CONTRACT_GROSS_PRICE_PROFILE`.
320
+ - The dependencies are those of 0.3.0, `@vanaheimr/cose` ^0.15.0 and `@vanaheimr/metrological-cbor` ^0.12.0. The
321
+ minimum runtime remains Node 22.18, and the package remains ESM-only. The package is 3.9 MB unpacked, where 0.3.0
322
+ was 3.7 MB.
323
+
324
+
325
+ ## 0.3.0 (2026-11-08)
326
+
327
+ WWCP_POI3 as the measure: a roaming network is read as WWCP_POI3 reads it and refused in its words, change sets apply
328
+ what WWCP_POI3 applies (the entities a party embeds, element changes, preconditions, no lastChange going back),
329
+ histories require the signature roles WWCP_POI requires and take a checkpoint with the signatures that verify, and EV
330
+ Taler phase 2 checks the steps and the settlement of a session. Each of them is probed against WWCP_POI3, what differs
331
+ named below, and the vectors of WWCPPOIConformanceTests `215023a` hold, none deviating. See [Migration](#migration)
332
+ before upgrading: versions, change sets and signatures 0.2.0 took are refused, and the types of three properties change.
333
+
334
+ - A roaming network is read as WWCP_POI3 reads it (`RoamingNetwork.TryParse` and the TryParse of every entity) and
335
+ refused in its words and its order: JSON types, mandatory properties, values, identifications, names of enumerations,
336
+ currencies, URLs, timestamps, texts in languages, data licenses, and what the constructor of each entity refuses of
337
+ what it read (`src/network/reading.ts`, `poiJSON.ts`, `poiValues.ts`). Every kind of entity it reads: the roaming
338
+ network and its operator; charging station operators with their pools, stations, EVSEs, connectors, energy meters,
339
+ templates, groups, tariffs, tariff templates, groups and offers, brands and parking spaces; manufacturers of charging
340
+ stations and energy meters with their models, transparency software manufacturers with their releases, conformity
341
+ assessment bodies, e-mobility providers, grid operators with their grid connection points, parking operators.
342
+ - The references the constructor of the roaming network checks, and the identifications it keeps unique among them,
343
+ interleaved in its order (`references.ts`): manufacturers, models, hardware revisions and firmware versions, releases,
344
+ certificates, grid connection points, charging stations, parking spaces and maintainers the network does not have, a
345
+ station or an energy meter contradicting its model, a key of two parties. After the last change of a change set, bare,
346
+ in the order of `RoamingNetwork.ChangeSets.cs`, a reference the version before resolves named as one the change set
347
+ withdrew. They replace the separate checks of identifications, versions, certificates, EV Taler and keys of parties.
348
+ - 1268 versions probed against WWCP_POI3 at 89d2e02 are taken and refused alike, in the same words, but for nine agreed:
349
+ five identifications of the roaming network and its operator given as numbers, which keep the words of this library
350
+ for WWCP_POI3 to take, and four messages of the CBOR and COSE libraries.
351
+ - Values WWCP_POI3 writes otherwise than given are refused in the words of its written form (`writtenForm.ts`): a
352
+ number, a boolean or a text given as another JSON type it reads it of (`"0.390"` as 0.390, `"TRUE"` as true, 5 as
353
+ `"5"`), coordinates of texts and booleans, durations in seconds in whole ticks of 100 ns without trailing zeros (1.50
354
+ as 1.5), the width and height of an image; opening times as Styx writes them, `24/7` always, hours `HH:mm`, periods
355
+ UTC, empty lists and free texts left out; NUTS codes in upper case, a list written otherwise compared element by
356
+ element with the one WWCP_POI3 writes; addresses with their countries by their alpha-3 codes, their street, postal
357
+ code and time zone trimmed, their other texts as given and left out where empty, their languages in lower case,
358
+ nothing else. A country is read as `Country.TryParse` of Styx reads it, of its 251 countries (`countries.ts`): by its
359
+ alpha-2 or alpha-3 code in any case, its numeric code, its calling code or one of its names, and refused where it
360
+ names none. Of 164 more versions probed against WWCP_POI3 at 89d2e02, 163 are taken and refused alike, in the same
361
+ words; the one left gives a number with an exponent, which the probe writes otherwise before WWCP_POI3 reads it.
362
+ - Two charging tariff offers of an operator must not overlap (`offers.ts`, `OverlappingOffers` of WWCP_POI3, SPECIFICATION.md
363
+ 9.4): covering one EVSE, for some of the same sessions (ad hoc, or contracts of a provider both name, or one names
364
+ none), at some of the same times within their `notBefore` and `notAfter`, both inclusive, and their opening times in the
365
+ time zone of the EVSE's charging pool, with what its templates give it, else UTC; local times as `TimeZoneInfo`
366
+ converts them, one a change to daylight saving time skips the first after it, one it gives twice the one of standard
367
+ time; exceptional closings
368
+ close, exceptional openings open. Refused in its words, at the first EVSE in the ordinal order, as the constructor of
369
+ the operator refuses it, also after a change set. After a change set also two offers of an e-mobility provider the
370
+ change set changed that overlap and did not in the version before (`NewProviderOfferOverlap`); by the changes of
371
+ others they may. Of 64 versions probed against WWCP_POI3 at d1c41ca, all are taken and refused alike, in the same
372
+ words, also those of time zones a charging pool may not name.
373
+ - The time zone of a charging pool or a charging pool template is one of a region of the IANA time zone database, as
374
+ the database writes it, which this platform knows (`timeZones.ts`, `POITimeZones` of WWCP_POI3 `d1c41ca`,
375
+ SPECIFICATION.md 9.4, D17 of the conformance tests): `Europe/Berlin`, also an older name the database keeps for one,
376
+ `Asia/Kolkata` as `Asia/Calcutta`, or of a part of a region, `America/Argentina/Buenos_Aires`. No name of another
377
+ system (`W. Europe Standard Time`), none without a region (`CET`, `UTC`, `Etc/GMT-1`, `US/Eastern`), none in another
378
+ case (`europe/berlin`), none the platform does not know: refused in the words of WWCP_POI3 as the constructor of the
379
+ pool or template refuses it, trimmed, after its images, no longer taken as UTC, nor a Windows name as its IANA name.
380
+ The case is the one Temporal gives (Node 26); without Temporal (Node 22 and 24) Intl gives it of a name, but of an
381
+ older name only the name it stands for, so an older name is then taken in any case (`Asia/KOLKATA`). Of 92 versions
382
+ probed against WWCP_POI3 at `d1c41ca`, 90 are taken and refused alike, in the same words; the two left name
383
+ `America/Ciudad_Juarez` and `Antarctica/Troll`, which .NET on Windows does not find, and this library takes, as Node
384
+ knows them: databases of different age know different names (SPECIFICATION.md 9.4). `poi-refusals.json` of
385
+ WWCPPOIConformanceTests vendored again without the marker of D17: its 10 time zones are refused by their reasons.
386
+ - A measurement public key is a key as BouncyCastle 2.7.0 makes one, as WWCP_POI3 checks it (`measurementPublicKey.ts`,
387
+ `MeasurementPublicKey.TryCheck`), refused in its words and those of BouncyCastle: a SubjectPublicKeyInfo of DER
388
+ (`ber.ts`), of RSA, also of PSS and OAEP, its modulus odd, of no prime factor up to 743, of 16384 bits at most and no
389
+ prime, its exponent odd (`rsa.ts`); of EC on the 84 named curves BouncyCastle knows (`curves.ts`, taken from it), a point
390
+ it decodes, compressed, uncompressed or hybrid, on its curve and of its order (`ecc.ts`); of Ed25519 and Ed448 a point
391
+ of no small order; of X25519, X448, ML-DSA, ML-KEM and SLH-DSA of their lengths; of DSA in its group. Its certificates
392
+ DER, as X509CertificateParser reads them, the first of the key, each one issued by the next one, its signature of
393
+ ECDSA, DSA, RSA of PKCS #1 v1.5 or PSS, Ed25519, Ed448 or ML-DSA verified with the key of the next one
394
+ (`certificates.ts`). Of 242 keys and chains of BouncyCastle and variants of them probed against WWCP_POI3 at 89d2e02,
395
+ 237 are taken and refused alike, in the same words; taken as given, where WWCP_POI3 checks them: points on curves over
396
+ binary fields, explicit curve parameters, keys of Diffie-Hellman, ElGamal, GOST and DSTU. Not verified, so refused: a
397
+ certificate signed with GOST, SM2 or SLH-DSA.
398
+ - Other words: decimals in messages invariant and line breaks `\n`, where WWCP_POI3 writes them in the culture of its
399
+ machine and `\r\n`; timestamps `DateTimeOffset` reads and the written form refuses, refused as invalid; an error of an
400
+ entity changed by a change set without the `Change N:` of WWCP_POI3.
401
+ - A price statement reads its price status as WWCP_POI3 reads it (`ChargingPrices.TryParse`), in its words and order,
402
+ under its "Invalid price status of the price statement:": the prices of each charging tariff once, the tariff named
403
+ by its identification `{operatorId}*T{suffix}` (SPECIFICATION.md 2, 12.1, D16 of the conformance tests), their
404
+ elements, at least one, as a tariff has them, no property or JSON-LD context besides. A statement naming another text
405
+ is no price statement: the price evidence of a session holding one has invalid signatures, as WWCP_POI3 gives it
406
+ (`sessionPrice-foreign-tariff`), not valid ones of a tariff outside the references of its EVSE.
407
+ - The nine vectors of WWCPPOIConformanceTests `fde40c9` vendored, `poi-refusals.json` of 129 vectors (a version naming a
408
+ charging tariff not as `{operatorId}*T{suffix}`), and `poi-verifier.json`, new here: 106 checks of the verifier
409
+ extensions, meter keys, historical meter evidence, session prices, trust transitions, keys of charging documents,
410
+ trust policy identifications, digests of key statements, offer hashes and publication receipts, against the POI
411
+ fixtures of ChargyWebApp at `fc513cd`, vendored as `tests/vectors/chargy-fc513cd`. Run in `tests/conformance.test.ts`
412
+ as WWCPPOIConformanceTests runs them against this library: a refusal after its control, by its reason; none deviates.
413
+ - `poi-refusals.json` of WWCPPOIConformanceTests `7d43d4b` vendored (`586a339` before a rebase there), of 142 vectors:
414
+ 13 price statements whose price status is none WWCP_POI3 reads (without one; its prices not an array; the prices of a
415
+ tariff not an object; a property or a JSON-LD context besides, of the status or of the prices of a tariff; a tariff
416
+ not named `{operatorId}*T{suffix}` or named twice, also in another spelling; its elements empty, one not an object or
417
+ without price components), refused as both read them, in the words both give. None deviates.
418
+ - `poi-refusals.json` of WWCPPOIConformanceTests `ed91741` vendored, of 273 vectors: 131 versions built from the tests
419
+ of this library and read as WWCP_POI3 reads them, 59 of reading, 18 of references, 33 of written forms, 11 of offers,
420
+ 10 of measurement public keys, refused in the words both give. None deviates. Two of them, offers of `Europe/Berlin`,
421
+ WWCP_POI3 takes where .NET runs without ICU, as it then finds no IANA time zone and compares them in UTC (O5 there).
422
+ - `@vanaheimr/cose` 0.14.0, the codec still `@vanaheimr/metrological-cbor` 0.12: a key of a party of key type RSA (3) is
423
+ read as Styx reads it, identified by its RFC 9679 thumbprint over n and e, and taken as WWCP_POI3 takes it; 0.12.0
424
+ refused it, "Invalid COSE key: The curve of a COSE key must be an integer!". POI names no signature algorithm of RSA
425
+ (SPECIFICATION.md 4), so such a key signs nothing this library verifies, as before.
426
+ - `@vanaheimr/cose` 0.15.0, the codec still `@vanaheimr/metrological-cbor` 0.12. It reads and verifies the
427
+ countersignature of RFC 8152 and holds a key an X.509 certificate writes as `id-RSASSA-PSS` to RSASSA-PSS, as Styx
428
+ `7642de3d` does. This library uses neither, and reads and verifies its keys and signatures as with 0.14.0. For
429
+ development, Playwright 1.65.0 and Vite 8.3.6. TypeScript stays at 6.0: typescript-eslint 8.72.1 requires a version
430
+ below 6.1, and its typed linting needs the compiler API that TypeScript 7 does not ship.
431
+ - `poi-refusals.json` of WWCPPOIConformanceTests `d388929` vendored, of 283 vectors: 10 versions naming a time zone of a
432
+ charging pool or a charging pool template that is none of a region of the IANA time zone database, refused by WWCP_POI3
433
+ since `d1c41ca` and taken by this library, marked as its deviation (D17 there).
434
+ - A change set changes the entities a party embeds, as WWCP_POI3 applies them (`apply.ts`; RoamingNetwork.OfferChanges.cs,
435
+ ProviderChanges.cs, GridChanges.cs and ParkingChanges.cs): the charging tariff offers of charging station operators
436
+ and e-mobility providers, the EVSE, charging station and charging pool groups of e-mobility providers, the grid
437
+ connection points of grid operators, the parking garages, spaces, sensors, space templates, space groups and products
438
+ of parking operators and the parking spaces of charging station operators. Added to the party an `Add` names as its
439
+ parent, found by its identification as its type compares it, e.g. a parking product with case, the properties a change
440
+ set may change, in the words of WWCP_POI3; with their timestamps, but a grid connection point and a parking product,
441
+ which have none. Owned by the party embedding them (4.1), the offers by keys that may sign `tariffs`, the others
442
+ `infrastructure`; a grid connection point by its maintainer, its maintainer changed by it, its grid operator or the
443
+ identity keys of the roaming network operator; also after the earlier changes of the change set, which may add or
444
+ remove them or their party. 0.2.0 refused them as not supported. Of 107 change sets probed against WWCP_POI3 at
445
+ `d1c41ca` (`CreateChangeSet`), the 50 it takes lead to the same content here, and the 57 it refuses are refused here
446
+ too, 34 in the same words; the others in the words of the entity read again without `Change N:`, of the change as
447
+ WWCP_POI3 parses it, or of the written form, where WWCP_POI3 names a property of infrastructure a change set may not
448
+ change.
449
+ - The `effectiveLastChanges` of a snapshot are read as WWCP_POI3 reads them (`TryTakeEffectiveFloors`,
450
+ POIEffectiveLastChange): absent or `null`, or an array of `{entityType, entityId, lastChange}`, a JSON-LD context only
451
+ its own, the timestamp in a form `DateTimeOffset` reads; refused otherwise in its words, "The effective last changes of
452
+ a snapshot must be a JSON array!", "Invalid effective last change '{…}' of a snapshot!". 0.2.0 took any. They are the
453
+ floors of the `lastChange` of effective members POI_API keeps at runtime (SPECIFICATION.md 8.8, point 3), found again
454
+ for what a change set changes; the effective tree takes the `lastChange` of an effective member without them (8.1),
455
+ as WWCP_POI3 does (`EffectiveChargingPoolsOfTheContent`), so this library reads them and keeps none.
456
+ - The preconditions of a change set hold, as WWCP_POI3 checks them (`preconditions.ts`; RoamingNetwork.Preconditions.cs,
457
+ SPECIFICATION.md 4): each names the ETag an entity still has in the version the change set is applied to, after its
458
+ `beforeETags` and before its changes, over the entity as the roaming network embeds it, with everything it embeds, its
459
+ canonical JSON or its deterministic CBOR, in the hash algorithm it names. The entity is found by its type and
460
+ identification as its type compares it, e.g. `DEGEFP1` as `DE*GEF*P1`, among the entities a change set addresses on
461
+ their own (`Flatten`), not a model, which is unique within its manufacturer only, as in WWCP_POI3. Refused otherwise in
462
+ its words, "The change set does not fit this version of the roaming network: Precondition 0: the ChargingPool
463
+ 'DE*GEF*P1' changed, it no longer has the ETag json:sha256:hex:…!", and a precondition not read as it reads one in a
464
+ change set, "Invalid precondition 1!". 0.2.0 refused change sets with preconditions as not supported. Of 79 change sets
465
+ with preconditions probed against WWCP_POI3 at `d1c41ca`, ETags of 40 entities of every kind among them, all are taken
466
+ and refused alike, 70 in the same words, the others refused when read, where the probe reads a precondition on its own.
467
+ - A change set changes elements, as WWCP_POI3 applies them (`elements.ts`, `apply.ts`; POIElements.cs and
468
+ RoamingNetwork.ElementChanges.cs): `AddElement`, `RemoveElement`, `ReplaceElement`, `UpdateElementProperty` and
469
+ `RemoveElementProperty` address an element of a property of an entity by their element path, e.g.
470
+ `chargingConnectors['1'].format`, an element of an array by its identification, as the type of the identifications of
471
+ the property compares them, e.g. `EVSEIds` without case, any other ordinally; an added element where the ordinal order
472
+ puts it, in `templateIds` at the end; an array left without elements removed. Each amounts to the `UpdateProperty` or
473
+ `RemoveProperty` of the property its path starts with, checked and owned as that change (4.1), its old value compared
474
+ with the element, or the property of the element, it addresses; keys and tree hash algorithms are changed as a whole
475
+ only. Element changes WWCP_POI3 does not make are refused when the change set is read: an element change without an
476
+ element path, another change with one, a new value of JSON `null`, of each kind of change what it does not take, e.g.
477
+ an `AddElement` with an old value, a segment of a path without a property name or with an empty identification.
478
+ 0.2.0 refused element changes as not supported. The properties of charging pools, stations, EVSEs, tariffs, groups and
479
+ templates a change set may change are those of WWCP_POI3 (RoamingNetwork.InfrastructureChanges.cs), refused otherwise
480
+ in its words, e.g. "the property 'chargingStations' of a charging pool can not be changed!", and an infrastructure
481
+ entity is named as WWCP_POI3 names it, e.g. "the charging tariff 'DE*GEF*T_AC'". Of 108 element changes probed against
482
+ WWCP_POI3 at `d1c41ca` (`CreateChangeSet`), the 47 it takes lead to the same content here, and the 61 it refuses are
483
+ refused here too, 31 in the same words; the others in the words of the entity read again without `Change N:`, of an
484
+ old value without the value found, or of a change as this library reads it. Of the 107 change sets of embedded
485
+ entities probed before, 93 are now in the same words.
486
+ - The elements of the prices of a charging tariff in a price statement are written as a charging tariff holds them
487
+ (SPECIFICATION.md 12.1, WWCP_POI3 `5a72313`, D18 of the conformance tests), as a version holds the elements of a tariff:
488
+ an element, a price component or restrictions naming a property their type does not define, a number given as a text,
489
+ the days of the week out of their order, the JSON-LD context of an embedded object are refused before the signature,
490
+ "'elements[0].restrictions.maxStateOfCharge' of the prices of the charging tariff 'DE*GEF*T_DC' is not written as a
491
+ charging tariff holds it: the content leaves it out!" under "Invalid price status of the price statement: ". A reader
492
+ not knowing a restriction would else price a session where its signer did not. The written form knows the
493
+ restrictions of an element now (`startTime` to `reservation`, as ChargingTariffRestrictions writes them): a version
494
+ naming another one, e.g. `maxStateOfCharge`, is refused, which 0.2.0 took. The price evidence of a session whose
495
+ statement names a restriction of OCPI, e.g. `max_kwh`, has invalid signatures now (`sessionPrice-conditional-price`).
496
+ `poi-refusals.json` and `poi-verifier.json` of WWCPPOIConformanceTests `c540c7f` vendored, 290 vectors, 7 of them new;
497
+ none deviates.
498
+ - `poi-histories.json` of WWCPPOIConformanceTests `bc4e5a0` vendored: the 13 replays this library left to WWCP_POI3
499
+ (`notApplicable`) hold here as well and are replayed: the references a version holds, e.g. `station-*`, `meter-*`,
500
+ `withdrawn-while-named-*`, checked since `813e1d7`, and the changes of the groups of a provider and of grid connection
501
+ points, applied since `1946013`, each refused in the words of its reason. `tests/conformance.test.ts` 760.
502
+ - `poi-histories.json` of WWCPPOIConformanceTests `70deea0` vendored: 14 replays of the entities a party embeds, as
503
+ `1946013` applies them, in `history-parties`: offers of an operator, signed for `tariffs`, not overlapping; an offer
504
+ and groups of a provider, signed by the provider, not by the operator of the same prefix; a grid connection point
505
+ handed on and named in one change set, by its new maintainer (csharp-review 9); parking data of a parking operator
506
+ and the parking space of a charging station operator, each signed by its own party. `tests/conformance.test.ts` 774.
507
+ - `poi-verifier.json` of WWCPPOIConformanceTests `aef4b38` vendored, with the price evidence of sessions it makes itself
508
+ (`tests/vectors/chargy/POIPrices`): the cases of `POIPrices` of ChargyWebApp `fc513cd`, every signature
509
+ deterministic. 23 `sessionPrice-*` vectors check the same as before on them; `-foreign-tariff` and
510
+ `-conditional-price` stay on the fixtures. Two new ones are taken: `-tariff-of-another-evse`, a genuine price of a
511
+ tariff of the pattern `{operatorId}*T{suffix}` the EVSE does not name, of which none applies, and
512
+ `-conditional-element`, an element restricted by `maxEnergy`, whose price is selected without a rate.
513
+ `tests/conformance.test.ts` 776.
514
+ - Transparency software releases and certificates are identified under their owners, as WWCP_POI3 `045e180` checks it
515
+ (SPECIFICATION.md 4.1, POI3-ISSUES.md S36, `ForeignTransparencySoftware`, `ForeignConformityCertificate`,
516
+ `IsIdentifiedUnder`): the `@id` of a release is the `@id` of its manufacturer, a colon and a name without one
517
+ (`GraphDefined:chargy-1.5.0`), of a certificate the `@id` of the body issuing it alike (`PTB:2026-0042`), the prefix
518
+ compared without case, both trimmed as read. A version holding one identified otherwise is refused before releases or
519
+ certificates given twice, in its words: "The transparency software release 'chargy-1.5.0' of the manufacturer
520
+ 'GraphDefined' is not identified as one of it: by 'GraphDefined:' and a name without a colon!"; so is a change set
521
+ leading to one, after the unknown parking spaces and before the releases given twice. References name them as before.
522
+ Of 38 versions and change sets probed against WWCP_POI3 at `045e180`, all are taken and refused alike, in the same
523
+ words. `tests/releaseIdentifications.test.ts`.
524
+ - The test vectors of a new run of the C# examples, `tests/vectors/csharp-045e180` in place of `csharp-33447ac`: WWCP_POI
525
+ `045e180`, examples A1 to A15, MakeProofs at `2026-11-05T19:46:00.000Z` (`tests/at.ts`). Releases and certificates
526
+ under their owners (`GraphDefined:chargy-1.5.0`, `PTB:2026-0017`, `PTB:2026-0042`); example A15 names the EV Taler
527
+ merchant of DE*GEF, so the head of epoch 2 is revision 11, while `QA.json` stays revision 8, as example A14 left it.
528
+ With them `poi-history.json` (100 vectors), `poi-refusals.json` (296, 6 of them of S36), `poi-histories.json` (75
529
+ replays, 8 of them of S36, the histories naming releases and certificates under their owners) and `poi-qidigital.json`
530
+ of WWCPPOIConformanceTests `2d0780f`. The tests on QA hold the values of the new run; those WWCP_POI3 calculated of
531
+ changed versions (element and embedded changes, preconditions, EV Taler) are calculated again by WWCP_POI3 at
532
+ `045e180`, and this library takes the very same versions. `tests/conformance.test.ts` 794.
533
+ - Required signature roles, as WWCP_POI `12e993a` checks them (POI3-ISSUES.md S24): `requiredSignatureRoles` of a history,
534
+ the roles a commit must be signed in, refused before it is applied where it has no signature in one ("The commit '…'
535
+ is not signed in the role '…'!"), and once its signatures are verified where none of them is by a key the version it
536
+ leads to lets sign in that role ("… by a key that may sign in it!", `keyMaySignIn`): in the role of the roaming
537
+ network operator a signing key certified for it, in the role of a charging station operator a signing key of the
538
+ operator its signer names; leaders and witnesses are the verifier's. So also the commit of a snapshot.
539
+ `requiredCheckpointSignatureRoles`, the roles a checkpoint must be signed in ("The checkpoint '…' is not signed in the
540
+ role '…'!"). A signed commit without a verifier is refused before it is applied, as WWCP_POI does. Signatures of one
541
+ key in one role, e.g. the twin of an ECDSA signature (S11), are kept as given, each verified, and counted once by the
542
+ holder or witness they are of, as WWCP_POI `f7b8643` reads them too (SPECIFICATION.md 5.4): it kept the first alone
543
+ since `12e993a`, so a forged signature behind a genuine one of its key went unchecked. `tests/signatureRoles.test.ts`.
544
+ - A checkpoint is taken with the signatures that verify here, as WWCP_POI `399b7c8` takes it (POI3-ISSUES.md S39,
545
+ SPECIFICATION.md 10.2): `verifyCheckpoint` of a history returns those of its signatures that verify, and the history
546
+ keeps the first of each key and role of them; one that does not verify, invalid or by a key not trusted here, is left
547
+ out, not refused, so that nobody blocks a checkpoint by adding a signature, and a witness trusted elsewhere and not here
548
+ keeps none from it. The roles a checkpoint must be signed in are counted among them: before, a role of a signature
549
+ `verifyCheckpointSignatures` passed over counted ("The checkpoint '…' is not signed in the role '…' by a signature that
550
+ verifies here!", and why the first one was left out). `takeCheckpoint` and a snapshot of a checkpoint hold it with
551
+ those only (`Checkpoint.withSignaturesOnly`). `verifyCheckpointSignatures` returns them as `verified`. The type of
552
+ `verifyCheckpoint` is now `(checkpoint) => readonly RoleSignature[] | undefined`: `undefined`, every signature verifies,
553
+ as before; a callback of a block without `return` is typed `void` and is no longer one.
554
+ - A signature of a commit or checkpoint is read as WWCP_POI3 reads it (`RoamingNetworkCommitSignature.TryParse`): its role,
555
+ time, algorithm, key and value trimmed, its signer as given, and what it signs is what was read; refused when its role,
556
+ signer, algorithm, key or value is empty or whitespace alone (`String.IsNullOrWhiteSpace`), and so is a signature of a
557
+ change set whose algorithm, key or value is ("The … of a signature of … must not be empty!"). Before, this library took
558
+ them, and a role padded with whitespace was not the role.
559
+ - Required signature roles probed against WWCP_POI3 at `12e993a`, `f7b8643` and `399b7c8` (`CheckSignatures` of a
560
+ snapshot, `TryAppend`, `TryAddCheckpoint`), on QA of epoch 2: 1879 cases of roles required of a snapshot, a commit and
561
+ a checkpoint, signed in every role, by keys of the roaming network operator, of DE*GEF and of nobody, by signers named
562
+ in every form, with and without a verifier; a second signature of one key in one role, the same, forged or signed
563
+ later; checkpoints verified by a rule leaving signatures out, forged ones before genuine ones among them; signatures
564
+ read with texts empty, padded and of other types. At `399b7c8` all are taken and refused alike, the checkpoints held with
565
+ the same signatures, the roles in the same words, signatures read refused in the words of this library. Stricter than
566
+ WWCP_POI3 there, which took them: a signer given as a number, an `@context` or another property in a signature of a
567
+ commit or checkpoint, and in the commit of a snapshot an algorithm its epoch does not name. WWCP_POI `b43ff1a` refuses
568
+ them too (POI3-ISSUES.md S40), and `7c6f91c` refuses a signature read in the words of this library, a snapshot naming
569
+ why its commit is refused: probed again at `7c6f91c`, all 1879 alike, word for word.
570
+ - `poi-refusals.json` and `poi-histories.json` of WWCPPOIConformanceTests `215023a` vendored, 806 vectors there, 13 of
571
+ them new: 8 refusals of a signature of the genesis commit or of checkpoint 1 not in its one form (another property,
572
+ `@context`, a signer as a number, a role of whitespace, a signedAt without milliseconds); a replay starting from the
573
+ snapshot of the genesis of epoch 3, its commit signed with Ed448, which the epoch does not name; 4 snapshots of
574
+ checkpoint 0 anchored with a signature that does not verify or a witness the trust anchor does not know (taken, S39), a
575
+ key of one holder signing twice (refused) and with the other holder too (taken). None deviates.
576
+ - What a proof gives of a child array, as WWCP_POI `d35c780` tells it (S29): `givesCompletely`, whether an entity as a proof
577
+ gives it has a child array completely, as an array or none, or pruned or partially; `sizeOf`, how many children it has,
578
+ also where the proof gives only some or none of them. A size or index beyond what this platform holds, and an object as
579
+ the identification of the roaming network, were refused already.
580
+ - No lastChange goes back, as WWCP_POI `12e993a` stamps a change set (SPECIFICATION.md 4, "Timestamps", POI3-ISSUES.md
581
+ S5): with its createdAt, or the lastChange of the roaming network of the version it is applied to, where that is later,
582
+ as the lastChange of the network, of what it changes, adds and embeds, and as the created of what it adds without one.
583
+ Its keys are still checked at its createdAt; nobody is refused for it, only the ETags after it differ (`stampOf`). The
584
+ after ETags of the element and embedded change sets and of the preconditions of the tests, created before QA, are
585
+ those WWCP_POI3 at `12e993a` calculated for the very same changes (67 change sets, all alike). `poi-histories.json` of
586
+ WWCPPOIConformanceTests `249d667` vendored: the three rebased change sets of `history-parties`, created at 08:06:30 on a
587
+ head of 08:07:00, are stamped 08:07:00. `tests/lastChange.test.ts`: every change set of both epochs of QA, created
588
+ before its version, stamped with its lastChange.
589
+ - EV Taler, phase 2, as WWCP_POI `c2d39e5` checks it (concepts/EVTaler.md 5 and 7, `src/evTaler`): the contract terms of
590
+ a step of a session (`EVTalerContractTerms`: version 0, an order, an amount and a maximum fee as GNU Taler writes them,
591
+ the merchant, its exchanges, the times, an auto refund) and their step in `extra.wwcp` (`EVTalerStep`: the roaming
592
+ network, the EVSE, the session, the step after the order before, the EV Taler tariff, the price statement, what it
593
+ covers, from step 2 on the signed meter value at the start, an object as OCPP 2.1 sends it), checked against the proof
594
+ of the station: the operator's merchant and exchanges in the currency of the EV Taler tariff of the EVSE, the price
595
+ statement current when the session began, each step following the one before, the signed start value verified with
596
+ the measurement key of the meter or the station the proof gives, by a transparency software passed in; the amount of a
597
+ step recalculated where the session is priced by one element of the simple profile (`EVTalerRates`), as System.Decimal
598
+ calculates it (`NetDecimal`: 96 bits, 28 places, rounded half to even), rounded up to the minor unit. A `taler://pay`
599
+ URI (`EVTalerPayURI`) names the merchant of the operator. The settlement statement (`EVTalerSettlement`), signed by the
600
+ operator with a key that may sign `payments`, refused without both signed meter values, of the steps of the session,
601
+ its energy and times those of the values, its price recalculated, half away from zero. Read and verified, not signed.
602
+ Of 178 sessions, steps, pay URIs and settlement statements probed against WWCP_POI3 at `c2d39e5`, on station proofs it
603
+ made, all are taken and refused alike, in the same words. `tests/evTalerSessions.test.ts`, on the proof of example A15.
604
+ - EV Taler: a step in minutes costs what it covers, rounded up to the cent, not a cent more, as WWCP_POI `a374855`
605
+ calculates it: the quantity multiplied by the price of an hour before it is divided by 60. A decimal holds no rate of a
606
+ minute such as 0.40 / 60 exactly, and rounding up took its last digit for a cent: 60 minutes at 0.40 EUR an hour were
607
+ 0.41 EUR, 30 minutes 0.21 EUR. Found by the probe of this port. The 178 cases probed again against WWCP_POI3 with
608
+ `a374855`: all alike.
609
+ - `hermodURL` as Hermod reads a URL: no `_` in host names, an empty port, an IPv6 address checked.
610
+ - Reading a version takes about two to three times as long as with 0.2.0, e.g. 1.0 to 1.3 s for 3204 charging pools of
611
+ 12816 EVSEs, each energy meter with a P-256 key of its own, where 0.2.0 took 0.4 s. A check of a version finds the
612
+ members of the operators in their order, their effective charging stations and energy meters and the charging stations
613
+ parking spaces name once (`whileChecking`); the identifications read of texts and the measurement public keys checked,
614
+ the last 65536 of each, are remembered for the next version too, as a history names most of them again. A key of P-256
615
+ takes about 20 µs, a key of RSA about as long as one exponentiation with its modulus, a certificate of ECDSA a few
616
+ milliseconds.
617
+
618
+
619
+ ### Migration
620
+
621
+ - Versions WWCP_POI3 refuses and 0.2.0 took are refused now, in the words of WWCP_POI3; of the probed versions and the
622
+ test vectors, none WWCP_POI3 takes. Messages of refusals checked by their text change where 0.2.0 worded them on its
623
+ own, e.g. a roaming network with keys of its own.
624
+ - A price statement whose price status 0.2.0 took and WWCP_POI3 refuses, e.g. naming the charging tariff
625
+ `DE*POI*FOREIGN`, is refused before its signature is looked at.
626
+ - A charging pool or a charging pool template naming its time zone by a Windows name, an abbreviation or in another case
627
+ than the IANA time zone database, which 0.2.0 took, is refused: it names it as the database writes it, e.g.
628
+ `Europe/Berlin`.
629
+ - `HistoryOptions.verifyCheckpoint` returns the signatures of the checkpoint that verified, `readonly RoleSignature[]`,
630
+ or `undefined` for all of them, e.g. `checkpoint => verifyCheckpointSignatures(checkpoint, trust, options).verified`.
631
+ A callback of a block without `return` no longer compiles; one that throws still refuses the checkpoint. A history,
632
+ `takeCheckpoint` and a snapshot of a checkpoint hold it with those signatures only, its identification the same: what
633
+ an app keeps of a checkpoint it took may hold fewer signatures than it was given (S39).
634
+ - `Change.elementPath` is an array of `ElementPathSegment`, `{propertyName, elementId}`, in place of the JSON objects
635
+ given, and `ChangeSet.preconditions` an array of `Precondition`, `{entityType, entityId, eTag}`, both read as WWCP_POI3
636
+ reads them; the JSON of a change and of a change set stays in its `json`. Both types are exported, and so are
637
+ `MerkleTreeHead`, `PrunedTree` and `TreeNodeKind`, which the API used already.
638
+ - A signature of a commit or checkpoint is read in its one form: its role, signedAt, algorithm, key and value trimmed,
639
+ and refused where one of them or its signer is empty or whitespace alone, where its signer is not a text, or where it
640
+ names an `@context` or another property; so is a signature of a change set whose algorithm, key or value is empty.
641
+ Producers write each signature in this form, as WWCP_POI `7c6f91c` reads it too.
642
+ - A change set created before the `lastChange` of the version it is applied to is stamped with that `lastChange`, not
643
+ with its `createdAt` (S5): the version it leads to, and so its after ETags, differ from those 0.2.0 calculated, and a
644
+ producer calculates them as WWCP_POI `12e993a` does. A change set created after its version, the usual case, is stamped
645
+ with its `createdAt` as before.
646
+ - A transparency software release is identified under its manufacturer, `{manufacturer}:{name}`, and a certificate
647
+ under the body issuing it, `{body}:{name}`, e.g. `GraphDefined:chargy-1.5.0` (S36): a version or change set
648
+ identifying one otherwise, which 0.2.0 took, is refused.
649
+ - The prices of a charging tariff in a price statement are written as a tariff holds them (D18): a statement naming a
650
+ property their type does not define, e.g. the OCPI restriction `max_kwh`, is refused before its signature, and the
651
+ price evidence of a session holding one has invalid signatures. A version naming a restriction outside `startTime` to
652
+ `reservation`, e.g. `maxStateOfCharge`, is refused.
653
+ - A snapshot whose `effectiveLastChanges` WWCP_POI3 does not read, which 0.2.0 took, is refused. Change sets 0.2.0
654
+ refused as not supported, of the entities a party embeds, with element changes or preconditions, are applied now.
655
+ - Reading a version takes two to three times as long as with 0.2.0, about a second for 12816 EVSEs. The package is
656
+ larger, 3.7 MB unpacked where 0.2.0 was 2.2 MB, of it `dist/index.js` 0.9 MB and its source map 2.3 MB.
657
+ - `@vanaheimr/cose` requires ^0.15.0 and `@vanaheimr/metrological-cbor` still ^0.12.0: keep both ranges aligned in
658
+ consumers that use them directly. The minimum runtime remains Node 22.18, and the package remains ESM-only. Without
659
+ Temporal, on Node 22 and 24, a time zone Intl names by another name is taken in any case, e.g. `Asia/KOLKATA`, which
660
+ it names `Asia/Calcutta`; Node 26 takes it only as the database writes it.
661
+
662
+
663
+ ## 0.2.0 (2026-11-02)
664
+
665
+ Shared trust and session verification APIs, previously delivered as the unpublished 0.1.2-dev.0 development candidate.
666
+ The experimental evidence and delegation profiles described below remain experimental in this release.
667
+
668
+ - Shared association of an EVSE, its energy meter and its key, with an explicit normalization of SubjectPublicKeyInfo
669
+ and COSE P-256 keys and a conservative freshness of the observation; no claim of a historical association.
670
+ - Historical meter evidence per reading, and checkpoints verified under the current local authorization.
671
+ - Prices bound to a session, their publication and their rates checked apart.
672
+ - A shared comparison of authenticated versions: rollback and fork constraints the caller keeps durably, historical and
673
+ current versions told apart, gaps filled, a change of the epoch refused unless verified.
674
+ - The charging document keys of a CPO, an authority of their own; experimental delegation of a policy signed by two
675
+ quorums, historical timelines of authorities, and verified epoch continuation and profiles.
676
+ - Experimental receipts of a public retrieval of prices, a policy of witness organizations, networks and clocks of
677
+ its own, restrictions of key usages and a conservative result before the signed start; no witness authorized
678
+ automatically, no claim of the actual start or of the consent of the customer.
679
+ - COSE and Metrological CBOR on ^0.12.0, so that direct and transitive consumers share one codec; existing proof and
680
+ signature fixtures remain valid.
681
+ - `trustPolicyId` is the SHA-256 of the canonical JSON of `{network, holders, minConsensus, urls}`, the URLs as a WHATWG
682
+ URL serializes them without trailing slashes, in their ordinal order, as WWCP_POI3 calculates it
683
+ (`TrustTransition.PolicyIdOf`); in place of their `JSON.stringify`, whose order of properties and escapes are those of
684
+ JavaScript, so that every implementation calculates the same. Every policy identification changes: decisions an app
685
+ keeps under one are made again, and transitions are signed again.
686
+ - A commit is refused without its `json`, `cbor` and `effectiveTree` ETags (SPECIFICATION.md 8.3), as WWCP_POI3 refuses
687
+ it; the epoch change of `tests/vectors/trust-transitions.json` names the ETags of its versions.
688
+ - `trustTransitions.ts` and `documentKeys.ts` with the license header and in the layout of the other sources.
689
+
690
+
691
+ ### Migration
692
+
693
+ - Commits must name all three version ETags: `json`, `cbor` and `effectiveTree`. A commit accepted by 0.1.1 with
694
+ only an effective-tree ETag is now refused. Producers must generate the complete ETags and sign the resulting commits
695
+ and checkpoints again; do not edit previously signed documents or silently repair an archived original.
696
+ - When moving from the unpublished 0.1.2-dev.0 candidate that used `JSON.stringify` for policy identity, recompute
697
+ `trustPolicyId` from canonical JSON. Reconfirm local decisions under the new identity and regenerate and sign affected
698
+ trust transitions. Retain old observations and original evidence; a changed policy identity must not erase known forks
699
+ or rollbacks. Candidates already using canonical JSON keep their policy identities.
700
+ - COSE and Metrological CBOR both require ^0.12.0. Keep both dependency ranges aligned in consumers that use them directly.
701
+ - The minimum runtime remains Node 22.18, and the package remains ESM-only. Local trust decisions, durable observation
702
+ storage and independently authorized witness policies remain the caller's responsibility. No production trust roots
703
+ or witness service are bundled.
704
+
705
+
706
+ ## 0.1.1 (2026-10-31)
707
+
708
+ `TrustContext`, for apps that import a trust anchor before they trust its keys. `TrustContext.evaluate` checks a key
709
+ statement against the identity keys of a trust anchor the app trusts locally and reports its signatures, signers, quorum
710
+ and validity apart (`TrustEvaluation`); a context only when all of them hold, `TrustContext.create` throws otherwise.
711
+ The context keeps the holders of the trust anchor and its quorum, and binds the roaming network, its operator, the
712
+ signed key statement and the time of the evaluation. `OperatorTrust.of` and `fromOperatorJSON`, `verifyVersion`,
713
+ `verifyChargingStation`, `ChargingStationProof.verify` and `verifyCheckpoint` take it in place of a trust anchor; given
714
+ a trust anchor, they verify as before. The app keeps storing and showing its decisions and creates a context from its
715
+ current decisions for every verification. Tested with genuine signatures, the proofs of the conformance tests and in
716
+ Chromium.
717
+
718
+ S31 and S32 of WWCP_POI `d6b0127`. A release keeps its version: a new version is a new release, so that a
719
+ certificate naming a release names the version that was assessed. A change set updating or removing the `version` of a
720
+ transparency software release is refused, and so is one removing a release and adding it again with another version,
721
+ after its last change (`ChangedTransparencySoftwareVersion`; versions compared trimmed and without case, as
722
+ `TransparencySoftwareVersion` compares them). The `notifiedBodyNumber` and `notifiedFor` of a conformity assessment
723
+ body are the roaming network operator's, which vets the body, also once the body has keys: a body issuing a
724
+ certificate under a legislation the same change set adds to its `notifiedFor` signs it together with the operator. A
725
+ number names one body, in every version and after every change set (`DuplicateNotifiedBodyNumber`).
726
+ `tests/releasesAndBodies.test.ts`, after `A_release_keeps_its_version` and
727
+ `The_roaming_network_operator_says_what_a_body_is_notified_for`.
728
+
729
+ A conformity assessment body is refused as its constructor in WWCP_POI refuses it, in its words and order: a
730
+ `notifiedBodyNumber` not of four digits (nothing after them, also no line feed, since WWCP_POI `f53d220`), a legislation
731
+ of `notifiedFor` empty or given twice (with case), a certificate issued under a legislation it is not notified for
732
+ (exactly as written, its certificates in the ordinal order of their identifications), each wrapped as WWCP_POI wraps it,
733
+ e.g. "The given JSON representation of a conformity assessment body is invalid: The certificate 'PTB-2026-0042' is issued
734
+ under the legislation 'CRA', which the conformity assessment body 'PTB' is not notified for! (Parameter 'Certificates')";
735
+ before them a `notifiedBodyNumber` that is no text and a `notifiedFor` that is no array of texts. A version is refused
736
+ for what WWCP_POI refuses of an entity as it reads it before what it refuses of the network made of them, the
737
+ identifications unique within the network last. Checked against WWCP_POI3 itself, on 33 versions of QA: the same, but
738
+ for two refusals of one body at once, a key and its number. In a change set WWCP_POI names the change ("Change 0: …"),
739
+ this library refuses the version it leads to.
740
+
741
+ The four vectors of WWCPPOIConformanceTests `d4a4928` vendored, `poi-refusals.json` of 93 vectors: a key of a party
742
+ whose `notAfter` is written with an offset, as a date only or as null, and one whose key is another CBOR of the same
743
+ COSE key. WWCP_POI refuses them too since `a425530` (D13 there, SPECIFICATION.md 2, Written forms), as this library did;
744
+ the key not in deterministic CBOR now in its words: "Invalid COSE key: Invalid deterministic CBOR: the key of the public
745
+ key '…' is not written as the deterministic CBOR of its COSE key!"
746
+
747
+
748
+ The four vectors of WWCPPOIConformanceTests `ad7c73e` vendored, `poi-refusals.json` of 97 vectors: a conformity
749
+ assessment body as its constructor in WWCP_POI checks it, a certificate issued under a legislation it is not notified
750
+ for, a notified body number of three digits, a legislation notified for empty, one given twice (D14 there, found against
751
+ `1f914ac`, fixed by `4ff1fc2` here).
752
+
753
+ The legislations a conformity assessment body is notified for are refused unless written in their ordinal order, as
754
+ WWCP_POI writes them (Achim's decision of 2026-10-29, as D13 decided it for keys): WWCP_POI takes another order and
755
+ writes it sorted, which written again would be another text than the one hashed. Refused as the body is read, before
756
+ what its constructor checks, in the words of this library: "The legislations the conformity assessment body 'PTB' is
757
+ notified for are written in their ordinal order!"
758
+
759
+ The four vectors of WWCPPOIConformanceTests `012ce9d` vendored, `poi-refusals.json` of 101 vectors: a conformity
760
+ assessment body as the content holds it, notified for legislations out of their ordinal order, with a notified body
761
+ number that is no text, with a notifiedFor that is no array of texts, and with the number of another body. WWCP_POI
762
+ refuses the order since `8108522`, in the words of this library.
763
+
764
+ WWCP_POI `f53d220`: its regular expressions end with `\z`, no longer with `$`, which in .NET matched before a last line
765
+ feed as well. A notified body number "0102" with a line feed is refused, which this library had taken as WWCP_POI took
766
+ it; a certificate timestamp with a line feed after its offset was refused here already, as every check of this library
767
+ ends at the end of its text.
768
+
769
+ The content as WWCP_POI writes it (SPECIFICATION.md 2, Written forms): what WWCP_POI takes of a version and writes otherwise
770
+ is refused, as written again it would be another text than the one hashed (Achim's decision of 2026-10-29, as D13
771
+ decided it for keys and 9decdb5 for the legislations a body is notified for). Of every entity, `notWrittenAsHeld`, as a
772
+ version is read, after everything WWCP_POI refuses of it, so that its words come first: what the content orders
773
+ (its ordered lists, `POIJSON.OrderedIds`, entities, keys, exchanges) out of its ordinal order, the texts of an ordered list
774
+ untrimmed, a list WWCP_POI writes only with an element given empty (about ninety; not the versions, releases and energy
775
+ meters of a certificate, the elements of a price, the exchanges of a merchant, the identity keys of a key statement), a
776
+ property given as null, an embedded object naming a JSON-LD context, a text in languages not naming each language as
777
+ Styx writes it, in lower case, with a text, a timestamp not UTC to the millisecond; custom data held as given. Of a key of
778
+ a party, a property WWCP_POI does not read; of an EV Taler merchant and its exchanges, such a property, a base URL not as
779
+ Hermod writes it, trimmed and with its scheme, and a currency not by its code. In the words of this library, e.g.
780
+ "'chargingStationOperators[0].chargingPools[1].paymentOptions' is not written as the content holds it: its elements are in the
781
+ order the content holds them in!". The new values of a change set are checked by the same rules (`asGiven`), as
782
+ WWCP_POI refuses a new value the content holds otherwise, "e.g. an empty array written as none". Found by probing
783
+ WWCP_POI3 at 8108522 with 201 versions of QA: of the 20 it takes and writes otherwise this library took all, and
784
+ refuses all now; every one it takes as given is taken, every one it refuses refused, and every version of the frozen run
785
+ is written as the content holds it. `tests/writtenForm.test.ts`.
786
+
787
+ What the constructor of the roaming network refuses, of a version, in the words of WWCP_POI: as an argument of its
788
+ parameter, which its TryParse names, e.g. "The given JSON representation of a roaming network is invalid: The notified
789
+ body number '0102' of the conformity assessment body 'PTB' is the one of the conformity assessment body 'BSI' already!
790
+ (Parameter 'ConformityAssessmentBodies')", in its order (the groups of providers and the offers before the releases): the
791
+ identifications unique within the network, and the key statement and the certificates of the roaming network operator
792
+ (`KeyStatementError`, 'Operator'), its key statement of another network now before its certificates. After the last change
793
+ of a change set they are named bare, in the order WWCP_POI checks them there (`RoamingNetwork.afterChangeSet`). The
794
+ golden vectors held the inner words only. Checked against WWCP_POI3 at 8108522 on 10 versions of QA.
795
+
796
+ The roaming network operator, its key statement and the certificates of its signing keys in the words and order of
797
+ WWCP_POI3 (the TryParse of `RoamingNetworkOperator`, `RoamingNetworkKeyStatement` and
798
+ `RoamingNetworkSigningKeyCertificate`), e.g. "A key statement requires its minConsensus, a whole number from 1 to 255!",
799
+ "Invalid key in the identityKeys of a key statement!", "A signing key certificate requires the signingKey it certifies!";
800
+ what this library refuses besides (properties it does not know, identity keys out of their order) after them. What the
801
+ constructor of the operator refuses as an argument of it, "The given JSON representation of a roaming network operator is
802
+ invalid: …": a certificate by the same identity key given twice, which this library took, a signing key certified in two
803
+ forms, a key both a signing and an identity key, a quorum its keys do not have; the holder of a key as it names it ("the
804
+ holder 'BetaOrg'"). The tree hash algorithms of a roaming network as `POITreeHashAlgorithms` names them, four refusals for
805
+ the one of this library. An entity without its identification as Styx refuses it, "Missing JSON property '@id'!", which
806
+ this library took but of the roaming network and its operator (`invalidIdentification`, first of all), and one of another
807
+ JSON type in the words of the kinds WWCP_POI3 refuses it of ("Invalid 'charging pool identification'!"). Certificates and
808
+ the versions of stations, meters, their templates and models without the prefix this library gave them ("The certificate
809
+ '…': "), as WWCP_POI3 names them, with the parameter its constructor names ("(Parameter 'Versions')", 'HardwareRevisions',
810
+ 'FirmwareVersions', 'Legislation', 'NotBefore', 'EnergyMeters'), a version twice in a model as an argument of the model; a
811
+ certificate without its identification, of a certified meter a model of another JSON type, in its words.
812
+
813
+ Of the written form also, as WWCP_POI3 takes and writes it otherwise: an entity without its lastChange, which it writes as
814
+ its created; a party without its name, which it makes up of its kind and identification ("Charging Station Manufacturer
815
+ 'GraphDefined'"); a text in languages without a language, but the name of a charging station operator, which it writes
816
+ so; the releases of a certificate and the URLs of an open source license left out, which it writes also empty; the energy
817
+ meters of a certificate given empty; an identification and the type of a charging connector given as a number.
818
+
819
+ Probed against WWCP_POI3 at 8108522 with 617 versions of QA, of every kind of object a property it does not read, every
820
+ property of it removed and given in another JSON type, the tree hash algorithms and the quorum of the operator, and 10
821
+ of certificates and models: none it takes as given is refused here; of what it refuses, this library refuses all in its
822
+ words but 20, of what it takes and writes otherwise all but the properties it does not read (25 kinds, which it leaves
823
+ out). Not ported yet: properties of another JSON type or missing that WWCP_POI3 refuses (168, in the words of every kind)
824
+ and the references it checks (23, of templates, tariffs, members, manufacturers, models, releases and certificates).
825
+ `tests/operator.test.ts`, and in `writtenForm`, `identifications` and `certificates`.
826
+
827
+ The 27 vectors of WWCPPOIConformanceTests `1c36c6d` vendored, `poi-refusals.json` of 128 vectors: versions not written as
828
+ the content holds them (`version-held-*`), the 20 versions WWCP_POI3 at 8108522 took and wrote otherwise and more of their
829
+ kinds (bodies, keys, payment options and exchanges out of their order, an untrimmed payment option, lists given empty, a
830
+ property given as null, embedded JSON-LD contexts, texts in languages not as Styx writes them, a timestamp with an offset,
831
+ properties of keys, merchants and exchanges not read, URLs and currencies not as written), ten with their eTags as
832
+ published, their reasons in the words of this library, which refuses them since `5c534d4`. WWCP_POI refuses them as well
833
+ since D15 of WWCPPOIConformanceTests, fixed there in `1c36c6d` (SPECIFICATION.md 2: a reader MUST refuse a version not
834
+ written so).
835
+
836
+ The written form as WWCP_POI3 refuses it since WWCP_POI `3de830a`, which writes a version again and walks both
837
+ (RoamingNetwork.WrittenForm.cs, FirstNotWritten): `notWritten` walks a version in that order and in those words, the
838
+ reasons of this library, which WWCP_POI took over, and two of its own. "the content leaves it out", of a property given
839
+ that WWCP_POI does not write: of every entity a property its type in WWCP_POI3 neither writes (ToJSON) nor reads
840
+ (PropertyKeys), drawn from it at `3de830a` for 33 kinds and checked against the 605 versions it wrote in the frozen run
841
+ and the probes, of a key statement, its signatures, a certificate of a signing key, a coordinate, a price component and
842
+ a public key of a meter as well; `dataLicenseIds`, which a version names completely (`dataLicenses`), a license it does not know
843
+ in its words; a text in languages without a language WWCP_POI reads. "the content holds it as '…'", of a value it writes
844
+ otherwise, by the path of that value: the lastChange of an entity naming none (its created), the name of a party naming
845
+ none (not of grid and parking operators), the lists it writes also empty, an identification given as a number, texts
846
+ its types write trimmed (identifications, references, currencies, versions, e-mail addresses, URLs), URLs with the
847
+ scheme https, the identification of a charging station operator in upper case, the role of a meter, the legal status of
848
+ a release and signature methods as they are named, coordinates as doubles (50.92710 as 50.9271; quantities keep their
849
+ decimals). Of a release and its licenses, a certificate and the meters it was issued with, a property they do not read
850
+ as WWCP_POI2 refuses it ("Unknown property 'x'."). The roaming network and its operator compared without case and
851
+ trimmed, as their types read them, also of the key statement and the certificates, signed as read. Seven cases keep the
852
+ words of this library, refused where it reads them, before the written form, as WWCP_POI takes them over (Achim's
853
+ decision of 2026-10-30): a property of a key of a party, of an EV Taler merchant or of an exchange, the merchant base URL,
854
+ the base URL and the currency of an exchange not as written. Probed against WWCP_POI3 at `3de830a` with 1257 versions of
855
+ QA: none it takes is refused here; of what it refuses as written otherwise, all in its words but those seven.
856
+
857
+
858
+ ## 0.1.0 (2026-10-29)
859
+
860
+ The first milestone: the examples A1 to A14 of WWCP_POI (commit `33447ac`) checked in TypeScript.
861
+
862
+ The findings of the review this port made of the C# implementation are cited as csharp-review N, N the number of the
863
+ finding in
864
+ [docs/csharp-review.md of WWCPPOIConformanceTests](https://github.com/OpenChargingCloud/WWCPPOIConformanceTests/blob/master/docs/csharp-review.md);
865
+ `docs/` of this repository held it up to `b9c786e`.
866
+
867
+ - Canonical JSON and deterministic CBOR of Styx; a lossless JSON parser keeping decimals as `System.Decimal` holds them.
868
+ - COSE keys, RFC 9679 thumbprints, signatures ES256/384/512, ES256K, ESP*, ESB*, Ed25519, Ed448, ML-DSA-44/65/87.
869
+ - Trust anchors (QR code, URL, JSON), key statements, certificates of signing keys; trust following the key statements
870
+ of a history (`OperatorTrust.followedBy`).
871
+ - Roaming networks with their ETags, effective members and the effective tree; proofs of charging stations, attestations,
872
+ the manufacturers and models of the station and its meters (`manufacturer`, `energyMeterModels`), the transparency
873
+ software and certificates of their energy meters.
874
+ - Change sets with their owners' signatures, commits, linear histories, checkpoints, their proofs and signatures by the
875
+ roaming network operator and by witnesses, archive segments; epochs, their final checkpoints and the genesis of the
876
+ next one.
877
+ - Charging station and energy meter manufacturers, parties stating their models; transparency software manufacturers and
878
+ conformity assessment bodies, parties holding their releases and certificates; the key usages `models`, `software` and
879
+ `certificates`; energy meter templates and groups.
880
+ - Price statements, their chains, checkpoint trees and proofs; evidence of a charging session as the app of example A10
881
+ keeps it (`parseEvidence`).
882
+ - The runtime status: status updates, frames of the WebSocket of a history, the runtime status of a snapshot.
883
+ - `POIClient`: the proof of a charging station and its prices, from the servers of a trust anchor.
884
+
885
+ Moving from the examples at `2d9511d` to `bbd4cbd` also made the ownership of changes the one of the C# implementation
886
+ where the port differed: identity keys of the roaming network operator counted for every change of a party, not only for
887
+ its keys; where it signs in place of a party without keys, its signing keys counted when they may sign `party`, not
888
+ when they may sign the usage of the change (csharp-review 13); its identity and signing keys counted alike for
889
+ everything it owns; and the signing keys of a party could not rotate its signing keys. The values of change sets are
890
+ kept in the order the C# implementation keeps them in (csharp-review 10).
891
+
892
+ Moving on to `801bbce`, the manufacturers became parties: the roaming network operator adds and removes them, they change
893
+ their own properties and state their models with a key that may sign `models`, every change of a model naming its
894
+ manufacturer. A change set changes only the properties of a party the C# implementation lets it change, so that the
895
+ models, releases and certificates of a party are not replaced as one of its properties by a key that may not sign them.
896
+ `forThisModel` of the transparency software of a meter asks for the version of the certificate among the versions of the
897
+ model as well.
898
+
899
+ Node.js 22.18 is the minimum, as of ChargyCore.TS: Node 20 has reached its end of life, and tsup 9, Vitest 5 and
900
+ ESLint 10 do not run on it. The code follows the ESLint rules of ChargyCore.TS; where a missing value used to be asserted
901
+ away, it is refused with a `POIError` saying what is missing. Tests of the bundle as published (its imports, its
902
+ declarations for TypeScript apps, the files `npm pack` takes) and of the library in Chromium join the tests in Node; GitHub
903
+ Actions run all of them on every push, and every night against floating dependencies, the newest codecs of Vanaheimr, and
904
+ the C# examples of WWCP_POI as they are.
905
+
906
+ The test vectors moved to WWCPPOIConformanceTests, which generates golden vectors - canonical JSON and CBOR, encodings,
907
+ the examples of section 6 of the specification, signed station proofs against a trust anchor, the documents of the run of
908
+ the C# examples, and documents to refuse - and runs them against this library and the C# implementation alike. This
909
+ repository vendors them in `tests/vectors` and runs them in `tests/conformance.test.ts`. The nightly check against the C#
910
+ examples of WWCP_POI master moved there as well.
911
+
912
+ The codecs of Vanaheimr are `@vanaheimr/cose` 0.10.0 and `@vanaheimr/metrological-cbor` 0.11.1: `@vanaheimr/cose` 0.9.0
913
+ brought a CBOR codec 0.10 of its own next to the 0.11 this library uses, so that an app installed two copies of it; now
914
+ there is one.
915
+
916
+ The digest of a price statement leaves its signature value out, as WWCP_POI does since `dbf39b0` (SPECIFICATION.md 12.1):
917
+ the SHA-256 of its signature input. The ECDSA twin `(r, n − s)` of a statement, which anybody can make of it, is the same
918
+ statement, of the same digest (csharp-review 2); `PriceStatement.sameSignedContent` is gone. The test vectors
919
+ are those WWCPPOIConformanceTests generates for it, with a new run of the C# examples at `dbf39b0`
920
+ (`tests/vectors/csharp-dbf39b0`).
921
+
922
+ The price statements of an entity are one chain in whichever spelling they name it, as WWCP_POI keys them since
923
+ `4680d99` (SPECIFICATION.md 12.1 to 12.3, `RoamingNetworkHistory.ChainOf`): by its type and its identification without
924
+ `*`, whitespace around it, or case (`priceStatementChainOf`, `PriceStatement.chain`), so that `DE*GEF*E1*1` and
925
+ `DE*GEF*E11` are one EVSE and a split view between them is seen. `follows` compares chains; `verifyPriceStatements`
926
+ refuses a statement naming an entity of the station otherwise than the roaming network does, before its signature is
927
+ looked at; `verifyCheckpointPriceStatements` refuses a checkpoint naming two statements of one entity and sequence with
928
+ different digests, in the order of its tree, both in the words of WWCP_POI. A sequence is at most 2^53 − 1
929
+ (`MAX_PRICE_STATEMENT_SEQUENCE`), as it was read here already. Not ported, as they bound what a server takes: a statement
930
+ more than 1,000,000 beyond the current one of its entity, and the statements a server keeps per operator for its next
931
+ checkpoint. The golden vectors hold both refusals (`tests/vectors/poi-refusals.json`, 75 vectors): the
932
+ statement of example A10 naming its EVSE as `DE*GEF*E11`, checked with the proof of its station
933
+ (`entityPriceStatement`), and checkpoint 4 naming a fourth statement of one sequence, in the other spelling and of
934
+ another price (`checkpointPriceStatements`, its control the checkpoint and its statements as published).
935
+
936
+ The holders of a quorum are counted as the trust anchor names them, as WWCP_POI does since `3e32f48` (SPECIFICATION.md
937
+ 4.1, 8.6; csharp-review 1): a trust anchor names the keys per holder, the `@id` of its key or the array of the
938
+ `@id`s of its keys (`TrustAnchor.holders`; in the URL the holders separated by `,`, the keys of one holder by `;`).
939
+ `KeyStatement.verifyHolders` counts the holders as the verifier knows them and refuses a statement naming the trusted
940
+ keys otherwise; a station proof, `OperatorTrust` and the signatures of checkpoints count an attestation or a signature
941
+ for the holder of the trusted identity key that certified its key. A change set naming a key of a party with another
942
+ holder than before is refused.
943
+
944
+ A change set is identified by its content, as WWCP_POI does since `bc3b1bd` (SPECIFICATION.md 4, 5.2;
945
+ csharp-review 4): its `@id` is the SHA-256 of its canonical JSON with its `@context`, without `@id` and
946
+ signatures (`changeSetIdentification`), so that nobody commits another change set in the place of one. `ChangeSet.parse`
947
+ refuses a change set identified otherwise, and one naming change sets in `metadata.rebasedFrom` it was not rebased from;
948
+ `Commit.parse` reads the change set of a commit as well and refuses a commit with one parent whose change set has no
949
+ changes or the identification of a merge. The test vectors are those of a new run of the C# examples at `bc3b1bd`
950
+ (`tests/vectors/csharp-bc3b1bd`).
951
+
952
+ A change set gives its new values as the content holds them, as WWCP_POI requires since `80e4ccc` (SPECIFICATION.md 2,
953
+ 4; csharp-review 10): a new value is applied as it is given, no longer written anew as the C# model would, and
954
+ one that names a JSON-LD context in what it embeds (custom data aside, which keeps it), gives identified elements, keys,
955
+ certificates or the lists of section 2 out of their order or twice, or a timestamp not UTC to the millisecond is
956
+ refused. An old value is compared as it is given. The test vectors are those of a new run of the C# examples at
957
+ `80e4ccc` (`tests/vectors/csharp-80e4ccc`).
958
+
959
+ An attestation gives its `validUntil`, at most 24 hours after its `signedAt`, as WWCP_POI requires since `be07037`
960
+ (SPECIFICATION.md 8.5, 8.6, 12.1; csharp-review 7): `Attestation.parse` refuses one without or valid longer
961
+ (`MAX_ATTESTATION_VALIDITY`), so that none is valid forever. The `signedAt` of an attestation or a price statement is the
962
+ choice of its signer: the signing key of an attestation counts when it is valid when it attested and at the time the proof
963
+ is checked, and a price statement is current no longer than its key.
964
+
965
+ A version naming twice an identification WWCP_POI keeps unique within the roaming network is refused, as WWCP_POI refuses
966
+ it (`DuplicateGridConnectionPoint` to `DuplicateEnergyMeterTemplate` of RoamingNetwork.cs): grid connection points, energy
967
+ meters, parking garages, spaces, sensors, space groups, space templates and products, transparency software releases and
968
+ certificates, energy meter templates and groups, the EVSE, charging station and charging pool groups of operators and
969
+ providers, and charging tariff offers, compared as their C# types compare them, without case but parking products, the
970
+ groups by the prefix of their party, `DE-GDF` as `DE*GDF` (`duplicateIdentification`). `RoamingNetwork.parse` checks every
971
+ version it reads, also the one a change set leads to: before, an operator could add a group of the identification a group
972
+ of the provider of its prefix has, and its change set was taken with the after ETags this library calculates. The golden
973
+ vectors `version-twice-*` of WWCPPOIConformanceTests hold both implementations to it, refused in the same words (`reason`).
974
+
975
+ The roaming network operator signs in place of a party without signing keys, e.g. the certificates of a conformity
976
+ assessment body or the grid connection points of a grid operator not taking part, by a signing key that may sign
977
+ `party`, as the examples certify its keys, as WWCP_POI does since `36bb176` (SPECIFICATION.md 4.1; csharp-review 13):
978
+ `checkOwnership` counts its signing keys that may sign `party` besides those that may sign the usage of the change.
979
+ A change set taken before is taken still.
980
+
981
+ A change set naming `rebasedFrom` with a change, but an `Add` or `AddElement`, without `oldValue` is refused, as WWCP_POI
982
+ refuses it since `1717ee2` (SPECIFICATION.md 4; csharp-review 12): only old values tell that a change set
983
+ committed meanwhile changed what it changes, which such a rebase may have overwritten. One not rebased is read as before.
984
+ The golden vector `changeSet-rebased-without-old-value` holds both implementations to it.
985
+
986
+ A model of charging stations or energy meters names its `hardwareRevisions` and `firmwareVersions`, and a charging
987
+ station and an energy meter one `hardwareRevision` and one `firmwareVersion` of it, as WWCP_POI does since `7e0b0ac`
988
+ (csharp-review 16); a certificate (`ConformityCertificate`, the `certificates` of a conformity assessment body)
989
+ is for a model of charging stations or of energy meters, in the hardware revisions and firmware versions it covers, and
990
+ names its components, the transparency software and, of a station model, the energy meters it was issued with; a
991
+ station and a meter name the certificates of their models in `certificateIds`, and the `transparencySoftwareStatus` of
992
+ a meter names a release and its legal status, no certificate, as WWCP_POI does since `987fa26` (csharp-review 17).
993
+ Change sets change them, effective members take them of their templates, and a certificate is unique within the
994
+ network. `verifyChargingStation` gives the `certificates` the station and its meters name, each with `forThisModel`:
995
+ whether it is for the model, the hardware revision and the firmware version of what names it; the `transparencySoftware`
996
+ of a meter gives the certificates naming its release, no longer a certificate of its own. The test vectors are those of
997
+ a new run of the C# examples at `987fa26` (`tests/vectors/csharp-987fa26`).
998
+
999
+ A charging station signing measurements itself names its `serialNumber`, `transparencySoftwareStatus`,
1000
+ `signatureMethods` and `publicKeys` as an energy meter does, and the `modelURL` of its model; an EVSE names its
1001
+ `iso15118Certificate`, as WWCP_POI does since `52f1172`. Change sets change them; an effective station takes the model
1002
+ URL, the releases and the signature methods of its templates besides its own, never the serial number, the public keys
1003
+ or the ISO 15118 certificate of the single device; and a change set giving the releases of a station or a meter out of
1004
+ the order of their identifications, or one twice, is refused. `verifyChargingStation` gives the releases the station
1005
+ names among its `transparencySoftware`, proven by the certificates of the station; each entry names what names it,
1006
+ `namedBy` as of a certificate, in place of `energyMeterId` (`NamedTransparencySoftware` in place of
1007
+ `MeterTransparencySoftware`). The test vectors stay those of the run at `987fa26`: no station of its examples signs
1008
+ measurements itself.
1009
+
1010
+ The first checkpoint of an epoch takes signatures of the algorithms of its epoch and of the epoch before, no other, as
1011
+ WWCP_POI does since `65e658d` (SPECIFICATION.md 11.3; csharp-review 14): a history started by
1012
+ `startNextEpoch` knows the profiles of the epoch it closed, one started from a snapshot those its `epoch` names as
1013
+ `previousProfiles` (`History.previousEpochProfiles`), and those of the first epoch are the default ones; without them
1014
+ it takes the algorithms of its own epoch alone. A snapshot of the epoch 2 naming profiles of the epoch before is refused.
1015
+
1016
+ A certificate the roaming network operator adds or changes in place of a conformity assessment body without signing keys
1017
+ says so, `"statedBy": "roamingNetworkOperator"`, as WWCP_POI requires since `65e658d` (SPECIFICATION.md 4.1;
1018
+ csharp-review 15): a change set adding or changing a certificate whose `statedBy` does not say who signs it, as
1019
+ the version before has the keys of the body, is refused. `verifyChargingStation` gives each certificate with `statedBy`,
1020
+ `ConformityAssessmentBody` or `RoamingNetworkOperator`, in place of `bodyHasKeys`, which could not tell them apart once
1021
+ the body had keys. The test vectors stay those of the run at `987fa26`: none of its examples has a third epoch or a
1022
+ certificate stated in place of a body.
1023
+
1024
+ A version naming a hardware revision, firmware version or serial number WWCP_POI refuses is refused, read or led to by a
1025
+ change set, as WWCP_POI reads them since `c31e5e3` and `3672b06` (`HardwareRevision`, `FirmwareVersion`,
1026
+ `SerialNumber`): a text as it is given, not empty and neither beginning nor ending with whitespace as `Char.IsWhiteSpace`
1027
+ tells it, of charging stations, energy meters and their templates, of the models of their manufacturers, and of
1028
+ certificates and the energy meters they were issued with, each once in a model, a certificate and such an energy meter
1029
+ (`ModelVersions.Of`), in the words of WWCP_POI (`invalidVersionText`, which `RoamingNetwork.parse` checks with
1030
+ `duplicateIdentification`, and for certificates `invalidCertificates`, below). Whether one is of the model of what names
1031
+ it stays with the references, which this library
1032
+ leaves to WWCP_POI. The test vectors stay those of the run at `987fa26`, whose versions name none it refuses; the vectors
1033
+ `version-text-*` of WWCPPOIConformanceTests, its head so changed, hold both implementations to it, refused in the same
1034
+ words (`reason`).
1035
+
1036
+ The golden vectors of WWCPPOIConformanceTests hold, besides the run of the C# examples, small histories it signs itself
1037
+ (`tests/vectors/poi-histories.json`), replayed step by step in `tests/conformance.test.ts`: who signs in place of a
1038
+ party without keys, who states a certificate, old values and rebases, keys expired and removed, new values as the
1039
+ content holds them, `lastChange` of what embeds a change, and three epochs (csharp-review 3, 8, 10, 12 to 15). Ten of
1040
+ them - the references a version holds, the groups of e-mobility providers, grid connection points - this library
1041
+ leaves to WWCP_POI, and does not run. They found a deviation of this library (D8 of WWCPPOIConformanceTests): a change
1042
+ set signed, besides by its owner, by a key of a party not valid at its `createdAt` was taken here, and refused by
1043
+ WWCP_POI. Every signature by a key of a party of the version is now by one valid at the `createdAt` of the change set,
1044
+ also one besides the signatures of the owners, refused otherwise in the words of WWCP_POI (`TryGetSigners`); only a new
1045
+ identity key, which proves its possession, may be one valid later (SPECIFICATION.md 4.1).
1046
+
1047
+ A version naming a certificate WWCP_POI refuses to read is refused, read or led to by a change set, as
1048
+ `ConformityCertificate` and `CertifiedEnergyMeter` of WWCP_POI read them (`src/network/certificates.ts`,
1049
+ `invalidCertificates`, which `RoamingNetwork.parse` checks after `invalidVersionText`): for exactly one model, with its
1050
+ manufacturer; at least one hardware revision and one firmware version, also of each energy meter it was issued with, as
1051
+ an object of its manufacturer and its model; an absolute `documentURL`, a legislation not empty, timestamps with an
1052
+ explicit offset, `notBefore` not after `notAfter` to the tick; at least one release, each once (compared ordinally once
1053
+ trimmed, as WWCP_POI does), none both verified and compatible (compared without case); energy meters of a charging
1054
+ station model only, each model once, compared without case. Refused in the words and in the order of WWCP_POI: first
1055
+ what it reads as the arguments of a certificate, by their path, then what its constructor checks. Its versions as
1056
+ texts moved there from `invalidVersionText`, in the same words. The vectors `version-certificate-*` of
1057
+ WWCPPOIConformanceTests found that this library took all of them (D9) and hold both implementations to it. Whether a
1058
+ certificate's model, versions, releases and legislation are those of the network stays with the references, which this
1059
+ library leaves to WWCP_POI; an unknown property, also of a certificate, this library does not refuse.
1060
+
1061
+ A history starts from a snapshot of another server only anchored in the trust anchor of the roaming network, fail
1062
+ closed, as a peer of WWCP_POI starts since `ff2c56f` (SPECIFICATION.md 10.5, *Anchored*; `History.fromSnapshot` with
1063
+ `SnapshotOptions`): a snapshot of a checkpoint by the checkpoint, signed in the role of the roaming network operator by
1064
+ signing keys of as many holders as the trust anchor and the key statement of its version require, each certified by a
1065
+ trusted identity key of its holder, valid when it signed and at `at` (`ChargingStationProof.verifyCheckpoint`, as
1066
+ `Verify(Checkpoint, TrustAnchor)` of WWCP_POI); a snapshot of the head by the `attestations` it carries since then (8.5),
1067
+ which must attest the root of its head as for a proof of the root (`ChargingStationProof.ofRoot`, as `VerifyHead` of
1068
+ WWCP_POI). Without a trust anchor no snapshot is taken, unless the caller takes it `unanchored` explicitly, e.g. a genesis
1069
+ whose key statement it verifies against the trust anchor itself; an archive segment replays its base so, anchored by the
1070
+ checkpoint of its end. Before, a snapshot of the head carrying attestations was refused, and every snapshot taken
1071
+ unanchored. An attestation of a snapshot that cannot be read is refused, not left out.
1072
+
1073
+ A signed commit or checkpoint, also the commit of a snapshot, is refused without a verifier of its signatures, as
1074
+ WWCP_POI does (`CheckSignatures`): before, its signatures were taken unchecked when `HistoryOptions.verifyCommit` or
1075
+ `verifyCheckpoint` was left out (O3 of WWCPPOIConformanceTests). `verifyCommit` checks the commit of a snapshot as well,
1076
+ its version before unknown there (`before` undefined).
1077
+
1078
+ QI-Digital documents, the proof of concept of WWCP_POI (`WWCP_POI3/RoamingNetwork/QIDigital`, as of `6a0f96e`, which
1079
+ example A14 uses), are read and checked, not signed (`src/qidigital/`): the digital calibration certificate of a charging
1080
+ station or an energy meter (`DigitalCalibrationCertificate`), the digital certificate of conformity of a model or, of a
1081
+ module of quality assurance, of the quality system of a manufacturer (`DigitalCertificateOfConformity`), and the digital
1082
+ declaration of conformity of a model (`DigitalDeclarationOfConformity`). Each is refused in the words and the order of
1083
+ WWCP_POI: only its own properties, its context, timestamps exactly as signed, numbers as numbers, what its constructor
1084
+ checks after its properties were read. Its signature input and its digest are what WWCP_POI writes again of what it read
1085
+ (`Content()`), not the text as given: numbers without trailing zeros, a whole one as an integer, identifications trimmed,
1086
+ a charging station as `DE*GEF*S1`, `mainSigner` false, an empty list or text and a revision 0 as WWCP_POI writes them.
1087
+ Checked against a roaming network as WWCP_POI checks them: the issuer a party of it, its key one that may sign the
1088
+ document and valid when it signed and at `at`; the notified body with its number, notified for the legislation; the
1089
+ model, its manufacturer, revisions and versions; the charging station or energy meter of a calibration certificate as it
1090
+ is at runtime, with what its templates give it, and with the serial number the roaming network gives it, not the one the
1091
+ certificate names (csharp-review 23). And against each other: a declaration and each certificate it names, a certificate
1092
+ and the item of a calibration certificate it covers. A text in languages of no language, one that is no text and a
1093
+ language given twice are refused, not left out, as WWCP_POI refuses them since `bbe8fb8`: left out, they would travel
1094
+ unsigned with a signed document.
1095
+ A URL is read as Hermod reads it as far as a document URL needs it: trimmed, https unless it names a scheme, of http or
1096
+ https with a host.
1097
+
1098
+ The test vectors are those of a new run of the C# examples at `33447ac` (`tests/vectors/csharp-33447ac`), the first with
1099
+ example A14: the QI-Digital documents of the meter of DE*GEF*E2*1 as WWCP_POI signed them, with ML-DSA-65 by the PTB and
1100
+ EM-LAB and with ES256 by SAALE, are checked against QA as example A14 published it, refused changed in the words of
1101
+ WWCP_POI, and written again as WWCP_POI wrote them. EM-LAB is a member of QA since, so the head is revision 8 of epoch 2.
1102
+ `examples/validate-examples.ts` checks example A14 as its `Validate()` does, and starts from the snapshots of examples A4,
1103
+ A5 and A11 anchored in the trust anchor, from the genesis and the live snapshot of example A12 unanchored, as the
1104
+ examples do: since the anchored start it refused them.
1105
+
1106
+ The golden vectors hold the QI-Digital documents of example A14 too (`tests/vectors/poi-qidigital.json`), run in
1107
+ `tests/conformance.test.ts` as WWCPPOIConformanceTests runs them against WWCP_POI: the four documents verified against
1108
+ QA, the declaration of conformity against the certificates it names, both certificates covering the meter of
1109
+ DE*GEF*E2*1, a calibration certificate of the charging station DE*GEF*S2, signed by the generator with a key QA patched
1110
+ gives EM-LAB, and 38 refusals, each with its control, of documents or of QA patched. Both implementations refuse a text
1111
+ of no language, or one that is no text (D10 there, fixed in WWCP_POI `bbe8fb8`), and the calibration certificate of a
1112
+ station against one of another serial number, or of none (csharp-review 23).
1113
+
1114
+ EV Taler, phase 1 of the concept of WWCP_POI for paying for charging with GNU Taler (`concepts/EVTaler.md`), as of
1115
+ `ec3dace`: a charging station operator names its merchant at a GNU Taler merchant backend and the exchanges whose coins it
1116
+ accepts (`evTaler`, `evTalerMerchantOf`), refused where WWCP_POI refuses it and in its words: base URLs of HTTPS without
1117
+ user, query and fragment, ending in '/', read as Hermod reads them; keys as GNU Taler writes them (`isTalerPublicKey`);
1118
+ at least one exchange, each once, of an ISO 4217 currency of Styx and with a name. A change of it needs a signing key
1119
+ that may sign the new usage `payments`. Payment options are an open set of names: any nonempty text, none twice whatever
1120
+ its case, united with those of the templates of a charging pool without case; a charging pool or template naming EVTaler
1121
+ needs the merchant of its operator, after the last change of a change set. An app reads the EV Taler tariff of an EVSE
1122
+ and the exchanges in its currency from the proof of its station (`evTalerTariffOf`, `evTalerExchangesAt`), and what
1123
+ drivers see, "EV Taler (Example Exchange, EUR)" (`paymentOptionName`). The JSON, the CBOR and the tree of a version with
1124
+ a merchant are those WWCP_POI writes: `tests/evTaler.test.ts` holds the ETags WWCP_POI3 calculates of QA with the merchant
1125
+ of its EVTalerTests. Not yet: golden vectors of WWCPPOIConformanceTests, and a station proof with a merchant as WWCP_POI
1126
+ signs it (example A15 came after the frozen run). Nothing here takes or makes a payment.
1127
+
1128
+ The keys of the parties of a roaming network are read where it is read, as WWCP_POI reads them wherever it reads a
1129
+ party (`POIPartyKeys.TryParse` and `Check`), in its words (D12 of WWCPPOIConformanceTests): before, a party's keys were
1130
+ only read where they were used, so a version naming an unknown usage of a party's key was taken. Every key as
1131
+ `POIPublicKey.parse` reads it, none twice, none both a signing and an identity key, no identity key naming usages; the
1132
+ parties in the order WWCP_POI reads them, the charging station operators last; the identity keys of the roaming network
1133
+ operator in the same words (`src/network/partyKeys.ts`). Checked against WWCP_POI3 at `ec3dace` itself, on 47 versions
1134
+ of QA with keys valid and not, of every kind of party: 40 are read as WWCP_POI3 reads them, in the same words; 5 both
1135
+ refuse, in other words of `POIPublicKey.parse` within the same wrapper (a key without identification or key, not
1136
+ Base64, expiring before it becomes valid, of another JSON-LD context); 2 name a `notAfter` WWCP_POI takes and writes
1137
+ otherwise (a date only, an offset), refused here as every timestamp not written as POI writes it. `poi-refusals.json` of
1138
+ WWCPPOIConformanceTests vendored, 89 vectors: the 13 of EV Taler and `version-key-usage-unknown`, D12 closed.
1139
+
1140
+ A key is read in the words and order of `POIPublicKey.TryParse` of WWCP_POI (`POIPublicKey.parse`): its identification
1141
+ and key as texts, then its JSON-LD context, then both given ("A public key needs its identification and its key!"); a
1142
+ key .NET can not read as Base64 in the words of .NET, one it reads but writes otherwise as not canonical; "Invalid COSE
1143
+ key: …", "A public key must not contain private parts!", "The holder of a public key must be a text!"; a key expiring
1144
+ before it becomes valid where the constructor of WWCP_POI refuses it, after its holder and usages, before its
1145
+ thumbprint. Checked against WWCP_POI3 at `ec3dace` itself, on 45 more versions of QA with keys valid and not: 34 in its
1146
+ words. Still other words: a key of CBOR WWCP_POI3 can not read (the words of `@vanaheimr/cose` and
1147
+ `@vanaheimr/metrological-cbor`, not of their C# originals), a timestamp both refuse ("Invalid value for 'not before'!"
1148
+ there). Refused here, taken and written otherwise by WWCP_POI3: a key not in deterministic CBOR, a timestamp not written
1149
+ as POI writes it, a `notBefore` or `notAfter` of null.
1150
+
1151
+ `poi-histories.json` of WWCPPOIConformanceTests `0734641` vendored: 8 replays of EV Taler in a history the generator
1152
+ signs, who may sign the merchant of DE*GEF (a signing key for `payments`; the roaming network operator only in place
1153
+ of a party without signing keys) and a charging pool naming EVTaler with and without it. A change needing signatures is
1154
+ named as WWCP_POI names it, with the property it changes, e.g. "Change 0: UpdateProperty of the
1155
+ ChargingStationOperator 'DE*GEF' ('evTaler') needs a signature by …".