Schemas and validators for the /.well-known/sustainability-data URI response format, as defined in draft-besleaga-sustainability-wellknown.
| File | Description |
|---|---|
response-schema.json |
JTD (JSON Type Definition) schema for the response |
response-schema.cddl |
CDDL (Concise Data Definition Language) schema for the response |
validator-json.py |
Validates a JSON response against response-schema.json using JTD |
validator-cddl.py |
Validates a JSON response against response-schema.cddl using the cddl Ruby gem |
validate-all.sh |
Runs both validators against all examples in ../example-responses/ |
requirements.txt |
Python dependencies |
install.py |
Installs all dependencies (pip packages + cddl Ruby gem) |
python3 install.pyThis installs:
jtdPython package (used byvalidator-json.py)cddlRuby gem (used byvalidator-cddl.py)
install.py runs pip install -r requirements.txt (retrying with
--break-system-packages if needed) and gem install cddl (retrying with
sudo only if the first attempt fails).
Ruby must be present on the system. If not:
- Ubuntu/Debian:
sudo apt install ruby-full - macOS:
brew install ruby
On a stock non-root Linux box, gem install cddl (no sudo) succeeds but
installs the cddl executable into your user gem directory — e.g.
~/.local/share/gem/ruby/X.Y.0/bin — which is not on $PATH by default.
When that happens, validate-all.sh reports
[CDDL] FAIL (Error: 'cddl' tool not found.) on every example even though the
installer said "OK".
Find your user-gem bin directory and add it to PATH:
# Show the user gem directory (the executable lives in <user_dir>/bin):
ruby -e 'puts Gem.user_dir' # e.g. /home/you/.local/share/gem/ruby/3.2.0
gem environment | grep -i 'user' # alternative: 'USER INSTALLATION DIRECTORY'
# Add its bin/ to PATH for the current shell (and persist it in ~/.bashrc):
export PATH="$(ruby -e 'print Gem.user_dir')/bin:$PATH"
echo 'export PATH="$(ruby -e '"'"'print Gem.user_dir'"'"')/bin:$PATH"' >> ~/.bashrcTo be explicit about a per-user install you can also run
gem install --user-install cddl (still requires the PATH step above).
Verify before running validate-all.sh: cddl --help must work.
cddl --help # should print usage, not "command not found"Only once cddl --help works will the CDDL leg of ./validate-all.sh pass.
./validate-all.shRuns both validators against every .json file in ../example-responses/ and prints a pass/fail summary.
# JTD (JSON schema)
python3 validator-json.py ../example-responses/example-response.json
# CDDL schema
python3 validator-cddl.py ../example-responses/example-response.jsonBoth validators must be run from the schemas-validators/ directory so they can locate the schema files.
The JTD and CDDL schemas validate structure: field types, required fields, and open extensibility (unknown members are permitted). They deliberately do not — and technically cannot — express a small number of the draft's cross-field and value-range prose rules, because neither CDDL nor JTD can encode conditional dependencies between fields or numeric bounds in a way these validators check. In particular:
sci-score⇒functional-unit— the draft states a MUST: "Ifsci-scoreis present,functional-unitMUST also be present." This is a cross-field conditional dependency, which neither CDDL nor JTD can express, so a document carryingsci-scorewithoutfunctional-unitpasses both validators.- Numeric ranges — the non-negativity rules on the gross-quantity members
(
energy-consumption,carbon-footprint,sci-score,carbon-intensity-gCO2e-per-kWh,estimated-annual-emissions-kgCO2e) and the0–100bound onrenewable-energy. The formal schemas type these as numbers but do not enforce the bounds. (scope-1/2/3MAY legitimately be negative, per the draft, to express removals/net accounting.) - Default units — when
energy-consumptionis present withoutenergy-unit, the defaultkWhapplies; whencarbon-footprint(or a scope) is present withoutcarbon-unit, the defaultgCO2eapplies. The schemas cannot bind a default to an absent member; consumers apply it when reading. - Date formats — the draft's date rules are prose-only:
updatedMUST be an RFC 3339 date-time, andreporting-periodMUST be one of the calendar-date precision formsYYYY,YYYY-MM, orYYYY-MM-DD(only the last is an RFC 3339full-date). The formal schemas type both as plain strings and do not check the formats. - Array uniformity of
target-type— in an array response, all entries MUST share the sametargetvalue and, when present, the sametarget-typevalue. Cross-entry rules cannot be expressed in either schema language. target-typetolerance for unrecognized values — the schemas close thetarget-typeenum (origin,path,organization,service,product,device,tenant,data-source), but the draft directs clients that encounter an unrecognized value in an enumerated member NOT to reject the document: they disregard the member and interprettargetas iftarget-typewere absent. That tolerance is applied at the application layer — a validator failure on such a value alone does not make the document unusable to a conformant client.- Minimum-reporting rule — a document SHOULD carry at least one reported
numeric metric or a disclosure/attestation URI; with none, the mandatory
methodology-uriMUST lead to the substantive disclosure. Not expressible in either schema language.
These rules are checked at the application layer: this repo's publisher/
and consumer/ implementations validate the sci-score ⇒ functional-unit
dependency and the numeric ranges, and apply the unit defaults. Treat a "PASS"
from the formal validators as "structurally valid", not "fully conformant to
every prose MUST in the draft".