Every version of a published TracePass passport is issued as a signed W3C verifiable credential in the UNTP 0.7.0 Digital Product Passport format. You can check that a passport's data came from TracePass and has not been altered, without an account and without asking us. This page walks through it on the public demo passport.
1. Fetch the credential
curl --http2 -H "Accept: application/vc+jwt" \
https://id.tracepass.eu/01/99999999999997/21/6B27A0000001 \
-o credential.jwtThe passport URL itself returns the credential when you ask for application/vc+jwt. Passport URLs require HTTP/2 (EN 18216), so tools that only speak HTTP/1.1 get a 505. The response's Link header, the passport's linkset and the HTML page's head all advertise the credential. A passport without a signed current version answers 406, and a suspended, expired or archived passport serves none.
2. Read the header
The credential is a compact JWS. Its protected header says how it was signed:
{
"alg": "ES256",
"typ": "vc+jwt",
"cty": "vc",
"kid": "did:web:www.tracepass.eu#key-1"
}The payload is the credential itself (VC-JOSE-COSE), not a VC 1.1 vc claim. kid names the issuer and the key that signed it.
3. Resolve the issuer's key
The issuer is did:web:www.tracepass.eu. A did:web identifier resolves to a document on that domain, here https://www.tracepass.eu/.well-known/did.json, which publishes the public key under the kid from the header. Control of the domain is what the signature ties the credential to.
4. Verify the signature
Either of these fetches the key and checks the ES256 signature:
// npm install jose
import { readFileSync } from "node:fs";
import { decodeProtectedHeader, importJWK, jwtVerify } from "jose";
const jws = readFileSync("credential.jwt", "utf8").trim();
const { kid } = decodeProtectedHeader(jws); // did:web:www.tracepass.eu#key-1
const did = kid.split("#")[0];
// did:web:<host> resolves to https://<host>/.well-known/did.json
const doc = await (await fetch(`https://${did.slice("did:web:".length)}/.well-known/did.json`)).json();
const method = doc.verificationMethod.find((m) => m.id === kid);
const { payload } = await jwtVerify(jws, await importJWK(method.publicKeyJwk, "ES256"), { typ: "vc+jwt" });
console.log("Verified. Issuer:", payload.issuer.id, "Product:", payload.credentialSubject.id);# pip install "pyjwt[crypto]" requests
import jwt, requests
token = open("credential.jwt").read().strip()
kid = jwt.get_unverified_header(token)["kid"] # did:web:www.tracepass.eu#key-1
did = kid.split("#")[0]
# did:web:<host> resolves to https://<host>/.well-known/did.json
doc = requests.get(f"https://{did.removeprefix('did:web:')}/.well-known/did.json").json()
jwk = next(m["publicKeyJwk"] for m in doc["verificationMethod"] if m["id"] == kid)
claims = jwt.decode(token, jwt.PyJWK(jwk).key, algorithms=["ES256"])
print("Verified. Issuer:", claims["issuer"]["id"], "Product:", claims["credentialSubject"]["id"])A credential changed after signing, by even one character, fails with a signature error.
5. Check the format (optional)
To confirm the credential follows UNTP 0.7.0, validate it against the official UNTP schema:
# pip install jsonschema requests "pyjwt[crypto]"
import jwt, requests
from jsonschema import Draft202012Validator
token = open("credential.jwt").read().strip()
credential = jwt.decode(token, options={"verify_signature": False}) # verify first, as above
for claim in ("iss", "sub", "iat"):
credential.pop(claim, None) # JWT claims, not part of the credential
schema = requests.get("https://untp.unece.org/artefacts/schema/v0.7.0/dpp/DigitalProductPassport.json").json()
errors = [e.message for e in Draft202012Validator(schema).iter_errors(credential)]
print("UNTP 0.7.0 schema errors:", errors or "none")UNTP requires the production facility's identifier and the country of production. A credential carries them when the passport records them, and otherwise omits them, so it is fully valid against the UNTP schema only for passports that do. The demo passport validates with no errors.
What a valid signature proves
That TracePass issued this exact data for this passport version, and nobody has changed it since. It is a did:web signature: it proves control of tracepass.eu, the same strength as HTTPS. It is not an eIDAS qualified seal.
It covers the passport's public view only, never restricted or authority-level fields, so it is not the complete legal record. There is no revocation list yet: a copy someone already holds keeps verifying after a passport is suspended, so check the live passport for its current status.