Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
Added
- Nationality set-membership circuit (
nationality-set-verify): Prove nationality is in an allowed set (e.g., all 27 EU member states) without revealing which country- Template
NationalitySetVerify(32)supports up to 32 allowed nationality codes - ~389 non-linear constraints; fits in
pot12_final.ptau - Public signals (35 total):
allowedCodes[32],credentialHash,nonce,requestTimestamp - Product-of-differences membership check (
β(nationality - allowedCodes[i]) === 0) with multiplicative-inverse zero guard - Region constants:
REGION_EU,REGION_US,REGION_US_EU,REGION_EEAin@zk-id/core - Prover functions:
generateNationalitySetProof,generateNationalitySetProofAutoin@zk-id/core - Verifier functions:
verifyNationalitySetProof,validateNationalitySetProofConstraintsin@zk-id/core - Solidity verifier:
UnationalityUsetUverify.solin@zk-id/contracts - Portal homepage US/EU Residency card promoted from βin developmentβ to live feature
- Template
0.7.2 - 2026-02-11
Added
- Developer Portal & Playground (T-011): Complete Astro-based developer portal at zk-id.io
- Interactive playground with browser-based ZK proof generation
- 4-step quick start guide (issue β prove β verify in <5 minutes)
- Full API reference with TypeScript, Python, and Go examples
- 24 documentation pages with content collections
- Custom domain support (zk-id.io)
- Production API deployment on Railway
- GitHub Pages deployment with automatic builds on push
Security
- Fixed CORS misconfiguration (HIGH): Replaced origin reflection with exact matching, preventing CORS-based attacks
- Fixed XSS vulnerabilities: Replaced
innerHTMLwithDOMParserin playground and quick-start scripts - Pinned all GitHub Actions to commit SHAs in deploy-portal.yml workflow
- Hardened Dockerfile: Added non-root user, pinned base images by SHA256 digest
- Secured build scripts: Added path validation to prevent traversal in copy-docs.mjs
- Dismissed 7 binary artifact false positives (circuit WASM files)
Changed
- Updated CORS allowed origins to include
https://zk-id.io - Container now runs as
appuser instead of root - Improved error messaging in portal scripts with dedicated API error formatter
Infrastructure
- Configured custom domain (zk-id.io) with GitHub Pages
- Set up Railway deployment for API server
- Automated portal deployment on push to main
0.7.1 - 2026-02-09
Added
- On-chain Groth16 verifier (
@zk-id/contractspackage): Solidity BN128 pairing verifier, age + nationality verification contracts, deployment scripts - W3C VC interoperability:
toW3CVerifiableCredential,fromW3CVerifiableCredential,ed25519PublicKeyToDidKey,didKeyToEd25519PublicKeyin@zk-id/core - W3C VC documentation (
docs/W3C-VC-INTEROPERABILITY.md)
Security
- Hardened nonce store with configurable TTL and background pruning (
InMemoryNonceStoreOptions) - Added scope validation (
validateBigIntString,validateFieldElement) to nullifier prover - Grace period audit logging in
validateSignedCredentialBindingfor forensic completeness - Defensive try-catch around grace period audit logging
- Pinned
actions/upload-artifactGitHub Action to commit hash (supply chain security) - Removed unnecessary
security-events:writeCI permission - Fixed incomplete URL substring sanitization in tests
- Added npm overrides for lodash, tmp, cookie, undici vulnerabilities
Fixed
- Grace period in
validateSignedCredentialBindingnow emitsgrace_period_acceptaudit log entry - Cross-reference comments between dual grace period check locations
- Circuit artifact hashes updated to match CI build environment
Tests
- Grace period test for
validateSignedCredentialBindingpath - Nonce pruning and nullifier scope validation tests
0.7.0 - 2026-02-09
Added
- Comprehensive threat model (
docs/THREAT-MODEL.md) - Circuit signal flow diagrams (
docs/CIRCUIT-DIAGRAMS.md) - Production deployment guide (
docs/DEPLOYMENT.md) - Full JSDoc coverage and TypeDoc setup
- Error sanitization (
sanitizeError) to prevent information leakage - Configurable
verboseErrorsmode for debugging maxFutureSkewMsto prevent time-shifted proof attacks
Security
- TypeScript strict mode enabled for all tests
- Fixed time-shifted proof attack vector
- Fixed under-constrained circuit signals
Fixed
- v0.7.0 implementation gaps found in post-release review
0.6.0 - 2026-02-09
Security
- Constant-time comparisons β Added
timing-safe.tsmodule in@zk-id/coreconstantTimeEqual(): string comparison usingcrypto.timingSafeEqualconstantTimeArrayEqual(): XOR accumulation for array comparison- Applied at 6 locations: issuer public key verification (2Γ), merkle root checks (1Γ), nonce verification (3Γ)
- Prevents timing attacks on cryptographic comparisons
Fixed
- Test suite failures β Fixed 3 failing tests that were blocking CI
- Installed missing
@digitalbazaar/bbs-signaturesoptional dependency for BBS+ signature tests - Fixed boundary test commitment format to use BigInt-compatible numeric strings
- Fixed integration test nonce generation to use proper numeric values for circuit compatibility
- Installed missing
- Memory leak in
InMemoryNonceStoreβ ReplacedsetTimeoutleak with Map-based lazy expiry- Nonces now stored with expiration timestamps; expired entries cleaned up on access
- Added
prune()method for optional periodic cleanup - Pattern matches
InMemoryChallengeStorefor consistency
- GreaterEqThan bit-width inconsistency β Widened from 8 bits to 12 bits in all age circuits
- Prevents age overflow (8-bit limit was 0-255, now 0-4095)
- Matches
LessEqThan(12)used forbirthYearCheck - Affects
age-verify.circom,age-verify-signed.circom,age-verify-revocable.circom
Added
- KMS/HSM integration β
EnvelopeKeyManager,FileKeyManagerin@zk-id/issuerEnvelopeKeyManager: AES-256-GCM envelope encryption for Ed25519 private keys with seal/unseal workflowFileKeyManager: PEM file-based key loading withfromPemFiles()andfromPemStrings()constructors- Both implement
IssuerKeyManagerand work withManagedCredentialIssuer
- Issuer policy tooling β
IssuerPolicy,checkKeyRotation,validateIssuerPolicy,generateRotationPlanin@zk-id/issuerIssuerPolicyinterface with configurable key age limits, rotation windows, credential caps, and metadata requirementsDEFAULT_ISSUER_POLICY(365-day keys) andSTRICT_ISSUER_POLICY(180-day keys, metadata required)checkKeyRotation()returns rotation status with days-until-expiry and human-readable messagesvalidateIssuerPolicy()checks issuer records against policy with violations and warningsgenerateRotationPlan()produces a 4-step rotation schedule with ISO 8601 dates
- Issuer dashboard prototype β
IssuerDashboard,DashboardStats,IssuerSummaryin@zk-id/sdkIssuerDashboardaggregates stats from registry, audit log, and revocation store- Per-issuer summaries: status, key count, credentials issued/revoked, last issuance, jurisdiction
- Aggregate stats: active/suspended/revoked issuers, total credentials, revocation counts
trackIssuer()/untrackIssuer()for scoped monitoring
- ISO 18013-5/7 standards alignment β country code conversion, mDL element mapping, age-over attestation in
@zk-id/issuerISO_3166_NUMERIC_TO_ALPHA2/ISO_3166_ALPHA2_TO_NUMERICbidirectional country code tablestoMdlElements()converts aSignedCredentialto ISO 18013-5 mDL data elementscreateAgeOverAttestation()produces ISO 18013-7age_over_NNattestation objectsSTANDARDS_MAPPINGSarray documenting full zk-id β ISO 18013-5/7 concept mappingMDL_NAMESPACE,MDL_ELEMENTSconstants for programmatic access
- Multi-claim proof API β
createMultiClaimRequest,expandMultiClaimRequest,aggregateVerificationResultsin@zk-id/coreMultiClaimRequestbundles multiple claim specs (age, nationality) with a shared nonceexpandMultiClaimRequest()converts to individual proof requests for parallel provingaggregateVerificationResults()combines per-claim results into an overall pass/fail- Supports
age,nationality, andage-revocableclaim types
- Proving system abstraction β
ProvingSystem,Groth16ProvingSystem,PLONKProvingSystemin@zk-id/core- Unified
ProvingSysteminterface decoupling from Groth16-specific snarkjs API - Pluggable proving system registry with
registerProvingSystem()/getProvingSystem() PROVING_SYSTEM_COMPARISONdocumenting tradeoffs (trusted setup, proof size, verification time)- PLONK scaffold ready for universal SRS setup (no per-circuit ceremony)
- Unified
- Nullifier system for sybil resistance β
computeNullifier,createNullifierScope,consumeNullifier,InMemoryNullifierStorein@zk-id/core- Deterministic nullifier computation:
Poseidon(commitment, scopeHash)prevents double-use per scope - Scope-based isolation: actions are unlinkable across different scopes
NullifierStoreinterface for pluggable backend (in-memory, Redis, Postgres)- Follows same pattern as Worldcoin/Semaphore for sybil-resistant anonymous actions
- Deterministic nullifier computation:
- Nullifier Circom circuit β
nullifier.circomwithNullifierComputetemplate in@zk-id/circuits- Two Poseidon constraints: credential binding + nullifier computation
- Public signals:
credentialHash,scopeHash,nullifier - Private inputs:
birthYear,nationality,salt - ~792 constraints (fits in small ptau 2^12)
- Prover functions:
generateNullifierProof(),generateNullifierProofAuto()in@zk-id/core - Verifier function:
verifyNullifierProof()integrated intoverifyBatch()in@zk-id/core - Full test coverage in
packages/circuits/test/nullifier.test.js
- Recursive proof aggregation scaffold β
LogicalAggregator,RecursiveAggregator,AggregatedProofin@zk-id/coreRecursiveAggregatorinterface for pluggable recursive proof backendsLogicalAggregatorpass-through implementation (bundles proofs without recursion)RECURSIVE_PROOF_STATUSdocumenting implementation state for Groth16-in-Groth16, Nova, and Halo2- Helpers:
createAggregateInput(),isRecursiveProof(),getConstituentPublicSignals()
- BBS selective disclosure β
generateBBSKeyPair,signBBSMessages,deriveBBSDisclosureProof,verifyBBSDisclosureProofin@zk-id/core- BBS signatures (BLS12-381-SHA-256 ciphersuite) per IETF draft-irtf-cfrg-bbs-signatures
- Credential fields signed as individual BBS messages enabling per-field selective disclosure
credentialFieldsToBBSMessages()encodes credential fields in canonical orderserializeBBSProof()/deserializeBBSProof()for JSON-safe transportgetDisclosedFields()extracts revealed field values from a disclosure proof- Complementary to ZK-SNARK predicates: BBS for βreveal field Xβ, SNARKs for βprove age >= 18β
- BBS credential issuer β
BBSCredentialIssuerin@zk-id/issuer- Issues credentials with BBS signatures (BLS12-381) instead of Ed25519
- Each credential field (id, birthYear, nationality, salt, issuedAt, issuer) is a separate BBS message
- Holders can derive selective disclosure proofs without issuer interaction
- Full audit logging with signature scheme metadata
- Unified revocation manager β
UnifiedRevocationManagerin@zk-id/core- Two cleanly separated stores:
ValidCredentialTree(Merkle tree),IssuedCredentialIndex(append-only) addCredential()writes to both tree and issued index;revokeCredential()removes from tree only (issued index is append-only)- Three-way
CredentialStatus:'valid'(in tree),'revoked'(was issued, removed from tree),'unknown'(never issued) getStatus(),isValid(),isRevoked(),reactivateCredential()with precise lifecycle semantics- Tree accessors:
getRoot(),getRootInfo(),getWitness(),validCount(),issuedCount() InMemoryIssuedCredentialIndexreference implementation; production should use persistent store
- Two cleanly separated stores:
- Sparse Merkle tree β
SparseMerkleTreein@zk-id/core- Hash-addressed leaves (
poseidonHash(commitment) mod 2^depth) β deterministic, reproducible across nodes - Sparse storage: O(n Γ depth) nodes instead of O(2^depth) pre-allocation
- Non-membership proofs via
getNonMembershipWitness()β proves a commitment is NOT in the tree - Drop-in
ValidCredentialTreeimplementation, works withUnifiedRevocationManager - Configurable depth 1β254 (BN128 field); default 20
- Hash-addressed leaves (
- Proof type discriminators β
proofTypefield on all 5 proof interfacesAgeProof.proofType: 'age',NationalityProof.proofType: 'nationality'AgeProofRevocable.proofType: 'age-revocable',AgeProofSigned.proofType: 'age-signed',NationalityProofSigned.proofType: 'nationality-signed'ZkProofdiscriminated union type andProofTypestring literal typeverifyBatch()now dispatches onproof.proofType(no separatetypeparameter needed)- All 5 proof types including signed variants supported in batch verification
- Boundary and concurrency tests β comprehensive edge case coverage
- Tree boundary: depth=1 (2 leaves), full tree, empty tree, idempotent add/remove, witness round-trip verification
- Concurrency: parallel adds, mixed add+remove, concurrent revocations, concurrent witness generation
- Poseidon hash edge cases, credential creation boundary values
- Pre-audit checklist β
docs/AUDIT.mdcovering circuits, crypto primitives, API security, code quality docs/STANDARDS.mddocumenting ISO 18013-5/7 mapping, privacy comparison, and architectural differences- 130+ new tests across all new modules
Security
- Fixed credential signature binding β
credentialSignaturePayloadnow includes issuer identity and issuance timestamp in the signed payload, preventing issuer substitution attacks where an attacker could swap theissuerfield on aSignedCredentialwithout invalidating the signature
Changed
- Revocation model β replaced dual blacklist+whitelist with two-store architecture (tree + issued index);
RevocationStorekept as standalone for consumers who need it verifyBatch()now dispatches onproof.proofTypediscriminated union (no separatetypeparameter needed)- Eliminated all
anytypes across core, issuer, SDK, and redis packages - Added input validation module with domain-specific validators (birth year, nationality, commitment format)
- Bumped all package versions from 0.5.0 to 0.6.0
0.5.0 - 2026-02-09
Added
- Distributed tree sync β
RedisTreeSyncChannelandSyncedValidCredentialTreein@zk-id/redis- Redis pub/sub channel for broadcasting tree mutation events across server nodes
SyncedValidCredentialTreewrapper that publishes root updates onadd()/remove()and notifies on remote changes- Self-notification deduplication via per-node
nodeId onRemoteUpdatecallback for cache invalidation when remote nodes mutate the tree
- Browser wallet β
BrowserWallet,CredentialStore,IndexedDBCredentialStore,InMemoryCredentialStorein@zk-id/sdkCredentialStoreinterface for pluggable persistent credential storageIndexedDBCredentialStorefor real browser environmentsInMemoryCredentialStorefor testing and Node.jsBrowserWalletimplementingWalletConnectorwith credential lifecycle management (add, remove, list, get)- Auto-select most recently issued credential, or user-provided
onProofRequestcallback for consent UI - JSON export/import for single credential backup and full wallet backup/restore
- Support for age, nationality, and age-revocable proof generation
- 43 new tests (15 for distributed tree sync, 28 for browser wallet)
- Performance benchmarks β
runBenchmark,checkTarget,formatResult,PERFORMANCE_TARGETSin@zk-id/core- Lightweight benchmark runner with warmup, per-iteration timing, and statistical aggregation (avg, median, p95, min, max, ops/s)
- 16 predefined performance targets covering Poseidon hashing, credential creation, Merkle tree operations, constraint validation, proof generation, and proof verification
checkTarget()for automated pass/fail checks against performance targetsformatResult()for human-readable benchmark output- Benchmark test suite validating Poseidon hash, credential creation, Merkle tree, and constraint validation against targets
docs/BENCHMARKS.mddocumenting all targets, methodology, and browser considerations
- Protocol deprecation policy β
DEPRECATION_SCHEDULE,DEPRECATION_POLICY,getVersionStatus,isVersionDeprecated,isVersionSunset,buildDeprecationHeadersin@zk-id/core- Three-stage lifecycle: Active β Deprecated β Sunset
- 90-day minimum deprecation window with 60-day recommended migration lead time
- Machine-readable
DEPRECATION_SCHEDULEfor programmatic enforcement buildDeprecationHeaders()for RFC 8594 compliantDeprecation,Sunset, andLinkHTTP headers- Protocol specification updated with deprecation rules and HTTP signaling documentation
- 28 new tests (10 for benchmarks, 18 for deprecation policy)
0.4.5 - 2026-02-09
Added
isWitnessFresh()helper method toZkIdClientfor checking witness staleness against current root- Incremental Merkle tree optimization for
InMemoryValidCredentialTreewith cached layers and path-only updates - Layer caching with invalidation to
PostgresValidCredentialTreefor improved read performance - Pre-computed zero hashes for efficient tree initialization
Changed
- Optimized
InMemoryValidCredentialTreefrom O(2^depth) per query to O(depth) per mutation and O(1) per read - Optimized
PostgresValidCredentialTreewith in-memory layer cache (first query loads, mutations update incrementally) - Workspace build order now sequential to prevent race conditions in CI
Removed
- Dead
RevocationAccumulatorscaffold code (unused interface and implementation) - Old
buildLayers()method fromPostgresValidCredentialTree(replaced byrebuildCache())
Performance
- At depth 10:
getRoot()reduced from 2047 Poseidon hashes to 0 (cached) - At depth 10:
add()/remove()now performs 10 Poseidon hashes (incremental path update) - At depth 10:
getWitness()reduced from 2047 Poseidon hashes to 0 (array lookups)
Docs
- Updated ARCHITECTURE.md with comprehensive revocation system documentation
- Clarified two-layer revocation model (blacklist + ZK Merkle whitelist)
- Documented circuit integration, root distribution, and privacy properties
0.4.4 - 2026-02-08
Removed
- Legacy server-side demo endpoints for proof generation (client-side generation is now the default)
- Dead imports and unused circuit path constants from web app server
- Signed circuit setup from build process (superseded by revocable proofs)
Docs
- Updated README to clarify client-side proof generation workflow
- Removed references to server-side proof endpoints from OpenAPI spec
0.4.3 - 2026-02-08
Added
- Browser-side ZK proof generation using snarkjs and WASM
- Client-side proof generation UI with progress indicators
- Circuit artifact streaming via CDN for in-browser proof generation
Changed
- Web demo now generates all proofs client-side (browser) instead of server-side
0.4.2 - 2026-02-08
Added
- Protocol version parsing + compatibility helpers in core with SDK enforcement policies
- Server-side protocol enforcement in SDK (strict/warn/off) and demo server header handling
- Revocation root metadata helpers (
getRevocationRootInfo/fetchRevocationRootInfo) and demo endpoint - Postgres-backed valid-credential tree implementation (
PostgresValidCredentialTree) - Demo API rate limiting (express-rate-limit)
- Issue templates, PR template, and CODEOWNERS
Security
- Temporary mitigation for elliptic ECDSA issue via override to patched fork (pending upstream release)
Docs
- Added protocol header CORS guidance and clarified supported vs. future use cases
- Expanded revocation/production storage examples
0.4.1 - 2026-02-08
Fixed
- Stabilized valid-credential tree indexing to avoid witness invalidation on removals
- Normalized commitment keys in valid-credential tree lookups
- Enforced Merkle root freshness checks for revocable proofs even when expected root is
'0'
Tests
- Added coverage for commitment normalization in valid-credential tree
- Added revocable Merkle root mismatch coverage in core + SDK
0.4.0 - 2026-02-08
Added
AgeProofRevocabletype andValidCredentialTreeinterface in@zk-id/coreInMemoryValidCredentialTreeclass (Poseidon Merkle tree with valid-set semantics)generateAgeProofRevocableandgenerateAgeProofRevocableAutoprover functionsverifyAgeProofRevocableandvalidateAgeProofRevocableConstraintsverifier functionsverifyBatch()support for'age-revocable'proof type- SDK server: revocable verification key config, policy enforcement, Merkle root freshness check
- SDK client:
verifyAgeRevocable()method and revocable circuit path config - Web app: credential lifecycle hooks and
POST /api/demo/verify-age-revocableendpoint - 17 new tests across core and SDK packages
0.3.0 - 2026-02-07
Added
- Merkle inclusion circuit (
age-verify-revocable) for credential validity (non-revocation) - In-memory Merkle revocation accumulator scaffold
- CI/CD workflow for circuit building and GitHub releases
- Stub test scripts in examples to fix CI
Fixed
- CI circom installation by disabling strict rustflags
- Mocha
--exitflag to prevent CI hangs
0.2.0 - 2026-02-07
Added
- CHANGELOG.md following Keep a Changelog format
- Package metadata for npm publishing (license, repository, files, exports) to all packages
- Build step to CI pipeline before tests
.nvmrcfile specifying Node 20- TODO comment in
.gitignoreabout tracked circuit build artifacts - Author field in root package.json
Changed
- Bumped all package versions from 0.1.0 to 0.2.0
- Updated internal dependency ranges to ^0.2.0
Removed
- Internal security review notes (findings already addressed in code)
0.1.1 - 2025-01-XX
Fixed
- High-severity snarkjs vulnerability (CVE-2024-45811) by upgrading to 0.7.6
- Nonce generation for signed credentials now uses BigInt to handle values > Number.MAX_SAFE_INTEGER
- Revocation logic for signed credentials (issuer secret was incorrectly included in proof)
Security
- Upgraded snarkjs from 0.7.0 to 0.7.6 to address critical security vulnerability
0.1.0 - 2025-01-XX
Added
- Initial release of zk-id system
@zk-id/core- Core cryptographic primitives and proof generation@zk-id/sdk- Client-side SDK for web integration@zk-id/issuer- Credential issuance service@zk-id/circuits- Zero-knowledge circuits for identity verification- Age verification circuit with range proofs
- Signed credential system with revocation support
- Poseidon hash-based commitments
- Example applications:
- Age gate demo
- Credential format comparison
- Full web application with issuer and verifier
- Comprehensive documentation and README