v0.1.2 — live at id.passant.io

Open-source digital product passports.

OpenPassant is an Apache 2.0 toolkit for issuing, resolving and verifying EU Digital Product Passports, with tamper-evidence anchored on a public blockchain. First target: the EU battery passport, mandatory from 18 February 2027.

verify.ts
import {
  computeBundleRoot,
  toHex,
} from '@openpassant/core';

// bundle: served next to every passport
const result = computeBundleRoot(bundle);

if (result.ok) {
  // compare with the root anchored on-chain
  console.log(toHex(result.root));
  console.log(result.checkedSections); // ['public']
}

Five parts, one repository.

PackageWhat it does
schemaThe battery passport as JSON Schema, every field tagged with its content section.
coreCanonicalisation, salted per-section hashing, Merkle trees, proof verification. Runs unchanged in Node and the browser.
serverIssuing API and resolver: a QR scan in, the right view of the passport out.
adapter-vechainAnchors Merkle roots on VeChainThor. Behind an interface, so other chains or RFC 3161 timestamping can be added.
webThe in-browser verifier: every passport page checks itself against the chain without trusting the server that served it.

From a QR scan to an on-chain root.

Verification is a pure function. The same code runs on the server and in the visitor's browser, so a passport page can check itself without trusting the server that sent it.

  1. Browser to resolverRequests the passport for the caller's role.
  2. Resolver to browserReturns the readable sections' JSON and salts, the other sections' hashes, a Merkle proof and a transaction id.
  3. In the browserHashes its sections, combines the four section hashes into the leaf, folds the proof to a root.
  4. Browser to VeChain nodeReads the anchored root from the chain by transaction id.
  5. In the browserRoots match: verified.

Each content section is hashed separately, so a member of the public can verify the public view without learning anything about the restricted data.

Decisions we will not revisit.

Hashes on-chain, data off-chain
Only 32-byte Merkle roots are anchored. Nothing that could ever need erasing touches the ledger.
Every section verifiable on its own
Salted per-section hashes combine into one leaf. Each reader verifies exactly the sections they may see.
The operator owns the address
Passports resolve on the operator's own subdomain, so they outlive any host, including us.
Chain-agnostic by construction
One adapter package imports chain code. Nothing else may.
Standards over inventions
GS1 Digital Link identifiers, RFC 8785 canonical JSON, an RFC 6962 tree shape, field definitions from the regulation and DIN DKE SPEC 99100.
Spec first, with test vectors
The byte-exact spec and its vectors are normative. Code that disagrees with them is wrong.

Where things stand.

MilestoneScopeStatus
M0Battery passport schema with content sectionsDone
M1core: hashing, Merkle, verification, test vectorsDone
M2server: models, batch issuing, versions, PostgreSQLDone
M3adapter-vechain: registry contract, batched anchoring, fee delegationDone
M4Resolver, public passport page, in-browser verificationDone
M5Docker Compose, demo data, one-hour self-hosted setupDone

18 October 2026: the reference instance is live at id.passant.io, running v0.1.2 with 100 demo passports anchored on the VeChain testnet — scan one and verify it from your phone. v0.1.1 added the production Compose override with automatic TLS and a deployment guide; v0.1.2 fixes a start-up failure when VECHAIN_PUBLIC_NODE_URL is left empty. Next: v0.2, starting with EU registry identifier registration now that the registry API is live, and restricted-section access once the Article 77(9) implementing act is adopted.

30 September 2026: all six milestones of the MVP slice are complete and tagged v0.1.0 — issue a passport, anchor it on the VeChain testnet, scan the QR, and watch the page verify itself in your browser.

20 September 2026: the crypto spec was revised to passant-crypto/2 — four salted section hashes per passport version, one per Annex XIII content point, replacing the earlier three access-tier layout after the access rules of Regulation (EU) 2023/1542 were verified against the EUR-Lex text. Nothing had been anchored under the old layout, so nothing breaks.

Early, and open to help.

The most useful contributions right now are reviews of the crypto spec, checks of the schema against the regulation text, and independent implementations of the test vectors in other languages.

Contributions are accepted under Apache 2.0 with a lightweight contributor licence agreement.

Said up front.

OpenPassant is, and will stay, open source under Apache 2.0. The maintainers also plan a hosted service, Passant, for companies that would rather not run it themselves. The hosted service runs this same code. We are telling you now so it is never a surprise later.

About the hosted service at passant.io