v7.0.0
7.0.0 (2026-05-10)
First release after a 5-year dormancy. v7 is a near-total rewrite aimed
at correctness, security, and keeping this library cheap to maintain
going forward. Everything below ships as one major bump.
⚠ BREAKING CHANGES
Relicense under AGPL-3.0-or-later
These affect every consumer. Plan the upgrade before running
npm update.
- Node.js ≥ 24.12.0 required (was
>=10.10). Locked to the Node
release where native TypeScript type-stripping andnode:testare
both stable. - Pure ESM only. The package is
"type": "module"with an
"exports"map. CommonJS consumers must migrate to
await import('@walletpass/pass-js'). No more
require('@walletpass/pass-js'). Template.certificateshape changed from aforge.pki.Certificate
object to a PEM string. Only matters if you read the field
directly. Public API methods (setCertificate,setPrivateKey,
loadCertificate) are unchanged.Template.keyshape changed from aforge.pki.PrivateKeyto a
node:cryptoKeyObject.NFCField.setPublicKey(key)takes PEM only. The previously
undocumentedforge.pki.PublicKeyobject form is gone (it was
unusable withoutnode-forgeanyway).setPrivateKeynow rejects non-RSA keys at load time with a
clearTypeError. Apple Pass Type ID certificates are always RSA;
this prevents producing signatures that Wallet silently rejects.- Unusual ZIP compression methods in
Template.fromBuffer
(BZIP2, LZMA, AES-encrypted) that happened to work with the old
yauzldep now throw. Standard pkpass bundles are unaffected — STORE
and DEFLATE are the only methods Apple emits. Template.fromBufferis stricter aboutpass.json: exactly one
is required; archives with zero or multiple now throw instead of
silently picking a decoy or returning an empty template.
🔐 Security + correctness
- Apple WWDR certificate rotated to G4 (valid through 2030-12-10).
The bundled G1 cert expired 2023-02-07; anything built with 6.9.x or
earlier has been silently shipping invalid passes for years. Closes
#616. Thanks to
@lucyyyyyyy whose 2022 PR
#626 supplied the
correct cert. - New runtime warning
WALLETPASS_WWDR_EXPIRING/
WALLETPASS_WWDR_EXPIREDemitted viaprocess.emitWarningwhen
the bundled cert is within 90 days of expiry. Silence with
node --disable-warning=WalletPassWWDRExpiring, or intercept with
process.on('warning', ...). Designed to prevent the next silent
rotation from recurring. node-forgedropped entirely. Six CVEs disclosed Nov 2025 –
early 2026 against ≤ 1.3.1 (incl. CVE-2025-12816 / CVSS 9.3 ASN.1
desync, CVE-2026-33894 RSA signature forgery). Replaced with
pkijs3.4 +
node:cryptofor PKCS#7 SignedData. Signatures verified end-to-end
againstopenssl cms -verify.
🧰 Toolchain rewrite
The devDependency footprint went from 13 packages to 5. None of these
affect what ships on npm — they're the local dev workflow only.
| Was | Is |
|---|---|
typescript@4.4 |
@typescript/native-preview (tsgo, the Go rewrite) |
eslint@8 + @typescript-eslint@5 + prettier |
oxlint (type-aware via oxlint-tsgolint) + oxfmt |
jest@27 + ts-jest + jest-extended + jest-junit + @types/jest |
Native node:test + node:assert/strict |
husky + lint-staged |
hk — uses Git 2.53 native hooks |
renovate.json |
.github/dependabot.yml |
(legacy npm publish with token) |
npm Trusted Publishing via OIDC — no long-lived secrets, provenance emitted automatically |
Test suite migrated to node:test: 84 tests, 83 passing, 1 skipped
(live-APNs opt-in). Coverage now uploaded to Codecov via OIDC — also
tokenless.
📦 Dependencies pruned
Dropped or inlined. Every one shaves external surface area and
supply-chain risk:
node-forge→pkijs+node:crypto(see above).yauzl+@types/yauzl+event-iterator→ ~200-LOC
in-repo ZIP reader + STORE writer atsrc/lib/zip.ts.do-not-zip(unmaintained since 2018) → folded into the same
in-repo zip module.buffer-crc32→ CRC-32 table inlined intozip.ts(~10 LOC).imagesize(created 2013, last touched 2022, still used
deprecatednew Buffer()) → ~30-LOC PNG-only dimensions reader at
src/lib/png-size.tscovering exactly what pkpass needs.
Runtime dependency count: 4
(pkijs,
color-name,
strip-json-comments,
plus pkijs's one transitive asn1js).
Every prod dep is pure JavaScript — no native addons, so the library
bundles cleanly with esbuild / @vercel/ncc / rollup. CI verifies this
on every PR via a single-file bundle smoke test.
✨ New features
- iOS 18 semantic tags (
semanticson passes and individual
fields). Closes #75
— a 6-year-old feature request. Re-applies PR
#657 by
@apples-kksk. Includes recursive
Date→ W3C string normalization with cycle detection. - iOS 18 poster event tickets:
preferredStyleSchemes,
relevantDates(replacing the singularrelevantDate, now
deprecated). appLaunchURL+ NFCrequiresAuthentication. Re-applies PR
#652 by
@navelencia. TheappLaunchURL
field was previously in the types but never serialized — pure bug
fix.Options.disableImageCheck. Closes
#480. Skips PNG
dimension validation when you're generating passes with
intentionally non-standard icon sizes. Re-applies PR
#479 by
@akoufa.
🧹 Other fixes worth calling out
- Cross-platform
.stringsoutput bug (pre-existing since 2018).
localizations.tssubstitutedos.EOLwhen encoding/decoding\n
escapes, silently producing different.pkpassbundles on Windows
vs. macOS/Linux. The previous Jest test matrix only ran on macOS, so
no one noticed. Fixed to always emit\nper Apple's spec. - Validator/parser mismatches in
isValidW3CDateString: theZ
branch wasn't anchored to end-of-string (so trailing garbage
validated); timezone-minute class only allowed:00and:30(so
+05:45/+12:45/other real offsets failed). - Bundle ZIP paths with leading
/,\, or..are now rejected
by the writer, matching the reader. pass.jsonselection inTemplate.fromBufferno longer matches
notpass.json.NFCField.setPublicKeynow validates the curve isprime256v1
(P-256 — what Apple requires) rather than accepting any EC key.
🤖 AI-friendly maintenance
CLAUDE.md+AGENTS.mdat repo root with architecture map,
non-obvious gotchas, WWDR rotation checklist, and CI debugging
cheatsheet.CONTRIBUTING.mddocuments Conventional Commits rules,
quality-gate commands, and scope guidance.- Code complexity metrics posted on every PR via
ophidiarium/mehen. release-pleaseautopilot — this release itself was produced by
merging the automated release PR; future releases work the same way.
PR backlog cleared
Every open PR at the start of v7 work was triaged. Community feature
PRs were re-applied on top of the new toolchain with credit preserved
in the commit history; stale renovate bumps and obsolete Snyk PRs were
closed with explanatory comments.
Full detail in PR #658
(14 commits, 40+ review comments addressed across 22 threads).